This commit is contained in:
parent
60fea74ebf
commit
6a06f32176
93
x0_main.py
93
x0_main.py
|
@ -5,6 +5,7 @@ import sys
|
||||||
import locale
|
import locale
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
|
|
||||||
# (Función get_console_encoding y variable CONSOLE_ENCODING como antes)
|
# (Función get_console_encoding y variable CONSOLE_ENCODING como antes)
|
||||||
def get_console_encoding():
|
def get_console_encoding():
|
||||||
"""Obtiene la codificación preferida de la consola, con fallback."""
|
"""Obtiene la codificación preferida de la consola, con fallback."""
|
||||||
|
@ -12,11 +13,13 @@ def get_console_encoding():
|
||||||
return locale.getpreferredencoding(False)
|
return locale.getpreferredencoding(False)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Fallback común en Windows si falla getpreferredencoding
|
# Fallback común en Windows si falla getpreferredencoding
|
||||||
return "cp1252" # O prueba con 'utf-8' si cp1252 da problemas
|
return "cp1252" # O prueba con 'utf-8' si cp1252 da problemas
|
||||||
|
|
||||||
|
|
||||||
CONSOLE_ENCODING = get_console_encoding()
|
CONSOLE_ENCODING = get_console_encoding()
|
||||||
# print(f"Detected console encoding: {CONSOLE_ENCODING}")
|
# print(f"Detected console encoding: {CONSOLE_ENCODING}")
|
||||||
|
|
||||||
|
|
||||||
# (Función run_script como antes, usando CONSOLE_ENCODING)
|
# (Función run_script como antes, usando CONSOLE_ENCODING)
|
||||||
def run_script(script_name, xml_arg):
|
def run_script(script_name, xml_arg):
|
||||||
"""Runs a given script with the specified XML file argument."""
|
"""Runs a given script with the specified XML file argument."""
|
||||||
|
@ -24,17 +27,21 @@ def run_script(script_name, xml_arg):
|
||||||
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), script_name)
|
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), script_name)
|
||||||
# Usar la ruta absoluta al ejecutable de Python actual
|
# Usar la ruta absoluta al ejecutable de Python actual
|
||||||
python_executable = sys.executable
|
python_executable = sys.executable
|
||||||
command = [python_executable, script_path, xml_arg] # Usar la ruta absoluta de python
|
command = [
|
||||||
|
python_executable,
|
||||||
|
script_path,
|
||||||
|
xml_arg,
|
||||||
|
] # Usar la ruta absoluta de python
|
||||||
print(f"\n--- Running {script_name} with argument: {xml_arg} ---")
|
print(f"\n--- Running {script_name} with argument: {xml_arg} ---")
|
||||||
try:
|
try:
|
||||||
# Ejecutar el proceso hijo
|
# Ejecutar el proceso hijo
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
command,
|
command,
|
||||||
check=True, # Lanza excepción si el script falla (return code != 0)
|
check=True, # Lanza excepción si el script falla (return code != 0)
|
||||||
capture_output=True,# Captura stdout y stderr
|
capture_output=True, # Captura stdout y stderr
|
||||||
text=True, # Decodifica stdout/stderr como texto
|
text=True, # Decodifica stdout/stderr como texto
|
||||||
encoding=CONSOLE_ENCODING, # Usa la codificación detectada
|
encoding=CONSOLE_ENCODING, # Usa la codificación detectada
|
||||||
errors='replace' # Reemplaza caracteres no decodificables
|
errors="replace", # Reemplaza caracteres no decodificables
|
||||||
)
|
)
|
||||||
|
|
||||||
# Imprimir stdout y stderr si no están vacíos
|
# Imprimir stdout y stderr si no están vacíos
|
||||||
|
@ -45,20 +52,28 @@ def run_script(script_name, xml_arg):
|
||||||
print(stdout_clean)
|
print(stdout_clean)
|
||||||
if stderr_clean:
|
if stderr_clean:
|
||||||
# Imprimir stderr claramente para errores del script hijo
|
# Imprimir stderr claramente para errores del script hijo
|
||||||
print(f"--- Stderr ({script_name}) ---", file=sys.stderr) # Imprimir en stderr
|
print(
|
||||||
|
f"--- Stderr ({script_name}) ---", file=sys.stderr
|
||||||
|
) # Imprimir en stderr
|
||||||
print(stderr_clean, file=sys.stderr)
|
print(stderr_clean, file=sys.stderr)
|
||||||
print("--------------------------", file=sys.stderr)
|
print("--------------------------", file=sys.stderr)
|
||||||
|
|
||||||
print(f"--- {script_name} finished successfully ---")
|
print(f"--- {script_name} finished successfully ---")
|
||||||
return True # Indicar éxito
|
return True # Indicar éxito
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# Error si el script python o el ejecutable no se encuentran
|
# Error si el script python o el ejecutable no se encuentran
|
||||||
print(f"Error: Script '{script_path}' or Python executable '{python_executable}' not found.", file=sys.stderr)
|
print(
|
||||||
|
f"Error: Script '{script_path}' or Python executable '{python_executable}' not found.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
# Error si el script hijo devuelve un código de error (ej., sys.exit(1))
|
# Error si el script hijo devuelve un código de error (ej., sys.exit(1))
|
||||||
print(f"Error running {script_name}: Script returned non-zero exit code {e.returncode}.", file=sys.stderr)
|
print(
|
||||||
|
f"Error running {script_name}: Script returned non-zero exit code {e.returncode}.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
# Decodificar e imprimir stdout/stderr del proceso fallido
|
# Decodificar e imprimir stdout/stderr del proceso fallido
|
||||||
stdout_decoded = e.stdout.strip() if e.stdout else ""
|
stdout_decoded = e.stdout.strip() if e.stdout else ""
|
||||||
|
@ -71,14 +86,18 @@ def run_script(script_name, xml_arg):
|
||||||
print(f"--- Stderr ({script_name}) ---", file=sys.stderr)
|
print(f"--- Stderr ({script_name}) ---", file=sys.stderr)
|
||||||
print(stderr_decoded, file=sys.stderr)
|
print(stderr_decoded, file=sys.stderr)
|
||||||
print("--------------------------", file=sys.stderr)
|
print("--------------------------", file=sys.stderr)
|
||||||
return False # Indicar fallo
|
return False # Indicar fallo
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Otros errores inesperados
|
# Otros errores inesperados
|
||||||
print(f"An unexpected error occurred while running {script_name}: {e}", file=sys.stderr)
|
print(
|
||||||
|
f"An unexpected error occurred while running {script_name}: {e}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
# Imprimir traceback para depuración
|
# Imprimir traceback para depuración
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
return False # Indicar fallo
|
return False # Indicar fallo
|
||||||
|
|
||||||
|
|
||||||
# --- NO SE NECESITA select_xml_file() si procesamos todos ---
|
# --- NO SE NECESITA select_xml_file() si procesamos todos ---
|
||||||
|
@ -95,20 +114,27 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Verificar si el directorio 'XML Project' existe
|
# Verificar si el directorio 'XML Project' existe
|
||||||
if not os.path.isdir(xml_project_dir):
|
if not os.path.isdir(xml_project_dir):
|
||||||
print(f"Error: El directorio '{xml_project_dir}' no existe o no es un directorio.", file=sys.stderr)
|
print(
|
||||||
print("Por favor, crea el directorio 'XML Project' en la misma carpeta que este script y coloca tus archivos XML dentro.")
|
f"Error: El directorio '{xml_project_dir}' no existe o no es un directorio.",
|
||||||
sys.exit(1) # Salir con error
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"Por favor, crea el directorio 'XML Project' en la misma carpeta que este script y coloca tus archivos XML dentro."
|
||||||
|
)
|
||||||
|
sys.exit(1) # Salir con error
|
||||||
|
|
||||||
# Buscar todos los archivos .xml recursivamente
|
# Buscar todos los archivos .xml recursivamente
|
||||||
search_pattern = os.path.join(xml_project_dir, "**", "*.xml")
|
search_pattern = os.path.join(xml_project_dir, "**", "*.xml")
|
||||||
xml_files_found = glob.glob(search_pattern, recursive=True)
|
xml_files_found = glob.glob(search_pattern, recursive=True)
|
||||||
|
|
||||||
if not xml_files_found:
|
if not xml_files_found:
|
||||||
print(f"No se encontraron archivos XML en '{xml_project_dir}' o sus subdirectorios.")
|
print(
|
||||||
sys.exit(0) # Salir limpiamente si no hay archivos
|
f"No se encontraron archivos XML en '{xml_project_dir}' o sus subdirectorios."
|
||||||
|
)
|
||||||
|
sys.exit(0) # Salir limpiamente si no hay archivos
|
||||||
|
|
||||||
print(f"Se encontraron {len(xml_files_found)} archivos XML para procesar:")
|
print(f"Se encontraron {len(xml_files_found)} archivos XML para procesar:")
|
||||||
xml_files_found.sort() # Ordenar para consistencia
|
xml_files_found.sort() # Ordenar para consistencia
|
||||||
for xml_file in xml_files_found:
|
for xml_file in xml_files_found:
|
||||||
print(f" - {os.path.relpath(xml_file, script_dir)}")
|
print(f" - {os.path.relpath(xml_file, script_dir)}")
|
||||||
|
|
||||||
|
@ -134,18 +160,29 @@ if __name__ == "__main__":
|
||||||
xml_dir = os.path.dirname(absolute_xml_filepath)
|
xml_dir = os.path.dirname(absolute_xml_filepath)
|
||||||
parsing_dir = os.path.join(xml_dir, "parsing")
|
parsing_dir = os.path.join(xml_dir, "parsing")
|
||||||
expected_json_file = os.path.join(parsing_dir, f"{xml_base_name}.json")
|
expected_json_file = os.path.join(parsing_dir, f"{xml_base_name}.json")
|
||||||
expected_processed_json = os.path.join(parsing_dir, f"{xml_base_name}_processed.json")
|
expected_processed_json = os.path.join(
|
||||||
|
parsing_dir, f"{xml_base_name}_processed.json"
|
||||||
|
)
|
||||||
|
|
||||||
# Ejecutar los scripts en secuencia
|
# Ejecutar los scripts en secuencia
|
||||||
success = True
|
success = True
|
||||||
if not run_script(script1, absolute_xml_filepath):
|
if not run_script(script1, absolute_xml_filepath):
|
||||||
print(f"\nPipeline falló en el script '{script1}' para el archivo: {relative_path}", file=sys.stderr)
|
print(
|
||||||
|
f"\nPipeline falló en el script '{script1}' para el archivo: {relative_path}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
success = False
|
success = False
|
||||||
elif not run_script(script2, absolute_xml_filepath):
|
elif not run_script(script2, absolute_xml_filepath):
|
||||||
print(f"\nPipeline falló en el script '{script2}' para el archivo: {relative_path}", file=sys.stderr)
|
print(
|
||||||
|
f"\nPipeline falló en el script '{script2}' para el archivo: {relative_path}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
success = False
|
success = False
|
||||||
elif not run_script(script3, absolute_xml_filepath):
|
elif not run_script(script3, absolute_xml_filepath):
|
||||||
print(f"\nPipeline falló en el script '{script3}' para el archivo: {relative_path}", file=sys.stderr)
|
print(
|
||||||
|
f"\nPipeline falló en el script '{script3}' para el archivo: {relative_path}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
# Actualizar contadores y mostrar estado
|
# Actualizar contadores y mostrar estado
|
||||||
|
@ -154,12 +191,16 @@ if __name__ == "__main__":
|
||||||
processed_count += 1
|
processed_count += 1
|
||||||
else:
|
else:
|
||||||
failed_count += 1
|
failed_count += 1
|
||||||
print(f"--- Pipeline falló para: {relative_path} ---", file=sys.stderr) # Indicar fallo
|
print(
|
||||||
|
f"--- Pipeline falló para: {relative_path} ---", file=sys.stderr
|
||||||
|
) # Indicar fallo
|
||||||
|
|
||||||
# --- PARTE 3: RESUMEN FINAL ---
|
# --- PARTE 3: RESUMEN FINAL ---
|
||||||
print("\n--- Resumen Final del Procesamiento ---")
|
print("\n--- Resumen Final del Procesamiento ---")
|
||||||
print(f"Total de archivos XML encontrados: {len(xml_files_found)}")
|
print(f"Total de archivos XML encontrados: {len(xml_files_found)}")
|
||||||
print(f"Archivos procesados exitosamente por el pipeline completo: {processed_count}")
|
print(
|
||||||
|
f"Archivos procesados exitosamente por el pipeline completo: {processed_count}"
|
||||||
|
)
|
||||||
print(f"Archivos que fallaron en algún punto del pipeline: {failed_count}")
|
print(f"Archivos que fallaron en algún punto del pipeline: {failed_count}")
|
||||||
print("---------------------------------------")
|
print("---------------------------------------")
|
||||||
|
|
||||||
|
|
406
x1_to_json.py
406
x1_to_json.py
|
@ -24,15 +24,16 @@ except ImportError as e:
|
||||||
|
|
||||||
# --- NUEVAS FUNCIONES DE PARSEO para UDT y Tag Table ---
|
# --- NUEVAS FUNCIONES DE PARSEO para UDT y Tag Table ---
|
||||||
|
|
||||||
|
|
||||||
def parse_udt(udt_element):
|
def parse_udt(udt_element):
|
||||||
"""Parsea un elemento <SW.Types.PlcStruct> (UDT)."""
|
"""Parsea un elemento <SW.Types.PlcStruct> (UDT)."""
|
||||||
print(" -> Detectado: PlcStruct (UDT)")
|
print(" -> Detectado: PlcStruct (UDT)")
|
||||||
block_data = {
|
block_data = {
|
||||||
"block_name": "UnknownUDT",
|
"block_name": "UnknownUDT",
|
||||||
"block_type": "PlcUDT", # Identificador para x3
|
"block_type": "PlcUDT", # Identificador para x3
|
||||||
"language": "UDT", # Lenguaje específico
|
"language": "UDT", # Lenguaje específico
|
||||||
"interface": {},
|
"interface": {},
|
||||||
"networks": [], # Los UDTs no tienen redes
|
"networks": [], # Los UDTs no tienen redes
|
||||||
"block_comment": "",
|
"block_comment": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,66 +44,87 @@ def parse_udt(udt_element):
|
||||||
name_node = attr_list.xpath("./Name/text()")
|
name_node = attr_list.xpath("./Name/text()")
|
||||||
block_data["block_name"] = name_node[0].strip() if name_node else "UnknownUDT"
|
block_data["block_name"] = name_node[0].strip() if name_node else "UnknownUDT"
|
||||||
# Comentario del UDT
|
# Comentario del UDT
|
||||||
comment_node_list = udt_element.xpath("./ObjectList/MultilingualText[@CompositionName='Comment']")
|
comment_node_list = udt_element.xpath(
|
||||||
|
"./ObjectList/MultilingualText[@CompositionName='Comment']"
|
||||||
|
)
|
||||||
if comment_node_list:
|
if comment_node_list:
|
||||||
block_data["block_comment"] = get_multilingual_text(comment_node_list[0])
|
block_data["block_comment"] = get_multilingual_text(comment_node_list[0])
|
||||||
else: # Fallback
|
else: # Fallback
|
||||||
comment_attr_node = attr_list.xpath("../ObjectList/MultilingualText[@CompositionName='Comment']") # Buscar desde el padre
|
comment_attr_node = attr_list.xpath(
|
||||||
if comment_attr_node :
|
"../ObjectList/MultilingualText[@CompositionName='Comment']"
|
||||||
block_data["block_comment"] = get_multilingual_text(comment_attr_node[0])
|
) # Buscar desde el padre
|
||||||
|
if comment_attr_node:
|
||||||
|
block_data["block_comment"] = get_multilingual_text(
|
||||||
|
comment_attr_node[0]
|
||||||
|
)
|
||||||
|
|
||||||
# Extraer interfaz (miembros)
|
# Extraer interfaz (miembros)
|
||||||
# La interfaz de un UDT suele estar directamente en <Interface><Sections><Section Name="None">
|
# La interfaz de un UDT suele estar directamente en <Interface><Sections><Section Name="None">
|
||||||
interface_node_list = udt_element.xpath(
|
interface_node_list = udt_element.xpath(
|
||||||
"./AttributeList/Interface/iface:Sections/iface:Section[@Name='None']", namespaces=ns
|
"./AttributeList/Interface/iface:Sections/iface:Section[@Name='None']",
|
||||||
|
namespaces=ns,
|
||||||
)
|
)
|
||||||
if interface_node_list:
|
if interface_node_list:
|
||||||
section_node = interface_node_list[0]
|
section_node = interface_node_list[0]
|
||||||
members_in_section = section_node.xpath("./iface:Member", namespaces=ns)
|
members_in_section = section_node.xpath("./iface:Member", namespaces=ns)
|
||||||
if members_in_section:
|
if members_in_section:
|
||||||
# Usar la función existente para parsear miembros
|
# Usar la función existente para parsear miembros
|
||||||
block_data["interface"]["None"] = parse_interface_members(members_in_section)
|
block_data["interface"]["None"] = parse_interface_members(
|
||||||
|
members_in_section
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(f"Advertencia: Sección 'None' encontrada en UDT '{block_data['block_name']}' pero sin miembros.")
|
print(
|
||||||
|
f"Advertencia: Sección 'None' encontrada en UDT '{block_data['block_name']}' pero sin miembros."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Intentar buscar interfaz directamente si no está en AttributeList (menos común)
|
# Intentar buscar interfaz directamente si no está en AttributeList (menos común)
|
||||||
interface_node_direct = udt_element.xpath(
|
interface_node_direct = udt_element.xpath(
|
||||||
".//iface:Interface/iface:Sections/iface:Section[@Name='None']", namespaces=ns
|
".//iface:Interface/iface:Sections/iface:Section[@Name='None']",
|
||||||
)
|
namespaces=ns,
|
||||||
if interface_node_direct:
|
)
|
||||||
section_node = interface_node_direct[0]
|
if interface_node_direct:
|
||||||
members_in_section = section_node.xpath("./iface:Member", namespaces=ns)
|
section_node = interface_node_direct[0]
|
||||||
if members_in_section:
|
members_in_section = section_node.xpath("./iface:Member", namespaces=ns)
|
||||||
block_data["interface"]["None"] = parse_interface_members(members_in_section)
|
if members_in_section:
|
||||||
else:
|
block_data["interface"]["None"] = parse_interface_members(
|
||||||
print(f"Advertencia: Sección 'None' encontrada directamente en UDT '{block_data['block_name']}' pero sin miembros.")
|
members_in_section
|
||||||
else:
|
)
|
||||||
print(f"Advertencia: No se encontró la sección 'None' de la interfaz para UDT '{block_data['block_name']}'.")
|
else:
|
||||||
|
print(
|
||||||
|
f"Advertencia: Sección 'None' encontrada directamente en UDT '{block_data['block_name']}' pero sin miembros."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Advertencia: No se encontró la sección 'None' de la interfaz para UDT '{block_data['block_name']}'."
|
||||||
|
)
|
||||||
|
|
||||||
if not block_data["interface"]:
|
if not block_data["interface"]:
|
||||||
print(f"Advertencia: No se pudo extraer la interfaz del UDT '{block_data['block_name']}'.")
|
print(
|
||||||
|
f"Advertencia: No se pudo extraer la interfaz del UDT '{block_data['block_name']}'."
|
||||||
|
)
|
||||||
|
|
||||||
return block_data
|
return block_data
|
||||||
|
|
||||||
|
|
||||||
def parse_tag_table(tag_table_element):
|
def parse_tag_table(tag_table_element):
|
||||||
"""Parsea un elemento <SW.Tags.PlcTagTable>."""
|
"""Parsea un elemento <SW.Tags.PlcTagTable>."""
|
||||||
print(" -> Detectado: PlcTagTable")
|
print(" -> Detectado: PlcTagTable")
|
||||||
table_data = {
|
table_data = {
|
||||||
"block_name": "UnknownTagTable",
|
"block_name": "UnknownTagTable",
|
||||||
"block_type": "PlcTagTable", # Identificador para x3
|
"block_type": "PlcTagTable", # Identificador para x3
|
||||||
"language": "TagTable", # Lenguaje específico
|
"language": "TagTable", # Lenguaje específico
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"networks": [], # Las Tag Tables no tienen redes
|
"networks": [], # Las Tag Tables no tienen redes
|
||||||
"block_comment": "", # Las tablas de tags no suelen tener comentario de bloque
|
"block_comment": "", # Las tablas de tags no suelen tener comentario de bloque
|
||||||
}
|
}
|
||||||
|
|
||||||
# Extraer nombre de la tabla
|
# Extraer nombre de la tabla
|
||||||
attribute_list_node = tag_table_element.xpath("./AttributeList")
|
attribute_list_node = tag_table_element.xpath("./AttributeList")
|
||||||
if attribute_list_node:
|
if attribute_list_node:
|
||||||
name_node = attribute_list_node[0].xpath("./Name/text()")
|
name_node = attribute_list_node[0].xpath("./Name/text()")
|
||||||
table_data["block_name"] = name_node[0].strip() if name_node else "UnknownTagTable"
|
table_data["block_name"] = (
|
||||||
|
name_node[0].strip() if name_node else "UnknownTagTable"
|
||||||
|
)
|
||||||
|
|
||||||
# Extraer tags
|
# Extraer tags
|
||||||
tag_elements = tag_table_element.xpath("./ObjectList/SW.Tags.PlcTag")
|
tag_elements = tag_table_element.xpath("./ObjectList/SW.Tags.PlcTag")
|
||||||
|
@ -112,7 +134,7 @@ def parse_tag_table(tag_table_element):
|
||||||
"name": "UnknownTag",
|
"name": "UnknownTag",
|
||||||
"datatype": "Unknown",
|
"datatype": "Unknown",
|
||||||
"address": None,
|
"address": None,
|
||||||
"comment": ""
|
"comment": "",
|
||||||
}
|
}
|
||||||
tag_attr_list = tag_elem.xpath("./AttributeList")
|
tag_attr_list = tag_elem.xpath("./AttributeList")
|
||||||
if tag_attr_list:
|
if tag_attr_list:
|
||||||
|
@ -125,7 +147,9 @@ def parse_tag_table(tag_table_element):
|
||||||
tag_info["address"] = addr_node[0].strip() if addr_node else None
|
tag_info["address"] = addr_node[0].strip() if addr_node else None
|
||||||
|
|
||||||
# Extraer comentario del tag
|
# Extraer comentario del tag
|
||||||
comment_node_list = tag_elem.xpath("./ObjectList/MultilingualText[@CompositionName='Comment']")
|
comment_node_list = tag_elem.xpath(
|
||||||
|
"./ObjectList/MultilingualText[@CompositionName='Comment']"
|
||||||
|
)
|
||||||
if comment_node_list:
|
if comment_node_list:
|
||||||
tag_info["comment"] = get_multilingual_text(comment_node_list[0])
|
tag_info["comment"] = get_multilingual_text(comment_node_list[0])
|
||||||
|
|
||||||
|
@ -133,6 +157,7 @@ def parse_tag_table(tag_table_element):
|
||||||
|
|
||||||
return table_data
|
return table_data
|
||||||
|
|
||||||
|
|
||||||
# --- Cargador Dinámico de Parsers (sin cambios) ---
|
# --- Cargador Dinámico de Parsers (sin cambios) ---
|
||||||
def load_parsers(parsers_dir="parsers"):
|
def load_parsers(parsers_dir="parsers"):
|
||||||
"""
|
"""
|
||||||
|
@ -145,7 +170,7 @@ def load_parsers(parsers_dir="parsers"):
|
||||||
parsers_dir_path = os.path.join(script_dir, parsers_dir)
|
parsers_dir_path = os.path.join(script_dir, parsers_dir)
|
||||||
if not os.path.isdir(parsers_dir_path):
|
if not os.path.isdir(parsers_dir_path):
|
||||||
print(f"Error: Directorio de parsers no encontrado: '{parsers_dir_path}'")
|
print(f"Error: Directorio de parsers no encontrado: '{parsers_dir_path}'")
|
||||||
return parser_map # Devuelve mapa vacío
|
return parser_map # Devuelve mapa vacío
|
||||||
|
|
||||||
print(f"Cargando parsers desde: '{parsers_dir_path}'")
|
print(f"Cargando parsers desde: '{parsers_dir_path}'")
|
||||||
parsers_package = os.path.basename(parsers_dir)
|
parsers_package = os.path.basename(parsers_dir)
|
||||||
|
@ -158,8 +183,10 @@ def load_parsers(parsers_dir="parsers"):
|
||||||
and filename.endswith(".py")
|
and filename.endswith(".py")
|
||||||
and filename not in ["__init__.py", "parser_utils.py"]
|
and filename not in ["__init__.py", "parser_utils.py"]
|
||||||
):
|
):
|
||||||
module_name_rel = filename[:-3] # Nombre sin .py (e.g., parse_lad_fbd)
|
module_name_rel = filename[:-3] # Nombre sin .py (e.g., parse_lad_fbd)
|
||||||
full_module_name = f"{parsers_package}.{module_name_rel}" # e.g., parsers.parse_lad_fbd
|
full_module_name = (
|
||||||
|
f"{parsers_package}.{module_name_rel}" # e.g., parsers.parse_lad_fbd
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
# Importar el módulo dinámicamente
|
# Importar el módulo dinámicamente
|
||||||
module = importlib.import_module(full_module_name)
|
module = importlib.import_module(full_module_name)
|
||||||
|
@ -181,7 +208,7 @@ def load_parsers(parsers_dir="parsers"):
|
||||||
if isinstance(languages, list) and callable(parser_func):
|
if isinstance(languages, list) and callable(parser_func):
|
||||||
# Añadir la función al mapa para cada lenguaje que soporta
|
# Añadir la función al mapa para cada lenguaje que soporta
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
lang_upper = lang.upper() # Usar mayúsculas como clave
|
lang_upper = lang.upper() # Usar mayúsculas como clave
|
||||||
if lang_upper in parser_map:
|
if lang_upper in parser_map:
|
||||||
print(
|
print(
|
||||||
f" Advertencia: Parser para '{lang_upper}' en {full_module_name} sobrescribe definición anterior."
|
f" Advertencia: Parser para '{lang_upper}' en {full_module_name} sobrescribe definición anterior."
|
||||||
|
@ -213,22 +240,25 @@ def load_parsers(parsers_dir="parsers"):
|
||||||
print(f"Lenguajes soportados: {list(parser_map.keys())}")
|
print(f"Lenguajes soportados: {list(parser_map.keys())}")
|
||||||
return parser_map
|
return parser_map
|
||||||
|
|
||||||
|
|
||||||
# --- Función Principal de Conversión (MODIFICADA) ---
|
# --- Función Principal de Conversión (MODIFICADA) ---
|
||||||
def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
||||||
"""Convierte XML a JSON, detectando tipo de bloque (FC/FB/OB/DB/UDT/TagTable)."""
|
"""Convierte XML a JSON, detectando tipo de bloque (FC/FB/OB/DB/UDT/TagTable)."""
|
||||||
print(f"Iniciando conversión de '{xml_filepath}' a '{json_filepath}'...")
|
print(f"Iniciando conversión de '{xml_filepath}' a '{json_filepath}'...")
|
||||||
if not os.path.exists(xml_filepath):
|
if not os.path.exists(xml_filepath):
|
||||||
print(f"Error Crítico: Archivo XML no encontrado: '{xml_filepath}'")
|
print(f"Error Crítico: Archivo XML no encontrado: '{xml_filepath}'")
|
||||||
return False # Indicar fallo
|
return False # Indicar fallo
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("Paso 1: Parseando archivo XML...")
|
print("Paso 1: Parseando archivo XML...")
|
||||||
parser = etree.XMLParser(remove_blank_text=True, recover=True) # recover=True puede ayudar
|
parser = etree.XMLParser(
|
||||||
|
remove_blank_text=True, recover=True
|
||||||
|
) # recover=True puede ayudar
|
||||||
tree = etree.parse(xml_filepath, parser)
|
tree = etree.parse(xml_filepath, parser)
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
print("Paso 1: Parseo XML completado.")
|
print("Paso 1: Parseo XML completado.")
|
||||||
|
|
||||||
result = None # Inicializar resultado
|
result = None # Inicializar resultado
|
||||||
|
|
||||||
# --- Detección del tipo de bloque/objeto principal ---
|
# --- Detección del tipo de bloque/objeto principal ---
|
||||||
print("Paso 2: Detectando tipo de objeto principal...")
|
print("Paso 2: Detectando tipo de objeto principal...")
|
||||||
|
@ -240,7 +270,9 @@ def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
||||||
|
|
||||||
# Buscar Tag Table si no es UDT
|
# Buscar Tag Table si no es UDT
|
||||||
if result is None:
|
if result is None:
|
||||||
tag_table_element = root.find(".//SW.Tags.PlcTagTable", namespaces=root.nsmap)
|
tag_table_element = root.find(
|
||||||
|
".//SW.Tags.PlcTagTable", namespaces=root.nsmap
|
||||||
|
)
|
||||||
if tag_table_element is not None:
|
if tag_table_element is not None:
|
||||||
result = parse_tag_table(tag_table_element)
|
result = parse_tag_table(tag_table_element)
|
||||||
|
|
||||||
|
@ -258,42 +290,73 @@ def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
||||||
if block_list:
|
if block_list:
|
||||||
the_block = block_list[0]
|
the_block = block_list[0]
|
||||||
block_tag_name = etree.QName(the_block.tag).localname
|
block_tag_name = etree.QName(the_block.tag).localname
|
||||||
if block_tag_name == "SW.Blocks.FC": block_type_found = "FC"
|
if block_tag_name == "SW.Blocks.FC":
|
||||||
elif block_tag_name == "SW.Blocks.FB": block_type_found = "FB"
|
block_type_found = "FC"
|
||||||
elif block_tag_name == "SW.Blocks.GlobalDB": block_type_found = "GlobalDB"
|
elif block_tag_name == "SW.Blocks.FB":
|
||||||
elif block_tag_name == "SW.Blocks.OB": block_type_found = "OB"
|
block_type_found = "FB"
|
||||||
print(f"Paso 2b: Bloque {block_tag_name} (Tipo: {block_type_found}) encontrado (ID={the_block.get('ID')}).")
|
elif block_tag_name == "SW.Blocks.GlobalDB":
|
||||||
|
block_type_found = "GlobalDB"
|
||||||
|
elif block_tag_name == "SW.Blocks.OB":
|
||||||
|
block_type_found = "OB"
|
||||||
|
print(
|
||||||
|
f"Paso 2b: Bloque {block_tag_name} (Tipo: {block_type_found}) encontrado (ID={the_block.get('ID')})."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print("Error Crítico: No se encontró el elemento raíz del bloque (<SW.Blocks.FC/FB/GlobalDB/OB>) ni UDT ni Tag Table.")
|
print(
|
||||||
return False # Fallo si no se encuentra ningún objeto principal
|
"Error Crítico: No se encontró el elemento raíz del bloque (<SW.Blocks.FC/FB/GlobalDB/OB>) ni UDT ni Tag Table."
|
||||||
|
)
|
||||||
|
return False # Fallo si no se encuentra ningún objeto principal
|
||||||
|
|
||||||
# --- Si es FC/FB/OB/DB, continuar con el parseo original ---
|
# --- Si es FC/FB/OB/DB, continuar con el parseo original ---
|
||||||
if the_block is not None:
|
if the_block is not None:
|
||||||
print("Paso 3: Extrayendo atributos del bloque...")
|
print("Paso 3: Extrayendo atributos del bloque...")
|
||||||
# (Extracción de atributos Name, Number, Language como antes...)
|
# (Extracción de atributos Name, Number, Language como antes...)
|
||||||
attribute_list_node = the_block.xpath("./AttributeList")
|
attribute_list_node = the_block.xpath("./AttributeList")
|
||||||
block_name_val, block_number_val, block_lang_val = "Unknown", None, "Unknown"
|
block_name_val, block_number_val, block_lang_val = (
|
||||||
|
"Unknown",
|
||||||
|
None,
|
||||||
|
"Unknown",
|
||||||
|
)
|
||||||
if attribute_list_node:
|
if attribute_list_node:
|
||||||
attr_list = attribute_list_node[0]
|
attr_list = attribute_list_node[0]
|
||||||
name_node = attr_list.xpath("./Name/text()")
|
name_node = attr_list.xpath("./Name/text()")
|
||||||
block_name_val = name_node[0].strip() if name_node else block_name_val
|
block_name_val = (
|
||||||
|
name_node[0].strip() if name_node else block_name_val
|
||||||
|
)
|
||||||
num_node = attr_list.xpath("./Number/text()")
|
num_node = attr_list.xpath("./Number/text()")
|
||||||
try: block_number_val = int(num_node[0]) if num_node else None
|
try:
|
||||||
except (ValueError, TypeError): block_number_val = None
|
block_number_val = int(num_node[0]) if num_node else None
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
block_number_val = None
|
||||||
lang_node = attr_list.xpath("./ProgrammingLanguage/text()")
|
lang_node = attr_list.xpath("./ProgrammingLanguage/text()")
|
||||||
block_lang_val = (lang_node[0].strip() if lang_node else ("DB" if block_type_found == "GlobalDB" else "Unknown"))
|
block_lang_val = (
|
||||||
print(f"Paso 3: Atributos: Nombre='{block_name_val}', Número={block_number_val}, Lenguaje Bloque='{block_lang_val}'")
|
lang_node[0].strip()
|
||||||
|
if lang_node
|
||||||
|
else ("DB" if block_type_found == "GlobalDB" else "Unknown")
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Paso 3: Atributos: Nombre='{block_name_val}', Número={block_number_val}, Lenguaje Bloque='{block_lang_val}'"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(f"Advertencia: No se encontró AttributeList para el bloque {block_type_found}.")
|
print(
|
||||||
if block_type_found == "GlobalDB": block_lang_val = "DB"
|
f"Advertencia: No se encontró AttributeList para el bloque {block_type_found}."
|
||||||
|
)
|
||||||
|
if block_type_found == "GlobalDB":
|
||||||
|
block_lang_val = "DB"
|
||||||
|
|
||||||
# (Extracción de comentario como antes...)
|
# (Extracción de comentario como antes...)
|
||||||
block_comment_val = ""
|
block_comment_val = ""
|
||||||
comment_node_list = the_block.xpath("./ObjectList/MultilingualText[@CompositionName='Comment']")
|
comment_node_list = the_block.xpath(
|
||||||
if comment_node_list: block_comment_val = get_multilingual_text(comment_node_list[0])
|
"./ObjectList/MultilingualText[@CompositionName='Comment']"
|
||||||
else: # Fallback
|
)
|
||||||
comment_attr_node = the_block.xpath("./AttributeList/Comment") # Buscar desde AttributeList
|
if comment_node_list:
|
||||||
if comment_attr_node : block_comment_val = get_multilingual_text(comment_attr_node[0])
|
block_comment_val = get_multilingual_text(comment_node_list[0])
|
||||||
|
else: # Fallback
|
||||||
|
comment_attr_node = the_block.xpath(
|
||||||
|
"./AttributeList/Comment"
|
||||||
|
) # Buscar desde AttributeList
|
||||||
|
if comment_attr_node:
|
||||||
|
block_comment_val = get_multilingual_text(comment_attr_node[0])
|
||||||
|
|
||||||
print(f"Paso 3b: Comentario bloque: '{block_comment_val[:50]}...'")
|
print(f"Paso 3b: Comentario bloque: '{block_comment_val[:50]}...'")
|
||||||
|
|
||||||
|
@ -305,34 +368,64 @@ def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
||||||
"block_type": block_type_found,
|
"block_type": block_type_found,
|
||||||
"block_comment": block_comment_val,
|
"block_comment": block_comment_val,
|
||||||
"interface": {},
|
"interface": {},
|
||||||
"networks": [], # Inicializar networks aquí
|
"networks": [], # Inicializar networks aquí
|
||||||
}
|
}
|
||||||
|
|
||||||
# (Extracción de interfaz como antes...)
|
# (Extracción de interfaz como antes...)
|
||||||
print("Paso 4: Extrayendo la interfaz del bloque...")
|
print("Paso 4: Extrayendo la interfaz del bloque...")
|
||||||
interface_node_list = attribute_list_node[0].xpath("./Interface") if attribute_list_node else []
|
interface_node_list = (
|
||||||
|
attribute_list_node[0].xpath("./Interface")
|
||||||
|
if attribute_list_node
|
||||||
|
else []
|
||||||
|
)
|
||||||
if interface_node_list:
|
if interface_node_list:
|
||||||
interface_node = interface_node_list[0]
|
interface_node = interface_node_list[0]
|
||||||
all_sections = interface_node.xpath(".//iface:Section", namespaces=ns)
|
all_sections = interface_node.xpath(
|
||||||
|
".//iface:Section", namespaces=ns
|
||||||
|
)
|
||||||
if all_sections:
|
if all_sections:
|
||||||
processed_sections = set()
|
processed_sections = set()
|
||||||
for section in all_sections:
|
for section in all_sections:
|
||||||
section_name = section.get("Name")
|
section_name = section.get("Name")
|
||||||
if not section_name or section_name in processed_sections: continue
|
if not section_name or section_name in processed_sections:
|
||||||
members_in_section = section.xpath("./iface:Member", namespaces=ns)
|
continue
|
||||||
|
members_in_section = section.xpath(
|
||||||
|
"./iface:Member", namespaces=ns
|
||||||
|
)
|
||||||
if members_in_section:
|
if members_in_section:
|
||||||
result["interface"][section_name] = parse_interface_members(members_in_section)
|
result["interface"][section_name] = (
|
||||||
|
parse_interface_members(members_in_section)
|
||||||
|
)
|
||||||
processed_sections.add(section_name)
|
processed_sections.add(section_name)
|
||||||
else: print("Advertencia: Nodo Interface no contiene secciones <iface:Section>.")
|
else:
|
||||||
if not result["interface"]: print("Advertencia: Interface encontrada pero sin secciones procesables.")
|
print(
|
||||||
|
"Advertencia: Nodo Interface no contiene secciones <iface:Section>."
|
||||||
|
)
|
||||||
|
if not result["interface"]:
|
||||||
|
print(
|
||||||
|
"Advertencia: Interface encontrada pero sin secciones procesables."
|
||||||
|
)
|
||||||
elif block_type_found == "GlobalDB":
|
elif block_type_found == "GlobalDB":
|
||||||
static_members = the_block.xpath(".//iface:Section[@Name='Static']/iface:Member", namespaces=ns)
|
static_members = the_block.xpath(
|
||||||
|
".//iface:Section[@Name='Static']/iface:Member", namespaces=ns
|
||||||
|
)
|
||||||
if static_members:
|
if static_members:
|
||||||
print("Paso 4: Encontrada sección Static para GlobalDB (sin nodo Interface).")
|
print(
|
||||||
result["interface"]["Static"] = parse_interface_members(static_members)
|
"Paso 4: Encontrada sección Static para GlobalDB (sin nodo Interface)."
|
||||||
else: print("Advertencia: No se encontró sección 'Static' para GlobalDB.")
|
)
|
||||||
else: print(f"Advertencia: No se encontró <Interface> para bloque {block_type_found}.")
|
result["interface"]["Static"] = parse_interface_members(
|
||||||
if not result["interface"]: print("Advertencia: No se pudo extraer información de la interfaz.")
|
static_members
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"Advertencia: No se encontró sección 'Static' para GlobalDB."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Advertencia: No se encontró <Interface> para bloque {block_type_found}."
|
||||||
|
)
|
||||||
|
if not result["interface"]:
|
||||||
|
print("Advertencia: No se pudo extraer información de la interfaz.")
|
||||||
|
|
||||||
# (Procesamiento de redes como antes, SOLO si NO es GlobalDB)
|
# (Procesamiento de redes como antes, SOLO si NO es GlobalDB)
|
||||||
if block_type_found != "GlobalDB":
|
if block_type_found != "GlobalDB":
|
||||||
|
@ -341,82 +434,147 @@ def convert_xml_to_json(xml_filepath, json_filepath, parser_map):
|
||||||
result["networks"] = []
|
result["networks"] = []
|
||||||
object_list_node = the_block.xpath("./ObjectList")
|
object_list_node = the_block.xpath("./ObjectList")
|
||||||
if object_list_node:
|
if object_list_node:
|
||||||
compile_units = object_list_node[0].xpath("./SW.Blocks.CompileUnit")
|
compile_units = object_list_node[0].xpath(
|
||||||
print(f"Paso 5: Se encontraron {len(compile_units)} elementos SW.Blocks.CompileUnit.")
|
"./SW.Blocks.CompileUnit"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Paso 5: Se encontraron {len(compile_units)} elementos SW.Blocks.CompileUnit."
|
||||||
|
)
|
||||||
|
|
||||||
# Bucle de parseo de redes (igual que antes)
|
# Bucle de parseo de redes (igual que antes)
|
||||||
for network_elem in compile_units:
|
for network_elem in compile_units:
|
||||||
networks_processed_count += 1
|
networks_processed_count += 1
|
||||||
network_id = network_elem.get("ID")
|
network_id = network_elem.get("ID")
|
||||||
if not network_id: continue
|
if not network_id:
|
||||||
|
continue
|
||||||
network_lang = "LAD"
|
network_lang = "LAD"
|
||||||
net_attr_list = network_elem.xpath("./AttributeList")
|
net_attr_list = network_elem.xpath("./AttributeList")
|
||||||
if net_attr_list:
|
if net_attr_list:
|
||||||
lang_node = net_attr_list[0].xpath("./ProgrammingLanguage/text()")
|
lang_node = net_attr_list[0].xpath(
|
||||||
if lang_node: network_lang = lang_node[0].strip()
|
"./ProgrammingLanguage/text()"
|
||||||
print(f" - Procesando Red ID={network_id}, Lenguaje Red={network_lang}")
|
)
|
||||||
|
if lang_node:
|
||||||
|
network_lang = lang_node[0].strip()
|
||||||
|
print(
|
||||||
|
f" - Procesando Red ID={network_id}, Lenguaje Red={network_lang}"
|
||||||
|
)
|
||||||
parser_func = parser_map.get(network_lang.upper())
|
parser_func = parser_map.get(network_lang.upper())
|
||||||
parsed_network_data = None
|
parsed_network_data = None
|
||||||
if parser_func:
|
if parser_func:
|
||||||
try:
|
try:
|
||||||
parsed_network_data = parser_func(network_elem)
|
parsed_network_data = parser_func(network_elem)
|
||||||
except Exception as e_parse:
|
except Exception as e_parse:
|
||||||
print(f" ERROR durante el parseo de Red {network_id} ({network_lang}): {e_parse}")
|
print(
|
||||||
|
f" ERROR durante el parseo de Red {network_id} ({network_lang}): {e_parse}"
|
||||||
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
parsed_network_data = {"id": network_id, "language": network_lang, "logic": [], "error": f"Parser failed: {e_parse}"}
|
parsed_network_data = {
|
||||||
|
"id": network_id,
|
||||||
|
"language": network_lang,
|
||||||
|
"logic": [],
|
||||||
|
"error": f"Parser failed: {e_parse}",
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
print(f" Advertencia: Lenguaje de red '{network_lang}' no soportado.")
|
print(
|
||||||
parsed_network_data = {"id": network_id, "language": network_lang, "logic": [], "error": f"Unsupported language: {network_lang}"}
|
f" Advertencia: Lenguaje de red '{network_lang}' no soportado."
|
||||||
|
)
|
||||||
|
parsed_network_data = {
|
||||||
|
"id": network_id,
|
||||||
|
"language": network_lang,
|
||||||
|
"logic": [],
|
||||||
|
"error": f"Unsupported language: {network_lang}",
|
||||||
|
}
|
||||||
|
|
||||||
if parsed_network_data:
|
if parsed_network_data:
|
||||||
title_element = network_elem.xpath(".//iface:MultilingualText[@CompositionName='Title']",namespaces=ns)
|
title_element = network_elem.xpath(
|
||||||
parsed_network_data["title"] = (get_multilingual_text(title_element[0]) if title_element else f"Network {network_id}")
|
".//iface:MultilingualText[@CompositionName='Title']",
|
||||||
comment_elem_net = network_elem.xpath("./ObjectList/MultilingualText[@CompositionName='Comment']", namespaces=ns)
|
namespaces=ns,
|
||||||
if not comment_elem_net: comment_elem_net = network_elem.xpath(".//MultilingualText[@CompositionName='Comment']", namespaces=ns) # Fallback
|
)
|
||||||
parsed_network_data["comment"] = (get_multilingual_text(comment_elem_net[0]) if comment_elem_net else "")
|
parsed_network_data["title"] = (
|
||||||
|
get_multilingual_text(title_element[0])
|
||||||
|
if title_element
|
||||||
|
else f"Network {network_id}"
|
||||||
|
)
|
||||||
|
comment_elem_net = network_elem.xpath(
|
||||||
|
"./ObjectList/MultilingualText[@CompositionName='Comment']",
|
||||||
|
namespaces=ns,
|
||||||
|
)
|
||||||
|
if not comment_elem_net:
|
||||||
|
comment_elem_net = network_elem.xpath(
|
||||||
|
".//MultilingualText[@CompositionName='Comment']",
|
||||||
|
namespaces=ns,
|
||||||
|
) # Fallback
|
||||||
|
parsed_network_data["comment"] = (
|
||||||
|
get_multilingual_text(comment_elem_net[0])
|
||||||
|
if comment_elem_net
|
||||||
|
else ""
|
||||||
|
)
|
||||||
result["networks"].append(parsed_network_data)
|
result["networks"].append(parsed_network_data)
|
||||||
|
|
||||||
if networks_processed_count == 0: print(f"Advertencia: ObjectList para {block_type_found} sin SW.Blocks.CompileUnit.")
|
if networks_processed_count == 0:
|
||||||
else: print(f"Advertencia: No se encontró ObjectList para el bloque {block_type_found}.")
|
print(
|
||||||
else: # Es GlobalDB
|
f"Advertencia: ObjectList para {block_type_found} sin SW.Blocks.CompileUnit."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Advertencia: No se encontró ObjectList para el bloque {block_type_found}."
|
||||||
|
)
|
||||||
|
else: # Es GlobalDB
|
||||||
print("Paso 5: Saltando procesamiento de redes para GlobalDB.")
|
print("Paso 5: Saltando procesamiento de redes para GlobalDB.")
|
||||||
|
|
||||||
|
|
||||||
# --- Escritura del JSON (si se encontró un objeto) ---
|
# --- Escritura del JSON (si se encontró un objeto) ---
|
||||||
if result:
|
if result:
|
||||||
print("Paso 6: Escribiendo el resultado en el archivo JSON...")
|
print("Paso 6: Escribiendo el resultado en el archivo JSON...")
|
||||||
# Validaciones finales
|
# Validaciones finales
|
||||||
if result.get("block_type") not in ["PlcUDT", "PlcTagTable"] and not result["interface"]:
|
if (
|
||||||
print("ADVERTENCIA FINAL: 'interface' está vacía en el JSON.")
|
result.get("block_type") not in ["PlcUDT", "PlcTagTable"]
|
||||||
if result.get("block_type") not in ["PlcUDT", "PlcTagTable", "GlobalDB"] and not result["networks"]:
|
and not result["interface"]
|
||||||
print("ADVERTENCIA FINAL: 'networks' está vacía en el JSON.")
|
):
|
||||||
|
print("ADVERTENCIA FINAL: 'interface' está vacía en el JSON.")
|
||||||
|
if (
|
||||||
|
result.get("block_type") not in ["PlcUDT", "PlcTagTable", "GlobalDB"]
|
||||||
|
and not result["networks"]
|
||||||
|
):
|
||||||
|
print("ADVERTENCIA FINAL: 'networks' está vacía en el JSON.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(json_filepath, "w", encoding="utf-8") as f:
|
with open(json_filepath, "w", encoding="utf-8") as f:
|
||||||
json.dump(result, f, indent=4, ensure_ascii=False)
|
json.dump(result, f, indent=4, ensure_ascii=False)
|
||||||
print("Paso 6: Escritura JSON completada.")
|
print("Paso 6: Escritura JSON completada.")
|
||||||
print(f"Conversión finalizada. JSON guardado en: '{os.path.relpath(json_filepath)}'")
|
print(
|
||||||
return True # Indicar éxito
|
f"Conversión finalizada. JSON guardado en: '{os.path.relpath(json_filepath)}'"
|
||||||
|
)
|
||||||
|
return True # Indicar éxito
|
||||||
|
|
||||||
except IOError as e: print(f"Error Crítico: No se pudo escribir JSON en '{json_filepath}'. Error: {e}"); return False
|
except IOError as e:
|
||||||
except TypeError as e: print(f"Error Crítico: Problema al serializar a JSON. Error: {e}"); return False
|
print(
|
||||||
|
f"Error Crítico: No se pudo escribir JSON en '{json_filepath}'. Error: {e}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
except TypeError as e:
|
||||||
|
print(f"Error Crítico: Problema al serializar a JSON. Error: {e}")
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
print("Error Crítico: No se pudo determinar el tipo de objeto principal en el XML.")
|
print(
|
||||||
return False
|
"Error Crítico: No se pudo determinar el tipo de objeto principal en el XML."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
except etree.XMLSyntaxError as e:
|
except etree.XMLSyntaxError as e:
|
||||||
print(f"Error Crítico: Sintaxis XML inválida en '{xml_filepath}'. Detalles: {e}")
|
print(
|
||||||
return False # Indicar fallo
|
f"Error Crítico: Sintaxis XML inválida en '{xml_filepath}'. Detalles: {e}"
|
||||||
|
)
|
||||||
|
return False # Indicar fallo
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error Crítico: Error inesperado durante la conversión: {e}")
|
print(f"Error Crítico: Error inesperado durante la conversión: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False # Indicar fallo
|
return False # Indicar fallo
|
||||||
|
|
||||||
|
|
||||||
# --- Punto de Entrada Principal (__main__) ---
|
# --- Punto de Entrada Principal (__main__) ---
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Convert Simatic XML (FC/FB/OB/DB/UDT/TagTable) to simplified JSON using dynamic parsers." # Actualizado
|
description="Convert Simatic XML (FC/FB/OB/DB/UDT/TagTable) to simplified JSON using dynamic parsers." # Actualizado
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"xml_filepath",
|
"xml_filepath",
|
||||||
|
@ -426,15 +584,20 @@ if __name__ == "__main__":
|
||||||
xml_input_file = args.xml_filepath
|
xml_input_file = args.xml_filepath
|
||||||
|
|
||||||
if not os.path.exists(xml_input_file):
|
if not os.path.exists(xml_input_file):
|
||||||
print(f"Error Crítico (x1): Archivo XML no encontrado: '{xml_input_file}'", file=sys.stderr)
|
print(
|
||||||
|
f"Error Crítico (x1): Archivo XML no encontrado: '{xml_input_file}'",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Cargar Parsers Dinámicamente ---
|
# --- Cargar Parsers Dinámicamente ---
|
||||||
loaded_parsers = load_parsers() # Carga parsers LAD/FBD/STL/SCL
|
loaded_parsers = load_parsers() # Carga parsers LAD/FBD/STL/SCL
|
||||||
if not loaded_parsers:
|
if not loaded_parsers:
|
||||||
# Continuar incluso sin parsers de red, ya que podríamos estar parseando UDT/TagTable
|
# Continuar incluso sin parsers de red, ya que podríamos estar parseando UDT/TagTable
|
||||||
print("Advertencia (x1): No se cargaron parsers de red. Se continuará para UDT/TagTable/DB.")
|
print(
|
||||||
#sys.exit(1) # Ya no salimos si no hay parsers de red
|
"Advertencia (x1): No se cargaron parsers de red. Se continuará para UDT/TagTable/DB."
|
||||||
|
)
|
||||||
|
# sys.exit(1) # Ya no salimos si no hay parsers de red
|
||||||
|
|
||||||
# Derivar nombre de salida JSON
|
# Derivar nombre de salida JSON
|
||||||
xml_filename_base = os.path.splitext(os.path.basename(xml_input_file))[0]
|
xml_filename_base = os.path.splitext(os.path.basename(xml_input_file))[0]
|
||||||
|
@ -443,14 +606,19 @@ if __name__ == "__main__":
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
json_output_file = os.path.join(output_dir, f"{xml_filename_base}.json")
|
json_output_file = os.path.join(output_dir, f"{xml_filename_base}.json")
|
||||||
|
|
||||||
print(f"(x1) Convirtiendo: '{os.path.relpath(xml_input_file)}' -> '{os.path.relpath(json_output_file)}'")
|
print(
|
||||||
|
f"(x1) Convirtiendo: '{os.path.relpath(xml_input_file)}' -> '{os.path.relpath(json_output_file)}'"
|
||||||
|
)
|
||||||
|
|
||||||
# Llamar a la función de conversión principal
|
# Llamar a la función de conversión principal
|
||||||
success = convert_xml_to_json(xml_input_file, json_output_file, loaded_parsers)
|
success = convert_xml_to_json(xml_input_file, json_output_file, loaded_parsers)
|
||||||
|
|
||||||
# Salir con código de error apropiado
|
# Salir con código de error apropiado
|
||||||
if success:
|
if success:
|
||||||
sys.exit(0) # Éxito
|
sys.exit(0) # Éxito
|
||||||
else:
|
else:
|
||||||
print(f"\nError durante la conversión de '{os.path.relpath(xml_input_file)}'.", file=sys.stderr)
|
print(
|
||||||
sys.exit(1) # Fallo
|
f"\nError durante la conversión de '{os.path.relpath(xml_input_file)}'.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1) # Fallo
|
||||||
|
|
216
x2_process.py
216
x2_process.py
|
@ -7,15 +7,15 @@ import traceback
|
||||||
import re
|
import re
|
||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
import sympy # Import sympy
|
import sympy # Import sympy
|
||||||
|
|
||||||
# Import necessary components from processors directory
|
# Import necessary components from processors directory
|
||||||
from processors.processor_utils import (
|
from processors.processor_utils import (
|
||||||
format_variable_name, # Keep if used outside processors
|
format_variable_name, # Keep if used outside processors
|
||||||
sympy_expr_to_scl, # Needed for IF grouping and maybe others
|
sympy_expr_to_scl, # Needed for IF grouping and maybe others
|
||||||
# get_target_scl_name might be used here? Unlikely.
|
# get_target_scl_name might be used here? Unlikely.
|
||||||
)
|
)
|
||||||
from processors.symbol_manager import SymbolManager # Import the manager
|
from processors.symbol_manager import SymbolManager # Import the manager
|
||||||
|
|
||||||
# --- Constantes y Configuración ---
|
# --- Constantes y Configuración ---
|
||||||
SCL_SUFFIX = "_sympy_processed"
|
SCL_SUFFIX = "_sympy_processed"
|
||||||
|
@ -25,6 +25,7 @@ SIMPLIFIED_IF_COMMENT = "// Simplified IF condition by script"
|
||||||
# Global data dictionary
|
# Global data dictionary
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
|
|
||||||
# --- (process_group_ifs y load_processors SIN CAMBIOS) ---
|
# --- (process_group_ifs y load_processors SIN CAMBIOS) ---
|
||||||
def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||||
"""
|
"""
|
||||||
|
@ -109,9 +110,15 @@ def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||||
# SCoil/RCoil might also be groupable if their SCL is final assignment
|
# SCoil/RCoil might also be groupable if their SCL is final assignment
|
||||||
"SCoil",
|
"SCoil",
|
||||||
"RCoil",
|
"RCoil",
|
||||||
"BLKMOV", # Added BLKMOV
|
"BLKMOV", # Added BLKMOV
|
||||||
"TON", "TOF", "TP", "Se", "Sd", # Added timers
|
"TON",
|
||||||
"CTU", "CTD", "CTUD", # Added counters
|
"TOF",
|
||||||
|
"TP",
|
||||||
|
"Se",
|
||||||
|
"Sd", # Added timers
|
||||||
|
"CTU",
|
||||||
|
"CTD",
|
||||||
|
"CTUD", # Added counters
|
||||||
]
|
]
|
||||||
|
|
||||||
for consumer_instr in network_logic:
|
for consumer_instr in network_logic:
|
||||||
|
@ -137,7 +144,7 @@ def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||||
# Check if consumer is groupable AND has its final SCL generated
|
# Check if consumer is groupable AND has its final SCL generated
|
||||||
if (
|
if (
|
||||||
is_enabled_by_us
|
is_enabled_by_us
|
||||||
and consumer_type.endswith(SCL_SUFFIX) # Check if processed
|
and consumer_type.endswith(SCL_SUFFIX) # Check if processed
|
||||||
and consumer_type_original in groupable_types
|
and consumer_type_original in groupable_types
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -148,7 +155,7 @@ def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||||
# If consumer SCL itself is an IF generated by EN, take the body
|
# If consumer SCL itself is an IF generated by EN, take the body
|
||||||
if consumer_scl.strip().startswith("IF"):
|
if consumer_scl.strip().startswith("IF"):
|
||||||
match = re.search(
|
match = re.search(
|
||||||
r"IF\s+.*?THEN\s*(.*?)\s*END_IF;", # More robust regex
|
r"IF\s+.*?THEN\s*(.*?)\s*END_IF;", # More robust regex
|
||||||
consumer_scl,
|
consumer_scl,
|
||||||
re.DOTALL | re.IGNORECASE,
|
re.DOTALL | re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
@ -203,18 +210,19 @@ def process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data):
|
||||||
|
|
||||||
return made_change
|
return made_change
|
||||||
|
|
||||||
|
|
||||||
def load_processors(processors_dir="processors"):
|
def load_processors(processors_dir="processors"):
|
||||||
"""
|
"""
|
||||||
Escanea el directorio, importa módulos, construye el mapa y una lista
|
Escanea el directorio, importa módulos, construye el mapa y una lista
|
||||||
ordenada por prioridad.
|
ordenada por prioridad.
|
||||||
"""
|
"""
|
||||||
processor_map = {}
|
processor_map = {}
|
||||||
processor_list_unsorted = [] # Lista para guardar (priority, type_name, func)
|
processor_list_unsorted = [] # Lista para guardar (priority, type_name, func)
|
||||||
default_priority = 10 # Prioridad si no se define en get_processor_info
|
default_priority = 10 # Prioridad si no se define en get_processor_info
|
||||||
|
|
||||||
if not os.path.isdir(processors_dir):
|
if not os.path.isdir(processors_dir):
|
||||||
print(f"Error: Directorio de procesadores no encontrado: '{processors_dir}'")
|
print(f"Error: Directorio de procesadores no encontrado: '{processors_dir}'")
|
||||||
return processor_map, [] # Devuelve mapa vacío y lista vacía
|
return processor_map, [] # Devuelve mapa vacío y lista vacía
|
||||||
|
|
||||||
print(f"Cargando procesadores desde: '{processors_dir}'")
|
print(f"Cargando procesadores desde: '{processors_dir}'")
|
||||||
processors_package = os.path.basename(processors_dir)
|
processors_package = os.path.basename(processors_dir)
|
||||||
|
@ -325,9 +333,15 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
print(f"Procesando bloque tipo: {block_type}")
|
print(f"Procesando bloque tipo: {block_type}")
|
||||||
|
|
||||||
# --- MODIFICADO: SALTAR PROCESAMIENTO PARA DB, UDT, TAG TABLE ---
|
# --- MODIFICADO: SALTAR PROCESAMIENTO PARA DB, UDT, TAG TABLE ---
|
||||||
if block_type in ["GlobalDB", "PlcUDT", "PlcTagTable"]: # <-- Comprobar tipos a saltar
|
if block_type in [
|
||||||
|
"GlobalDB",
|
||||||
|
"PlcUDT",
|
||||||
|
"PlcTagTable",
|
||||||
|
]: # <-- Comprobar tipos a saltar
|
||||||
print(f"INFO: El bloque es {block_type}. Saltando procesamiento lógico de x2.")
|
print(f"INFO: El bloque es {block_type}. Saltando procesamiento lógico de x2.")
|
||||||
print(f"Guardando JSON de {block_type} (sin cambios lógicos) en: {output_json_filepath}")
|
print(
|
||||||
|
f"Guardando JSON de {block_type} (sin cambios lógicos) en: {output_json_filepath}"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
with open(output_json_filepath, "w", encoding="utf-8") as f:
|
with open(output_json_filepath, "w", encoding="utf-8") as f:
|
||||||
json.dump(data, f, indent=4, ensure_ascii=False)
|
json.dump(data, f, indent=4, ensure_ascii=False)
|
||||||
|
@ -355,14 +369,26 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
current_access_map = {}
|
current_access_map = {}
|
||||||
for instr in network.get("logic", []):
|
for instr in network.get("logic", []):
|
||||||
for _, source in instr.get("inputs", {}).items():
|
for _, source in instr.get("inputs", {}).items():
|
||||||
sources_to_check = (source if isinstance(source, list) else ([source] if isinstance(source, dict) else []))
|
sources_to_check = (
|
||||||
|
source
|
||||||
|
if isinstance(source, list)
|
||||||
|
else ([source] if isinstance(source, dict) else [])
|
||||||
|
)
|
||||||
for src in sources_to_check:
|
for src in sources_to_check:
|
||||||
if (isinstance(src, dict) and src.get("uid") and src.get("type") in ["variable", "constant"]):
|
if (
|
||||||
|
isinstance(src, dict)
|
||||||
|
and src.get("uid")
|
||||||
|
and src.get("type") in ["variable", "constant"]
|
||||||
|
):
|
||||||
current_access_map[src["uid"]] = src
|
current_access_map[src["uid"]] = src
|
||||||
for _, dest_list in instr.get("outputs", {}).items():
|
for _, dest_list in instr.get("outputs", {}).items():
|
||||||
if isinstance(dest_list, list):
|
if isinstance(dest_list, list):
|
||||||
for dest in dest_list:
|
for dest in dest_list:
|
||||||
if (isinstance(dest, dict) and dest.get("uid") and dest.get("type") in ["variable", "constant"]):
|
if (
|
||||||
|
isinstance(dest, dict)
|
||||||
|
and dest.get("uid")
|
||||||
|
and dest.get("type") in ["variable", "constant"]
|
||||||
|
):
|
||||||
current_access_map[dest["uid"]] = dest
|
current_access_map[dest["uid"]] = dest
|
||||||
network_access_maps[net_id] = current_access_map
|
network_access_maps[net_id] = current_access_map
|
||||||
|
|
||||||
|
@ -391,7 +417,8 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
for network in data.get("networks", []):
|
for network in data.get("networks", []):
|
||||||
network_id = network["id"]
|
network_id = network["id"]
|
||||||
network_lang = network.get("language", "LAD")
|
network_lang = network.get("language", "LAD")
|
||||||
if network_lang == "STL": continue
|
if network_lang == "STL":
|
||||||
|
continue
|
||||||
|
|
||||||
access_map = network_access_maps.get(network_id, {})
|
access_map = network_access_maps.get(network_id, {})
|
||||||
network_logic = network.get("logic", [])
|
network_logic = network.get("logic", [])
|
||||||
|
@ -399,30 +426,51 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
instr_uid = instruction.get("instruction_uid")
|
instr_uid = instruction.get("instruction_uid")
|
||||||
instr_type_current = instruction.get("type", "Unknown")
|
instr_type_current = instruction.get("type", "Unknown")
|
||||||
|
|
||||||
if (instr_type_current.endswith(SCL_SUFFIX) or "_error" in instr_type_current or instruction.get("grouped", False) or
|
if (
|
||||||
instr_type_current in ["RAW_STL_CHUNK", "RAW_SCL_CHUNK", "UNSUPPORTED_LANG", "UNSUPPORTED_CONTENT", "PARSING_ERROR"]):
|
instr_type_current.endswith(SCL_SUFFIX)
|
||||||
|
or "_error" in instr_type_current
|
||||||
|
or instruction.get("grouped", False)
|
||||||
|
or instr_type_current
|
||||||
|
in [
|
||||||
|
"RAW_STL_CHUNK",
|
||||||
|
"RAW_SCL_CHUNK",
|
||||||
|
"UNSUPPORTED_LANG",
|
||||||
|
"UNSUPPORTED_CONTENT",
|
||||||
|
"PARSING_ERROR",
|
||||||
|
]
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
lookup_key = instr_type_current.lower()
|
lookup_key = instr_type_current.lower()
|
||||||
effective_type_name = lookup_key
|
effective_type_name = lookup_key
|
||||||
if instr_type_current == "Call":
|
if instr_type_current == "Call":
|
||||||
call_block_type = instruction.get("block_type", "").upper()
|
call_block_type = instruction.get("block_type", "").upper()
|
||||||
if call_block_type == "FC": effective_type_name = "call_fc"
|
if call_block_type == "FC":
|
||||||
elif call_block_type == "FB": effective_type_name = "call_fb"
|
effective_type_name = "call_fc"
|
||||||
|
elif call_block_type == "FB":
|
||||||
|
effective_type_name = "call_fb"
|
||||||
|
|
||||||
if effective_type_name == current_type_name:
|
if effective_type_name == current_type_name:
|
||||||
try:
|
try:
|
||||||
changed = func_to_call(instruction, network_id, sympy_map, symbol_manager, data)
|
changed = func_to_call(
|
||||||
|
instruction, network_id, sympy_map, symbol_manager, data
|
||||||
|
)
|
||||||
if changed:
|
if changed:
|
||||||
made_change_in_base_pass = True
|
made_change_in_base_pass = True
|
||||||
num_sympy_processed_this_pass += 1
|
num_sympy_processed_this_pass += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR(SymPy Base) al procesar {instr_type_current} UID {instr_uid}: {e}")
|
print(
|
||||||
|
f"ERROR(SymPy Base) al procesar {instr_type_current} UID {instr_uid}: {e}"
|
||||||
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
instruction["scl"] = f"// ERROR en SymPy procesador base: {e}"
|
instruction["scl"] = (
|
||||||
|
f"// ERROR en SymPy procesador base: {e}"
|
||||||
|
)
|
||||||
instruction["type"] = instr_type_current + "_error"
|
instruction["type"] = instr_type_current + "_error"
|
||||||
made_change_in_base_pass = True
|
made_change_in_base_pass = True
|
||||||
print(f" -> {num_sympy_processed_this_pass} instrucciones (no STL) procesadas con SymPy.")
|
print(
|
||||||
|
f" -> {num_sympy_processed_this_pass} instrucciones (no STL) procesadas con SymPy."
|
||||||
|
)
|
||||||
|
|
||||||
# FASE 2: Agrupación IF (Ignorando STL)
|
# FASE 2: Agrupación IF (Ignorando STL)
|
||||||
if made_change_in_base_pass or passes == 1:
|
if made_change_in_base_pass or passes == 1:
|
||||||
|
@ -431,30 +479,58 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
for network in data.get("networks", []):
|
for network in data.get("networks", []):
|
||||||
network_id = network["id"]
|
network_id = network["id"]
|
||||||
network_lang = network.get("language", "LAD")
|
network_lang = network.get("language", "LAD")
|
||||||
if network_lang == "STL": continue
|
if network_lang == "STL":
|
||||||
|
continue
|
||||||
network_logic = network.get("logic", [])
|
network_logic = network.get("logic", [])
|
||||||
uids_in_network = sorted([instr.get("instruction_uid", "Z") for instr in network_logic if instr.get("instruction_uid")])
|
uids_in_network = sorted(
|
||||||
|
[
|
||||||
|
instr.get("instruction_uid", "Z")
|
||||||
|
for instr in network_logic
|
||||||
|
if instr.get("instruction_uid")
|
||||||
|
]
|
||||||
|
)
|
||||||
for uid_to_process in uids_in_network:
|
for uid_to_process in uids_in_network:
|
||||||
instruction = next((instr for instr in network_logic if instr.get("instruction_uid") == uid_to_process), None)
|
instruction = next(
|
||||||
if not instruction: continue
|
(
|
||||||
if instruction.get("grouped") or "_error" in instruction.get("type", ""): continue
|
instr
|
||||||
if instruction.get("type", "").endswith(SCL_SUFFIX):
|
for instr in network_logic
|
||||||
|
if instr.get("instruction_uid") == uid_to_process
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if not instruction:
|
||||||
|
continue
|
||||||
|
if instruction.get("grouped") or "_error" in instruction.get(
|
||||||
|
"type", ""
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
if instruction.get("type", "").endswith(SCL_SUFFIX):
|
||||||
try:
|
try:
|
||||||
group_changed = process_group_ifs(instruction, network_id, sympy_map, symbol_manager, data)
|
group_changed = process_group_ifs(
|
||||||
|
instruction, network_id, sympy_map, symbol_manager, data
|
||||||
|
)
|
||||||
if group_changed:
|
if group_changed:
|
||||||
made_change_in_group_pass = True
|
made_change_in_group_pass = True
|
||||||
num_grouped_this_pass += 1
|
num_grouped_this_pass += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR(GroupLoop) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}")
|
print(
|
||||||
|
f"ERROR(GroupLoop) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}"
|
||||||
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print(f" -> {num_grouped_this_pass} agrupaciones realizadas (en redes no STL).")
|
print(
|
||||||
|
f" -> {num_grouped_this_pass} agrupaciones realizadas (en redes no STL)."
|
||||||
|
)
|
||||||
|
|
||||||
# Comprobar si se completó
|
# Comprobar si se completó
|
||||||
if not made_change_in_base_pass and not made_change_in_group_pass:
|
if not made_change_in_base_pass and not made_change_in_group_pass:
|
||||||
print(f"\n--- No se hicieron más cambios en el pase {passes}. Proceso iterativo completado. ---")
|
print(
|
||||||
|
f"\n--- No se hicieron más cambios en el pase {passes}. Proceso iterativo completado. ---"
|
||||||
|
)
|
||||||
processing_complete = True
|
processing_complete = True
|
||||||
else:
|
else:
|
||||||
print(f"--- Fin Pase {passes}: {num_sympy_processed_this_pass} proc SymPy, {num_grouped_this_pass} agrup. Continuando...")
|
print(
|
||||||
|
f"--- Fin Pase {passes}: {num_sympy_processed_this_pass} proc SymPy, {num_grouped_this_pass} agrup. Continuando..."
|
||||||
|
)
|
||||||
if passes == max_passes and not processing_complete:
|
if passes == max_passes and not processing_complete:
|
||||||
print(f"\n--- ADVERTENCIA: Límite de {max_passes} pases alcanzado...")
|
print(f"\n--- ADVERTENCIA: Límite de {max_passes} pases alcanzado...")
|
||||||
|
|
||||||
|
@ -464,23 +540,43 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
print(f"\n--- Verificación Final de Instrucciones No Procesadas ({block_type}) ---")
|
print(f"\n--- Verificación Final de Instrucciones No Procesadas ({block_type}) ---")
|
||||||
unprocessed_count = 0
|
unprocessed_count = 0
|
||||||
unprocessed_details = []
|
unprocessed_details = []
|
||||||
ignored_types = ["raw_scl_chunk", "unsupported_lang", "raw_stl_chunk", "unsupported_content", "parsing_error"]
|
ignored_types = [
|
||||||
|
"raw_scl_chunk",
|
||||||
|
"unsupported_lang",
|
||||||
|
"raw_stl_chunk",
|
||||||
|
"unsupported_content",
|
||||||
|
"parsing_error",
|
||||||
|
]
|
||||||
for network in data.get("networks", []):
|
for network in data.get("networks", []):
|
||||||
network_id = network.get("id", "Unknown ID")
|
network_id = network.get("id", "Unknown ID")
|
||||||
network_title = network.get("title", f"Network {network_id}")
|
network_title = network.get("title", f"Network {network_id}")
|
||||||
network_lang = network.get("language", "LAD")
|
network_lang = network.get("language", "LAD")
|
||||||
if network_lang == "STL": continue
|
if network_lang == "STL":
|
||||||
|
continue
|
||||||
for instruction in network.get("logic", []):
|
for instruction in network.get("logic", []):
|
||||||
instr_uid = instruction.get("instruction_uid", "Unknown UID")
|
instr_uid = instruction.get("instruction_uid", "Unknown UID")
|
||||||
instr_type = instruction.get("type", "Unknown Type")
|
instr_type = instruction.get("type", "Unknown Type")
|
||||||
is_grouped = instruction.get("grouped", False)
|
is_grouped = instruction.get("grouped", False)
|
||||||
if (not instr_type.endswith(SCL_SUFFIX) and "_error" not in instr_type and not is_grouped and instr_type.lower() not in ignored_types):
|
if (
|
||||||
|
not instr_type.endswith(SCL_SUFFIX)
|
||||||
|
and "_error" not in instr_type
|
||||||
|
and not is_grouped
|
||||||
|
and instr_type.lower() not in ignored_types
|
||||||
|
):
|
||||||
unprocessed_count += 1
|
unprocessed_count += 1
|
||||||
unprocessed_details.append(f" - Red '{network_title}' (ID: {network_id}, Lang: {network_lang}), Instrucción UID: {instr_uid}, Tipo: '{instr_type}'")
|
unprocessed_details.append(
|
||||||
|
f" - Red '{network_title}' (ID: {network_id}, Lang: {network_lang}), Instrucción UID: {instr_uid}, Tipo: '{instr_type}'"
|
||||||
|
)
|
||||||
if unprocessed_count > 0:
|
if unprocessed_count > 0:
|
||||||
print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones (no STL) que parecen no haber sido procesadas:")
|
print(
|
||||||
for detail in unprocessed_details: print(detail)
|
f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones (no STL) que parecen no haber sido procesadas:"
|
||||||
else: print("INFO: Todas las instrucciones relevantes (no STL) parecen haber sido procesadas o agrupadas.")
|
)
|
||||||
|
for detail in unprocessed_details:
|
||||||
|
print(detail)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"INFO: Todas las instrucciones relevantes (no STL) parecen haber sido procesadas o agrupadas."
|
||||||
|
)
|
||||||
|
|
||||||
print(f"\nGuardando JSON procesado ({block_type}) en: {output_json_filepath}")
|
print(f"\nGuardando JSON procesado ({block_type}) en: {output_json_filepath}")
|
||||||
try:
|
try:
|
||||||
|
@ -489,19 +585,27 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
||||||
print("Guardado completado.")
|
print("Guardado completado.")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error Crítico al guardar JSON procesado: {e}");
|
print(f"Error Crítico al guardar JSON procesado: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# --- Ejecución (MODIFICADO) ---
|
# --- Ejecución (MODIFICADO) ---
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Process simplified JSON to embed SCL logic. Expects original XML filepath as argument.")
|
parser = argparse.ArgumentParser(
|
||||||
parser.add_argument("source_xml_filepath", help="Path to the original source XML file (passed from x0_main.py).")
|
description="Process simplified JSON to embed SCL logic. Expects original XML filepath as argument."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"source_xml_filepath",
|
||||||
|
help="Path to the original source XML file (passed from x0_main.py).",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
source_xml_file = args.source_xml_filepath
|
source_xml_file = args.source_xml_filepath
|
||||||
|
|
||||||
if not os.path.exists(source_xml_file):
|
if not os.path.exists(source_xml_file):
|
||||||
print(f"Advertencia (x2): Archivo XML original no encontrado: '{source_xml_file}', pero se intentará encontrar el JSON correspondiente.")
|
print(
|
||||||
|
f"Advertencia (x2): Archivo XML original no encontrado: '{source_xml_file}', pero se intentará encontrar el JSON correspondiente."
|
||||||
|
)
|
||||||
|
|
||||||
xml_filename_base = os.path.splitext(os.path.basename(source_xml_file))[0]
|
xml_filename_base = os.path.splitext(os.path.basename(source_xml_file))[0]
|
||||||
base_dir = os.path.dirname(source_xml_file)
|
base_dir = os.path.dirname(source_xml_file)
|
||||||
|
@ -511,11 +615,17 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
os.makedirs(parsing_dir, exist_ok=True)
|
os.makedirs(parsing_dir, exist_ok=True)
|
||||||
|
|
||||||
print(f"(x2) Procesando: '{os.path.relpath(input_json_file)}' -> '{os.path.relpath(output_json_file)}'")
|
print(
|
||||||
|
f"(x2) Procesando: '{os.path.relpath(input_json_file)}' -> '{os.path.relpath(output_json_file)}'"
|
||||||
|
)
|
||||||
|
|
||||||
if not os.path.exists(input_json_file):
|
if not os.path.exists(input_json_file):
|
||||||
print(f"Error Fatal (x2): El archivo de entrada JSON no existe: '{input_json_file}'")
|
print(
|
||||||
print(f"Asegúrate de que 'x1_to_json.py' se ejecutó correctamente para '{os.path.relpath(source_xml_file)}'.")
|
f"Error Fatal (x2): El archivo de entrada JSON no existe: '{input_json_file}'"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Asegúrate de que 'x1_to_json.py' se ejecutó correctamente para '{os.path.relpath(source_xml_file)}'."
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -525,6 +635,8 @@ if __name__ == "__main__":
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error Crítico (x2) durante el procesamiento de '{input_json_file}': {e}")
|
print(
|
||||||
|
f"Error Crítico (x2) durante el procesamiento de '{input_json_file}': {e}"
|
||||||
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
|
@ -13,6 +13,7 @@ try:
|
||||||
from generators.generate_scl_code_block import generate_scl_for_code_block
|
from generators.generate_scl_code_block import generate_scl_for_code_block
|
||||||
from generators.generate_md_udt import generate_udt_markdown
|
from generators.generate_md_udt import generate_udt_markdown
|
||||||
from generators.generate_md_tag_table import generate_tag_table_markdown
|
from generators.generate_md_tag_table import generate_tag_table_markdown
|
||||||
|
|
||||||
# Importar format_variable_name (necesario para el nombre de archivo)
|
# Importar format_variable_name (necesario para el nombre de archivo)
|
||||||
from generators.generator_utils import format_variable_name
|
from generators.generator_utils import format_variable_name
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
|
@ -20,6 +21,7 @@ except ImportError as e:
|
||||||
print("Asegúrate de que el directorio 'generators' y sus archivos .py existen.")
|
print("Asegúrate de que el directorio 'generators' y sus archivos .py existen.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
# --- Función Principal de Generación (Despachador) ---
|
# --- Función Principal de Generación (Despachador) ---
|
||||||
def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
||||||
"""
|
"""
|
||||||
|
@ -35,15 +37,19 @@ def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
||||||
with open(processed_json_filepath, "r", encoding="utf-8") as f:
|
with open(processed_json_filepath, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error al cargar/parsear JSON: {e}"); traceback.print_exc(); return
|
print(f"Error al cargar/parsear JSON: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
return
|
||||||
|
|
||||||
block_name = data.get("block_name", "UnknownBlock")
|
block_name = data.get("block_name", "UnknownBlock")
|
||||||
block_type = data.get("block_type", "Unknown")
|
block_type = data.get("block_type", "Unknown")
|
||||||
scl_block_name = format_variable_name(block_name) # Nombre seguro para archivo
|
scl_block_name = format_variable_name(block_name) # Nombre seguro para archivo
|
||||||
output_content = []
|
output_content = []
|
||||||
output_extension = ".scl" # Default
|
output_extension = ".scl" # Default
|
||||||
|
|
||||||
print(f"Generando salida para: {block_type} '{scl_block_name}' (Original: {block_name})")
|
print(
|
||||||
|
f"Generando salida para: {block_type} '{scl_block_name}' (Original: {block_name})"
|
||||||
|
)
|
||||||
|
|
||||||
# --- Selección del Generador y Extensión ---
|
# --- Selección del Generador y Extensión ---
|
||||||
generation_function = None
|
generation_function = None
|
||||||
|
@ -63,8 +69,10 @@ def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
||||||
print(f" -> Modo de generación: {block_type} SCL")
|
print(f" -> Modo de generación: {block_type} SCL")
|
||||||
generation_function = generate_scl_for_code_block
|
generation_function = generate_scl_for_code_block
|
||||||
output_extension = ".scl"
|
output_extension = ".scl"
|
||||||
else: # Tipo desconocido
|
else: # Tipo desconocido
|
||||||
print(f"Error: Tipo de bloque desconocido '{block_type}'. No se generará archivo.")
|
print(
|
||||||
|
f"Error: Tipo de bloque desconocido '{block_type}'. No se generará archivo."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# --- Llamar a la función generadora ---
|
# --- Llamar a la función generadora ---
|
||||||
|
@ -72,9 +80,11 @@ def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
||||||
try:
|
try:
|
||||||
output_content = generation_function(data)
|
output_content = generation_function(data)
|
||||||
except Exception as gen_e:
|
except Exception as gen_e:
|
||||||
print(f"Error durante la generación de contenido para {block_type} '{scl_block_name}': {gen_e}")
|
print(
|
||||||
|
f"Error durante la generación de contenido para {block_type} '{scl_block_name}': {gen_e}"
|
||||||
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return # No intentar escribir si la generación falla
|
return # No intentar escribir si la generación falla
|
||||||
|
|
||||||
# --- Escritura del Archivo de Salida ---
|
# --- Escritura del Archivo de Salida ---
|
||||||
output_filename_base = f"{scl_block_name}{output_extension}"
|
output_filename_base = f"{scl_block_name}{output_extension}"
|
||||||
|
@ -91,20 +101,35 @@ def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
||||||
print(f"Error al escribir el archivo {output_extension.upper()}: {e}")
|
print(f"Error al escribir el archivo {output_extension.upper()}: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
# --- Ejecución ---
|
# --- Ejecución ---
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Generate final SCL or Markdown file.")
|
parser = argparse.ArgumentParser(description="Generate final SCL or Markdown file.")
|
||||||
parser.add_argument("source_xml_filepath", help="Path to the original source XML file.")
|
parser.add_argument(
|
||||||
args = parser.parse_args(); source_xml_file = args.source_xml_filepath
|
"source_xml_filepath", help="Path to the original source XML file."
|
||||||
if not os.path.exists(source_xml_file): print(f"Advertencia (x3): Archivo XML original no encontrado: '{source_xml_file}'.")
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
source_xml_file = args.source_xml_filepath
|
||||||
|
if not os.path.exists(source_xml_file):
|
||||||
|
print(
|
||||||
|
f"Advertencia (x3): Archivo XML original no encontrado: '{source_xml_file}'."
|
||||||
|
)
|
||||||
xml_filename_base = os.path.splitext(os.path.basename(source_xml_file))[0]
|
xml_filename_base = os.path.splitext(os.path.basename(source_xml_file))[0]
|
||||||
base_dir = os.path.dirname(source_xml_file)
|
base_dir = os.path.dirname(source_xml_file)
|
||||||
parsing_dir = os.path.join(base_dir, "parsing")
|
parsing_dir = os.path.join(base_dir, "parsing")
|
||||||
input_json_file = os.path.join(parsing_dir, f"{xml_filename_base}_processed.json")
|
input_json_file = os.path.join(parsing_dir, f"{xml_filename_base}_processed.json")
|
||||||
output_dir = base_dir
|
output_dir = base_dir
|
||||||
print(f"(x3) Generando SCL/MD desde: '{os.path.relpath(input_json_file)}' en directorio: '{os.path.relpath(output_dir)}'")
|
print(
|
||||||
|
f"(x3) Generando SCL/MD desde: '{os.path.relpath(input_json_file)}' en directorio: '{os.path.relpath(output_dir)}'"
|
||||||
|
)
|
||||||
if not os.path.exists(input_json_file):
|
if not os.path.exists(input_json_file):
|
||||||
print(f"Error Fatal (x3): JSON procesado no encontrado: '{input_json_file}'"); sys.exit(1)
|
print(f"Error Fatal (x3): JSON procesado no encontrado: '{input_json_file}'")
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
try: generate_scl_or_markdown(input_json_file, output_dir); sys.exit(0)
|
try:
|
||||||
except Exception as e: print(f"Error Crítico (x3): {e}"); traceback.print_exc(); sys.exit(1)
|
generate_scl_or_markdown(input_json_file, output_dir)
|
||||||
|
sys.exit(0)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error Crítico (x3): {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
|
|
Loading…
Reference in New Issue