Compare commits

..

No commits in common. "734e6637bc4be7105cedd4100e33a6a7610fd1ac" and "be3b3334918c05ff6b90ddbaa070eb1d1801631b" have entirely different histories.

37 changed files with 562 additions and 30513 deletions

3
.gitignore vendored
View File

@ -25,9 +25,6 @@ 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,37 +33,6 @@ 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):
@ -80,34 +49,49 @@ def handle_websocket(ws):
def broadcast_message(message): def broadcast_message(message):
"""Acumula mensajes en un buffer y los envía por lotes cada 500 ms.""" """Envía un mensaje a todas las conexiones WebSocket activas y guarda en log."""
dead_connections = set()
timestamp = datetime.now().strftime("[%H:%M:%S] ") timestamp = datetime.now().strftime("[%H:%M:%S] ")
# Normalizar entrada a lista de mensajes # Normalize input to a list of messages
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() raw_msg = raw_msg[closing_bracket:].strip() # Update raw_msg itself
else: else:
break break
except ValueError: except:
break break
# Registrar en archivo (la clase Logger añade timestamp propio) # Log the raw message using the config_manager's logger
# The logger will handle its own timestamping for the file.
config_manager.append_log(raw_msg) config_manager.append_log(raw_msg)
# Formatear para el WebSocket y añadir al buffer # Format message with timestamp *for WebSocket broadcast*
formatted_msg_for_ws = f"{timestamp}{raw_msg}" formatted_msg_for_ws = f"{timestamp}{raw_msg}"
with buffer_lock:
broadcast_buffer.append(formatted_msg_for_ws) # Enviar a todos los clientes WebSocket
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"])
@ -1069,8 +1053,3 @@ 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,199 +1,251 @@
--- Log de Ejecución: x4.py --- --- Log de Ejecución: x4.py ---
Grupo: ObtainIOFromProjectTia Grupo: ObtainIOFromProjectTia
Directorio de Trabajo: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML
Inicio: 2025-06-13 11:14:30 Inicio: 2025-05-20 12:12:51
Fin: 2025-06-13 11:16:43 Fin: 2025-05-20 12:20:01
Duración: 0:02:13.165274 Duración: 0:07:09.648304
Estado: SUCCESS (Código de Salida: 0) Estado: SUCCESS (Código de Salida: 0)
--- SALIDA ESTÁNDAR (STDOUT) --- --- SALIDA ESTÁNDAR (STDOUT) ---
--- Exportador de Referencias Cruzadas de TIA Portal --- --- TIA Portal Cross-Reference Exporter ---
Versión de TIA Portal detectada: 19.0 (de la extensión .ap19)
Proyecto seleccionado: D:/Trabajo/VM/22 - 93841 - Sidel - Tilting/InLavoro/PLC/93841_PLC_28/93841_PLC_28.ap19 Selected Project: C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452/SAE452.ap18
Usando directorio base de exportación: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports Using Base Export Directory: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML
Conectando a TIA Portal V19.0... Connecting to TIA Portal V18.0...
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,780 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
2025-06-13 11:14:34,731 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface 2025-05-20 12:12:56,804 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - With user interface
Conectado a TIA Portal. Connected to TIA Portal.
2025-06-13 11:14:58,165 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal GetProcessId - Process id: 30140 2025-05-20 12:13:30,582 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal GetProcessId - Process id: 21952
ID del proceso del Portal: 30140 Portal Process ID: 21952
Abriendo proyecto: 93841_PLC_28.ap19... Opening project: 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 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
Proyecto abierto exitosamente. Project opened successfully.
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 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:30,551 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc SIDEL Transport Example with parent name S71500/ET200MP station_1 Found 1 PLC(s). Starting cross-reference export process...
Se encontraron 2 PLC(s). Iniciando proceso de exportación de referencias cruzadas...
--- Procesando PLC: VM 1512 --- --- Processing PLC: CPU 315F-2 PN/DP ---
[PLC: VM 1512] Exportando referencias cruzadas de bloques de programa... [PLC: CPU 315F-2 PN/DP] Exporting Program Block Cross-References...
Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\ProgramBlocks_CR Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\ProgramBlocks_CR
Se encontraron 201 bloques de programa. Found 410 program blocks.
Procesando bloque: FC General COM... Processing block: ISOonTCP_or_TCP_Protocol...
Exportando referencias cruzadas para FC General COM... Exporting cross-references for ISOonTCP_or_TCP_Protocol...
Procesando bloque: From_SIDEL... Processing block: PIDControl...
Exportando referencias cruzadas para From_SIDEL... Exporting cross-references for PIDControl...
Procesando bloque: To_SIDEL... Processing block: DETAIL_DP_DIAG...
Exportando referencias cruzadas para To_SIDEL... Exporting cross-references for DETAIL_DP_DIAG...
Procesando bloque: DB Early Restart Blower... Processing block: Net Dosing Sys Prof...
Exportando referencias cruzadas para DB Early Restart Blower... Exporting cross-references for Net Dosing Sys Prof...
Procesando bloque: DB Early Restart Filler... Processing block: ICS Profibus Comm...
Exportando referencias cruzadas para DB Early Restart Filler... Exporting cross-references for ICS Profibus Comm...
Procesando bloque: DB Early Restart SynchroBlock... Processing block: GNS DriveDiag...
Exportando referencias cruzadas para DB Early Restart SynchroBlock... Exporting cross-references for GNS DriveDiag...
Procesando bloque: FB Early Restart... Processing block: HMI_Blender_Parameters...
Exportando referencias cruzadas para FB Early Restart... Exporting cross-references for HMI_Blender_Parameters...
Procesando bloque: DB Signal Transport... Processing block: HMI Drive...
Exportando referencias cruzadas para DB Signal Transport... Exporting cross-references for HMI Drive...
Procesando bloque: FC Signal Transport... Processing block: GNS DriveDiagMain...
Exportando referencias cruzadas para FC Signal Transport... Exporting cross-references for GNS DriveDiagMain...
Procesando bloque: DB Lube - Dry Ecolab... Processing block: Integral...
Exportando referencias cruzadas para DB Lube - Dry Ecolab... Exporting cross-references for Integral...
Procesando bloque: FB Lube - Water/Dry... Processing block: LowPassFilter...
Exportando referencias cruzadas para FB Lube - Water/Dry... Exporting cross-references for LowPassFilter...
Procesando bloque: FB Lube - Dry Ecolab... Processing block: SlewLimit...
Exportando referencias cruzadas para FB Lube - Dry Ecolab... Exporting cross-references for SlewLimit...
Procesando bloque: FB Lube - EcoLab VM... Processing block: MSE Slope...
Exportando referencias cruzadas para FB Lube - EcoLab VM... Exporting cross-references for MSE Slope...
Procesando bloque: FB Lube - Ecolab... Processing block: Statistical_Analisys...
Exportando referencias cruzadas para FB Lube - Ecolab... Exporting cross-references for Statistical_Analisys...
Procesando bloque: DB LUBE - Ecolab... Processing block: Blender_Variables...
Exportando referencias cruzadas para DB LUBE - Ecolab... Exporting cross-references for Blender_Variables...
Procesando bloque: FC Ttop Configuration... Processing block: BrixTracking_ProdSamples...
Exportando referencias cruzadas para FC Ttop Configuration... Exporting cross-references for BrixTracking_ProdSamples...
Procesando bloque: FC Ttop Run... Processing block: Procedure_Variables...
Exportando referencias cruzadas para FC Ttop Run... Exporting cross-references for Procedure_Variables...
Procesando bloque: FC Ttop Alarms... Processing block: Blender_Constants...
Exportando referencias cruzadas para FC Ttop Alarms... Exporting cross-references for Blender_Constants...
Procesando bloque: DB Ttop Run... Processing block: BrixTracking_SampleTime...
Exportando referencias cruzadas para DB Ttop Run... Exporting cross-references for BrixTracking_SampleTime...
Procesando bloque: DB Ttop Motor CFG... Processing block: Delay...
Exportando referencias cruzadas para DB Ttop Motor CFG... Exporting cross-references for Delay...
Procesando bloque: DB Ttop Alarm... Processing block: CO2Tracking_ProdSamples...
Exportando referencias cruzadas para DB Ttop Alarm... Exporting cross-references for CO2Tracking_ProdSamples...
Procesando bloque: FC Ttop Motor 31... Processing block: CO2Tracking_SampleTime...
Exportando referencias cruzadas para FC Ttop Motor 31... Exporting cross-references for CO2Tracking_SampleTime...
Procesando bloque: FC Ttop Motor 32... Processing block: Interlocking_Variables...
Exportando referencias cruzadas para FC Ttop Motor 32... Exporting cross-references for Interlocking_Variables...
Procesando bloque: FC Ttop Motor 34... Processing block: System_RunOut_Variables...
Exportando referencias cruzadas para FC Ttop Motor 34... Exporting cross-references for System_RunOut_Variables...
Procesando bloque: FC Ttop Motor 35... Processing block: CIP_Program_Variables...
Exportando referencias cruzadas para FC Ttop Motor 35... Exporting cross-references for CIP_Program_Variables...
Procesando bloque: FC Ttop Motor 36... Processing block: Filler_Head_Variables...
Exportando referencias cruzadas para FC Ttop Motor 36... Exporting cross-references for Filler_Head_Variables...
Procesando bloque: DB Ttop Motor 31... Processing block: Filling_Time_Tranfer_DB...
Exportando referencias cruzadas para DB Ttop Motor 31... Exporting cross-references for Filling_Time_Tranfer_DB...
Procesando bloque: DB Ttop Motor 32... Processing block: Blender_Variables_Pers...
Exportando referencias cruzadas para DB Ttop Motor 32... Exporting cross-references for Blender_Variables_Pers...
Procesando bloque: DB Ttop Motor 34... Processing block: HMI_Alarms...
Exportando referencias cruzadas para DB Ttop Motor 34... Exporting cross-references for HMI_Alarms...
Procesando bloque: DB Ttop Motor 35... Processing block: HMI_Local_CIP_Variables...
Exportando referencias cruzadas para DB Ttop Motor 35... Exporting cross-references for HMI_Local_CIP_Variables...
Procesando bloque: DB Ttop Minimotor Cfg 32... Processing block: HMI_Service...
Exportando referencias cruzadas para DB Ttop Minimotor Cfg 32... Exporting cross-references for HMI_Service...
Procesando bloque: DB Ttop Minimotor Data 32... Processing block: HMI_Variables_Cmd...
Exportando referencias cruzadas para DB Ttop Minimotor Data 32... Exporting cross-references for HMI_Variables_Cmd...
Procesando bloque: DB Ttop Motor 36... Processing block: HMI_Variables_Status...
Exportando referencias cruzadas para DB Ttop Motor 36... Exporting cross-references for HMI_Variables_Status...
Procesando bloque: FB Ttop Dryer... Processing block: HMI_Device...
Exportando referencias cruzadas para FB Ttop Dryer... Exporting cross-references for HMI_Device...
Procesando bloque: FB Ttop Energy Saving... Processing block: HMI_Instrument...
Exportando referencias cruzadas para FB Ttop Energy Saving... Exporting cross-references for HMI_Instrument...
Procesando bloque: FB SKID... Processing block: HMI_Digital...
Exportando referencias cruzadas para FB SKID... Exporting cross-references for HMI_Digital...
Procesando bloque: FC Analog Sensor Process... Processing block: HMI_PID...
Exportando referencias cruzadas para FC Analog Sensor Process... Exporting cross-references for HMI_PID...
Procesando bloque: FC Valve... Processing block: HMI_ICS...
Exportando referencias cruzadas para FC Valve... Exporting cross-references for HMI_ICS...
Procesando bloque: FB SpeedRegulation... Processing block: HMI_Device_AVS...
Exportando referencias cruzadas para FB SpeedRegulation... Exporting cross-references for HMI_Device_AVS...
Procesando bloque: FC Simple PID... Processing block: Profibus_Variables...
Exportando referencias cruzadas para FC Simple PID... Exporting cross-references for Profibus_Variables...
Procesando bloque: FC Scale Real... Processing block: Input_CheckFlowMetersSta...
Exportando referencias cruzadas para FC Scale Real... Exporting cross-references for Input_CheckFlowMetersSta...
Procesando bloque: FB Correct Speed F/Pulses... Processing block: Input_DigitalScanner...
Exportando referencias cruzadas para FB Correct Speed F/Pulses... Exporting cross-references for Input_DigitalScanner...
ERROR GENERAL al exportar referencias cruzadas para el bloque FB Correct Speed F/Pulses: OpennessAccessException: Unexpected exception - no exception message available. Processing block: 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. Exporting cross-references for ProductLiterInTank...
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.
TIA Portal has either been disposed or stopped running. [PLC: CPU 315F-2 PN/DP] Exporting PLC Tag Table Cross-References...
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: VM 1512] Exportando referencias cruzadas de tablas de variables... [PLC: CPU 315F-2 PN/DP] Exporting PLC Data Type (UDT) Cross-References...
Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcTags_CR Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\PlcDataTypes_CR
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'. Found 21 UDTs.
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: VM 1512] Exportando referencias cruzadas de tipos de datos PLC (UDTs)... [PLC: CPU 315F-2 PN/DP] Attempting to Export System Block Cross-References...
Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\PlcDataTypes_CR Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SystemBlocks_CR
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'. Found 12 system blocks (using get_system_blocks).
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: VM 1512] Intentando exportar referencias cruzadas de bloques de sistema... [PLC: CPU 315F-2 PN/DP] Attempting to Export Software Unit Cross-References...
Destino: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports\VM 1512\SystemBlocks_CR Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SoftwareUnits_CR
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'. Found 0 Software Units.
Software Unit CR Export Summary: Exported=0, Skipped/Errors=0
[PLC: VM 1512] Intentando exportar referencias cruzadas de unidades de software... --- Finished processing PLC: CPU 315F-2 PN/DP ---
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'.
--- Finalizado el procesamiento del PLC: VM 1512 --- Cross-reference export process completed.
Ocurrió un error inesperado: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.HW.DeviceItemImpl' is not possible. Closing TIA Portal...
2025-05-20 12:19:54,187 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal ClosePortal - Close TIA Portal
TIA Portal closed.
TIA Portal has either been disposed or stopped running. Script finished.
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 128, in export_plc_cross_references File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 99, 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: OpennessAccessException: Access to a disposed object of type 'Siemens.Engineering.SW.Blocks.FB' is not possible. ValueError: RemotingException: El objeto '/bd68de4c_3307_463d_b3ce_57a1378b3bde/lnycb9uriadrpgmom_mjdspm_249.rem' se desconectó o no existe en el servidor.
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\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports" "working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source"
} }

