diff --git a/backend/script_groups/XML Parser to SCL/.example/FB_HMI_Interlock.scl b/backend/script_groups/XML Parser to SCL/.example/FB_HMI_Interlock.scl
index 1d58efd..ec5a092 100644
--- a/backend/script_groups/XML Parser to SCL/.example/FB_HMI_Interlock.scl
+++ b/backend/script_groups/XML Parser to SCL/.example/FB_HMI_Interlock.scl
@@ -54,7 +54,7 @@ BEGIN
// Cancel the actual use after timeout
#Status.HMI_InUse := FALSE;
FOR #i:=0 TO 7 DO
- #Status.o_HMI_CanBeUsedBy[] := FALSE;
+ #Status.o_HMI_CanBeUsedBy[#i] := FALSE;
END_FOR;
END_IF;
IF NOT #Status.HMI_InUse THEN
@@ -65,7 +65,7 @@ BEGIN
END_IF;
IF #Status.HMI_CanBeUsedBy > 0 AND #Status.i_Request[#Status.HMI_CanBeUsedBy] THEN
// Return that the actual request is active and can use the HMI
- #Status.o_HMI_CanBeUsedBy[] := TRUE;
+ #Status.o_HMI_CanBeUsedBy[#Status.HMI_CanBeUsedBy] := TRUE;
#Status.HMI_InUse := TRUE;
END_IF;
END_IF;
@@ -73,7 +73,7 @@ BEGIN
// request on every cycle until is granted.
//
FOR #i:=0 TO 7 DO
- #Status.i_Request[] := FALSE;
+ #Status.i_Request[#i] := FALSE;
END_FOR;
#Status.o_HMI_CanBeUsedBy[0] := NOT #Status.HMI_InUse;
diff --git a/backend/script_groups/XML Parser to SCL/debug_array_parsing.py b/backend/script_groups/XML Parser to SCL/debug_array_parsing.py
new file mode 100644
index 0000000..899aabe
--- /dev/null
+++ b/backend/script_groups/XML Parser to SCL/debug_array_parsing.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Script de debugging para entender por qué los índices de array simples como [#i] no funcionan
+"""
+
+import os
+import sys
+from lxml import etree
+
+# Agregar el directorio del script al path
+script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(script_dir)
+
+from parsers.parse_scl import reconstruct_scl_from_tokens
+from parsers.parser_utils import ns
+
+
+def debug_array_parsing():
+ """Función para debug del parsing de arrays"""
+
+ # Cargar el archivo XML problemático
+ xml_path = r"D:\Trabajo\VM\45 - HENKEL - VM Auto Changeover\ExportTia\PLC_TL27_Q1\ProgramBlocks_XML\FB HMI Interlock.xml"
+
+ print(f"Cargando XML: {xml_path}")
+
+ # Cargar y parsear el XML
+ with open(xml_path, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ root = etree.fromstring(content)
+
+ # Buscar todas las instancias de i_Request
+ print("\n=== Buscando todas las instancias de i_Request ===")
+
+ # Buscar elementos Component con Name="i_Request"
+ components = root.xpath("//Component[@Name='i_Request']")
+
+ print(f"Encontrados {len(components)} componentes i_Request")
+
+ # Si no encontramos nada, buscar de manera más amplia
+ if len(components) == 0:
+ print("No se encontraron con XPath directo, buscando de manera recursiva...")
+ components = []
+ for elem in root.iter():
+ if elem.tag.endswith("Component") and elem.get("Name") == "i_Request":
+ components.append(elem)
+ print(
+ f"Encontrados {len(components)} componentes i_Request con búsqueda recursiva"
+ )
+
+ for i, comp in enumerate(components):
+ print(f"\n--- Componente {i+1} (UId={comp.get('UId')}) ---")
+
+ # Obtener todos los hijos
+ children = comp.xpath("./*")
+ print(f"Número de hijos: {len(children)}")
+
+ for j, child in enumerate(children):
+ tag = etree.QName(child.tag).localname
+ print(f" Hijo {j}: {tag}")
+ if tag == "Token":
+ print(f" Text: '{child.get('Text')}'")
+ elif tag == "Access":
+ print(f" Scope: '{child.get('Scope')}'")
+ # Buscar Symbol dentro del Access
+ symbols = child.xpath(".//Symbol")
+ if symbols:
+ symbol_components = symbols[0].xpath(".//Component")
+ for sc in symbol_components:
+ print(f" Component: '{sc.get('Name')}'")
+
+ # Verificar si este componente tiene patrón de array
+ has_array_pattern = False
+ bracket_start_idx = -1
+ bracket_end_idx = -1
+
+ # Buscar los tokens [ y ]
+ for idx, child in enumerate(children):
+ tag = etree.QName(child.tag).localname
+ if tag == "Token":
+ text = child.get("Text")
+ if text == "[" and bracket_start_idx == -1:
+ bracket_start_idx = idx
+ elif text == "]" and bracket_start_idx != -1:
+ bracket_end_idx = idx
+ break
+
+ if bracket_start_idx != -1 and bracket_end_idx != -1:
+ has_array_pattern = True
+ print(
+ f" Corchetes encontrados en índices: {bracket_start_idx} y {bracket_end_idx}"
+ )
+
+ print(f" Tiene patrón de array: {has_array_pattern}")
+
+ if has_array_pattern:
+ print(" >>> Este componente debería ser procesado como array <<<")
+
+ # Simular el procesamiento manual del Access del medio
+ for middle_idx in range(bracket_start_idx + 1, bracket_end_idx):
+ middle_child = children[middle_idx]
+ child_tag = etree.QName(middle_child.tag).localname
+ print(f" Procesando elemento medio {middle_idx}: {child_tag}")
+ if child_tag == "Access":
+ scope = middle_child.get("Scope")
+ print(f" Scope: {scope}")
+
+ if scope == "LocalVariable":
+ print(" >>> Es LocalVariable, procesando manualmente <<<")
+
+ # Debug: mostrar toda la estructura del Access
+ print(" Estructura completa del Access:")
+
+ def print_xml_structure(elem, indent=" "):
+ tag = etree.QName(elem.tag).localname
+ attrs = dict(elem.attrib)
+ print(f"{indent}{tag}: {attrs}")
+ for child in elem:
+ print_xml_structure(child, indent + " ")
+
+ print_xml_structure(middle_child)
+
+ # Buscar Symbol con diferentes métodos
+ symbol_elem_ns = middle_child.xpath(
+ "./st:Symbol", namespaces=ns
+ )
+ symbol_elem_no_ns = middle_child.xpath("./Symbol")
+ symbol_elem_recursive = []
+ for child in middle_child:
+ if etree.QName(child.tag).localname == "Symbol":
+ symbol_elem_recursive.append(child)
+
+ print(f" Symbol con namespace: {len(symbol_elem_ns)}")
+ print(f" Symbol sin namespace: {len(symbol_elem_no_ns)}")
+ print(
+ f" Symbol recursivo manual: {len(symbol_elem_recursive)}"
+ )
+
+ # Usar el método que funcione
+ symbol_elem = None
+ if symbol_elem_ns:
+ symbol_elem = symbol_elem_ns
+ print(" Usando Symbol con namespace")
+ elif symbol_elem_no_ns:
+ symbol_elem = symbol_elem_no_ns
+ print(" Usando Symbol sin namespace")
+ elif symbol_elem_recursive:
+ symbol_elem = symbol_elem_recursive
+ print(" Usando Symbol recursivo manual")
+
+ if symbol_elem:
+ print(
+ f" Procesando Symbol (total: {len(symbol_elem)})"
+ )
+
+ # Buscar componentes dentro del Symbol
+ components_inner_ns = symbol_elem[0].xpath(
+ "./st:Component", namespaces=ns
+ )
+ components_inner_no_ns = symbol_elem[0].xpath("./Component")
+ components_inner_manual = []
+ for child in symbol_elem[0]:
+ if etree.QName(child.tag).localname == "Component":
+ components_inner_manual.append(child)
+
+ print(
+ f" Componentes con namespace: {len(components_inner_ns)}"
+ )
+ print(
+ f" Componentes sin namespace: {len(components_inner_no_ns)}"
+ )
+ print(
+ f" Componentes manual: {len(components_inner_manual)}"
+ )
+
+ # Usar el método que funcione
+ components_inner = None
+ if components_inner_ns:
+ components_inner = components_inner_ns
+ print(" Usando componentes con namespace")
+ elif components_inner_no_ns:
+ components_inner = components_inner_no_ns
+ print(" Usando componentes sin namespace")
+ elif components_inner_manual:
+ components_inner = components_inner_manual
+ print(" Usando componentes manual")
+
+ if components_inner:
+ print(
+ f" Componentes internos encontrados: {len(components_inner)}"
+ )
+ result_parts = []
+ for k, comp_inner in enumerate(components_inner):
+ name = comp_inner.get("Name", "_ERR_COMP_")
+ print(f" Componente {k}: '{name}'")
+ if k == 0:
+ result_parts.append(f"#{name}")
+ print(f" -> Se convertirá en: #{name}")
+ else:
+ result_parts.append(f".{name}")
+ print(f" -> Se convertirá en: .{name}")
+ final_result = "".join(result_parts)
+ print(
+ f" >>> RESULTADO FINAL: '{final_result}' <<<"
+ )
+ else:
+ print(" ERROR: No se encontraron componentes")
+ else:
+ print(
+ " ERROR: No se encontró Symbol dentro del Access"
+ )
+
+ print("\n=== Fin del análisis ===")
+
+
+if __name__ == "__main__":
+ debug_array_parsing()
diff --git a/backend/script_groups/XML Parser to SCL/parsers/parse_scl.py b/backend/script_groups/XML Parser to SCL/parsers/parse_scl.py
index 19c9239..e69a580 100644
--- a/backend/script_groups/XML Parser to SCL/parsers/parse_scl.py
+++ b/backend/script_groups/XML Parser to SCL/parsers/parse_scl.py
@@ -140,146 +140,139 @@ def reconstruct_scl_from_tokens(st_node):
# --- Array Index Access ---
# Verificar si este componente tiene hijos que indican acceso de array
- # Buscar estructura:
+ # Buscar estructura: [algún contenido]
children = comp.xpath("./*") # Todos los hijos directos
- if len(children) >= 3:
- # Verificar patrón: primer hijo es Token "[", último es Token "]"
- first_child = children[0]
- last_child = children[-1]
- first_is_open_bracket = (
- etree.QName(first_child.tag).localname == "Token"
- and first_child.get("Text") == "["
- )
- last_is_close_bracket = (
- etree.QName(last_child.tag).localname == "Token"
- and last_child.get("Text") == "]"
- )
+ # Buscar los tokens [ y ] en cualquier posición
+ bracket_start_idx = -1
+ bracket_end_idx = -1
- if first_is_open_bracket and last_is_close_bracket:
- # Hay acceso de array - procesar los elementos entre los corchetes
- indices_parts = []
+ for idx, child in enumerate(children):
+ child_tag = etree.QName(child.tag).localname
+ if child_tag == "Token":
+ text = child.get("Text")
+ if text == "[" and bracket_start_idx == -1:
+ bracket_start_idx = idx
+ elif text == "]" and bracket_start_idx != -1:
+ bracket_end_idx = idx
+ break
- # Mark the bracket tokens and middle elements as processed
- first_uid = first_child.get("UId")
- last_uid = last_child.get("UId")
- if first_uid:
- processed_elements.add(first_uid)
- if last_uid:
- processed_elements.add(last_uid)
+ if bracket_start_idx != -1 and bracket_end_idx != -1:
+ # Hay acceso de array - procesar los elementos entre los corchetes
+ indices_parts = []
- for middle_child in children[
- 1:-1
- ]: # Todo excepto primer y último hijo
- middle_uid = middle_child.get("UId")
- if middle_uid:
- processed_elements.add(middle_uid)
+ # Mark the bracket tokens and middle elements as processed
+ start_token = children[bracket_start_idx]
+ end_token = children[bracket_end_idx]
+ start_uid = start_token.get("UId")
+ end_uid = end_token.get("UId")
+ if start_uid:
+ processed_elements.add(start_uid)
+ if end_uid:
+ processed_elements.add(end_uid)
- child_tag = etree.QName(middle_child.tag).localname
- if child_tag == "Access":
- # Procesar el Access para obtener el índice
- scope = middle_child.get("Scope")
- if scope == "LiteralConstant":
- # Buscar el valor de la constante - tanto con namespace como sin namespace
+ for middle_idx in range(
+ bracket_start_idx + 1, bracket_end_idx
+ ):
+ middle_child = children[middle_idx]
+ middle_uid = middle_child.get("UId")
+ if middle_uid:
+ processed_elements.add(middle_uid)
+
+ child_tag = etree.QName(middle_child.tag).localname
+ if child_tag == "Access":
+ # Procesar el Access para obtener el índice
+ scope = middle_child.get("Scope")
+ if scope == "LiteralConstant":
+ # Buscar el valor de la constante - tanto con namespace como sin namespace
+ constant_elem = middle_child.xpath(
+ "./st:Constant", namespaces=ns
+ )
+ if not constant_elem:
constant_elem = middle_child.xpath(
- "./st:Constant", namespaces=ns
+ "./Constant"
)
- if not constant_elem:
- constant_elem = middle_child.xpath(
- "./Constant"
- )
- if constant_elem:
- # Buscar ConstantValue tanto con namespace como sin namespace
+ if constant_elem:
+ # Buscar ConstantValue tanto con namespace como sin namespace
+ val_nodes = constant_elem[0].xpath(
+ "./st:ConstantValue", namespaces=ns
+ )
+ if not val_nodes:
val_nodes = constant_elem[0].xpath(
- "./st:ConstantValue", namespaces=ns
+ "./ConstantValue"
)
- if not val_nodes:
- val_nodes = constant_elem[0].xpath(
- "./ConstantValue"
- )
- if val_nodes and val_nodes[0].text:
- indices_parts.append(
- val_nodes[0].text.strip()
- )
- else:
- # Para otros tipos de acceso, procesar manualmente en lugar de recursión
- if (
- middle_child.get("Scope")
- == "LocalVariable"
- ):
- # Procesar LocalVariable manualmente
+ if val_nodes and val_nodes[0].text:
+ indices_parts.append(
+ val_nodes[0].text.strip()
+ )
+ else:
+ # Para otros tipos de acceso, procesar manualmente en lugar de recursión
+ if middle_child.get("Scope") == "LocalVariable":
+ # Procesar LocalVariable manualmente
+ symbol_elem = middle_child.xpath(
+ "./st:Symbol", namespaces=ns
+ )
+ if not symbol_elem:
symbol_elem = middle_child.xpath(
- "./st:Symbol", namespaces=ns
+ "./Symbol"
)
- if not symbol_elem:
- symbol_elem = middle_child.xpath(
- "./Symbol"
- )
- if symbol_elem:
+ if symbol_elem:
+ components = symbol_elem[0].xpath(
+ "./st:Component", namespaces=ns
+ )
+ if not components:
components = symbol_elem[0].xpath(
- "./st:Component", namespaces=ns
+ "./Component"
)
- if not components:
- components = symbol_elem[
- 0
- ].xpath("./Component")
- # Construir la variable manualmente
- var_parts = []
- for i, comp in enumerate(
- components
- ):
- name = comp.get(
- "Name", "_ERR_COMP_"
- )
- if i == 0:
- var_parts.append(
- f"#{name}"
- ) # Primer componente con #
- else:
- var_parts.append(
- f".{name}"
- ) # Componentes subsecuentes con .
-
- idx_result = "".join(var_parts)
- if idx_result:
- indices_parts.append(idx_result)
+ # Construir la variable manualmente
+ var_parts = []
+ for i, comp in enumerate(components):
+ name = comp.get(
+ "Name", "_ERR_COMP_"
+ )
+ if i == 0:
+ var_parts.append(
+ f"#{name}"
+ ) # Primer componente con #
else:
- indices_parts.append(
- "/*_ERR_EMPTY_VAR_*/"
- )
+ var_parts.append(
+ f".{name}"
+ ) # Componentes subsecuentes con .
+
+ idx_result = "".join(var_parts)
+ if idx_result:
+ indices_parts.append(idx_result)
else:
indices_parts.append(
- "/*_ERR_NO_SYMBOL_*/"
+ "/*_ERR_EMPTY_VAR_*/"
)
else:
- # Para otros scopes, usar recursión como fallback
- idx_result = (
- reconstruct_scl_from_tokens(
- middle_child
- )
+ indices_parts.append(
+ "/*_ERR_NO_SYMBOL_*/"
)
- if idx_result and idx_result.strip():
- indices_parts.append(
- idx_result.strip()
- )
- else:
- indices_parts.append(
- "/*_ERR_RECURSIVE_EMPTY_*/"
- )
- elif child_tag == "Token":
- # Token de separación (como ",")
- token_text = middle_child.get("Text", "")
- if token_text.strip():
- indices_parts.append(token_text)
+ else:
+ # Para otros scopes, usar recursión como fallback
+ idx_result = reconstruct_scl_from_tokens(
+ middle_child
+ )
+ if idx_result and idx_result.strip():
+ indices_parts.append(idx_result.strip())
+ else:
+ indices_parts.append(
+ "/*_ERR_RECURSIVE_EMPTY_*/"
+ )
+ elif child_tag == "Token":
+ # Token de separación (como ",")
+ token_text = middle_child.get("Text", "")
+ if token_text.strip():
+ indices_parts.append(token_text)
- if indices_parts:
- symbol_text_parts.append(
- f"[{','.join(indices_parts)}]"
- )
+ if indices_parts:
+ symbol_text_parts.append(f"[{','.join(indices_parts)}]")
else:
# No es acceso de array, buscar Access anidados de la forma tradicional
index_access_nodes = comp.xpath(