Compare commits

...

5 Commits

Author SHA1 Message Date
Miguel 734e6637bc Implementación de envío por lotes de logs y mejora en la gestión de conexiones WebSocket
- Se añadió un nuevo sistema para el envío por lotes de logs a través de WebSocket, permitiendo un vaciado del buffer cada 500 ms.
- Se implementó un hilo en segundo plano para gestionar el vaciado del buffer de logs, mejorando la eficiencia en el envío de mensajes.
- Se realizaron ajustes en la función de broadcast para acumular mensajes en un buffer antes de enviarlos, optimizando el rendimiento.
- Se actualizaron los logs de ejecución para reflejar los cambios en la gestión de logs y el nuevo comportamiento del sistema.
2025-06-13 11:16:52 +02:00
Miguel 6150c719ca Actualización de directorios de trabajo y mejora en la gestión de logs
- Se modificaron los directorios de trabajo en `script_config.json` y `work_dir.json` para apuntar a la nueva ubicación de los archivos relacionados con el proyecto SIDEL.
- Se actualizaron los logs de ejecución en `log_x1.txt` y `log_x4.txt` para reflejar las nuevas fechas, duraciones y resultados de los procesos de exportación.
- Se implementó una nueva función de limpieza en `x0_main.py` para eliminar artefactos generados durante la ejecución de los scripts, mejorando la gestión de archivos temporales.
- Se realizaron ajustes en la interfaz de usuario para mejorar la experiencia al seleccionar y confirmar directorios de trabajo.
2025-06-13 10:22:00 +02:00
Miguel 0488624d64 Mejorado para Tia 19 2025-06-12 22:18:54 +02:00
Miguel e70852ecf1 Actualización del script x0_main.py y mejora en la gestión de logs
- Se implementó un nuevo sistema de argumentos para permitir el procesamiento de múltiples PLCs desde un directorio de trabajo.
- Se mejoró la lógica de búsqueda de archivos XML, eliminando la dependencia de un subdirectorio específico.
- Se actualizaron los logs de ejecución para reflejar el estado exitoso del procesamiento y se añadieron detalles sobre los archivos encontrados y procesados.
- Se generaron nuevos archivos de salida en formato Markdown y se mejoró la estructura de los logs para facilitar la comprensión del proceso.
2025-06-12 21:44:58 +02:00
Miguel 04084e7289 Actualización de scripts de exportación y mejora en la gestión de versiones de TIA Portal
- Se modificó el script x1.py para soportar múltiples versiones de TIA Portal (18, 19 y 20) y se mejoró la detección de la versión a partir de la extensión del archivo del proyecto.
- Se actualizó la descripción del script en scripts_description.json para reflejar las nuevas versiones soportadas.
- Se realizaron ajustes en los logs de ejecución para reflejar las nuevas fechas y duraciones en el script x1.
- Se mejoró la gestión de directorios de exportación y se añadieron mensajes en español para una mejor comprensión del proceso de exportación.
2025-06-12 20:17:36 +02:00
37 changed files with 30513 additions and 562 deletions

3
.gitignore vendored
View File

@ -25,6 +25,9 @@ share/python-wheels/
.installed.cfg .installed.cfg
*.egg *.egg
MANIFEST MANIFEST
*.txt
*.json
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template

69
app.py
View File

@ -33,6 +33,37 @@ websocket_connections = set()
# --- Globals for Tray Icon --- # --- Globals for Tray Icon ---
tray_icon = None tray_icon = None
# --- Parámetros para envío por lotes de logs ---
BATCH_FLUSH_INTERVAL = 0.5 # segundos
broadcast_buffer = [] # Almacena líneas formateadas pendientes de envío
buffer_lock = threading.Lock() # Sincroniza acceso al buffer
def _broadcast_flush_loop():
"""Hilo que vacía el buffer de logs cada BATCH_FLUSH_INTERVAL segundos."""
while True:
time.sleep(BATCH_FLUSH_INTERVAL)
with buffer_lock:
if not broadcast_buffer:
continue
batch = "\n".join(broadcast_buffer)
broadcast_buffer.clear()
_send_batch_to_clients(batch)
def _send_batch_to_clients(batch_message: str):
"""Envía un bloque de texto a todas las conexiones WebSocket activas."""
dead_connections = set()
for ws in list(websocket_connections):
try:
if ws.connected:
ws.send(batch_message + "\n")
except Exception:
dead_connections.add(ws)
websocket_connections.difference_update(dead_connections)
# Iniciar hilo de vaciado en segundo plano (ahora que las dependencias están definidas)
flusher_thread = threading.Thread(target=_broadcast_flush_loop, daemon=True)
flusher_thread.start()
@sock.route("/ws") @sock.route("/ws")
def handle_websocket(ws): def handle_websocket(ws):
@ -49,49 +80,34 @@ def handle_websocket(ws):
def broadcast_message(message): def broadcast_message(message):
"""Envía un mensaje a todas las conexiones WebSocket activas y guarda en log.""" """Acumula mensajes en un buffer y los envía por lotes cada 500 ms."""
dead_connections = set()
timestamp = datetime.now().strftime("[%H:%M:%S] ") timestamp = datetime.now().strftime("[%H:%M:%S] ")
# Normalize input to a list of messages # Normalizar entrada a lista de mensajes
if isinstance(message, list): if isinstance(message, list):
messages = message messages = message
else: else:
# Si es un solo mensaje, dividirlo en líneas
messages = [line.strip() for line in message.splitlines() if line.strip()] messages = [line.strip() for line in message.splitlines() if line.strip()]
# Procesar cada mensaje
for raw_msg in messages: for raw_msg in messages:
# Limpiar timestamps duplicados al inicio del mensaje # Limpiar timestamps duplicados al inicio del mensaje
while raw_msg.startswith("[") and "]" in raw_msg: while raw_msg.startswith("[") and "]" in raw_msg:
try: try:
closing_bracket = raw_msg.index("]") + 1 closing_bracket = raw_msg.index("]") + 1
if raw_msg[1 : closing_bracket - 1].replace(":", "").isdigit(): if raw_msg[1:closing_bracket - 1].replace(":", "").isdigit():
raw_msg = raw_msg[closing_bracket:].strip() # Update raw_msg itself raw_msg = raw_msg[closing_bracket:].strip()
else: else:
break break
except: except ValueError:
break break
# Log the raw message using the config_manager's logger # Registrar en archivo (la clase Logger añade timestamp propio)
# The logger will handle its own timestamping for the file.
config_manager.append_log(raw_msg) config_manager.append_log(raw_msg)
# Format message with timestamp *for WebSocket broadcast* # Formatear para el WebSocket y añadir al buffer
formatted_msg_for_ws = f"{timestamp}{raw_msg}" formatted_msg_for_ws = f"{timestamp}{raw_msg}"
with buffer_lock:
# Enviar a todos los clientes WebSocket broadcast_buffer.append(formatted_msg_for_ws)
for ws in list(websocket_connections):
try:
if ws.connected: # Check if ws is still connected before sending
ws.send(
f"{formatted_msg_for_ws}\n"
) # Use the correct variable name here
except Exception:
dead_connections.add(ws) # Collect dead connections
# Limpiar conexiones muertas
websocket_connections.difference_update(dead_connections)
@app.route("/api/execute_script", methods=["POST"]) @app.route("/api/execute_script", methods=["POST"])
@ -1053,3 +1069,8 @@ if __name__ == "__main__":
print(f"Error al iniciar el icono de notificación: {e}", file=sys.stderr) print(f"Error al iniciar el icono de notificación: {e}", file=sys.stderr)
print("Aplicación finalizada.") print("Aplicación finalizada.")
# --- Iniciar hilo de vaciado en segundo plano (única vez) ---
# flusher_thread = threading.Thread(target=_broadcast_flush_loop, daemon=True)
# flusher_thread.start()
# ------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -1,251 +1,199 @@
--- Log de Ejecución: x4.py --- --- Log de Ejecución: x4.py ---
Grupo: ObtainIOFromProjectTia Grupo: ObtainIOFromProjectTia
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML Directorio de Trabajo: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports
Inicio: 2025-05-20 12:12:51 Inicio: 2025-06-13 11:14:30
Fin: 2025-05-20 12:20:01 Fin: 2025-06-13 11:16:43
Duración: 0:07:09.648304 Duración: 0:02:13.165274
Estado: SUCCESS (Código de Salida: 0) Estado: SUCCESS (Código de Salida: 0)
--- SALIDA ESTÁNDAR (STDOUT) --- --- SALIDA ESTÁNDAR (STDOUT) ---
--- TIA Portal Cross-Reference Exporter --- --- Exportador de Referencias Cruzadas de TIA Portal ---
Versión de TIA Portal detectada: 19.0 (de la extensión .ap19)
Selected Project: C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452/SAE452.ap18 Proyecto seleccionado: D:/Trabajo/VM/22 - 93841 - Sidel - Tilting/InLavoro/PLC/93841_PLC_28/93841_PLC_28.ap19
Using Base Export Directory: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML Usando directorio base de exportación: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports
Connecting to TIA Portal V18.0... Conectando a TIA Portal V19.0...
2025-05-20 12:12:56,780 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog. 2025-06-13 11:14:34,713 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
2025-05-20 12:12:56,804 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - With user interface 2025-06-13 11:14:34,731 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
Connected to TIA Portal. Conectado a TIA Portal.
2025-05-20 12:13:30,582 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal GetProcessId - Process id: 21952 2025-06-13 11:14:58,165 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal GetProcessId - Process id: 30140
Portal Process ID: 21952 ID del proceso del Portal: 30140
Opening project: SAE452.ap18... Abriendo proyecto: 93841_PLC_28.ap19...
2025-05-20 12:13:31,077 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal OpenProject - Open project... C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\Migration\SAE452\SAE452.ap18 2025-06-13 11:14:58,500 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\InLavoro\PLC\93841_PLC_28\93841_PLC_28.ap19
Project opened successfully. Proyecto abierto exitosamente.
2025-05-20 12:14:15,234 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Project GetPlcs - Found plc CPU 315F-2 PN/DP with parent name _SSAE0452 2025-06-13 11:15:29,701 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc VM 1512 with parent name ET 200SP station_1
Found 1 PLC(s). Starting cross-reference export process... 2025-06-13 11:15:30,551 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc SIDEL Transport Example with parent name S71500/ET200MP station_1
Se encontraron 2 PLC(s). Iniciando proceso de exportación de referencias cruzadas...
--- Processing PLC: CPU 315F-2 PN/DP --- --- Procesando PLC: VM 1512 ---
[PLC: CPU 315F-2 PN/DP] Exporting Program Block Cross-References... [PLC: VM 1512] Exportando referencias cruzadas de bloques de programa...
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\ProgramBlocks_CR Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\ProgramBlocks_CR
Found 410 program blocks. Se encontraron 201 bloques de programa.
Processing block: ISOonTCP_or_TCP_Protocol... Procesando bloque: FC General COM...
Exporting cross-references for ISOonTCP_or_TCP_Protocol... Exportando referencias cruzadas para FC General COM...
Processing block: PIDControl... Procesando bloque: From_SIDEL...
Exporting cross-references for PIDControl... Exportando referencias cruzadas para From_SIDEL...
Processing block: DETAIL_DP_DIAG... Procesando bloque: To_SIDEL...
Exporting cross-references for DETAIL_DP_DIAG... Exportando referencias cruzadas para To_SIDEL...
Processing block: Net Dosing Sys Prof... Procesando bloque: DB Early Restart Blower...
Exporting cross-references for Net Dosing Sys Prof... Exportando referencias cruzadas para DB Early Restart Blower...
Processing block: ICS Profibus Comm... Procesando bloque: DB Early Restart Filler...
Exporting cross-references for ICS Profibus Comm... Exportando referencias cruzadas para DB Early Restart Filler...
Processing block: GNS DriveDiag... Procesando bloque: DB Early Restart SynchroBlock...
Exporting cross-references for GNS DriveDiag... Exportando referencias cruzadas para DB Early Restart SynchroBlock...
Processing block: HMI_Blender_Parameters... Procesando bloque: FB Early Restart...
Exporting cross-references for HMI_Blender_Parameters... Exportando referencias cruzadas para FB Early Restart...
Processing block: HMI Drive... Procesando bloque: DB Signal Transport...
Exporting cross-references for HMI Drive... Exportando referencias cruzadas para DB Signal Transport...
Processing block: GNS DriveDiagMain... Procesando bloque: FC Signal Transport...
Exporting cross-references for GNS DriveDiagMain... Exportando referencias cruzadas para FC Signal Transport...
Processing block: Integral... Procesando bloque: DB Lube - Dry Ecolab...
Exporting cross-references for Integral... Exportando referencias cruzadas para DB Lube - Dry Ecolab...
Processing block: LowPassFilter... Procesando bloque: FB Lube - Water/Dry...
Exporting cross-references for LowPassFilter... Exportando referencias cruzadas para FB Lube - Water/Dry...
Processing block: SlewLimit... Procesando bloque: FB Lube - Dry Ecolab...
Exporting cross-references for SlewLimit... Exportando referencias cruzadas para FB Lube - Dry Ecolab...
Processing block: MSE Slope... Procesando bloque: FB Lube - EcoLab VM...
Exporting cross-references for MSE Slope... Exportando referencias cruzadas para FB Lube - EcoLab VM...
Processing block: Statistical_Analisys... Procesando bloque: FB Lube - Ecolab...
Exporting cross-references for Statistical_Analisys... Exportando referencias cruzadas para FB Lube - Ecolab...
Processing block: Blender_Variables... Procesando bloque: DB LUBE - Ecolab...
Exporting cross-references for Blender_Variables... Exportando referencias cruzadas para DB LUBE - Ecolab...
Processing block: BrixTracking_ProdSamples... Procesando bloque: FC Ttop Configuration...
Exporting cross-references for BrixTracking_ProdSamples... Exportando referencias cruzadas para FC Ttop Configuration...
Processing block: Procedure_Variables... Procesando bloque: FC Ttop Run...
Exporting cross-references for Procedure_Variables... Exportando referencias cruzadas para FC Ttop Run...
Processing block: Blender_Constants... Procesando bloque: FC Ttop Alarms...
Exporting cross-references for Blender_Constants... Exportando referencias cruzadas para FC Ttop Alarms...
Processing block: BrixTracking_SampleTime... Procesando bloque: DB Ttop Run...
Exporting cross-references for BrixTracking_SampleTime... Exportando referencias cruzadas para DB Ttop Run...
Processing block: Delay... Procesando bloque: DB Ttop Motor CFG...
Exporting cross-references for Delay... Exportando referencias cruzadas para DB Ttop Motor CFG...
Processing block: CO2Tracking_ProdSamples... Procesando bloque: DB Ttop Alarm...
Exporting cross-references for CO2Tracking_ProdSamples... Exportando referencias cruzadas para DB Ttop Alarm...
Processing block: CO2Tracking_SampleTime... Procesando bloque: FC Ttop Motor 31...
Exporting cross-references for CO2Tracking_SampleTime... Exportando referencias cruzadas para FC Ttop Motor 31...
Processing block: Interlocking_Variables... Procesando bloque: FC Ttop Motor 32...
Exporting cross-references for Interlocking_Variables... Exportando referencias cruzadas para FC Ttop Motor 32...
Processing block: System_RunOut_Variables... Procesando bloque: FC Ttop Motor 34...
Exporting cross-references for System_RunOut_Variables... Exportando referencias cruzadas para FC Ttop Motor 34...
Processing block: CIP_Program_Variables... Procesando bloque: FC Ttop Motor 35...
Exporting cross-references for CIP_Program_Variables... Exportando referencias cruzadas para FC Ttop Motor 35...
Processing block: Filler_Head_Variables... Procesando bloque: FC Ttop Motor 36...
Exporting cross-references for Filler_Head_Variables... Exportando referencias cruzadas para FC Ttop Motor 36...
Processing block: Filling_Time_Tranfer_DB... Procesando bloque: DB Ttop Motor 31...
Exporting cross-references for Filling_Time_Tranfer_DB... Exportando referencias cruzadas para DB Ttop Motor 31...
Processing block: Blender_Variables_Pers... Procesando bloque: DB Ttop Motor 32...
Exporting cross-references for Blender_Variables_Pers... Exportando referencias cruzadas para DB Ttop Motor 32...
Processing block: HMI_Alarms... Procesando bloque: DB Ttop Motor 34...
Exporting cross-references for HMI_Alarms... Exportando referencias cruzadas para DB Ttop Motor 34...
Processing block: HMI_Local_CIP_Variables... Procesando bloque: DB Ttop Motor 35...
Exporting cross-references for HMI_Local_CIP_Variables... Exportando referencias cruzadas para DB Ttop Motor 35...
Processing block: HMI_Service... Procesando bloque: DB Ttop Minimotor Cfg 32...
Exporting cross-references for HMI_Service... Exportando referencias cruzadas para DB Ttop Minimotor Cfg 32...
Processing block: HMI_Variables_Cmd... Procesando bloque: DB Ttop Minimotor Data 32...
Exporting cross-references for HMI_Variables_Cmd... Exportando referencias cruzadas para DB Ttop Minimotor Data 32...
Processing block: HMI_Variables_Status... Procesando bloque: DB Ttop Motor 36...
Exporting cross-references for HMI_Variables_Status... Exportando referencias cruzadas para DB Ttop Motor 36...
Processing block: HMI_Device... Procesando bloque: FB Ttop Dryer...
Exporting cross-references for HMI_Device... Exportando referencias cruzadas para FB Ttop Dryer...
Processing block: HMI_Instrument... Procesando bloque: FB Ttop Energy Saving...
Exporting cross-references for HMI_Instrument... Exportando referencias cruzadas para FB Ttop Energy Saving...
Processing block: HMI_Digital... Procesando bloque: FB SKID...
Exporting cross-references for HMI_Digital... Exportando referencias cruzadas para FB SKID...
Processing block: HMI_PID... Procesando bloque: FC Analog Sensor Process...
Exporting cross-references for HMI_PID... Exportando referencias cruzadas para FC Analog Sensor Process...
Processing block: HMI_ICS... Procesando bloque: FC Valve...
Exporting cross-references for HMI_ICS... Exportando referencias cruzadas para FC Valve...
Processing block: HMI_Device_AVS... Procesando bloque: FB SpeedRegulation...
Exporting cross-references for HMI_Device_AVS... Exportando referencias cruzadas para FB SpeedRegulation...
Processing block: Profibus_Variables... Procesando bloque: FC Simple PID...
Exporting cross-references for Profibus_Variables... Exportando referencias cruzadas para FC Simple PID...
Processing block: Input_CheckFlowMetersSta... Procesando bloque: FC Scale Real...
Exporting cross-references for Input_CheckFlowMetersSta... Exportando referencias cruzadas para FC Scale Real...
Processing block: Input_DigitalScanner... Procesando bloque: FB Correct Speed F/Pulses...
Exporting cross-references for Input_DigitalScanner... Exportando referencias cruzadas para FB Correct Speed F/Pulses...
Processing block: ProductLiterInTank... ERROR GENERAL al exportar referencias cruzadas para el bloque FB Correct Speed F/Pulses: OpennessAccessException: Unexpected exception - no exception message available.
Exporting cross-references for ProductLiterInTank... ERROR al acceder a los bloques de programa para exportar referencias cruzadas: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.SW.Blocks.FB' is not possible.
Processing block: ProductAvailable...
Exporting cross-references for ProductAvailable...
Processing block: T_Timer...
Exporting cross-references for T_Timer...
Processing block: SEL_I...
Exporting cross-references for SEL_I...
Processing block: _StepMove...
Exporting cross-references for _StepMove...
Processing block: ProductPipeDrain_Seq...
Exporting cross-references for ProductPipeDrain_Seq...
Processing block: ProductPipeDrain...
Exporting cross-references for ProductPipeDrain...
Processing block: ProductPipeRunOut_Seq...
Exporting cross-references for ProductPipeRunOut_Seq...
Processing block: SEL_R...
Exporting cross-references for SEL_R...
Processing block: ProductPipeRunOut...
Exporting cross-references for ProductPipeRunOut...
Processing block: LIMIT_I...
Exporting cross-references for LIMIT_I...
Processing block: System_Run_Out...
Exporting cross-references for System_Run_Out...
Processing block: System_Run_Out_Data...
Exporting cross-references for System_Run_Out_Data...
ERROR accessing Program Blocks for cross-reference export: RemotingException: El objeto '/bd68de4c_3307_463d_b3ce_57a1378b3bde/lnycb9uriadrpgmom_mjdspm_249.rem' se desconectó o no existe en el servidor.
[PLC: CPU 315F-2 PN/DP] Exporting PLC Tag Table Cross-References... TIA Portal has either been disposed or stopped running.
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\PlcTags_CR
Found 2 Tag Tables.
Processing Tag Table: Default tag table...
Exporting cross-references for Default tag table...
Processing Tag Table: STEP7 classic symbols...
Exporting cross-references for STEP7 classic symbols...
Tag Table CR Export Summary: Exported=2, Skipped/Errors=0
[PLC: CPU 315F-2 PN/DP] Exporting PLC Data Type (UDT) Cross-References... [PLC: VM 1512] Exportando referencias cruzadas de tablas de variables...
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\PlcDataTypes_CR Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcTags_CR
Found 21 UDTs. ERROR al acceder a las tablas de variables para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Processing UDT: AnyPoint...
Exporting cross-references for AnyPoint...
Processing UDT: FunctionButton...
Exporting cross-references for FunctionButton...
Processing UDT: TADDR_PAR...
Exporting cross-references for TADDR_PAR...
Processing UDT: Device...
Exporting cross-references for Device...
Processing UDT: AnalogInstrument...
Exporting cross-references for AnalogInstrument...
Processing UDT: DigitalInstrument...
Exporting cross-references for DigitalInstrument...
Processing UDT: PID...
Exporting cross-references for PID...
Processing UDT: EHS16...
Exporting cross-references for EHS16...
Processing UDT: Danfoss Diag...
Exporting cross-references for Danfoss Diag...
Processing UDT: QCO Timer...
Exporting cross-references for QCO Timer...
Processing UDT: QCO Phase...
Exporting cross-references for QCO Phase...
Processing UDT: ReportCIPSimpleData...
Exporting cross-references for ReportCIPSimpleData...
Processing UDT: CIP_WaitEvent_Type...
Exporting cross-references for CIP_WaitEvent_Type...
Processing UDT: CIP_Step_Type_New...
Exporting cross-references for CIP_Step_Type_New...
Processing UDT: CIP_Simple_Type...
Exporting cross-references for CIP_Simple_Type...
Processing UDT: CIP_Link_Type...
Exporting cross-references for CIP_Link_Type...
Processing UDT: CIP_Step_Type...
Exporting cross-references for CIP_Step_Type...
Processing UDT: Recipe_Prod...
Exporting cross-references for Recipe_Prod...
Processing UDT: ICS Hndsk receive signal...
Exporting cross-references for ICS Hndsk receive signal...
Processing UDT: ICS Hndsk send signal...
Exporting cross-references for ICS Hndsk send signal...
Processing UDT: TCON_PAR...
Exporting cross-references for TCON_PAR...
UDT CR Export Summary: Exported=21, Skipped/Errors=0
[PLC: CPU 315F-2 PN/DP] Attempting to Export System Block Cross-References... [PLC: VM 1512] Exportando referencias cruzadas de tipos de datos PLC (UDTs)...
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SystemBlocks_CR Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcDataTypes_CR
Found 12 system blocks (using get_system_blocks). ERROR al acceder a los UDTs para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Processing System Block: TIM_S5TI...
Exporting cross-references for TIM_S5TI...
Processing System Block: REPLACE...
Exporting cross-references for REPLACE...
Processing System Block: LIMIT...
Exporting cross-references for LIMIT...
Processing System Block: NE_STRNG...
Exporting cross-references for NE_STRNG...
Processing System Block: TURCV...
Exporting cross-references for TURCV...
Processing System Block: TUSEND...
Exporting cross-references for TUSEND...
Processing System Block: PID_Continuos...
Exporting cross-references for PID_Continuos...
Processing System Block: TDISCON...
Exporting cross-references for TDISCON...
Processing System Block: TCON...
Exporting cross-references for TCON...
Processing System Block: TRCV...
Exporting cross-references for TRCV...
Processing System Block: TSEND...
Exporting cross-references for TSEND...
Processing System Block: DT_DATE...
Exporting cross-references for DT_DATE...
System Block CR Export Summary: Exported=12, Skipped/Errors=0
[PLC: CPU 315F-2 PN/DP] Attempting to Export Software Unit Cross-References... [PLC: VM 1512] Intentando exportar referencias cruzadas de bloques de sistema...
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SoftwareUnits_CR Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\SystemBlocks_CR
Found 0 Software Units. ERROR al acceder/procesar bloques de sistema para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Software Unit CR Export Summary: Exported=0, Skipped/Errors=0
--- Finished processing PLC: CPU 315F-2 PN/DP --- [PLC: VM 1512] Intentando exportar referencias cruzadas de unidades de software...
Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\SoftwareUnits_CR
ERROR al acceder/procesar unidades de software para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Cross-reference export process completed. --- Finalizado el procesamiento del PLC: VM 1512 ---
Closing TIA Portal... Ocurrió un error inesperado: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.HW.DeviceItemImpl' is not possible.
2025-05-20 12:19:54,187 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal ClosePortal - Close TIA Portal
TIA Portal closed.
Script finished. TIA Portal has either been disposed or stopped running.
Cerrando TIA Portal...
2025-06-13 11:16:43,486 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
TIA Portal cerrado.
Script finalizado.
--- ERRORES (STDERR) --- --- ERRORES (STDERR) ---
2025-06-13 11:16:43,458 [1] ERROR Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock ExportCrossReferences -
Siemens.TiaPortal.OpennessContracts.OpennessAccessException: Unexpected exception - no exception message available.
Traceback (most recent call last): Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 99, in export_plc_cross_references File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 128, in export_plc_cross_references
block.export_cross_references(
ValueError: OpennessAccessException: Unexpected exception - no exception message available.
2025-06-13 11:16:43,462 [1] ERROR Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock GetName -
Siemens.TiaPortal.OpennessContracts.OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.SW.Blocks.FB' is not possible.
TIA Portal has either been disposed or stopped running.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 124, in export_plc_cross_references
block_name = block.get_name() block_name = block.get_name()
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
ValueError: RemotingException: El objeto '/bd68de4c_3307_463d_b3ce_57a1378b3bde/lnycb9uriadrpgmom_mjdspm_249.rem' se desconectó o no existe en el servidor. ValueError: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.SW.Blocks.FB' is not possible.
TIA Portal has either been disposed or stopped running.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 164, in export_plc_cross_references
tag_tables = plc.get_plc_tag_tables()
^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 207, in export_plc_cross_references
udts = plc.get_user_data_types()
^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 251, in export_plc_cross_references
system_blocks = plc.get_system_blocks()
^^^^^^^^^^^^^^^^^^^^^^^
ValueError: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 305, in export_plc_cross_references
software_units = plc.get_software_units()
^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
Traceback (most recent call last):
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 415, in <module>
export_plc_cross_references(
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 105, in export_plc_cross_references
plc_name = plc.get_name()
^^^^^^^^^^^^^^
ValueError: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.HW.DeviceItemImpl' is not possible.
TIA Portal has either been disposed or stopped running.
--- FIN DEL LOG --- --- FIN DEL LOG ---

View File

@ -5,5 +5,5 @@
}, },
"level2": {}, "level2": {},
"level3": {}, "level3": {},
"working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source" "working_directory": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports"
} }