View File

@ -1,6 +1,6 @@
{ {
"x1.py": { "x1.py": {
"display_name": "1: Exportar Lógica desde TIA Portal v18,v19,v20 en XML", "display_name": "1: Exportar Lógica desde TIA Portal v18 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,7 +1,6 @@
{ {
"path": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports", "path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"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,7 +7,6 @@ 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__)))
@ -16,15 +15,11 @@ sys.path.append(script_root)
from backend.script_utils import load_configuration from backend.script_utils import load_configuration
# --- Configuration --- # --- Configuration ---
# Supported TIA Portal versions mapping (extension -> version) TIA_PORTAL_VERSION = "18.0" # Target TIA Portal version (e.g., "18.0")
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 = True # Replicate TIA project folder structure in export directory KEEP_FOLDER_STRUCTURE = (
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
@ -64,33 +59,6 @@ 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."""
@ -98,11 +66,16 @@ 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=get_supported_filetypes() 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 se seleccionó ningún archivo de proyecto. Saliendo.") print("No project file selected. Exiting.")
sys.exit(0) sys.exit(0)
return file_path return file_path
@ -122,7 +95,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--- Procesando PLC: {plc_name} ---") print(f"\n--- Processing 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)
@ -131,140 +104,143 @@ 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}] Exportando bloques de programa...") print(f"\n[PLC: {plc_name}] Exporting Program Blocks...")
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" Destino XML: {xml_blocks_path}") print(f" XML Target: {xml_blocks_path}")
print(f" Destino SCL: {scl_blocks_path}") print(f" SCL Target: {scl_blocks_path}")
try: try:
program_blocks = plc.get_program_blocks() program_blocks = plc.get_program_blocks() #
print(f" Se encontraron {len(program_blocks)} bloques de programa.") print(f" Found {len(program_blocks)} program blocks.")
for block in program_blocks: for block in program_blocks:
block_name = block.get_name() block_name = block.get_name() # Assuming get_name() exists
print(f" Procesando bloque: {block_name}...") print(f" Processing block: {block_name}...")
try: try:
if not block.is_consistent(): if not block.is_consistent(): #
print(f" Compilando bloque {block_name}...") print(f" Compiling block {block_name}...")
block.compile() block.compile() #
if not block.is_consistent(): if not block.is_consistent():
print( print(
f" ADVERTENCIA: Bloque {block_name} inconsistente después de compilar. Omitiendo." f" WARNING: Block {block_name} inconsistent after compile. Skipping."
) )
blocks_skipped += 1 blocks_skipped += 1
continue continue
print(f" Exportando {block_name} como XML...") print(f" Exporting {block_name} as 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" Exportando {block_name} como SCL...") print(f" Exporting {block_name} as 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" No se pudo obtener el lenguaje de programación para {block_name}. Omitiendo SCL. Error: {prop_ex}" f" Could not get ProgrammingLanguage for {block_name}. Skipping SCL. Error: {prop_ex}"
) )
blocks_exported += 1 blocks_exported += 1
except Exception as block_ex: except Exception as block_ex:
print(f" ERROR exportando bloque {block_name}: {block_ex}") print(f" ERROR exporting block {block_name}: {block_ex}")
blocks_skipped += 1 blocks_skipped += 1
print( print(
f" Resumen de exportación de bloques: Exportados={blocks_exported}, Omitidos/Errores={blocks_skipped}" f" Program Blocks Export Summary: Exported={blocks_exported}, Skipped/Errors={blocks_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR procesando bloques de programa: {e}") print(f" ERROR processing Program Blocks: {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}] Exportando tipos de datos PLC (UDTs)...") print(f"\n[PLC: {plc_name}] Exporting PLC Data Types (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" Destino: {udt_export_path}") print(f" Target: {udt_export_path}")
try: try:
udts = plc.get_user_data_types() udts = plc.get_user_data_types() #
print(f" Se encontraron {len(udts)} UDTs.") print(f" Found {len(udts)} UDTs.")
for udt in udts: for udt in udts:
udt_name = udt.get_name() udt_name = udt.get_name() #
print(f" Procesando UDT: {udt_name}...") print(f" Processing UDT: {udt_name}...")
try: try:
if not udt.is_consistent(): if not udt.is_consistent(): #
print(f" Compilando UDT {udt_name}...") print(f" Compiling UDT {udt_name}...")
udt.compile() udt.compile() #
if not udt.is_consistent(): if not udt.is_consistent():
print( print(
f" ADVERTENCIA: UDT {udt_name} inconsistente después de compilar. Omitiendo." f" WARNING: UDT {udt_name} inconsistent after compile. Skipping."
) )
udts_skipped += 1 udts_skipped += 1
continue continue
print(f" Exportando {udt_name}...") print(f" Exporting {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 exportando UDT {udt_name}: {udt_ex}") print(f" ERROR exporting UDT {udt_name}: {udt_ex}")
udts_skipped += 1 udts_skipped += 1
print( print(
f" Resumen de exportación de UDTs: Exportados={udts_exported}, Omitidos/Errores={udts_skipped}" f" UDT Export Summary: Exported={udts_exported}, Skipped/Errors={udts_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR procesando UDTs: {e}") print(f" ERROR processing 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}] Exportando tablas de variables PLC...") print(f"\n[PLC: {plc_name}] Exporting PLC Tag Tables...")
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" Destino: {tags_export_path}") print(f" Target: {tags_export_path}")
try: try:
tag_tables = plc.get_plc_tag_tables() tag_tables = plc.get_plc_tag_tables() #
print(f" Se encontraron {len(tag_tables)} tablas de variables.") print(f" Found {len(tag_tables)} Tag Tables.")
for table in tag_tables: for table in tag_tables:
table_name = table.get_name() table_name = table.get_name() #
print(f" Procesando tabla de variables: {table_name}...") print(f" Processing Tag Table: {table_name}...")
try: try:
print(f" Exportando {table_name}...") # Note: Consistency check might not be available/needed for tag tables like blocks/UDTs
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 exportando tabla de variables {table_name}: {table_ex}") print(f" ERROR exporting Tag Table {table_name}: {table_ex}")
tags_skipped += 1 tags_skipped += 1
print( print(
f" Resumen de exportación de tablas de variables: Exportados={tags_exported}, Omitidos/Errores={tags_skipped}" f" Tag Table Export Summary: Exported={tags_exported}, Skipped/Errors={tags_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR procesando tablas de variables: {e}") print(f" ERROR processing Tag Tables: {e}")
traceback.print_exc() traceback.print_exc()
print(f"\n--- Finalizado el procesamiento del PLC: {plc_name} ---") print(f"\n--- Finished processing PLC: {plc_name} ---")
# --- Main Script --- # --- Main Script ---
@ -274,76 +250,75 @@ if __name__ == "__main__":
configs = load_configuration() configs = load_configuration()
working_directory = configs.get("working_directory") working_directory = configs.get("working_directory")
print("--- Exportador de datos TIA Portal (Bloques, UDTs, Variables) ---") print("--- TIA Portal Data Exporter (Blocks, UDTs, Tags) ---")
# 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: Directorio de trabajo no configurado o inválido.") print("ERROR: Working directory not set or invalid in configuration.")
print("Por favor configure el directorio de trabajo usando la aplicación principal.") print("Please configure the working directory using the main application.")
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
# 2. Detect TIA Portal version from project file print(f"\nSelected Project: {project_file}")
tia_version = detect_tia_version(project_file) print(f"Using Export Directory (Working Directory): {export_dir}")
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:
# 3. Connect to TIA Portal with detected version # 2. Connect to TIA Portal
print(f"\nConectando a TIA Portal V{tia_version}...") print(f"\nConnecting to TIA Portal V{TIA_PORTAL_VERSION}...")
portal_instance = ts.open_portal( portal_instance = ts.open_portal(
version=tia_version, version=TIA_PORTAL_VERSION,
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface, portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
) )
print("Conectado a TIA Portal.") print("Connected to TIA Portal.")
print(f"ID del proceso del Portal: {portal_instance.get_process_id()}") print(f"Portal Process ID: {portal_instance.get_process_id()}") #
# 4. Open Project # 3. Open Project
print(f"Abriendo proyecto: {os.path.basename(project_file)}...") print(f"Opening project: {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("El proyecto podría estar ya abierto, intentando obtener el manejador...") print("Project might already be open, attempting to get handle...")
project_object = portal_instance.get_project() project_object = portal_instance.get_project() #
if project_object is None: if project_object is None:
raise Exception("No se pudo abrir u obtener el proyecto especificado.") raise Exception("Failed to open or get the specified project.")
print("Proyecto abierto exitosamente.") print("Project opened successfully.")
# 5. Get PLCs # 4. Get PLCs
plcs = project_object.get_plcs() plcs = project_object.get_plcs() #
if not plcs: if not plcs:
print("No se encontraron dispositivos PLC en el proyecto.") print("No PLC devices found in the project.")
else: else:
print(f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación...") print(f"Found {len(plcs)} PLC(s). Starting export process...")
# 6. Iterate and Export Data for each PLC # 5. Iterate and Export Data for each PLC
for plc_device in plcs: for plc_device in plcs:
export_plc_data(plc=plc_device, export_base_dir=export_dir) export_plc_data(
plc=plc_device, export_base_dir=export_dir
) # Pass export_dir
print("\nProceso de exportación completado.") print("\nExport process completed.")
except ts.TiaException as tia_ex: except ts.TiaException as tia_ex:
print(f"\nError de TIA Portal Openness: {tia_ex}") print(f"\nTIA Portal Openness Error: {tia_ex}")
traceback.print_exc() traceback.print_exc()
except FileNotFoundError: except FileNotFoundError:
print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}") print(f"\nERROR: Project file not found at {project_file}")
except Exception as e: except Exception as e:
print(f"\nOcurrió un error inesperado: {e}") print(f"\nAn unexpected error occurred: {e}")
traceback.print_exc() traceback.print_exc()
finally: finally:
# 7. Cleanup # 6. Cleanup
if portal_instance: if portal_instance:
try: try:
print("\nCerrando TIA Portal...") print("\nClosing TIA Portal...")
portal_instance.close_portal() portal_instance.close_portal() #
print("TIA Portal cerrado.") print("TIA Portal closed.")
except Exception as close_ex: except Exception as close_ex:
print(f"Error durante la limpieza de TIA Portal: {close_ex}") print(f"Error during TIA Portal cleanup: {close_ex}")
print("\nScript finalizado.") print("\nScript finished.")

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

View File

@ -1,81 +0,0 @@
{
"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
}

View File

@ -1,81 +0,0 @@
{
"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
}

View File

@ -1,455 +0,0 @@
// 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

@ -1,93 +0,0 @@
--- 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,19 +61,15 @@ 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 ---
# Paso 1: localizar el nodo <NetworkSource> (sin importar namespace) # Buscar NetworkSource y luego FlgNet (ambos usan namespace flg)
network_source_node = network_element.xpath(".//*[local-name()='NetworkSource']") network_source_node = network_element.xpath(".//flg:NetworkSource", namespaces=ns)
flgnet = None flgnet = None
if network_source_node: if network_source_node:
# Buscar FlgNet dentro del NetworkSource flgnet_list = network_source_node[0].xpath("./flg:FlgNet", namespaces=ns)
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
# Paso 2: si no se encontró, intentar buscar FlgNet directamente en el CompileUnit flgnet_list = network_element.xpath(".//flg:FlgNet", namespaces=ns)
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,43 +215,32 @@ 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
# --- Obtener título y comentario para coherencia con otros parsers --- # Buscar NetworkSource y luego StructuredText
title_elem = network_element.xpath( network_source_node = network_element.xpath(".//flg:NetworkSource", namespaces=ns)
"./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:
st_nodes = network_source_node[0].xpath(".//*[local-name()='StructuredText']") structured_text_node_list = network_source_node[0].xpath("./st:StructuredText", namespaces=ns)
if st_nodes: if structured_text_node_list:
structured_text_node = st_nodes[0] structured_text_node = structured_text_node_list[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": [ "logic": [ # SCL se guarda como un único bloque lógico
{ {
"instruction_uid": f"SCL_{network_id}", "instruction_uid": f"SCL_{network_id}", # UID sintético
"type": "RAW_SCL_CHUNK", "type": "RAW_SCL_CHUNK", # Tipo especial para SCL crudo
"scl": reconstructed_scl, "scl": reconstructed_scl, # El código SCL reconstruido
} }
], ],
# 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,74 +478,3 @@ 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\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports" "working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source"
} }

View File

@ -1,6 +1,6 @@
{ {
"x0_main.py": { "x0_main.py": {
"display_name": "1: Procesar Exportación XML de un proyecto a SCL", "display_name": "1: Procesar Exportación XML",
"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,11 +34,5 @@
"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,7 +1,6 @@
{ {
"path": "D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports", "path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"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,225 +158,76 @@ 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")
# Conversión de enteros con control de errores # Ensure max_call_depth is an integer
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 ---
# Se trabaja exclusivamente dentro del PLC indicado. # <-- MODIFICADO: Apuntar al subdirectorio 'PLC' dentro del working_directory -->
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: '{xml_project_dir}'", log_f f"Buscando archivos XML recursivamente en el subdirectorio: '{xml_project_dir}'", log_f
) )
# Patrón de búsqueda global para todos los PLC # Verificar si el directorio PLC existe
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:
@ -572,7 +423,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)) # Copia directa al scl_output del PLC dest_scl_path = os.path.join(scl_output_dir, os.path.basename(src_scl_path)) # Copy directly into scl_output_dir
# 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):
@ -662,8 +513,7 @@ if __name__ == "__main__":
run_x5 = False run_x5 = False
if run_x5: if run_x5:
# El archivo agregado se guarda dentro del PLC para mantener salidas separadas output_agg_file = os.path.join(working_directory, cfg_aggregated_filename) # Usar valor de config
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
@ -740,7 +590,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_dynamic}' para detalles." f"\n{final_console_message} Consulta '{LOG_FILENAME}' 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, adapt_namespaces from parsers.parser_utils import ns, get_multilingual_text, parse_interface_members
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,11 +253,6 @@ 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,18 +366,6 @@ 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
@ -658,8 +646,7 @@ 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í
# 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"))
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

@ -1,39 +0,0 @@
"""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,18 +1,5 @@
{ {
"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,129 +1,5 @@
[11:14:30] Iniciando ejecución de x4.py en D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports... [20:03:36] Iniciando ejecución de x1.py en D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Source...
[11:14:30] --- Exportador de Referencias Cruzadas de TIA Portal --- [20:03:37] --- TIA Portal Data Exporter (Blocks, UDTs, Tags) ---
[11:14:34] Versión de TIA Portal detectada: 19.0 (de la extensión .ap19) [20:03:47] No project file selected. Exiting.
[11:14:34] Proyecto seleccionado: D:/Trabajo/VM/22 - 93841 - Sidel - Tilting/InLavoro/PLC/93841_PLC_28/93841_PLC_28.ap19 [20:03:47] Ejecución de x1.py finalizada (success). Duración: 0:00:10.497365.
[11:14:34] Usando directorio base de exportación: D:\Trabajo\VM\22 - 93841 - Sidel - Tilting\Reporte\TiaExports [20:03:47] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\log_x1.txt
[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()">
Buscar Explorar
</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>
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()"> </div>
Salvar <button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()">
</button> Confirmar
</div> </button>
</div> </div>
<!-- Add directory history dropdown --> <!-- Add directory history dropdown -->
<div class="mt-2"> <div class="mt-2">