441 lines
18 KiB
Python
441 lines
18 KiB
Python
"""
|
|
export_cross_references_from_tia : Script para exportar las referencias cruzadas
|
|
de un proyecto TIA Portal a archivos (probablemente XML).
|
|
"""
|
|
|
|
import tkinter as tk
|
|
from tkinter import filedialog
|
|
import os
|
|
import sys
|
|
import traceback
|
|
from pathlib import Path
|
|
|
|
script_root = os.path.dirname(
|
|
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
|
)
|
|
sys.path.append(script_root)
|
|
from backend.script_utils import load_configuration
|
|
|
|
# --- Configuration ---
|
|
# Supported TIA Portal versions mapping (extension -> version)
|
|
SUPPORTED_TIA_VERSIONS = {
|
|
".ap18": "18.0",
|
|
".ap19": "19.0",
|
|
".ap20": "20.0"
|
|
}
|
|
|
|
# Filter for cross-references. Based on documentation:
|
|
# 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects'
|
|
# Using 1 to export all. 0 might also work as a default in some API versions.
|
|
CROSS_REF_FILTER = 1
|
|
|
|
# --- TIA Scripting Import Handling ---
|
|
if os.getenv("TIA_SCRIPTING"):
|
|
sys.path.append(os.getenv("TIA_SCRIPTING"))
|
|
else:
|
|
pass
|
|
|
|
try:
|
|
import siemens_tia_scripting as ts
|
|
except ImportError:
|
|
print("ERROR: Error al importar 'siemens_tia_scripting'.")
|
|
print("Asegúrese de que:")
|
|
print("1. TIA Portal Openness está instalado.")
|
|
print(
|
|
"2. El módulo 'siemens_tia_scripting' de Python está instalado (pip install ...) o"
|
|
)
|
|
print(
|
|
" la ruta a sus binarios está configurada en la variable de entorno 'TIA_SCRIPTING'."
|
|
)
|
|
print(
|
|
"3. Está usando una versión compatible de Python (ej. 3.12.X según la documentación)."
|
|
)
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"Ocurrió un error inesperado durante la importación: {e}")
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
# --- Functions ---
|
|
|
|
def get_supported_filetypes():
|
|
"""Returns the supported file types for TIA Portal projects."""
|
|
filetypes = []
|
|
for ext, version in SUPPORTED_TIA_VERSIONS.items():
|
|
version_major = version.split('.')[0]
|
|
filetypes.append((f"TIA Portal V{version_major} Projects", f"*{ext}"))
|
|
|
|
# Add option to show all supported files
|
|
all_extensions = " ".join([f"*{ext}" for ext in SUPPORTED_TIA_VERSIONS.keys()])
|
|
filetypes.insert(0, ("All TIA Portal Projects", all_extensions))
|
|
|
|
return filetypes
|
|
|
|
def detect_tia_version(project_file_path):
|
|
"""Detects TIA Portal version based on file extension."""
|
|
file_path = Path(project_file_path)
|
|
file_extension = file_path.suffix.lower()
|
|
|
|
if file_extension in SUPPORTED_TIA_VERSIONS:
|
|
detected_version = SUPPORTED_TIA_VERSIONS[file_extension]
|
|
print(f"Versión de TIA Portal detectada: {detected_version} (de la extensión {file_extension})")
|
|
return detected_version
|
|
else:
|
|
print(f"ADVERTENCIA: Extensión de archivo no reconocida '{file_extension}'. Extensiones soportadas: {list(SUPPORTED_TIA_VERSIONS.keys())}")
|
|
# Default to version 18.0 for backward compatibility
|
|
print("Usando por defecto TIA Portal V18.0")
|
|
return "18.0"
|
|
|
|
def select_project_file():
|
|
"""Opens a dialog to select a TIA Portal project file."""
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
file_path = filedialog.askopenfilename(
|
|
title="Seleccionar archivo de proyecto TIA Portal",
|
|
filetypes=get_supported_filetypes()
|
|
)
|
|
root.destroy()
|
|
if not file_path:
|
|
print("No se seleccionó ningún archivo de proyecto. Saliendo.")
|
|
sys.exit(0)
|
|
return file_path
|
|
|
|
def export_plc_cross_references(plc, export_base_dir):
|
|
"""Exports cross-references for various elements from a given PLC."""
|
|
plc_name = plc.get_name()
|
|
print(f"\n--- Procesando PLC: {plc_name} ---")
|
|
|
|
# Define base export path for this PLC's cross-references
|
|
plc_export_dir = export_base_dir / plc_name
|
|
plc_export_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# --- Export Program Block Cross-References ---
|
|
blocks_cr_exported = 0
|
|
blocks_cr_skipped = 0
|
|
print(f"\n[PLC: {plc_name}] Exportando referencias cruzadas de bloques de programa...")
|
|
blocks_cr_path = plc_export_dir / "ProgramBlocks_CR"
|
|
blocks_cr_path.mkdir(exist_ok=True)
|
|
print(f" Destino: {blocks_cr_path}")
|
|
|
|
try:
|
|
program_blocks = plc.get_program_blocks()
|
|
print(f" Se encontraron {len(program_blocks)} bloques de programa.")
|
|
for block in program_blocks:
|
|
block_name = block.get_name()
|
|
print(f" Procesando bloque: {block_name}...")
|
|
try:
|
|
print(f" Exportando referencias cruzadas para {block_name}...")
|
|
block.export_cross_references(
|
|
target_directorypath=str(blocks_cr_path),
|
|
filter=CROSS_REF_FILTER,
|
|
)
|
|
blocks_cr_exported += 1
|
|
except RuntimeError as block_ex:
|
|
print(
|
|
f" ERROR TIA al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
|
)
|
|
blocks_cr_skipped += 1
|
|
except Exception as block_ex:
|
|
print(
|
|
f" ERROR GENERAL al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
|
)
|
|
traceback.print_exc()
|
|
blocks_cr_skipped += 1
|
|
print(
|
|
f" Resumen de exportación de referencias cruzadas de bloques: Exportados={blocks_cr_exported}, Omitidos/Errores={blocks_cr_skipped}"
|
|
)
|
|
except AttributeError:
|
|
print(
|
|
" Error de atributo: No se pudo encontrar 'get_program_blocks' en el objeto PLC. Omitiendo bloques de programa."
|
|
)
|
|
except Exception as e:
|
|
print(f" ERROR al acceder a los bloques de programa para exportar referencias cruzadas: {e}")
|
|
traceback.print_exc()
|
|
|
|
# --- Export PLC Tag Table Cross-References ---
|
|
tags_cr_exported = 0
|
|
tags_cr_skipped = 0
|
|
print(f"\n[PLC: {plc_name}] Exportando referencias cruzadas de tablas de variables...")
|
|
tags_cr_path = plc_export_dir / "PlcTags_CR"
|
|
tags_cr_path.mkdir(exist_ok=True)
|
|
print(f" Destino: {tags_cr_path}")
|
|
|
|
try:
|
|
tag_tables = plc.get_plc_tag_tables()
|
|
print(f" Se encontraron {len(tag_tables)} tablas de variables.")
|
|
for table in tag_tables:
|
|
table_name = table.get_name()
|
|
print(f" Procesando tabla de variables: {table_name}...")
|
|
try:
|
|
print(f" Exportando referencias cruzadas para {table_name}...")
|
|
table.export_cross_references(
|
|
target_directorypath=str(tags_cr_path),
|
|
filter=CROSS_REF_FILTER
|
|
)
|
|
tags_cr_exported += 1
|
|
except RuntimeError as table_ex:
|
|
print(
|
|
f" ERROR TIA al exportar referencias cruzadas para la tabla {table_name}: {table_ex}"
|
|
)
|
|
tags_cr_skipped += 1
|
|
except Exception as table_ex:
|
|
print(
|
|
f" ERROR GENERAL al exportar referencias cruzadas para la tabla {table_name}: {table_ex}"
|
|
)
|
|
traceback.print_exc()
|
|
tags_cr_skipped += 1
|
|
print(
|
|
f" Resumen de exportación de referencias cruzadas de tablas: Exportados={tags_cr_exported}, Omitidos/Errores={tags_cr_skipped}"
|
|
)
|
|
except AttributeError:
|
|
print(
|
|
" Error de atributo: No se pudo encontrar 'get_plc_tag_tables' en el objeto PLC. Omitiendo tablas de variables."
|
|
)
|
|
except Exception as e:
|
|
print(f" ERROR al acceder a las tablas de variables para exportar referencias cruzadas: {e}")
|
|
traceback.print_exc()
|
|
|
|
# --- Export PLC Data Type (UDT) Cross-References ---
|
|
udts_cr_exported = 0
|
|
udts_cr_skipped = 0
|
|
print(f"\n[PLC: {plc_name}] Exportando referencias cruzadas de tipos de datos PLC (UDTs)...")
|
|
udts_cr_path = plc_export_dir / "PlcDataTypes_CR"
|
|
udts_cr_path.mkdir(exist_ok=True)
|
|
print(f" Destino: {udts_cr_path}")
|
|
|
|
try:
|
|
udts = plc.get_user_data_types()
|
|
print(f" Se encontraron {len(udts)} UDTs.")
|
|
for udt in udts:
|
|
udt_name = udt.get_name()
|
|
print(f" Procesando UDT: {udt_name}...")
|
|
try:
|
|
print(f" Exportando referencias cruzadas para {udt_name}...")
|
|
udt.export_cross_references(
|
|
target_directorypath=str(udts_cr_path),
|
|
filter=CROSS_REF_FILTER
|
|
)
|
|
udts_cr_exported += 1
|
|
except RuntimeError as udt_ex:
|
|
print(
|
|
f" ERROR TIA al exportar referencias cruzadas para el UDT {udt_name}: {udt_ex}"
|
|
)
|
|
udts_cr_skipped += 1
|
|
except Exception as udt_ex:
|
|
print(
|
|
f" ERROR GENERAL al exportar referencias cruzadas para el UDT {udt_name}: {udt_ex}"
|
|
)
|
|
traceback.print_exc()
|
|
udts_cr_skipped += 1
|
|
print(
|
|
f" Resumen de exportación de referencias cruzadas de UDTs: Exportados={udts_cr_exported}, Omitidos/Errores={udts_cr_skipped}"
|
|
)
|
|
except AttributeError:
|
|
print(
|
|
" Error de atributo: No se pudo encontrar 'get_user_data_types' en el objeto PLC. Omitiendo UDTs."
|
|
)
|
|
except Exception as e:
|
|
print(f" ERROR al acceder a los UDTs para exportar referencias cruzadas: {e}")
|
|
traceback.print_exc()
|
|
|
|
# --- Export System Block Cross-References ---
|
|
sys_blocks_cr_exported = 0
|
|
sys_blocks_cr_skipped = 0
|
|
print(f"\n[PLC: {plc_name}] Intentando exportar referencias cruzadas de bloques de sistema...")
|
|
sys_blocks_cr_path = plc_export_dir / "SystemBlocks_CR"
|
|
sys_blocks_cr_path.mkdir(exist_ok=True)
|
|
print(f" Destino: {sys_blocks_cr_path}")
|
|
|
|
try:
|
|
if hasattr(plc, "get_system_blocks"):
|
|
system_blocks = plc.get_system_blocks()
|
|
print(
|
|
f" Se encontraron {len(system_blocks)} bloques de sistema."
|
|
)
|
|
for sys_block in system_blocks:
|
|
sys_block_name = sys_block.get_name()
|
|
print(f" Procesando bloque de sistema: {sys_block_name}...")
|
|
try:
|
|
print(f" Exportando referencias cruzadas para {sys_block_name}...")
|
|
sys_block.export_cross_references(
|
|
target_directorypath=str(sys_blocks_cr_path),
|
|
filter=CROSS_REF_FILTER,
|
|
)
|
|
sys_blocks_cr_exported += 1
|
|
except RuntimeError as sys_ex:
|
|
print(
|
|
f" ERROR TIA al exportar referencias cruzadas para el bloque de sistema {sys_block_name}: {sys_ex}"
|
|
)
|
|
sys_blocks_cr_skipped += 1
|
|
except Exception as sys_ex:
|
|
print(
|
|
f" ERROR GENERAL al exportar referencias cruzadas para el bloque de sistema {sys_block_name}: {sys_ex}"
|
|
)
|
|
traceback.print_exc()
|
|
sys_blocks_cr_skipped += 1
|
|
else:
|
|
print(
|
|
" Método 'get_system_blocks' no encontrado en el objeto PLC. Omitiendo bloques de sistema."
|
|
)
|
|
|
|
print(
|
|
f" Resumen de exportación de referencias cruzadas de bloques de sistema: Exportados={sys_blocks_cr_exported}, Omitidos/Errores={sys_blocks_cr_skipped}"
|
|
)
|
|
except AttributeError:
|
|
print(
|
|
" Error de atributo durante el procesamiento de bloques de sistema. Omitiendo bloques de sistema restantes."
|
|
)
|
|
traceback.print_exc()
|
|
except Exception as e:
|
|
print(
|
|
f" ERROR al acceder/procesar bloques de sistema para exportar referencias cruzadas: {e}"
|
|
)
|
|
traceback.print_exc()
|
|
|
|
# --- Export Software Unit Cross-References ---
|
|
sw_units_cr_exported = 0
|
|
sw_units_cr_skipped = 0
|
|
print(f"\n[PLC: {plc_name}] Intentando exportar referencias cruzadas de unidades de software...")
|
|
sw_units_cr_path = plc_export_dir / "SoftwareUnits_CR"
|
|
sw_units_cr_path.mkdir(exist_ok=True)
|
|
print(f" Destino: {sw_units_cr_path}")
|
|
|
|
try:
|
|
if hasattr(plc, "get_software_units"):
|
|
software_units = plc.get_software_units()
|
|
print(f" Se encontraron {len(software_units)} unidades de software.")
|
|
for unit in software_units:
|
|
unit_name = unit.get_name()
|
|
print(f" Procesando unidad de software: {unit_name}...")
|
|
try:
|
|
print(f" Exportando referencias cruzadas para {unit_name}...")
|
|
unit.export_cross_references(
|
|
target_directorypath=str(sw_units_cr_path),
|
|
filter=CROSS_REF_FILTER,
|
|
)
|
|
sw_units_cr_exported += 1
|
|
except RuntimeError as unit_ex:
|
|
print(
|
|
f" ERROR TIA al exportar referencias cruzadas para la unidad de software {unit_name}: {unit_ex}"
|
|
)
|
|
sw_units_cr_skipped += 1
|
|
except Exception as unit_ex:
|
|
print(
|
|
f" ERROR GENERAL al exportar referencias cruzadas para la unidad de software {unit_name}: {unit_ex}"
|
|
)
|
|
traceback.print_exc()
|
|
sw_units_cr_skipped += 1
|
|
print(
|
|
f" Resumen de exportación de referencias cruzadas de unidades de software: Exportados={sw_units_cr_exported}, Omitidos/Errores={sw_units_cr_skipped}"
|
|
)
|
|
else:
|
|
print(
|
|
" Método 'get_software_units' no encontrado en el objeto PLC. Omitiendo unidades de software."
|
|
)
|
|
except AttributeError:
|
|
print(
|
|
" Error de atributo durante el procesamiento de unidades de software. Omitiendo unidades restantes."
|
|
)
|
|
traceback.print_exc()
|
|
except Exception as e:
|
|
print(
|
|
f" ERROR al acceder/procesar unidades de software para exportar referencias cruzadas: {e}"
|
|
)
|
|
traceback.print_exc()
|
|
|
|
print(f"\n--- Finalizado el procesamiento del PLC: {plc_name} ---")
|
|
|
|
# --- Main Script ---
|
|
|
|
if __name__ == "__main__":
|
|
configs = load_configuration()
|
|
working_directory = configs.get("working_directory")
|
|
|
|
print("--- Exportador de Referencias Cruzadas de TIA Portal ---")
|
|
|
|
# Validate working directory
|
|
if not working_directory or not os.path.isdir(working_directory):
|
|
print("ERROR: Directorio de trabajo no configurado o inválido.")
|
|
print("Por favor configure el directorio de trabajo usando la aplicación principal.")
|
|
sys.exit(1)
|
|
|
|
# 1. Select Project File
|
|
project_file = select_project_file()
|
|
|
|
# 2. Detect TIA Portal version from project file
|
|
tia_version = detect_tia_version(project_file)
|
|
|
|
# 3. Define Export Directory using working_directory and subfolder
|
|
export_base_dir = Path(working_directory)
|
|
try:
|
|
export_base_dir.mkdir(parents=True, exist_ok=True)
|
|
print(f"\nProyecto seleccionado: {project_file}")
|
|
print(f"Usando directorio base de exportación: {export_base_dir.resolve()}")
|
|
except Exception as e:
|
|
print(f"ERROR: No se pudo crear el directorio de exportación '{export_base_dir}'. Error: {e}")
|
|
sys.exit(1)
|
|
|
|
portal_instance = None
|
|
project_object = None
|
|
|
|
try:
|
|
# 4. Connect to TIA Portal with detected version
|
|
print(f"\nConectando a TIA Portal V{tia_version}...")
|
|
portal_instance = ts.open_portal(
|
|
version=tia_version,
|
|
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
|
|
)
|
|
print("Conectado a TIA Portal.")
|
|
print(f"ID del proceso del Portal: {portal_instance.get_process_id()}")
|
|
|
|
# 5. Open Project
|
|
print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
|
|
project_path_obj = Path(project_file)
|
|
project_object = portal_instance.open_project(
|
|
project_file_path=str(project_path_obj)
|
|
)
|
|
if project_object is None:
|
|
print("El proyecto podría estar ya abierto, intentando obtener el manejador...")
|
|
project_object = portal_instance.get_project()
|
|
if project_object is None:
|
|
raise Exception("No se pudo abrir u obtener el proyecto especificado.")
|
|
print("Proyecto abierto exitosamente.")
|
|
|
|
# 6. Get PLCs
|
|
plcs = project_object.get_plcs()
|
|
if not plcs:
|
|
print("No se encontraron dispositivos PLC en el proyecto.")
|
|
else:
|
|
print(
|
|
f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación de referencias cruzadas..."
|
|
)
|
|
|
|
# 7. Iterate and Export Cross-References for each PLC
|
|
for plc_device in plcs:
|
|
export_plc_cross_references(
|
|
plc=plc_device,
|
|
export_base_dir=export_base_dir,
|
|
)
|
|
|
|
print("\nProceso de exportación de referencias cruzadas completado.")
|
|
|
|
except RuntimeError as tia_ex:
|
|
print(f"\nError de TIA Portal Openness: {tia_ex}")
|
|
traceback.print_exc()
|
|
except FileNotFoundError:
|
|
print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
|
|
except Exception as e:
|
|
print(f"\nOcurrió un error inesperado: {e}")
|
|
traceback.print_exc()
|
|
finally:
|
|
# 8. Cleanup
|
|
if portal_instance:
|
|
try:
|
|
print("\nCerrando TIA Portal...")
|
|
portal_instance.close_portal()
|
|
print("TIA Portal cerrado.")
|
|
except Exception as close_ex:
|
|
print(f"Error durante la limpieza de TIA Portal: {close_ex}")
|
|
|
|
print("\nScript finalizado.")
|