View File

@ -1,6 +1,6 @@
{ {
"x1.py": { "x1.py": {
"display_name": "1: Exportar Lógica desde TIA Portal v18 en XML", "display_name": "1: Exportar Lógica desde TIA Portal v18,v19,v20 en XML",
"short_description": "Exporta la lógica del PLC desde TIA Portal en archivos XML y SCL.", "short_description": "Exporta la lógica del PLC desde TIA Portal en archivos XML y SCL.",
"long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal.\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.", "long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal.\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.",
"hidden": false "hidden": false

View File

@ -1,6 +1,7 @@
{ {
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "path": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports",
"history": [ "history": [
"D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports",
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourcdSD", "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourcdSD",
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML" "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML"

View File

@ -7,6 +7,7 @@ from tkinter import filedialog
import os import os
import sys import sys
import traceback import traceback
from pathlib import Path # Import Path
script_root = os.path.dirname( script_root = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__))) os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
@ -15,11 +16,15 @@ sys.path.append(script_root)
from backend.script_utils import load_configuration from backend.script_utils import load_configuration
# --- Configuration --- # --- Configuration ---
TIA_PORTAL_VERSION = "18.0" # Target TIA Portal version (e.g., "18.0") # Supported TIA Portal versions mapping (extension -> version)
SUPPORTED_TIA_VERSIONS = {
".ap18": "18.0",
".ap19": "19.0",
".ap20": "20.0"
}
EXPORT_OPTIONS = None # Use default export options EXPORT_OPTIONS = None # Use default export options
KEEP_FOLDER_STRUCTURE = ( KEEP_FOLDER_STRUCTURE = True # Replicate TIA project folder structure in export directory
True # Replicate TIA project folder structure in export directory
)
# --- TIA Scripting Import Handling --- # --- TIA Scripting Import Handling ---
# Check if the TIA_SCRIPTING environment variable is set # Check if the TIA_SCRIPTING environment variable is set
@ -59,6 +64,33 @@ except Exception as e:
# --- Functions --- # --- 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(): def select_project_file():
"""Opens a dialog to select a TIA Portal project file.""" """Opens a dialog to select a TIA Portal project file."""
@ -66,16 +98,11 @@ def select_project_file():
root.withdraw() # Hide the main tkinter window root.withdraw() # Hide the main tkinter window
file_path = filedialog.askopenfilename( file_path = filedialog.askopenfilename(
title="Select TIA Portal Project File", title="Select TIA Portal Project File",
filetypes=[ filetypes=get_supported_filetypes()
(
f"TIA Portal V{TIA_PORTAL_VERSION} Projects",
f"*.ap{TIA_PORTAL_VERSION.split('.')[0]}",
)
], # e.g. *.ap18
) )
root.destroy() root.destroy()
if not file_path: if not file_path:
print("No project file selected. Exiting.") print("No se seleccionó ningún archivo de proyecto. Saliendo.")
sys.exit(0) sys.exit(0)
return file_path return file_path
@ -95,7 +122,7 @@ def select_export_directory():
def export_plc_data(plc, export_base_dir): def export_plc_data(plc, export_base_dir):
"""Exports Blocks, UDTs, and Tag Tables from a given PLC.""" """Exports Blocks, UDTs, and Tag Tables from a given PLC."""
plc_name = plc.get_name() plc_name = plc.get_name()
print(f"\n--- Processing PLC: {plc_name} ---") print(f"\n--- Procesando PLC: {plc_name} ---")
# Define base export path for this PLC # Define base export path for this PLC
plc_export_dir = os.path.join(export_base_dir, plc_name) plc_export_dir = os.path.join(export_base_dir, plc_name)
@ -104,143 +131,140 @@ def export_plc_data(plc, export_base_dir):
# --- Export Program Blocks --- # --- Export Program Blocks ---
blocks_exported = 0 blocks_exported = 0
blocks_skipped = 0 blocks_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting Program Blocks...") print(f"\n[PLC: {plc_name}] Exportando bloques de programa...")
xml_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_XML") xml_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_XML")
scl_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_SCL") scl_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_SCL")
os.makedirs(xml_blocks_path, exist_ok=True) os.makedirs(xml_blocks_path, exist_ok=True)
os.makedirs(scl_blocks_path, exist_ok=True) os.makedirs(scl_blocks_path, exist_ok=True)
print(f" XML Target: {xml_blocks_path}") print(f" Destino XML: {xml_blocks_path}")
print(f" SCL Target: {scl_blocks_path}") print(f" Destino SCL: {scl_blocks_path}")
try: try:
program_blocks = plc.get_program_blocks() # program_blocks = plc.get_program_blocks()
print(f" Found {len(program_blocks)} program blocks.") print(f" Se encontraron {len(program_blocks)} bloques de programa.")
for block in program_blocks: for block in program_blocks:
block_name = block.get_name() # Assuming get_name() exists block_name = block.get_name()
print(f" Processing block: {block_name}...") print(f" Procesando bloque: {block_name}...")
try: try:
if not block.is_consistent(): # if not block.is_consistent():
print(f" Compiling block {block_name}...") print(f" Compilando bloque {block_name}...")
block.compile() # block.compile()
if not block.is_consistent(): if not block.is_consistent():
print( print(
f" WARNING: Block {block_name} inconsistent after compile. Skipping." f" ADVERTENCIA: Bloque {block_name} inconsistente después de compilar. Omitiendo."
) )
blocks_skipped += 1 blocks_skipped += 1
continue continue
print(f" Exporting {block_name} as XML...") print(f" Exportando {block_name} como XML...")
block.export( block.export(
target_directory_path=xml_blocks_path, # target_directory_path=xml_blocks_path,
export_options=EXPORT_OPTIONS, # export_options=EXPORT_OPTIONS,
export_format=ts.Enums.ExportFormats.SimaticML, # export_format=ts.Enums.ExportFormats.SimaticML,
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) # )
try: try:
prog_language = block.get_property(name="ProgrammingLanguage") prog_language = block.get_property(name="ProgrammingLanguage")
if prog_language == "SCL": if prog_language == "SCL":
print(f" Exporting {block_name} as SCL...") print(f" Exportando {block_name} como SCL...")
block.export( block.export(
target_directory_path=scl_blocks_path, target_directory_path=scl_blocks_path,
export_options=EXPORT_OPTIONS, export_options=EXPORT_OPTIONS,
export_format=ts.Enums.ExportFormats.ExternalSource, # export_format=ts.Enums.ExportFormats.ExternalSource,
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) )
except Exception as prop_ex: except Exception as prop_ex:
print( print(
f" Could not get ProgrammingLanguage for {block_name}. Skipping SCL. Error: {prop_ex}" f" No se pudo obtener el lenguaje de programación para {block_name}. Omitiendo SCL. Error: {prop_ex}"
) )
blocks_exported += 1 blocks_exported += 1
except Exception as block_ex: except Exception as block_ex:
print(f" ERROR exporting block {block_name}: {block_ex}") print(f" ERROR exportando bloque {block_name}: {block_ex}")
blocks_skipped += 1 blocks_skipped += 1
print( print(
f" Program Blocks Export Summary: Exported={blocks_exported}, Skipped/Errors={blocks_skipped}" f" Resumen de exportación de bloques: Exportados={blocks_exported}, Omitidos/Errores={blocks_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR processing Program Blocks: {e}") print(f" ERROR procesando bloques de programa: {e}")
traceback.print_exc() traceback.print_exc()
# --- Export PLC Data Types (UDTs) --- # --- Export PLC Data Types (UDTs) ---
udts_exported = 0 udts_exported = 0
udts_skipped = 0 udts_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting PLC Data Types (UDTs)...") print(f"\n[PLC: {plc_name}] Exportando tipos de datos PLC (UDTs)...")
udt_export_path = os.path.join(plc_export_dir, "PlcDataTypes") udt_export_path = os.path.join(plc_export_dir, "PlcDataTypes")
os.makedirs(udt_export_path, exist_ok=True) os.makedirs(udt_export_path, exist_ok=True)
print(f" Target: {udt_export_path}") print(f" Destino: {udt_export_path}")
try: try:
udts = plc.get_user_data_types() # udts = plc.get_user_data_types()
print(f" Found {len(udts)} UDTs.") print(f" Se encontraron {len(udts)} UDTs.")
for udt in udts: for udt in udts:
udt_name = udt.get_name() # udt_name = udt.get_name()
print(f" Processing UDT: {udt_name}...") print(f" Procesando UDT: {udt_name}...")
try: try:
if not udt.is_consistent(): # if not udt.is_consistent():
print(f" Compiling UDT {udt_name}...") print(f" Compilando UDT {udt_name}...")
udt.compile() # udt.compile()
if not udt.is_consistent(): if not udt.is_consistent():
print( print(
f" WARNING: UDT {udt_name} inconsistent after compile. Skipping." f" ADVERTENCIA: UDT {udt_name} inconsistente después de compilar. Omitiendo."
) )
udts_skipped += 1 udts_skipped += 1
continue continue
print(f" Exporting {udt_name}...") print(f" Exportando {udt_name}...")
udt.export( udt.export(
target_directory_path=udt_export_path, # target_directory_path=udt_export_path,
export_options=EXPORT_OPTIONS, # export_options=EXPORT_OPTIONS,
# export_format defaults to SimaticML for UDTs
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) # )
udts_exported += 1 udts_exported += 1
except Exception as udt_ex: except Exception as udt_ex:
print(f" ERROR exporting UDT {udt_name}: {udt_ex}") print(f" ERROR exportando UDT {udt_name}: {udt_ex}")
udts_skipped += 1 udts_skipped += 1
print( print(
f" UDT Export Summary: Exported={udts_exported}, Skipped/Errors={udts_skipped}" f" Resumen de exportación de UDTs: Exportados={udts_exported}, Omitidos/Errores={udts_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR processing UDTs: {e}") print(f" ERROR procesando UDTs: {e}")
traceback.print_exc() traceback.print_exc()
# --- Export PLC Tag Tables --- # --- Export PLC Tag Tables ---
tags_exported = 0 tags_exported = 0
tags_skipped = 0 tags_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting PLC Tag Tables...") print(f"\n[PLC: {plc_name}] Exportando tablas de variables PLC...")
tags_export_path = os.path.join(plc_export_dir, "PlcTags") tags_export_path = os.path.join(plc_export_dir, "PlcTags")
os.makedirs(tags_export_path, exist_ok=True) os.makedirs(tags_export_path, exist_ok=True)
print(f" Target: {tags_export_path}") print(f" Destino: {tags_export_path}")
try: try:
tag_tables = plc.get_plc_tag_tables() # tag_tables = plc.get_plc_tag_tables()
print(f" Found {len(tag_tables)} Tag Tables.") print(f" Se encontraron {len(tag_tables)} tablas de variables.")
for table in tag_tables: for table in tag_tables:
table_name = table.get_name() # table_name = table.get_name()
print(f" Processing Tag Table: {table_name}...") print(f" Procesando tabla de variables: {table_name}...")
try: try:
# Note: Consistency check might not be available/needed for tag tables like blocks/UDTs print(f" Exportando {table_name}...")
print(f" Exporting {table_name}...")
table.export( table.export(
target_directory_path=tags_export_path, # target_directory_path=tags_export_path,
export_options=EXPORT_OPTIONS, # export_options=EXPORT_OPTIONS,
# export_format defaults to SimaticML for Tag Tables
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) # )
tags_exported += 1 tags_exported += 1
except Exception as table_ex: except Exception as table_ex:
print(f" ERROR exporting Tag Table {table_name}: {table_ex}") print(f" ERROR exportando tabla de variables {table_name}: {table_ex}")
tags_skipped += 1 tags_skipped += 1
print( print(
f" Tag Table Export Summary: Exported={tags_exported}, Skipped/Errors={tags_skipped}" f" Resumen de exportación de tablas de variables: Exportados={tags_exported}, Omitidos/Errores={tags_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR processing Tag Tables: {e}") print(f" ERROR procesando tablas de variables: {e}")
traceback.print_exc() traceback.print_exc()
print(f"\n--- Finished processing PLC: {plc_name} ---") print(f"\n--- Finalizado el procesamiento del PLC: {plc_name} ---")
# --- Main Script --- # --- Main Script ---
@ -250,75 +274,76 @@ if __name__ == "__main__":
configs = load_configuration() configs = load_configuration()
working_directory = configs.get("working_directory") working_directory = configs.get("working_directory")
print("--- TIA Portal Data Exporter (Blocks, UDTs, Tags) ---") print("--- Exportador de datos TIA Portal (Bloques, UDTs, Variables) ---")
# Validate working directory # Validate working directory
if not working_directory or not os.path.isdir(working_directory): if not working_directory or not os.path.isdir(working_directory):
print("ERROR: Working directory not set or invalid in configuration.") print("ERROR: Directorio de trabajo no configurado o inválido.")
print("Please configure the working directory using the main application.") print("Por favor configure el directorio de trabajo usando la aplicación principal.")
sys.exit(1) sys.exit(1)
# 1. Select Project File, Export Directory comes from config # 1. Select Project File, Export Directory comes from config
project_file = select_project_file() project_file = select_project_file()
export_dir = working_directory # Use working directory from config export_dir = working_directory # Use working directory from config
print(f"\nSelected Project: {project_file}") # 2. Detect TIA Portal version from project file
print(f"Using Export Directory (Working Directory): {export_dir}") tia_version = detect_tia_version(project_file)
print(f"\nProyecto seleccionado: {project_file}")
print(f"Usando directorio de exportación (Directorio de trabajo): {export_dir}")
portal_instance = None portal_instance = None
project_object = None project_object = None
try: try:
# 2. Connect to TIA Portal # 3. Connect to TIA Portal with detected version
print(f"\nConnecting to TIA Portal V{TIA_PORTAL_VERSION}...") print(f"\nConectando a TIA Portal V{tia_version}...")
portal_instance = ts.open_portal( portal_instance = ts.open_portal(
version=TIA_PORTAL_VERSION, version=tia_version,
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface, portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
) )
print("Connected to TIA Portal.") print("Conectado a TIA Portal.")
print(f"Portal Process ID: {portal_instance.get_process_id()}") # print(f"ID del proceso del Portal: {portal_instance.get_process_id()}")
# 3. Open Project # 4. Open Project
print(f"Opening project: {os.path.basename(project_file)}...") print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
project_object = portal_instance.open_project(project_file_path=project_file) # project_object = portal_instance.open_project(project_file_path=project_file)
if project_object is None: if project_object is None:
print("Project might already be open, attempting to get handle...") print("El proyecto podría estar ya abierto, intentando obtener el manejador...")
project_object = portal_instance.get_project() # project_object = portal_instance.get_project()
if project_object is None: if project_object is None:
raise Exception("Failed to open or get the specified project.") raise Exception("No se pudo abrir u obtener el proyecto especificado.")
print("Project opened successfully.") print("Proyecto abierto exitosamente.")
# 4. Get PLCs # 5. Get PLCs
plcs = project_object.get_plcs() # plcs = project_object.get_plcs()
if not plcs: if not plcs:
print("No PLC devices found in the project.") print("No se encontraron dispositivos PLC en el proyecto.")
else: else:
print(f"Found {len(plcs)} PLC(s). Starting export process...") print(f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación...")
# 5. Iterate and Export Data for each PLC # 6. Iterate and Export Data for each PLC
for plc_device in plcs: for plc_device in plcs:
export_plc_data( export_plc_data(plc=plc_device, export_base_dir=export_dir)
plc=plc_device, export_base_dir=export_dir
) # Pass export_dir
print("\nExport process completed.") print("\nProceso de exportación completado.")
except ts.TiaException as tia_ex: except ts.TiaException as tia_ex:
print(f"\nTIA Portal Openness Error: {tia_ex}") print(f"\nError de TIA Portal Openness: {tia_ex}")
traceback.print_exc() traceback.print_exc()
except FileNotFoundError: except FileNotFoundError:
print(f"\nERROR: Project file not found at {project_file}") print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
except Exception as e: except Exception as e:
print(f"\nAn unexpected error occurred: {e}") print(f"\nOcurrió un error inesperado: {e}")
traceback.print_exc() traceback.print_exc()
finally: finally:
# 6. Cleanup # 7. Cleanup
if portal_instance: if portal_instance:
try: try:
print("\nClosing TIA Portal...") print("\nCerrando TIA Portal...")
portal_instance.close_portal() # portal_instance.close_portal()
print("TIA Portal closed.") print("TIA Portal cerrado.")
except Exception as close_ex: except Exception as close_ex:
print(f"Error during TIA Portal cleanup: {close_ex}") print(f"Error durante la limpieza de TIA Portal: {close_ex}")
print("\nScript finished.") print("\nScript finalizado.")

View File

@ -8,7 +8,7 @@ from tkinter import filedialog
import os import os
import sys import sys
import traceback import traceback
from pathlib import Path # Import Path for easier path manipulation from pathlib import Path
script_root = os.path.dirname( script_root = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__))) os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
@ -17,14 +17,19 @@ sys.path.append(script_root)
from backend.script_utils import load_configuration from backend.script_utils import load_configuration
# --- Configuration --- # --- Configuration ---
TIA_PORTAL_VERSION = "18.0" # Target TIA Portal version (e.g., "18.0") # 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: # Filter for cross-references. Based on documentation:
# 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects' # 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects'
# Using 1 to export all. 0 might also work as a default in some API versions. # Using 1 to export all. 0 might also work as a default in some API versions.
CROSS_REF_FILTER = 1 CROSS_REF_FILTER = 1
# --- TIA Scripting Import Handling --- # --- TIA Scripting Import Handling ---
# (Same import handling as x1.py)
if os.getenv("TIA_SCRIPTING"): if os.getenv("TIA_SCRIPTING"):
sys.path.append(os.getenv("TIA_SCRIPTING")) sys.path.append(os.getenv("TIA_SCRIPTING"))
else: else:
@ -33,209 +38,225 @@ else:
try: try:
import siemens_tia_scripting as ts import siemens_tia_scripting as ts
except ImportError: except ImportError:
print("ERROR: Failed to import 'siemens_tia_scripting'.") print("ERROR: Error al importar 'siemens_tia_scripting'.")
print("Ensure:") print("Asegúrese de que:")
print(f"1. TIA Portal Openness for V{TIA_PORTAL_VERSION} is installed.") print("1. TIA Portal Openness está instalado.")
print( print(
"2. The 'siemens_tia_scripting' Python module is installed (pip install ...) or" "2. El módulo 'siemens_tia_scripting' de Python está instalado (pip install ...) o"
) )
print( print(
" the path to its binaries is set in the 'TIA_SCRIPTING' environment variable." " la ruta a sus binarios está configurada en la variable de entorno 'TIA_SCRIPTING'."
) )
print( print(
"3. You are using a compatible Python version (e.g., 3.12.X as per documentation)." "3. Está usando una versión compatible de Python (ej. 3.12.X según la documentación)."
) )
sys.exit(1) sys.exit(1)
except Exception as e: except Exception as e:
print(f"An unexpected error occurred during import: {e}") print(f"Ocurrió un error inesperado durante la importación: {e}")
traceback.print_exc() traceback.print_exc()
sys.exit(1) sys.exit(1)
# --- Functions --- # --- 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(): def select_project_file():
"""Opens a dialog to select a TIA Portal project file.""" """Opens a dialog to select a TIA Portal project file."""
root = tk.Tk() root = tk.Tk()
root.withdraw() # Hide the main tkinter window root.withdraw()
file_path = filedialog.askopenfilename( file_path = filedialog.askopenfilename(
title="Select TIA Portal Project File", title="Seleccionar archivo de proyecto TIA Portal",
filetypes=[ filetypes=get_supported_filetypes()
(
f"TIA Portal V{TIA_PORTAL_VERSION} Projects",
f"*.ap{TIA_PORTAL_VERSION.split('.')[0]}",
)
], # e.g. *.ap18
) )
root.destroy() root.destroy()
if not file_path: if not file_path:
print("No project file selected. Exiting.") print("No se seleccionó ningún archivo de proyecto. Saliendo.")
sys.exit(0) sys.exit(0)
return file_path return file_path
def export_plc_cross_references(plc, export_base_dir): def export_plc_cross_references(plc, export_base_dir):
"""Exports cross-references for various elements from a given PLC.""" """Exports cross-references for various elements from a given PLC."""
plc_name = plc.get_name() plc_name = plc.get_name()
print(f"\n--- Processing PLC: {plc_name} ---") print(f"\n--- Procesando PLC: {plc_name} ---")
# Define base export path for this PLC's cross-references # Define base export path for this PLC's cross-references
plc_export_dir = export_base_dir / plc_name plc_export_dir = export_base_dir / plc_name
plc_export_dir.mkdir(parents=True, exist_ok=True) # Use pathlib's mkdir plc_export_dir.mkdir(parents=True, exist_ok=True)
# --- Export Program Block Cross-References --- # --- Export Program Block Cross-References ---
blocks_cr_exported = 0 blocks_cr_exported = 0
blocks_cr_skipped = 0 blocks_cr_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting Program Block Cross-References...") print(f"\n[PLC: {plc_name}] Exportando referencias cruzadas de bloques de programa...")
blocks_cr_path = plc_export_dir / "ProgramBlocks_CR" blocks_cr_path = plc_export_dir / "ProgramBlocks_CR"
blocks_cr_path.mkdir(exist_ok=True) blocks_cr_path.mkdir(exist_ok=True)
print(f" Target: {blocks_cr_path}") print(f" Destino: {blocks_cr_path}")
try: try:
# Assuming get_program_blocks() doesn't need folder_path to get all blocks
program_blocks = plc.get_program_blocks() program_blocks = plc.get_program_blocks()
print(f" Found {len(program_blocks)} program blocks.") print(f" Se encontraron {len(program_blocks)} bloques de programa.")
for block in program_blocks: for block in program_blocks:
block_name = block.get_name() block_name = block.get_name()
print(f" Processing block: {block_name}...") print(f" Procesando bloque: {block_name}...")
try: try:
# Note: Consistency check might not be needed/available before cross-ref export print(f" Exportando referencias cruzadas para {block_name}...")
print(f" Exporting cross-references for {block_name}...")
block.export_cross_references( block.export_cross_references(
target_directorypath=str( target_directorypath=str(blocks_cr_path),
blocks_cr_path
), # API likely needs string path
filter=CROSS_REF_FILTER, filter=CROSS_REF_FILTER,
) )
blocks_cr_exported += 1 blocks_cr_exported += 1
except RuntimeError as block_ex: except RuntimeError as block_ex:
print( print(
f" TIA ERROR exporting cross-references for block {block_name}: {block_ex}" f" ERROR TIA al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
) )
blocks_cr_skipped += 1 blocks_cr_skipped += 1
except Exception as block_ex: except Exception as block_ex:
print( print(
f" GENERAL ERROR exporting cross-references for block {block_name}: {block_ex}" f" ERROR GENERAL al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
) )
traceback.print_exc() # Print stack trace for general errors traceback.print_exc()
blocks_cr_skipped += 1 blocks_cr_skipped += 1
print( print(
f" Program Block CR Export Summary: Exported={blocks_cr_exported}, Skipped/Errors={blocks_cr_skipped}" f" Resumen de exportación de referencias cruzadas de bloques: Exportados={blocks_cr_exported}, Omitidos/Errores={blocks_cr_skipped}"
) )
except AttributeError: except AttributeError:
print( print(
" AttributeError: Could not find 'get_program_blocks' on PLC object. Skipping Program Blocks." " Error de atributo: No se pudo encontrar 'get_program_blocks' en el objeto PLC. Omitiendo bloques de programa."
) )
except Exception as e: except Exception as e:
print(f" ERROR accessing Program Blocks for cross-reference export: {e}") print(f" ERROR al acceder a los bloques de programa para exportar referencias cruzadas: {e}")
traceback.print_exc() traceback.print_exc()
# --- Export PLC Tag Table Cross-References --- # --- Export PLC Tag Table Cross-References ---
tags_cr_exported = 0 tags_cr_exported = 0
tags_cr_skipped = 0 tags_cr_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting PLC Tag Table Cross-References...") print(f"\n[PLC: {plc_name}] Exportando referencias cruzadas de tablas de variables...")
tags_cr_path = plc_export_dir / "PlcTags_CR" tags_cr_path = plc_export_dir / "PlcTags_CR"
tags_cr_path.mkdir(exist_ok=True) tags_cr_path.mkdir(exist_ok=True)
print(f" Target: {tags_cr_path}") print(f" Destino: {tags_cr_path}")
try: try:
# Assuming get_plc_tag_tables() doesn't need folder_path to get all tables
tag_tables = plc.get_plc_tag_tables() tag_tables = plc.get_plc_tag_tables()
print(f" Found {len(tag_tables)} Tag Tables.") print(f" Se encontraron {len(tag_tables)} tablas de variables.")
for table in tag_tables: for table in tag_tables:
table_name = table.get_name() table_name = table.get_name()
print(f" Processing Tag Table: {table_name}...") print(f" Procesando tabla de variables: {table_name}...")
try: try:
print(f" Exporting cross-references for {table_name}...") print(f" Exportando referencias cruzadas para {table_name}...")
table.export_cross_references( table.export_cross_references(
target_directorypath=str(tags_cr_path), filter=CROSS_REF_FILTER target_directorypath=str(tags_cr_path),
filter=CROSS_REF_FILTER
) )
tags_cr_exported += 1 tags_cr_exported += 1
except RuntimeError as table_ex: except RuntimeError as table_ex:
print( print(
f" TIA ERROR exporting cross-references for Tag Table {table_name}: {table_ex}" f" ERROR TIA al exportar referencias cruzadas para la tabla {table_name}: {table_ex}"
) )
tags_cr_skipped += 1 tags_cr_skipped += 1
except Exception as table_ex: except Exception as table_ex:
print( print(
f" GENERAL ERROR exporting cross-references for Tag Table {table_name}: {table_ex}" f" ERROR GENERAL al exportar referencias cruzadas para la tabla {table_name}: {table_ex}"
) )
traceback.print_exc() traceback.print_exc()
tags_cr_skipped += 1 tags_cr_skipped += 1
print( print(
f" Tag Table CR Export Summary: Exported={tags_cr_exported}, Skipped/Errors={tags_cr_skipped}" f" Resumen de exportación de referencias cruzadas de tablas: Exportados={tags_cr_exported}, Omitidos/Errores={tags_cr_skipped}"
) )
except AttributeError: except AttributeError:
print( print(
" AttributeError: Could not find 'get_plc_tag_tables' on PLC object. Skipping Tag Tables." " Error de atributo: No se pudo encontrar 'get_plc_tag_tables' en el objeto PLC. Omitiendo tablas de variables."
) )
except Exception as e: except Exception as e:
print(f" ERROR accessing Tag Tables for cross-reference export: {e}") print(f" ERROR al acceder a las tablas de variables para exportar referencias cruzadas: {e}")
traceback.print_exc() traceback.print_exc()
# --- Export PLC Data Type (UDT) Cross-References --- # --- Export PLC Data Type (UDT) Cross-References ---
udts_cr_exported = 0 udts_cr_exported = 0
udts_cr_skipped = 0 udts_cr_skipped = 0
print(f"\n[PLC: {plc_name}] Exporting PLC Data Type (UDT) Cross-References...") 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 = plc_export_dir / "PlcDataTypes_CR"
udts_cr_path.mkdir(exist_ok=True) udts_cr_path.mkdir(exist_ok=True)
print(f" Target: {udts_cr_path}") print(f" Destino: {udts_cr_path}")
try: try:
# Assuming get_user_data_types() doesn't need folder_path to get all UDTs
udts = plc.get_user_data_types() udts = plc.get_user_data_types()
print(f" Found {len(udts)} UDTs.") print(f" Se encontraron {len(udts)} UDTs.")
for udt in udts: for udt in udts:
udt_name = udt.get_name() udt_name = udt.get_name()
print(f" Processing UDT: {udt_name}...") print(f" Procesando UDT: {udt_name}...")
try: try:
print(f" Exporting cross-references for {udt_name}...") print(f" Exportando referencias cruzadas para {udt_name}...")
udt.export_cross_references( udt.export_cross_references(
target_directorypath=str(udts_cr_path), filter=CROSS_REF_FILTER target_directorypath=str(udts_cr_path),
filter=CROSS_REF_FILTER
) )
udts_cr_exported += 1 udts_cr_exported += 1
except RuntimeError as udt_ex: except RuntimeError as udt_ex:
print( print(
f" TIA ERROR exporting cross-references for UDT {udt_name}: {udt_ex}" f" ERROR TIA al exportar referencias cruzadas para el UDT {udt_name}: {udt_ex}"
) )
udts_cr_skipped += 1 udts_cr_skipped += 1
except Exception as udt_ex: except Exception as udt_ex:
print( print(
f" GENERAL ERROR exporting cross-references for UDT {udt_name}: {udt_ex}" f" ERROR GENERAL al exportar referencias cruzadas para el UDT {udt_name}: {udt_ex}"
) )
traceback.print_exc() traceback.print_exc()
udts_cr_skipped += 1 udts_cr_skipped += 1
print( print(
f" UDT CR Export Summary: Exported={udts_cr_exported}, Skipped/Errors={udts_cr_skipped}" f" Resumen de exportación de referencias cruzadas de UDTs: Exportados={udts_cr_exported}, Omitidos/Errores={udts_cr_skipped}"
) )
except AttributeError: except AttributeError:
print( print(
" AttributeError: Could not find 'get_user_data_types' on PLC object. Skipping UDTs." " Error de atributo: No se pudo encontrar 'get_user_data_types' en el objeto PLC. Omitiendo UDTs."
) )
except Exception as e: except Exception as e:
print(f" ERROR accessing UDTs for cross-reference export: {e}") print(f" ERROR al acceder a los UDTs para exportar referencias cruzadas: {e}")
traceback.print_exc() traceback.print_exc()
# --- Export System Block Cross-References --- # --- Export System Block Cross-References ---
sys_blocks_cr_exported = 0 sys_blocks_cr_exported = 0
sys_blocks_cr_skipped = 0 sys_blocks_cr_skipped = 0
print(f"\n[PLC: {plc_name}] Attempting to Export System Block Cross-References...") 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 = plc_export_dir / "SystemBlocks_CR"
sys_blocks_cr_path.mkdir(exist_ok=True) sys_blocks_cr_path.mkdir(exist_ok=True)
print(f" Target: {sys_blocks_cr_path}") print(f" Destino: {sys_blocks_cr_path}")
try: try:
# Check if method exists before calling
if hasattr(plc, "get_system_blocks"): if hasattr(plc, "get_system_blocks"):
system_blocks = plc.get_system_blocks() system_blocks = plc.get_system_blocks()
print( print(
f" Found {len(system_blocks)} system blocks (using get_system_blocks)." f" Se encontraron {len(system_blocks)} bloques de sistema."
) )
for sys_block in system_blocks: for sys_block in system_blocks:
sys_block_name = sys_block.get_name() sys_block_name = sys_block.get_name()
print(f" Processing System Block: {sys_block_name}...") print(f" Procesando bloque de sistema: {sys_block_name}...")
try: try:
print(f" Exporting cross-references for {sys_block_name}...") print(f" Exportando referencias cruzadas para {sys_block_name}...")
sys_block.export_cross_references( sys_block.export_cross_references(
target_directorypath=str(sys_blocks_cr_path), target_directorypath=str(sys_blocks_cr_path),
filter=CROSS_REF_FILTER, filter=CROSS_REF_FILTER,
@ -243,53 +264,51 @@ def export_plc_cross_references(plc, export_base_dir):
sys_blocks_cr_exported += 1 sys_blocks_cr_exported += 1
except RuntimeError as sys_ex: except RuntimeError as sys_ex:
print( print(
f" TIA ERROR exporting cross-references for System Block {sys_block_name}: {sys_ex}" f" ERROR TIA al exportar referencias cruzadas para el bloque de sistema {sys_block_name}: {sys_ex}"
) )
sys_blocks_cr_skipped += 1 sys_blocks_cr_skipped += 1
except Exception as sys_ex: except Exception as sys_ex:
print( print(
f" GENERAL ERROR exporting cross-references for System Block {sys_block_name}: {sys_ex}" f" ERROR GENERAL al exportar referencias cruzadas para el bloque de sistema {sys_block_name}: {sys_ex}"
) )
traceback.print_exc() traceback.print_exc()
sys_blocks_cr_skipped += 1 sys_blocks_cr_skipped += 1
else: else:
print( print(
" Method 'get_system_blocks' not found on PLC object. Skipping System Blocks." " Método 'get_system_blocks' no encontrado en el objeto PLC. Omitiendo bloques de sistema."
) )
# Alternative: Try navigating DeviceItems if needed, but that's more complex.
print( print(
f" System Block CR Export Summary: Exported={sys_blocks_cr_exported}, Skipped/Errors={sys_blocks_cr_skipped}" 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: # Catch if get_name() or other methods fail on sys_block except AttributeError:
print( print(
" AttributeError during System Block processing. Skipping remaining System Blocks." " Error de atributo durante el procesamiento de bloques de sistema. Omitiendo bloques de sistema restantes."
) )
traceback.print_exc() traceback.print_exc()
except Exception as e: except Exception as e:
print( print(
f" ERROR accessing/processing System Blocks for cross-reference export: {e}" f" ERROR al acceder/procesar bloques de sistema para exportar referencias cruzadas: {e}"
) )
traceback.print_exc() traceback.print_exc()
# --- Export Software Unit Cross-References --- # --- Export Software Unit Cross-References ---
sw_units_cr_exported = 0 sw_units_cr_exported = 0
sw_units_cr_skipped = 0 sw_units_cr_skipped = 0
print(f"\n[PLC: {plc_name}] Attempting to Export Software Unit Cross-References...") 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 = plc_export_dir / "SoftwareUnits_CR"
sw_units_cr_path.mkdir(exist_ok=True) sw_units_cr_path.mkdir(exist_ok=True)
print(f" Target: {sw_units_cr_path}") print(f" Destino: {sw_units_cr_path}")
try: try:
# Check if method exists before calling
if hasattr(plc, "get_software_units"): if hasattr(plc, "get_software_units"):
software_units = plc.get_software_units() software_units = plc.get_software_units()
print(f" Found {len(software_units)} Software Units.") print(f" Se encontraron {len(software_units)} unidades de software.")
for unit in software_units: for unit in software_units:
unit_name = unit.get_name() unit_name = unit.get_name()
print(f" Processing Software Unit: {unit_name}...") print(f" Procesando unidad de software: {unit_name}...")
try: try:
print(f" Exporting cross-references for {unit_name}...") print(f" Exportando referencias cruzadas para {unit_name}...")
unit.export_cross_references( unit.export_cross_references(
target_directorypath=str(sw_units_cr_path), target_directorypath=str(sw_units_cr_path),
filter=CROSS_REF_FILTER, filter=CROSS_REF_FILTER,
@ -297,35 +316,34 @@ def export_plc_cross_references(plc, export_base_dir):
sw_units_cr_exported += 1 sw_units_cr_exported += 1
except RuntimeError as unit_ex: except RuntimeError as unit_ex:
print( print(
f" TIA ERROR exporting cross-references for Software Unit {unit_name}: {unit_ex}" f" ERROR TIA al exportar referencias cruzadas para la unidad de software {unit_name}: {unit_ex}"
) )
sw_units_cr_skipped += 1 sw_units_cr_skipped += 1
except Exception as unit_ex: except Exception as unit_ex:
print( print(
f" GENERAL ERROR exporting cross-references for Software Unit {unit_name}: {unit_ex}" f" ERROR GENERAL al exportar referencias cruzadas para la unidad de software {unit_name}: {unit_ex}"
) )
traceback.print_exc() traceback.print_exc()
sw_units_cr_skipped += 1 sw_units_cr_skipped += 1
print( print(
f" Software Unit CR Export Summary: Exported={sw_units_cr_exported}, Skipped/Errors={sw_units_cr_skipped}" f" Resumen de exportación de referencias cruzadas de unidades de software: Exportados={sw_units_cr_exported}, Omitidos/Errores={sw_units_cr_skipped}"
) )
else: else:
print( print(
" Method 'get_software_units' not found on PLC object. Skipping Software Units." " Método 'get_software_units' no encontrado en el objeto PLC. Omitiendo unidades de software."
) )
except AttributeError: # Catch if get_name() or other methods fail on unit except AttributeError:
print( print(
" AttributeError during Software Unit processing. Skipping remaining Software Units." " Error de atributo durante el procesamiento de unidades de software. Omitiendo unidades restantes."
) )
traceback.print_exc() traceback.print_exc()
except Exception as e: except Exception as e:
print( print(
f" ERROR accessing/processing Software Units for cross-reference export: {e}" f" ERROR al acceder/procesar unidades de software para exportar referencias cruzadas: {e}"
) )
traceback.print_exc() traceback.print_exc()
print(f"\n--- Finished processing PLC: {plc_name} ---") print(f"\n--- Finalizado el procesamiento del PLC: {plc_name} ---")
# --- Main Script --- # --- Main Script ---
@ -333,90 +351,90 @@ if __name__ == "__main__":
configs = load_configuration() configs = load_configuration()
working_directory = configs.get("working_directory") working_directory = configs.get("working_directory")
print("--- TIA Portal Cross-Reference Exporter ---") print("--- Exportador de Referencias Cruzadas de TIA Portal ---")
# Validate working directory # Validate working directory
if not working_directory or not os.path.isdir(working_directory): if not working_directory or not os.path.isdir(working_directory):
print("ERROR: Working directory not set or invalid in configuration.") print("ERROR: Directorio de trabajo no configurado o inválido.")
print("Please configure the working directory using the main application.") print("Por favor configure el directorio de trabajo usando la aplicación principal.")
sys.exit(1) sys.exit(1)
# 1. Select Project File # 1. Select Project File
project_file = select_project_file() project_file = select_project_file()
# 2. Define Export Directory using working_directory and subfolder # 2. Detect TIA Portal version from project file
# The export base directory is the working directory. PLC-specific folders will be created inside. tia_version = detect_tia_version(project_file)
# 3. Define Export Directory using working_directory and subfolder
export_base_dir = Path(working_directory) export_base_dir = Path(working_directory)
try: try:
# Ensure the base working directory exists (it should, but check doesn't hurt)
export_base_dir.mkdir(parents=True, exist_ok=True) export_base_dir.mkdir(parents=True, exist_ok=True)
print(f"\nSelected Project: {project_file}") print(f"\nProyecto seleccionado: {project_file}")
print(f"Using Base Export Directory: {export_base_dir.resolve()}") print(f"Usando directorio base de exportación: {export_base_dir.resolve()}")
except Exception as e: except Exception as e:
print(f"ERROR: Could not create export directory '{export_dir}'. Error: {e}") print(f"ERROR: No se pudo crear el directorio de exportación '{export_base_dir}'. Error: {e}")
sys.exit(1) sys.exit(1)
portal_instance = None portal_instance = None
project_object = None project_object = None
try: try:
# 3. Connect to TIA Portal # 4. Connect to TIA Portal with detected version
print(f"\nConnecting to TIA Portal V{TIA_PORTAL_VERSION}...") print(f"\nConectando a TIA Portal V{tia_version}...")
# Connect using WithGraphicalUserInterface mode for visibility
portal_instance = ts.open_portal( portal_instance = ts.open_portal(
version=TIA_PORTAL_VERSION, version=tia_version,
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface, portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
) )
print("Connected to TIA Portal.") print("Conectado a TIA Portal.")
print(f"Portal Process ID: {portal_instance.get_process_id()}") print(f"ID del proceso del Portal: {portal_instance.get_process_id()}")
# 4. Open Project # 5. Open Project
print(f"Opening project: {os.path.basename(project_file)}...") print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
project_path_obj = Path(project_file) # Use Path object project_path_obj = Path(project_file)
project_object = portal_instance.open_project( project_object = portal_instance.open_project(
project_file_path=str(project_path_obj) project_file_path=str(project_path_obj)
) )
if project_object is None: if project_object is None:
print("Project might already be open, attempting to get handle...") print("El proyecto podría estar ya abierto, intentando obtener el manejador...")
project_object = portal_instance.get_project() project_object = portal_instance.get_project()
if project_object is None: if project_object is None:
raise Exception("Failed to open or get the specified project.") raise Exception("No se pudo abrir u obtener el proyecto especificado.")
print("Project opened successfully.") print("Proyecto abierto exitosamente.")
# 5. Get PLCs # 6. Get PLCs
plcs = project_object.get_plcs() plcs = project_object.get_plcs()
if not plcs: if not plcs:
print("No PLC devices found in the project.") print("No se encontraron dispositivos PLC en el proyecto.")
else: else:
print( print(
f"Found {len(plcs)} PLC(s). Starting cross-reference export process..." f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación de referencias cruzadas..."
) )
# 6. Iterate and Export Cross-References for each PLC # 7. Iterate and Export Cross-References for each PLC
for plc_device in plcs: for plc_device in plcs:
export_plc_cross_references( export_plc_cross_references(
plc=plc_device, plc=plc_device,
export_base_dir=export_base_dir, # Pass the base directory export_base_dir=export_base_dir,
) )
print("\nCross-reference export process completed.") print("\nProceso de exportación de referencias cruzadas completado.")
except RuntimeError as tia_ex: except RuntimeError as tia_ex:
print(f"\nTIA Portal Openness Error: {tia_ex}") print(f"\nError de TIA Portal Openness: {tia_ex}")
traceback.print_exc() traceback.print_exc()
except FileNotFoundError: except FileNotFoundError:
print(f"\nERROR: Project file not found at {project_file}") print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
except Exception as e: except Exception as e:
print(f"\nAn unexpected error occurred: {e}") print(f"\nOcurrió un error inesperado: {e}")
traceback.print_exc() traceback.print_exc()
finally: finally:
# 7. Cleanup # 8. Cleanup
if portal_instance: if portal_instance:
try: try:
print("\nClosing TIA Portal...") print("\nCerrando TIA Portal...")
portal_instance.close_portal() portal_instance.close_portal()
print("TIA Portal closed.") print("TIA Portal cerrado.")
except Exception as close_ex: except Exception as close_ex:
print(f"Error during TIA Portal cleanup: {close_ex}") print(f"Error durante la limpieza de TIA Portal: {close_ex}")
print("\nScript finished.") print("\nScript finalizado.")

View File

@ -0,0 +1,81 @@
{
"block_name": "FC General Lamp",
"block_number": 172,
"language": "LAD",
"block_type": "FC",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void",
"remanence": "NonRetain",
"accessibility": "Public",
"start_value": null,
"comment": null,
"children": [],
"array_elements": {}
}
]
},
"networks": [
{
"id": "4",
"title": "Lamp Alarm - Q.E. - Light Green",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "B",
"title": "Lamp Alarm - Q.E. - Light Red",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "12",
"title": "Lamp Alarm - Q.E. - Buzzer",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "19",
"title": "Lamp Alarm - Q.E. - Light Blue",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "20",
"title": "Lamp - Alarm Presence",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "27",
"title": "Light Signal Phased Stop Machine",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "2E",
"title": "",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
}
],
"source_xml_mod_time": 1749751920.2702959,
"source_xml_size": 39346
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
{
"block_name": "FC General Lamp",
"block_number": 172,
"language": "LAD",
"block_type": "FC",
"block_comment": "",
"interface": {
"Return": [
{
"name": "Ret_Val",
"datatype": "Void",
"remanence": "NonRetain",
"accessibility": "Public",
"start_value": null,
"comment": null,
"children": [],
"array_elements": {}
}
]
},
"networks": [
{
"id": "4",
"title": "Lamp Alarm - Q.E. - Light Green",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "B",
"title": "Lamp Alarm - Q.E. - Light Red",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "12",
"title": "Lamp Alarm - Q.E. - Buzzer",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "19",
"title": "Lamp Alarm - Q.E. - Light Blue",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "20",
"title": "Lamp - Alarm Presence",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "27",
"title": "Light Signal Phased Stop Machine",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
},
{
"id": "2E",
"title": "",
"comment": "",
"language": "LAD",
"logic": [],
"error": "FlgNet not found inside NetworkSource or CompileUnit"
}
],
"source_xml_mod_time": 1749751920.2702959,
"source_xml_size": 39346
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,455 @@
// Block Type: FC
// Block Name (Original): FC Ttop Motor M31010
// Block Number: 327
// Original Network Languages: LAD, SCL
FUNCTION "FC_Ttop_Motor_M31010" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INOUT
Motor : STRUCT
RCP_Speed_Fix_01 : Int;
RCP_Speed_Fix_02 : Int;
RCP_Speed_Fix_03 : Int;
RCP_Speed_Fix_04 : Int;
RCP_Speed_Fix_05 : Int;
RCP_Speed_Sync_01 : Int;
RCP_Speed_Sync_02 : Int;
RCP_Speed_Sync_03 : Int;
RCP_Speed_Sync_04 : Int;
RCP_Speed_Sync_05 : Int;
RCP_Timer_01 : Int;
RCP_Timer_02 : Int;
RCP_Timer_03 : Int;
RCP_Timer_04 : Int;
RCP_Timer_05 : Int;
RCP_Speed_Fix_01_mBar : Int;
RCP_Speed_Fix_02_mBar : Int;
RCP_Speed_Fix_03_mBar : Int;
RCP_Speed_Fix_04_mBar : Int;
RCP_Speed_Fix_05_mBar : Int;
RCP_ACC_Ramp : Int;
RCP_DEC_Ramp : Int;
RCP_W044 : Int;
RCP_W046 : Int;
RCP_W048 : Int;
CFG_VFD : Bool;
CFG_DP : Bool;
CFG_Analog_Speed : Bool;
CFG_EN_BWD : Bool;
CFG_Reverse : Bool;
CFG_Motor_N_Sel : Bool;
CFG_PN : Bool;
CFG_X050_7 : Bool;
CFG_TH_CTR_Single : Bool;
CFG_SW_CTR_Single : Bool;
CFG_TRIP_CTR_Single : Bool;
CFG_Speed_User : Bool;
CFG_mBar : Bool;
CFG_SW_CTR_OnOff : Bool;
CFG_Plug_CTR_Single : Bool;
CFG_X051_7 : Bool;
CFG_Min_Speed_Hz : Int;
CFG_Max_Speed_Hz : Int;
CFG_mBar_Type : Byte;
CFG_B57 : Byte;
CFG_Max_mBar : Int;
CFG_EOLO_Zone : Bool;
CFG_TableTop_Zone : Bool;
CFG_Pack_Zone : Bool;
CFG_VIS_Sp_User_Step200 : Bool;
CFG_X060_4 : Bool;
CFG_X060_5 : Bool;
CFG_X060_6 : Bool;
CFG_X060_7 : Bool;
CFG_MPrew : Int;
CFG_MNext : Int;
CFG_DBExternal1 : Int;
CFG_DBExternal2 : Int;
CFG_Vis_Fix_00 : Bool;
CFG_VIS_Fix_01 : Bool;
CFG_VIS_Fix_02 : Bool;
CFG_VIS_Fix_03 : Bool;
CFG_VIS_Fix_04 : Bool;
CFG_VIS_Fix_05 : Bool;
CFG_VIS_Fix_06 : Bool;
CFG_VIS_Fix_07 : Bool;
CFG_VIS_Sync_00 : Bool;
CFG_VIS_Sync_01 : Bool;
CFG_VIS_Sync_02 : Bool;
CFG_VIS_Sync_03 : Bool;
CFG_VIS_Sync_04 : Bool;
CFG_VIS_Sync_05 : Bool;
CFG_VIS_Sync_06 : Bool;
CFG_VIS_Sync_07 : Bool;
CFG_VIS_Timer_00 : Bool;
CFG_VIS_Timer_01 : Bool;
CFG_VIS_Timer_02 : Bool;
CFG_VIS_Timer_03 : Bool;
CFG_VIS_Timer_04 : Bool;
CFG_VIS_Timer_05 : Bool;
CFG_VIS_Timer_06 : Bool;
CFG_VIS_Timer_07 : Bool;
CFG_VIS_SA : Bool;
CFG_VIS_SB : Bool;
CFG_VIS_SC : Bool;
CFG_VIS_SD : Bool;
CFG_VIS_BA : Bool;
CFG_VIS_BB : Bool;
CFG_VIS_EXTA : Bool;
CFG_VIS_EXTB : Bool;
CFG_VIS_SW : Bool;
CFG_VIS_TH : Bool;
CFG_VIS_TRIP : Bool;
CFG_VIS_PAW : Bool;
CFG_VIS_RUN_FWD : Bool;
CFG_VIS_RUN_BWD : Bool;
CFG_VIS_Kspeed : Bool;
CFG_VIS_PLUG : Bool;
CFG_VIS_PB_Auto : Bool;
CFG_VIS_PB_Man : Bool;
CFG_VIS_PB_Jog : Bool;
CFG_VIS_PB_Stop : Bool;
CFG_VIS_PB_Reverse : Bool;
CFG_VIS_PB_sp_05 : Bool;
CFG_VIS_PB_sp_06 : Bool;
CFG_VIS_ManSpeed : Bool;
CFG_VIS_ACT_Torque : Bool;
CFG_VIS_ACC_Ramp : Bool;
CFG_VIS_DEC_Ramp : Bool;
CFG_VIS_X76_3 : Bool;
CFG_VIS_X76_4 : Bool;
CFG_VIS_X76_5 : Bool;
CFG_VIS_X76_6 : Bool;
CFG_VIS_X76 : Bool;
CFG_B77 : Byte;
CFG_W078 : Int;
CFG_Add_Signal_SA : UInt;
CFG_Add_Signal_SB : UInt;
CFG_Add_Signal_SC : UInt;
CFG_Add_Signal_SD : UInt;
CFG_Add_Signal_BA : UInt;
CFG_Add_Signal_BB : UInt;
CFG_Add_Signal_EXTA : UInt;
CFG_Add_Signal_EXTB : UInt;
CFG_Add_Signal_SW : UInt;
CFG_Add_Signal_TH : UInt;
CFG_Add_Signal_TRIP : UInt;
CFG_Add_Signal_PAW : Int;
CFG_Add_Signal_RUN_FWD : UInt;
CFG_Add_Signal_RUN_BWD : UInt;
CFG_Add_Signal_mBar : Int;
CFG_Add_Signal_PLUG : Int;
CFG_Add_Signal_SP02 : Int;
CFG_DB_Machine : Int;
CFG_DB_NextMotor : Int;
CFG_W118 : Int;
CFG_Stop_Empty : Bool;
CFG_Stop_Full : Bool;
CFG_Stop_STBY : Bool;
CFG_Pressurization : Bool;
CFG_EOLO_Press_Speed : Int;
Spare_124 : Array[124..145] of Byte;
CFG_Motor_N : DInt;
CFG_Phylosopy_N : Int;
CFG_Motor_HW_IO : "HW_IO";
CFG_Node_N : Int;
CFG_Inverter_Type : Int;
CFG_W158 : Int;
CFG_Kspeed_User50Hz : Int;
CFG_Min_Speed_User : Int;
CFG_Max_Speed_User : Int;
CFG_W166 : Int;
CFG_W168 : Int;
CFG_EN_mBar_FCT : Bool;
CFG_EN_mBar_FilterALM : Bool;
CFG_Isteresi_mBar : Int;
CFG_Gain_Mbar : Int;
CFG_Max_Speed_FilterALM : Int;
CFG_W178 : Int;
CFG_T_Gain : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
CFG_T_FilterALM : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
IN_PB_Start : Bool;
IN_PB_Stop : Bool;
IN_PB_Reset : Bool;
IN_PB_Silence : Bool;
IN_X200_4 : Bool;
IN_X200_5 : Bool;
IN_X200_6 : Bool;
IN_X200_7 : Bool;
IN_KG_PowerON : Bool;
IN_SW_ManAuto : Bool;
IN_Cycle_ON : Bool;
IN_X201_3 : Bool;
IN_X201_4 : Bool;
IN_X201_5 : Bool;
IN_X201_6 : Bool;
IN_X201_7 : Bool;
IN_SW_HMI_Auto : Bool;
IN_SW_HMI_Man : Bool;
IN_SW_HMI_Jog : Bool;
IN_SW_HMI_Stop : Bool;
IN_SW_HMI_Reverse : Bool;
IN_SW_HMI_sp_05 : Bool;
IN_SW_HMI_sp_06 : Bool;
IN_SW_HMI_ManSpeed : Bool;
IN_SW_HMI_sp_08 : Bool;
IN_SW_HMI_VVFix1 : Bool;
IN_SW_HMI_VVFix2 : Bool;
IN_SW_HMI_VVFix3 : Bool;
IN_SW_HMI_VVFix4 : Bool;
IN_SW_HMI_VVFix5 : Bool;
IN_SW_HMI_sp_14 : Bool;
IN_SW_HMI_sp_15 : Bool;
IN_HMI_ManSpeed : Int;
IN_Signal_SA : Bool;
IN_Signal_SB : Bool;
IN_Signal_SC : Bool;
IN_Signal_SD : Bool;
IN_Signal_BA : Bool;
IN_Signal_BB : Bool;
IN_Signal_EXTA : Bool;
IN_Signal_EXTB : Bool;
IN_Signal_SW : Bool;
IN_Signal_TH : Bool;
IN_Signal_TRIP : Bool;
IN_Signal_RUN_FWD : Bool;
IN_Signal_RUN_BWD : Bool;
IN_Signal_sp_05 : Bool;
IN_Signal_sp_06 : Bool;
IN_Signal_PLUG : Bool;
IN_Signal_sp_08 : Int;
IN_Signal_PEW_mBar : Int;
IN_Signal_mBar : Int;
IN_Motor_DI : "Struct";
IN_W216 : Int;
IN_W218 : Int;
IN_Line_Empty : Bool;
IN_Line_Full : Bool;
IN_Line_StandBy : Bool;
Spare_222 : Array[222..249] of Byte;
OUT_VFD_Run_FWD : Bool;
OUT_VFD_Run_BWD : Bool;
OUT_VFD_Reverse : Bool;
OUT_VFD_Qstop : Bool;
OUT_VFD_Reset : Bool;
OUT_X250_5 : Bool;
OUT_X250_6 : Bool;
OUT_EnergySavingON : Bool;
OUT_VFD_REQ_Speed_Hz : Int;
OUT_VFD_REQ_Speed_User : Int;
OUT_VFD_ACT_Sync_Speed : Int;
OUT_VFD_REQ_Speed_mBar : Int;
OUT_Motor_DO : "Struct";
OUT_W262 : Int;
OUT_W264 : Int;
OUT_W266 : Int;
OUT_W268 : Int;
STATUS_VFD_Run_FWD : Bool;
STATUS_VFD_Run_BWD : Bool;
STATUS_VFD_Trip : Bool;
STATUS_VFD_Warning : Bool;
STATUS_Ready : Bool;
STATUS_VFD_Ready : Bool;
STATUS_VFD_Coasting : Bool;
STATUS_X270_7 : Bool;
STATUS_VFD_ACT_Speed_Hz : Int;
STATUS_VFD_ACT_Speed_Use : Int;
STATUS_VFD_ACT_Torque : Int;
STATUS_MainFault_MovigearADV : Byte;
STATUS_Subfault_MovigearADV : Byte;
STATUS_W280 : Int;
STATUS_W282 : Int;
STATUS_PWR_OFF : Bool;
STATUS_CYCLE_OFF : Bool;
STATUS_ALARM : Bool;
STATUS_AUTO : Bool;
STATUS_MAN : Bool;
STATUS_JOG : Bool;
STATUS_STOP : Bool;
STATUS_X284_7 : Bool;
STATUS_X285_0 : Bool;
STATUS_X285_1 : Bool;
STATUS_X285_2 : Bool;
STATUS_X285_3 : Bool;
STATUS_X285_4 : Bool;
STATUS_X285_5 : Bool;
STATUS_X285_6 : Bool;
STATUS_X285_7 : Bool;
STATUS_NOTRUN : Bool;
STATUS_RUN : Bool;
STATUS_X286_2 : Bool;
STATUS_X286_3 : Bool;
STATUS_X286_4 : Bool;
STATUS_X286_5 : Bool;
STATUS_X286_6 : Bool;
STATUS_X286_7 : Bool;
Spare_288 : Array[288..289] of Byte;
Alarm_09 : Bool;
Alarm_10 : Bool;
Alarm_11 : Bool;
Alarm_12 : Bool;
Alarm_13 : Bool;
Alarm_14 : Bool;
Alarm_15 : Bool;
Alarm_16 : Bool;
Alarm_01 : Bool;
Alarm_02 : Bool;
Alarm_03 : Bool;
Alarm_04 : Bool;
Alarm_05 : Bool;
Alarm_06 : Bool;
Alarm_07 : Bool;
Alarm_08 : Bool;
Spare_292 : Array[292..299] of Byte;
M_Power_ON : Bool;
M_Cycle_ON_AUTO : Bool;
M_Cycle_ON_MAN : Bool;
M_W302 : Int;
M_W304 : Int;
M_W306 : Int;
M_W308 : Int;
M_Delay_Cycle_ON_Auto : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
Spare_320 : Array[320..349] of Byte;
REQ_EN_Run : Bool;
REQ_Start_FWD : Bool;
REQ_Start_BWD : Bool;
REQ_QStop : Bool;
REQ_X350_4 : Bool;
REQ_X350_5 : Bool;
REQ_X350_6 : Bool;
REQ_X350_7 : Bool;
REQ_X351_0 : Bool;
REQ_X351_1 : Bool;
REQ_X351_2 : Bool;
REQ_X351_3 : Bool;
REQ_X351_4 : Bool;
REQ_X351_5 : Bool;
REQ_X351_6 : Bool;
REQ_X351_7 : Bool;
REQ_W352 : Int;
REQ_Master_Speed_Sync : Int;
REQ_W356 : Int;
REQ_Speed_Fix_00_NU : Bool;
REQ_Speed_Fix_01 : Bool;
REQ_Speed_Fix_02 : Bool;
REQ_Speed_Fix_03 : Bool;
REQ_Speed_Fix_04 : Bool;
REQ_Speed_Fix_05 : Bool;
REQ_Speed_Fix_06_NU : Bool;
REQ_Speed_Fix_07_NU : Bool;
REQ_Speed_Sync_00_NU : Bool;
REQ_Speed_Sync_01 : Bool;
REQ_Speed_Sync_02 : Bool;
REQ_Speed_Sync_03 : Bool;
REQ_Speed_Sync_04 : Bool;
REQ_Speed_Sync_05 : Bool;
REQ_Speed_Sync_06_NU : Bool;
REQ_Speed_Sync_07_NU : Bool;
REQ_T01 : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
REQ_T02 : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
REQ_T03 : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
REQ_T04 : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
REQ_T05 : STRUCT
S : Bool;
Q : Bool;
TW : Int;
ST : Int;
ACT : Int;
W008 : Int;
END_STRUCT;
END_STRUCT;
END_VAR
VAR_TEMP
RetVal : Int;
MotorNumber : Int;
DBNumber : Int;
END_VAR
BEGIN
// Network 1: INIT Configuration (Original Language: SCL)
// SCL extraction failed: StructuredText node not found.
// Network 2: (Original Language: LAD)
// Network 2 has no logic elements.
// Network 3: EN run (Original Language: LAD)
"Motor"."REQ_EN_Run" := "M0.1";
// Network 4: REQ Auto RUN (Original Language: LAD)
"Motor"."REQ_Start_FWD" := TRUE;
// Network 5: Request Speed Fix 01 (Original Language: LAD)
"Motor"."REQ_Speed_Fix_01" := "M0.1";
// Network 6: INIT Configuration (Original Language: SCL)
// SCL extraction failed: StructuredText node not found.
END_FUNCTION

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
--- Log de Ejecución: x7_clear.py ---
Grupo: XML Parser to SCL
Directorio de Trabajo: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Source
Inicio: 2025-06-13 01:01:10
Fin: 2025-06-13 01:01:11
Duración: 0:00:00.701052
Estado: SUCCESS (Código de Salida: 0)
--- SALIDA ESTÁNDAR (STDOUT) ---
INFO: format_variable_name importado desde generators.generator_utils
=== Limpiando PLC: 98050_PLC ===
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\CONVEYORS\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\CONVEYORS\MiniMotor\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\CONVEYORS\MiniMotor\DBS55_PN_Extend-A\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\CONVEYORS\SICK AG\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\CONVEYORS\TRANSFER\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\ConveyorsBase\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Library\Motion\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Library\Motion\Siemens\LCamHdl_Types\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Library\Motion\Technology\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Library\SeamlessDivider\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Library\SeamlessDivider\Technology\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Machine\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcDataTypes\Machine\Cycle\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcTags\parsing
- Eliminado directorio de parsing: 98050_PLC\PlcTags\Library\Motion\Siemens\LCamHdl_Tags\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\1-AIR Philosophy\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\2-TTOP Philosophy\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\3-Motors Manage\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\3-Motors Manage\MiniMotor_PN\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\3-Motors Manage\MiniMotor_PN\MiniMotor_PN\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\HMI\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\FC\MACHINE SIGNALS\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!! SYS !!!\OB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\!!!TRANSFER\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\0 - MAIN\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\0 - MAIN\FC\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\0 - MAIN\OB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Device\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Device\FB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Device\FC\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\General\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\General\FC\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Motor\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Motor\DB\Minimotor\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Motor\FC\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\2 - TTOP\Motor\FC\Minimotor\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\4 - LUBE\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\4 - LUBE\FB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\1 - CONVEYORS\4 - LUBE\FB\OLD\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\2 - MACHINE\DB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\2 - MACHINE\FB\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\! ConveyorsSTD\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\! ConveyorsSTD\Hmi\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\! ConveyorsSTD\System\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\! ConveyorsSTD\TimeZone\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\AAA_Debug\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\AAA_VirtualMaster\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\ExchangeSignals\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\ExchangeSignals\Loop\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\HMI\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Instances\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\Generic\Alarms\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\Motion\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\Motion\Siemens\LCamHdl_Blocks\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\Motion\Technology\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\Motion\Utilities\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\SeamlessDivider\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Libraries\SeamlessDivider\Technology\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Machine\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Machine\Instances\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\Setup\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\TimingBelt (downstream divider)\parsing
- Eliminado directorio de parsing: 98050_PLC\ProgramBlocks_XML\Divider\TimingBelt (downstream divider)\Instances\parsing
- Eliminado directorio 'scl_output': 98050_PLC\scl_output
- Eliminado directorio 'xref_output': 98050_PLC\xref_output
- Eliminado archivo agregado: 98050_PLC\full_project_representation.md
- Eliminado log: log_98050_PLC.txt
--- Resumen de limpieza ---
Directorios eliminados: 70
Archivos eliminados: 2
Limpieza completada.
--- ERRORES (STDERR) ---
Ninguno
--- FIN DEL LOG ---

View File

@ -61,15 +61,19 @@ def parse_lad_fbd_network(network_element):
network_lang = lang_node_net[0].strip() network_lang = lang_node_net[0].strip()
# --- Buscar FlgNet --- # --- Buscar FlgNet ---
# Buscar NetworkSource y luego FlgNet (ambos usan namespace flg) # Paso 1: localizar el nodo <NetworkSource> (sin importar namespace)
network_source_node = network_element.xpath(".//flg:NetworkSource", namespaces=ns) network_source_node = network_element.xpath(".//*[local-name()='NetworkSource']")
flgnet = None flgnet = None
if network_source_node: if network_source_node:
flgnet_list = network_source_node[0].xpath("./flg:FlgNet", namespaces=ns) # Buscar FlgNet dentro del NetworkSource
flgnet_list = network_source_node[0].xpath(".//*[local-name()='FlgNet']")
if flgnet_list: if flgnet_list:
flgnet = flgnet_list[0] flgnet = flgnet_list[0]
else: # Intentar buscar FlgNet directamente si no hay NetworkSource
flgnet_list = network_element.xpath(".//flg:FlgNet", namespaces=ns) # Paso 2: si no se encontró, intentar buscar FlgNet directamente en el CompileUnit
if flgnet is None:
flgnet_list = network_element.xpath(".//*[local-name()='FlgNet']")
if flgnet_list: if flgnet_list:
flgnet = flgnet_list[0] flgnet = flgnet_list[0]

View File

@ -215,32 +215,43 @@ def parse_scl_network(network_element):
Devuelve un diccionario representando la red para el JSON. Devuelve un diccionario representando la red para el JSON.
""" """
network_id = network_element.get("ID", "UnknownSCL_ID") network_id = network_element.get("ID", "UnknownSCL_ID")
network_lang = "SCL" # Sabemos que es SCL network_lang = "SCL" # Sabemos que es SCL
# Buscar NetworkSource y luego StructuredText # --- Obtener título y comentario para coherencia con otros parsers ---
network_source_node = network_element.xpath(".//flg:NetworkSource", namespaces=ns) title_elem = network_element.xpath(
"./ObjectList/MultilingualText[@CompositionName='Title']", namespaces=ns
)
network_title = get_multilingual_text(title_elem[0]) if title_elem else f"Network {network_id}"
comment_elem = network_element.xpath(
"./ObjectList/MultilingualText[@CompositionName='Comment']", namespaces=ns
)
network_comment = get_multilingual_text(comment_elem[0]) if comment_elem else ""
# --- Buscar NetworkSource y StructuredText sin depender del namespace ---
network_source_node = network_element.xpath(".//*[local-name()='NetworkSource']")
structured_text_node = None structured_text_node = None
if network_source_node: if network_source_node:
structured_text_node_list = network_source_node[0].xpath("./st:StructuredText", namespaces=ns) st_nodes = network_source_node[0].xpath(".//*[local-name()='StructuredText']")
if structured_text_node_list: if st_nodes:
structured_text_node = structured_text_node_list[0] structured_text_node = st_nodes[0]
reconstructed_scl = "// SCL extraction failed: StructuredText node not found.\n" reconstructed_scl = "// SCL extraction failed: StructuredText node not found.\n"
if structured_text_node is not None: if structured_text_node is not None:
reconstructed_scl = reconstruct_scl_from_tokens(structured_text_node) reconstructed_scl = reconstruct_scl_from_tokens(structured_text_node)
# Crear la estructura de datos para la red
parsed_network_data = { parsed_network_data = {
"id": network_id, "id": network_id,
"title": network_title,
"comment": network_comment,
"language": network_lang, "language": network_lang,
"logic": [ # SCL se guarda como un único bloque lógico "logic": [
{ {
"instruction_uid": f"SCL_{network_id}", # UID sintético "instruction_uid": f"SCL_{network_id}",
"type": "RAW_SCL_CHUNK", # Tipo especial para SCL crudo "type": "RAW_SCL_CHUNK",
"scl": reconstructed_scl, # El código SCL reconstruido "scl": reconstructed_scl,
} }
], ],
# No añadimos error aquí, reconstruct_scl_from_tokens ya incluye comentarios de error
} }
return parsed_network_data return parsed_network_data

View File

@ -478,3 +478,74 @@ def parse_interface_members(member_elements):
} # Guardar como dict simple si no hay comment } # Guardar como dict simple si no hay comment
members_data.append(member_info) members_data.append(member_info)
return members_data return members_data
# --- NUEVA FUNCIÓN: Adaptación dinámica de namespaces ---
def adapt_namespaces(root):
"""Actualiza dinámicamente los valores en el diccionario global `ns` para que
coincidan con los namespaces reales presentes en el XML exportado por TIA.
Debe llamarse después de obtener la raíz (`root = tree.getroot()`). Si en el
XML aparecen nuevas versiones (p.ej. v6) de los URIs, esta función las
detectará y sobreescribirá las entradas correspondientes en `ns`.
"""
if root is None:
return # nada que hacer
detected = {}
# 1) Examinar los namespaces declarados en la raíz (cuando existan)
if hasattr(root, "nsmap") and root.nsmap:
for uri in root.nsmap.values():
if not uri or "siemens.com/automation" not in str(uri):
continue
_assign_uri_to_prefix(str(uri), detected)
# 2) Escaneo rápido por elementos clave si aún no hemos encontrado URIs
# Utilizamos búsquedas sin namespace (local-name) para localizar el primer
# elemento relevante y extraer su URI real.
# helper interno
def find_uri_by_localname(tag_local):
elem = root.xpath(f'//*[local-name()="{tag_local}"]')
if elem:
return etree.QName(elem[0]).namespace
return None
if "flg" not in detected or not detected.get("flg"):
flg_uri = find_uri_by_localname("FlgNet")
if flg_uri:
detected["flg"] = flg_uri
if "st" not in detected or not detected.get("st"):
st_uri = find_uri_by_localname("StructuredText")
if st_uri:
detected["st"] = st_uri
if "stl" not in detected or not detected.get("stl"):
stl_uri = find_uri_by_localname("StatementList")
if stl_uri:
detected["stl"] = stl_uri
if "iface" not in detected or not detected.get("iface"):
iface_uri = find_uri_by_localname("Sections")
if iface_uri and "/Interface/" in iface_uri:
detected["iface"] = iface_uri
if detected:
ns.update(detected)
# --- función auxiliar privada para adapt_namespaces ---
def _assign_uri_to_prefix(uri_str: str, out_dict: dict):
"""Asigna un URI concreto al prefijo adecuado en `out_dict`."""
if "/Interface/" in uri_str:
out_dict["iface"] = uri_str
elif "/NetworkSource/FlgNet/" in uri_str:
out_dict["flg"] = uri_str
elif "/NetworkSource/StructuredText/" in uri_str:
out_dict["st"] = uri_str
elif "/NetworkSource/StatementList/" in uri_str:
out_dict["stl"] = uri_str

View File

@ -15,5 +15,5 @@
"xref_source_subdir": "source" "xref_source_subdir": "source"
}, },
"level3": {}, "level3": {},
"working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source" "working_directory": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports"
} }

View File

@ -1,6 +1,6 @@
{ {
"x0_main.py": { "x0_main.py": {
"display_name": "1: Procesar Exportación XML", "display_name": "1: Procesar Exportación XML de un proyecto a SCL",
"short_description": "LadderToSCL - Conversor de Siemens LAD/FUP XML a SCL", "short_description": "LadderToSCL - Conversor de Siemens LAD/FUP XML a SCL",
"long_description": "Este script es el punto de entrada y orquestador principal para el proceso de conversión de archivos XML de Siemens TIA Portal (LAD/FUP) a código SCL y la generación de documentación relacionada.\n\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, nombres de carpetas de salida, etc.).\n2. **Logging:** Inicia un archivo `log.txt` para registrar detalladamente el progreso y los errores.\n3. **Descubrimiento:** Busca recursivamente todos los archivos `.xml` dentro del subdirectorio `PLC` del directorio de trabajo configurado.\n4. **Procesamiento Individual (Pasos x1-x3):**\n * Itera sobre cada archivo XML encontrado.\n * Implementa lógica para **saltar** pasos si el XML no ha cambiado y las salidas ya existen y están actualizadas.\n * Llama a funciones de `x1_to_json.py`, `x2_process.py`, y `x3_generate_scl.py` para convertir XML -> JSON intermedio -> JSON procesado -> archivo SCL/Markdown final.\n5. **Referencias Cruzadas (Paso x4):** Llama a una función de `x4_cross_reference.py` para generar análisis de llamadas, uso de DBs, etc., basándose en los archivos procesados.\n6. **Agregación (Paso x5):** Llama a una función de `x5_aggregate.py` para combinar las salidas SCL/Markdown y las referencias cruzadas en un único archivo Markdown resumen.\n7. **Resumen y Salida:** Registra un resumen final del proceso (éxitos, saltos, fallos) y finaliza con un código de estado (0 para éxito, 1 si hubo errores).\n", "long_description": "Este script es el punto de entrada y orquestador principal para el proceso de conversión de archivos XML de Siemens TIA Portal (LAD/FUP) a código SCL y la generación de documentación relacionada.\n\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, nombres de carpetas de salida, etc.).\n2. **Logging:** Inicia un archivo `log.txt` para registrar detalladamente el progreso y los errores.\n3. **Descubrimiento:** Busca recursivamente todos los archivos `.xml` dentro del subdirectorio `PLC` del directorio de trabajo configurado.\n4. **Procesamiento Individual (Pasos x1-x3):**\n * Itera sobre cada archivo XML encontrado.\n * Implementa lógica para **saltar** pasos si el XML no ha cambiado y las salidas ya existen y están actualizadas.\n * Llama a funciones de `x1_to_json.py`, `x2_process.py`, y `x3_generate_scl.py` para convertir XML -> JSON intermedio -> JSON procesado -> archivo SCL/Markdown final.\n5. **Referencias Cruzadas (Paso x4):** Llama a una función de `x4_cross_reference.py` para generar análisis de llamadas, uso de DBs, etc., basándose en los archivos procesados.\n6. **Agregación (Paso x5):** Llama a una función de `x5_aggregate.py` para combinar las salidas SCL/Markdown y las referencias cruzadas en un único archivo Markdown resumen.\n7. **Resumen y Salida:** Registra un resumen final del proceso (éxitos, saltos, fallos) y finaliza con un código de estado (0 para éxito, 1 si hubo errores).\n",
"hidden": false "hidden": false
@ -34,5 +34,11 @@
"short_description": "LadderToSCL - Conversor de Siemens LAD/FUP XML a SCL", "short_description": "LadderToSCL - Conversor de Siemens LAD/FUP XML a SCL",
"long_description": "", "long_description": "",
"hidden": true "hidden": true
},
"x7_clear.py": {
"display_name": "3: Limpiar archivos json y md",
"short_description": "3: Limpiar archivos json y md generados por (1)",
"long_description": "",
"hidden": false
} }
} }

View File

@ -1,6 +1,7 @@
{ {
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "path": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports",
"history": [ "history": [
"D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports",
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML", "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML",
"C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport" "C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport"

View File

@ -158,76 +158,225 @@ def check_skip_status(
return status return status
# --- FUNCIÓN DE LIMPIEZA (x7) ---------------------------------------------------------------------------
def clear_generated_outputs(plc_dir: str = None) -> bool:
"""Elimina todos los artefactos (JSON, SCL, MD, logs) generados por este script.
Si *plc_dir* es None, se comporta de forma análoga al modo orquestador,
localizando todos los PLCs bajo el *working_directory* configurado y
limpiándolos uno a uno. Devuelve *True* si la operación terminó sin
errores críticos, *False* en caso contrario.
"""
errors_found = False
try:
configs = load_configuration()
working_directory = configs.get("working_directory")
if not working_directory or not os.path.isdir(working_directory):
print("Error: 'working_directory' inválido en la configuración.", file=sys.stderr)
return False
xml_parser_config = configs.get("level2", {})
cfg_scl_output_dirname = xml_parser_config.get("scl_output_dir", "scl_output")
cfg_xref_output_dirname = xml_parser_config.get("xref_output_dir", "xref_output")
cfg_aggregated_filename = xml_parser_config.get("aggregated_filename", "full_project_representation.md")
# Determinar la lista de PLCs a limpiar
if plc_dir is not None:
plc_dirs = [os.path.abspath(plc_dir)]
if not os.path.isdir(plc_dirs[0]):
print(f"Advertencia: El directorio PLC especificado no existe: {plc_dirs[0]}")
return False
else:
plc_dirs = []
for entry in os.listdir(working_directory):
cand_path = os.path.join(working_directory, entry)
if os.path.isdir(cand_path) and glob.glob(os.path.join(cand_path, "**", "*.xml"), recursive=True):
plc_dirs.append(cand_path)
if not plc_dirs:
plc_dirs = [working_directory]
total_dirs_removed = 0
total_files_removed = 0
for plc_path in plc_dirs:
plc_path = os.path.abspath(plc_path)
plc_name_safe = os.path.basename(plc_path.strip(os.sep))
print(f"\n=== Limpiando PLC: {plc_name_safe} ===")
# 1) Eliminar carpetas 'parsing' (y su contenido JSON)
for parsing_dir in glob.glob(os.path.join(plc_path, "**", "parsing"), recursive=True):
if os.path.isdir(parsing_dir):
try:
shutil.rmtree(parsing_dir)
print(f" - Eliminado directorio de parsing: {os.path.relpath(parsing_dir, working_directory)}")
total_dirs_removed += 1
except Exception as e:
print(f" - ERROR al eliminar {parsing_dir}: {e}")
errors_found = True
# 2) Eliminar directorios de salida SCL y XRef
for dirname in [cfg_scl_output_dirname, cfg_xref_output_dirname]:
target_dir = os.path.join(plc_path, dirname)
if os.path.isdir(target_dir):
try:
shutil.rmtree(target_dir)
print(f" - Eliminado directorio '{dirname}': {os.path.relpath(target_dir, working_directory)}")
total_dirs_removed += 1
except Exception as e:
print(f" - ERROR al eliminar {target_dir}: {e}")
errors_found = True
# 3) Eliminar archivo agregado principal
agg_file = os.path.join(plc_path, cfg_aggregated_filename)
if os.path.isfile(agg_file):
try:
os.remove(agg_file)
print(f" - Eliminado archivo agregado: {os.path.relpath(agg_file, working_directory)}")
total_files_removed += 1
except Exception as e:
print(f" - ERROR al eliminar {agg_file}: {e}")
errors_found = True
# 4) Eliminar logs específicos del PLC
script_dir = os.path.dirname(os.path.abspath(__file__))
log_path = os.path.join(script_dir, f"log_{plc_name_safe}.txt")
if os.path.isfile(log_path):
try:
os.remove(log_path)
print(f" - Eliminado log: {os.path.basename(log_path)}")
total_files_removed += 1
except Exception as e:
print(f" - ERROR al eliminar {log_path}: {e}")
errors_found = True
print("\n--- Resumen de limpieza ---")
print(f" Directorios eliminados: {total_dirs_removed}")
print(f" Archivos eliminados: {total_files_removed}")
print(" Limpieza completada." if not errors_found else " Limpieza completada con errores.")
return not errors_found
except Exception as e:
print(f"ERROR inesperado durante la limpieza: {e}", file=sys.stderr)
traceback.print_exc()
return False
# --- FIN FUNCIÓN DE LIMPIEZA -----------------------------------------------------------------------------
# --- Bloque Principal --- # --- Bloque Principal ---
if __name__ == "__main__": if __name__ == "__main__":
# -------------------------------------------------------------------------
# 1. Analizar argumentos de línea de comandos
# --plc-dir : ruta al PLC a procesar directamente (modo interno)
# Si NO se pasa el flag, el script actuará como "orquestador" detectando
# todos los PLCs bajo el working_directory y lanzándose a sí mismo para
# cada uno de ellos.
# -------------------------------------------------------------------------
arg_parser = argparse.ArgumentParser(description="Convertidor XML→SCL (multi-PLC)")
arg_parser.add_argument("--plc-dir", dest="plc_dir", help="Ruta del PLC a procesar (uso interno).", default=None)
cli_args, _ = arg_parser.parse_known_args()
# Cargar configuración
configs = load_configuration() configs = load_configuration()
working_directory = configs.get("working_directory") working_directory = configs.get("working_directory")
# -------------------------------------------------------------------------
# 2. Si NO se indicó --plc-dir ⇒ modo ORQUESTADOR
# Detecta todos los PLC (subdirectorios con al menos un .xml) y lanza
# este mismo script para cada uno con el flag --plc-dir.
# -------------------------------------------------------------------------
if cli_args.plc_dir is None:
if not working_directory or not os.path.isdir(working_directory):
print("Error: 'working_directory' inválido en la configuración.", file=sys.stderr)
sys.exit(1)
# Detectar PLCs como subdirectorios que contengan al menos un XML
detected_plc_dirs = []
for entry in os.listdir(working_directory):
cand_path = os.path.join(working_directory, entry)
if os.path.isdir(cand_path):
if glob.glob(os.path.join(cand_path, "**", "*.xml"), recursive=True):
detected_plc_dirs.append(cand_path)
# Si no se encontró ningún PLC (quizás el working_directory ya ES el PLC)
if not detected_plc_dirs:
detected_plc_dirs = [working_directory]
# Ejecutar secuencialmente el script para cada PLC
overall_exit_code = 0
for plc_dir in detected_plc_dirs:
print(f"\n=== Lanzando procesamiento para PLC: {os.path.basename(plc_dir)} ===")
ret = subprocess.call([sys.executable, os.path.abspath(__file__), "--plc-dir", plc_dir])
if ret != 0:
overall_exit_code = 1 # Registrar fallo global si algún PLC falla
sys.exit(overall_exit_code)
# -------------------------------------------------------------------------
# 3. Modo INTERNO (se recibió --plc-dir) ⇒ procesar sólo ese PLC
# -------------------------------------------------------------------------
xml_project_dir = os.path.abspath(cli_args.plc_dir)
if not os.path.isdir(xml_project_dir):
print(f"Error: El directorio PLC especificado no existe: {xml_project_dir}", file=sys.stderr)
sys.exit(1)
# Usaremos el nombre del PLC para diferenciar los logs
plc_name_safe = os.path.basename(xml_project_dir.strip(os.sep))
# ---------------------------------------------------------------------
# 3.1 Leer parámetros específicos del grupo para reutilizarlos más abajo
# ---------------------------------------------------------------------
xml_parser_config = configs.get("level2", {}) xml_parser_config = configs.get("level2", {})
# <-- NUEVO: Leer parámetros de configuración para x3, x4, x5 -->
# xml_parser_config = configs.get("XML Parser to SCL", {})
cfg_scl_output_dirname = xml_parser_config.get("scl_output_dir", "scl_output") cfg_scl_output_dirname = xml_parser_config.get("scl_output_dir", "scl_output")
cfg_xref_output_dirname = xml_parser_config.get("xref_output_dir", "xref_output") cfg_xref_output_dirname = xml_parser_config.get("xref_output_dir", "xref_output")
cfg_xref_source_subdir = xml_parser_config.get("xref_source_subdir", "source") cfg_xref_source_subdir = xml_parser_config.get("xref_source_subdir", "source")
cfg_call_xref_filename = xml_parser_config.get("call_xref_filename", "xref_calls_tree.md") cfg_call_xref_filename = xml_parser_config.get("call_xref_filename", "xref_calls_tree.md")
cfg_db_usage_xref_filename = xml_parser_config.get("db_usage_xref_filename", "xref_db_usage_summary.md") cfg_db_usage_xref_filename = xml_parser_config.get("db_usage_xref_filename", "xref_db_usage_summary.md")
cfg_plc_tag_xref_filename = xml_parser_config.get("plc_tag_xref_filename", "xref_plc_tags_summary.md") cfg_plc_tag_xref_filename = xml_parser_config.get("plc_tag_xref_filename", "xref_plc_tags_summary.md")
# Ensure max_call_depth is an integer # Conversión de enteros con control de errores
try: try:
cfg_max_call_depth = int(xml_parser_config.get("max_call_depth", 5)) cfg_max_call_depth = int(xml_parser_config.get("max_call_depth", 5))
except (ValueError, TypeError): except (ValueError, TypeError):
print("Advertencia: Valor inválido para 'max_call_depth' en la configuración. Usando valor por defecto 5.", file=sys.stderr) print("Advertencia: Valor inválido para 'max_call_depth' en la configuración. Usando valor por defecto 5.", file=sys.stderr)
cfg_max_call_depth = 5 cfg_max_call_depth = 5
# Ensure max_users_list is an integer
try: try:
cfg_max_users_list = int(xml_parser_config.get("max_users_list", 20)) cfg_max_users_list = int(xml_parser_config.get("max_users_list", 20))
except (ValueError, TypeError): except (ValueError, TypeError):
print("Advertencia: Valor inválido para 'max_users_list' en la configuración. Usando valor por defecto 20.", file=sys.stderr) print("Advertencia: Valor inválido para 'max_users_list' en la configuración. Usando valor por defecto 20.", file=sys.stderr)
cfg_max_users_list = 20 cfg_max_users_list = 20
cfg_aggregated_filename = xml_parser_config.get("aggregated_filename", "full_project_representation.md") cfg_aggregated_filename = xml_parser_config.get("aggregated_filename", "full_project_representation.md")
# <-- FIN NUEVO -->
# Generar un nombre de log específico por PLC
log_filename_dynamic = f"log_{plc_name_safe}.txt"
log_filepath = os.path.join(
os.path.dirname(os.path.abspath(__file__)), log_filename_dynamic
)
# Directorio donde se encuentra este script (x0_main.py) # Directorio donde se encuentra este script (x0_main.py)
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
# <-- MODIFICADO: Abrir archivo log --> # <-- MODIFICADO: Abrir archivo log -->
log_filepath = os.path.join(
os.path.dirname(os.path.abspath(__file__)), LOG_FILENAME
)
with open( with open(
log_filepath, "w", encoding="utf-8" log_filepath, "w", encoding="utf-8"
) as log_f: # Usar 'a' para añadir al log ) as log_f: # Usar 'a' para añadir al log
log_message("=" * 40 + " LOG START " + "=" * 40, log_f) log_message("=" * 40 + " LOG START " + "=" * 40, log_f)
# --- PARTE 1: BUSCAR ARCHIVOS --- # --- PARTE 1: BUSCAR ARCHIVOS ---
# <-- MODIFICADO: Apuntar al subdirectorio 'PLC' dentro del working_directory --> # Se trabaja exclusivamente dentro del PLC indicado.
plc_subdir_name = "PLC" # Nombre estándar del subdirectorio de TIA Portal
xml_project_dir = os.path.join(working_directory, plc_subdir_name)
log_message( log_message(
f"Directorio de trabajo base configurado: '{working_directory}'", log_f f"Directorio de trabajo base configurado: '{working_directory}'", log_f
) )
log_message( log_message(
f"Buscando archivos XML recursivamente en el subdirectorio: '{xml_project_dir}'", log_f f"Buscando archivos XML recursivamente en: '{xml_project_dir}'", log_f
) )
# Verificar si el directorio PLC existe # Patrón de búsqueda global para todos los PLC
if not os.path.isdir(xml_project_dir):
log_message(
f"Error: El subdirectorio '{plc_subdir_name}' no existe dentro de '{working_directory}'. "
f"Se esperaba encontrar la estructura del proyecto TIA Portal en '{xml_project_dir}'.",
log_f,
also_print=False,
)
print(
f"Error: El subdirectorio '{plc_subdir_name}' no existe dentro de '{working_directory}'. "
f"Asegúrese de que la ruta del directorio de trabajo apunte a la carpeta que *contiene* la carpeta '{plc_subdir_name}'.", file=sys.stderr
)
sys.exit(1)
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:
@ -423,7 +572,7 @@ if __name__ == "__main__":
log_message(f"Se encontraron {len(filtered_scl_files)} archivos .scl existentes para copiar:", log_f) log_message(f"Se encontraron {len(filtered_scl_files)} archivos .scl existentes para copiar:", log_f)
for src_scl_path in filtered_scl_files: for src_scl_path in filtered_scl_files:
relative_scl_path = os.path.relpath(src_scl_path, xml_project_dir) relative_scl_path = os.path.relpath(src_scl_path, xml_project_dir)
dest_scl_path = os.path.join(scl_output_dir, os.path.basename(src_scl_path)) # Copy directly into scl_output_dir dest_scl_path = os.path.join(scl_output_dir, os.path.basename(src_scl_path)) # Copia directa al scl_output del PLC
# Check if a file with the same name was already generated from XML # Check if a file with the same name was already generated from XML
if os.path.exists(dest_scl_path): if os.path.exists(dest_scl_path):
@ -513,7 +662,8 @@ if __name__ == "__main__":
run_x5 = False run_x5 = False
if run_x5: if run_x5:
output_agg_file = os.path.join(working_directory, cfg_aggregated_filename) # Usar valor de config # El archivo agregado se guarda dentro del PLC para mantener salidas separadas
output_agg_file = os.path.join(xml_project_dir, cfg_aggregated_filename)
log_message( log_message(
f"Ejecutando x5 (aggregate_outputs) sobre: {xml_project_dir}, salida agregada en: {output_agg_file}", f"Ejecutando x5 (aggregate_outputs) sobre: {xml_project_dir}, salida agregada en: {output_agg_file}",
log_f log_f
@ -590,7 +740,7 @@ if __name__ == "__main__":
log_message(final_console_message, log_f) # Loguear mensaje final log_message(final_console_message, log_f) # Loguear mensaje final
print( print(
f"\n{final_console_message} Consulta '{LOG_FILENAME}' para detalles." f"\n{final_console_message} Consulta '{log_filename_dynamic}' para detalles."
) # Mostrar mensaje en consola ) # Mostrar mensaje en consola
log_message("="*41 + " LOG END " + "="*42, log_f) log_message("="*41 + " LOG END " + "="*42, log_f)

View File

@ -25,7 +25,7 @@ from backend.script_utils import load_configuration
# Importar funciones comunes y namespaces desde el nuevo módulo de utils # Importar funciones comunes y namespaces desde el nuevo módulo de utils
try: try:
from parsers.parser_utils import ns, get_multilingual_text, parse_interface_members from parsers.parser_utils import ns, get_multilingual_text, parse_interface_members, adapt_namespaces
except ImportError as e: except ImportError as e:
print( print(
f"Error crítico: No se pudieron importar funciones desde parsers.parser_utils: {e}" f"Error crítico: No se pudieron importar funciones desde parsers.parser_utils: {e}"
@ -253,6 +253,11 @@ def convert_xml_to_json(xml_filepath, json_filepath):
parser = etree.XMLParser(remove_blank_text=True, recover=True) parser = etree.XMLParser(remove_blank_text=True, recover=True)
tree = etree.parse(xml_filepath, parser) tree = etree.parse(xml_filepath, parser)
root = tree.getroot() root = tree.getroot()
# Ajustar namespaces dinámicamente para soportar distintas versiones de TIA
try:
adapt_namespaces(root)
except Exception as e_ns:
print(f"Advertencia: No se pudo adaptar namespaces dinámicamente: {e_ns}")
print("Paso 1: Parseo XML completado.") print("Paso 1: Parseo XML completado.")
result = None result = None

View File

@ -366,6 +366,18 @@ def generate_call_tree_output(call_graph, block_data, base_xref_dir, max_call_de
""" """
output_lines = ["# Árbol de Referencias Cruzadas de Llamadas\n"] output_lines = ["# Árbol de Referencias Cruzadas de Llamadas\n"]
output_lines.append(f"(Profundidad máxima: {max_call_depth})\n") # <-- Usar el parámetro output_lines.append(f"(Profundidad máxima: {max_call_depth})\n") # <-- Usar el parámetro
# ------------------------------------------------------------
# Aviso cuando NO se han detectado llamadas entre bloques
# ------------------------------------------------------------
has_any_call = any(len(callees) > 0 for callees in call_graph.values())
if not has_any_call:
output_lines.append(
"\n> ⚠️ Nota: No se detectaron referencias cruzadas de llamadas. "
"Es posible que no existan los archivos '*_XRef.xml' o que aún no "
"se haya ejecutado el análisis de fallback sobre los bloques SCL.\n"
)
root_nodes = sorted( # Encontrar OBs root_nodes = sorted( # Encontrar OBs
[ [
name name
@ -646,7 +658,8 @@ def generate_cross_references(
# 2. Construir Grafo de Llamadas desde XML XRef # 2. Construir Grafo de Llamadas desde XML XRef
print("Construyendo grafo de llamadas desde archivos XML XRef...") print("Construyendo grafo de llamadas desde archivos XML XRef...")
call_graph = defaultdict(list) # Usamos lista, no necesitamos contar llamadas múltiples aquí call_graph = defaultdict(list) # Usamos lista, no necesitamos contar llamadas múltiples aquí
xref_xml_files = glob.glob(os.path.join(xref_xml_dir, "*_XRef.xml")) # Buscar recursivamente en todas las subcarpetas (algunos proyectos guardan los XML en estructuras anidadas)
xref_xml_files = glob.glob(os.path.join(xref_xml_dir, "**", "*_XRef.xml"), recursive=True)
if not xref_xml_files: if not xref_xml_files:
print(f"ADVERTENCIA: No se encontraron archivos '*_XRef.xml' en {xref_xml_dir}. El árbol de llamadas estará vacío.", file=sys.stderr) print(f"ADVERTENCIA: No se encontraron archivos '*_XRef.xml' en {xref_xml_dir}. El árbol de llamadas estará vacío.", file=sys.stderr)
else: else:

View File

@ -0,0 +1,39 @@
"""x7_clear.py
Script de limpieza para eliminar todos los artefactos generados por x0_main.py.
Este script actúa como envoltorio del método `clear_generated_outputs` definido
en `x0_main.py`. De esta forma, la lógica de eliminación se mantiene en un solo
lugar y se adapta automáticamente a futuros cambios en la estructura de
salidas de x0.
"""
import argparse
import sys
# Importar la función de limpieza desde x0_main.py
from x0_main import clear_generated_outputs # noqa: E402 import absoluto intencional
def main() -> None:
parser = argparse.ArgumentParser(
description="Elimina los archivos generados por x0_main.py (JSON, SCL, MD, logs)."
)
parser.add_argument(
"--plc-dir",
dest="plc_dir",
default=None,
help=(
"Ruta de un PLC específico a limpiar. Si se omite, se limpiarán "
"todos los PLCs detectados bajo el working_directory definido en la configuración."
),
)
args = parser.parse_args()
success = clear_generated_outputs(args.plc_dir)
# Salir con 0 si todo fue bien, 1 en caso de errores
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()

View File

@ -1,5 +1,18 @@
{ {
"history": [ "history": [
{
"id": "15176a5f",
"group_id": "1",
"script_name": "calc.py",
"executed_date": "2025-06-13T10:53:37.648203Z",
"arguments": [],
"working_directory": "D:/Proyectos/Scripts/Calcv2",
"python_env": "tia_scripting",
"executable_type": "pythonw.exe",
"status": "running",
"pid": 21072,
"execution_time": null
},
{ {
"id": "a599effd", "id": "a599effd",
"group_id": "4", "group_id": "4",

View File

@ -1,5 +1,129 @@
[20:03:36] Iniciando ejecución de x1.py en D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Source... [11:14:30] Iniciando ejecución de x4.py en D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports...
[20:03:37] --- TIA Portal Data Exporter (Blocks, UDTs, Tags) --- [11:14:30] --- Exportador de Referencias Cruzadas de TIA Portal ---
[20:03:47] No project file selected. Exiting. [11:14:34] Versión de TIA Portal detectada: 19.0 (de la extensión .ap19)
[20:03:47] Ejecución de x1.py finalizada (success). Duración: 0:00:10.497365. [11:14:34] Proyecto seleccionado: D:/Trabajo/VM/22 - 93841 - Sidel - Tilting/InLavoro/PLC/93841_PLC_28/93841_PLC_28.ap19
[20:03:47] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\log_x1.txt [11:14:34] Usando directorio base de exportación: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports
[11:14:34] Conectando a TIA Portal V19.0...
[11:14:34] 2025-06-13 11:14:34,713 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
[11:14:34] 2025-06-13 11:14:34,731 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
[11:14:58] Conectado a TIA Portal.
[11:14:58] 2025-06-13 11:14:58,165 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal GetProcessId - Process id: 30140
[11:14:58] ID del proceso del Portal: 30140
[11:14:58] Abriendo proyecto: 93841_PLC_28.ap19...
[11:14:58] 2025-06-13 11:14:58,500 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\InLavoro\PLC\93841_PLC_28\93841_PLC_28.ap19
[11:15:25] Proyecto abierto exitosamente.
[11:15:29] 2025-06-13 11:15:29,701 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc VM 1512 with parent name ET 200SP station_1
[11:15:30] 2025-06-13 11:15:30,551 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc SIDEL Transport Example with parent name S71500/ET200MP station_1
[11:15:34] Se encontraron 2 PLC(s). Iniciando proceso de exportación de referencias cruzadas...
[11:15:34] --- Procesando PLC: VM 1512 ---
[11:15:34] [PLC: VM 1512] Exportando referencias cruzadas de bloques de programa...
[11:15:34] Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\ProgramBlocks_CR
[11:15:34] Se encontraron 201 bloques de programa.
[11:15:34] Procesando bloque: FC General COM...
[11:15:34] Exportando referencias cruzadas para FC General COM...
[11:15:38] Procesando bloque: From_SIDEL...
[11:15:38] Exportando referencias cruzadas para From_SIDEL...
[11:15:38] Procesando bloque: To_SIDEL...
[11:15:38] Exportando referencias cruzadas para To_SIDEL...
[11:15:38] Procesando bloque: DB Early Restart Blower...
[11:15:38] Exportando referencias cruzadas para DB Early Restart Blower...
[11:15:39] Procesando bloque: DB Early Restart Filler...
[11:15:39] Exportando referencias cruzadas para DB Early Restart Filler...
[11:15:39] Procesando bloque: DB Early Restart SynchroBlock...
[11:15:39] Exportando referencias cruzadas para DB Early Restart SynchroBlock...
[11:15:40] Procesando bloque: FB Early Restart...
[11:15:40] Exportando referencias cruzadas para FB Early Restart...
[11:15:40] Procesando bloque: DB Signal Transport...
[11:15:40] Exportando referencias cruzadas para DB Signal Transport...
[11:15:42] Procesando bloque: FC Signal Transport...
[11:15:42] Exportando referencias cruzadas para FC Signal Transport...
[11:15:43] Procesando bloque: DB Lube - Dry Ecolab...
[11:15:43] Exportando referencias cruzadas para DB Lube - Dry Ecolab...
[11:15:46] Procesando bloque: FB Lube - Water/Dry...
[11:15:46] Exportando referencias cruzadas para FB Lube - Water/Dry...
[11:15:46] Procesando bloque: FB Lube - Dry Ecolab...
[11:15:46] Exportando referencias cruzadas para FB Lube - Dry Ecolab...
[11:15:47] Procesando bloque: FB Lube - EcoLab VM...
[11:15:47] Exportando referencias cruzadas para FB Lube - EcoLab VM...
[11:15:48] Procesando bloque: FB Lube - Ecolab...
[11:15:48] Exportando referencias cruzadas para FB Lube - Ecolab...
[11:15:49] Procesando bloque: DB LUBE - Ecolab...
[11:15:49] Exportando referencias cruzadas para DB LUBE - Ecolab...
[11:15:57] Procesando bloque: FC Ttop Configuration...
[11:15:57] Exportando referencias cruzadas para FC Ttop Configuration...
[11:15:57] Procesando bloque: FC Ttop Run...
[11:15:57] Exportando referencias cruzadas para FC Ttop Run...
[11:15:58] Procesando bloque: FC Ttop Alarms...
[11:15:58] Exportando referencias cruzadas para FC Ttop Alarms...
[11:15:58] Procesando bloque: DB Ttop Run...
[11:15:58] Exportando referencias cruzadas para DB Ttop Run...
[11:15:59] Procesando bloque: DB Ttop Motor CFG...
[11:15:59] Exportando referencias cruzadas para DB Ttop Motor CFG...
[11:16:02] Procesando bloque: DB Ttop Alarm...
[11:16:02] Exportando referencias cruzadas para DB Ttop Alarm...
[11:16:07] Procesando bloque: FC Ttop Motor 31...
[11:16:07] Exportando referencias cruzadas para FC Ttop Motor 31...
[11:16:07] Procesando bloque: FC Ttop Motor 32...
[11:16:07] Exportando referencias cruzadas para FC Ttop Motor 32...
[11:16:07] Procesando bloque: FC Ttop Motor 34...
[11:16:07] Exportando referencias cruzadas para FC Ttop Motor 34...
[11:16:08] Procesando bloque: FC Ttop Motor 35...
[11:16:08] Exportando referencias cruzadas para FC Ttop Motor 35...
[11:16:08] Procesando bloque: FC Ttop Motor 36...
[11:16:08] Exportando referencias cruzadas para FC Ttop Motor 36...
[11:16:08] Procesando bloque: DB Ttop Motor 31...
[11:16:08] Exportando referencias cruzadas para DB Ttop Motor 31...
[11:16:13] Procesando bloque: DB Ttop Motor 32...
[11:16:13] Exportando referencias cruzadas para DB Ttop Motor 32...
[11:16:18] Procesando bloque: DB Ttop Motor 34...
[11:16:18] Exportando referencias cruzadas para DB Ttop Motor 34...
[11:16:23] Procesando bloque: DB Ttop Motor 35...
[11:16:23] Exportando referencias cruzadas para DB Ttop Motor 35...
[11:16:27] Procesando bloque: DB Ttop Minimotor Cfg 32...
[11:16:27] Exportando referencias cruzadas para DB Ttop Minimotor Cfg 32...
[11:16:29] Procesando bloque: DB Ttop Minimotor Data 32...
[11:16:29] Exportando referencias cruzadas para DB Ttop Minimotor Data 32...
[11:16:31] Procesando bloque: DB Ttop Motor 36...
[11:16:31] Exportando referencias cruzadas para DB Ttop Motor 36...
[11:16:35] Procesando bloque: FB Ttop Dryer...
[11:16:35] Exportando referencias cruzadas para FB Ttop Dryer...
[11:16:36] Procesando bloque: FB Ttop Energy Saving...
[11:16:36] Exportando referencias cruzadas para FB Ttop Energy Saving...
[11:16:36] Procesando bloque: FB SKID...
[11:16:36] Exportando referencias cruzadas para FB SKID...
[11:16:36] Procesando bloque: FC Analog Sensor Process...
[11:16:36] Exportando referencias cruzadas para FC Analog Sensor Process...
[11:16:37] Procesando bloque: FC Valve...
[11:16:37] Exportando referencias cruzadas para FC Valve...
[11:16:37] Procesando bloque: FB SpeedRegulation...
[11:16:37] Exportando referencias cruzadas para FB SpeedRegulation...
[11:16:37] Procesando bloque: FC Simple PID...
[11:16:37] Exportando referencias cruzadas para FC Simple PID...
[11:16:38] Procesando bloque: FC Scale Real...
[11:16:38] Exportando referencias cruzadas para FC Scale Real...
[11:16:38] Procesando bloque: FB Correct Speed F/Pulses...
[11:16:38] Exportando referencias cruzadas para FB Correct Speed F/Pulses...
[11:16:43] ERROR GENERAL al exportar referencias cruzadas para el bloque FB Correct Speed F/Pulses: OpennessAccessException: Unexpected exception - no exception message available.
[11:16:43] ERROR al acceder a los bloques de programa para exportar referencias cruzadas: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.SW.Blocks.FB' is not possible.
[11:16:43] TIA Portal has either been disposed or stopped running.
[11:16:43] [PLC: VM 1512] Exportando referencias cruzadas de tablas de variables...
[11:16:43] Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcTags_CR
[11:16:43] ERROR al acceder a las tablas de variables para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
[11:16:43] [PLC: VM 1512] Exportando referencias cruzadas de tipos de datos PLC (UDTs)...
[11:16:43] Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcDataTypes_CR
[11:16:43] ERROR al acceder a los UDTs para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
[11:16:43] [PLC: VM 1512] Intentando exportar referencias cruzadas de bloques de sistema...
[11:16:43] Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\SystemBlocks_CR
[11:16:43] ERROR al acceder/procesar bloques de sistema para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
[11:16:43] [PLC: VM 1512] Intentando exportar referencias cruzadas de unidades de software...
[11:16:43] Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\SoftwareUnits_CR
[11:16:43] ERROR al acceder/procesar unidades de software para exportar referencias cruzadas: SerializationException: No se puede encontrar el ensamblado 'Siemens.Engineering, Version=19.0.0.0, Culture=neutral, PublicKeyToken=d29ec89bac048f84'.
[11:16:43] --- Finalizado el procesamiento del PLC: VM 1512 ---
[11:16:43] Ocurrió un error inesperado: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.HW.DeviceItemImpl' is not possible.
[11:16:43] TIA Portal has either been disposed or stopped running.
[11:16:43] Cerrando TIA Portal...
[11:16:43] 2025-06-13 11:16:43,486 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
[11:16:43] TIA Portal cerrado.
[11:16:43] Script finalizado.
[11:16:43] Ejecución de x4.py finalizada (success). Duración: 0:02:13.165274. Se detectaron errores (ver log).
[11:16:43] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\log_x4.txt

View File

@ -181,17 +181,17 @@
<div class="flex-1 flex gap-2"> <div class="flex-1 flex gap-2">
<input type="text" id="working-directory" class="flex-1 p-2 border rounded bg-green-50"> <input type="text" id="working-directory" class="flex-1 p-2 border rounded bg-green-50">
<button class="bg-gray-500 text-white px-4 py-2 rounded" onclick="browseDirectory()"> <button class="bg-gray-500 text-white px-4 py-2 rounded" onclick="browseDirectory()">
Explorar Buscar
</button> </button>
<button id="open-in-explorer-btn" <button id="open-in-explorer-btn"
class="bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded" class="bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded"
title="Abrir directorio actual en el explorador de archivos"> title="Abrir directorio actual en el explorador de archivos">
Abrir Carpeta Abrir Carpeta
</button> </button>
</div> <button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()">
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()"> Salvar
Confirmar </button>
</button> </div>
</div> </div>
<!-- Add directory history dropdown --> <!-- Add directory history dropdown -->
<div class="mt-2"> <div class="mt-2">