Actualización de scripts de adaptación IO y mejora en la gestión de directorios de trabajo
- Se modificaron los logs de ejecución para reflejar nuevas fechas y duraciones en los scripts x1 y x2. - Se implementó un sistema de fallback para el directorio de trabajo, creando un directorio `.debug` en caso de que no se pueda cargar la configuración. - Se añadieron nuevas funcionalidades en el script x2_process_CAx.py para generar informes en Excel con detalles de IOs por nodos del PLC. - Se mejoró la documentación en el archivo readme.md, incluyendo detalles sobre el uso del directorio de debug y su estructura. - Se actualizaron las descripciones de los scripts en scripts_description.json para reflejar cambios recientes y nuevas funcionalidades.
This commit is contained in:
parent
88806ee4e4
commit
9ac769e2fc
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
# IO Summary Table for PLC: A40510
|
||||
|
||||
| Network | Type | Address | Device Name | Sub-Device | OrderNo | Type | IO Type | IO Address | Number of Bits |
|
||||
|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.31 | U30110-AxisX | DO SERVO_1 | N/A | DO SERVO | Input | `EW 100..119` | 160 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.31 | U30110-AxisX | DO SERVO_1 | N/A | DO SERVO | Output | `AW 100..119` | 160 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.32 | U30210-AxisY | DO SERVO_1 | N/A | DO SERVO | Input | `EW 120..139` | 160 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.32 | U30210-AxisY | DO SERVO_1 | N/A | DO SERVO | Output | `AW 120..139` | 160 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.33 | U30310 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Input | `EW 4..51` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.33 | U30310 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Output | `AW 3..50` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.34 | U30410 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Input | `EW 52..99` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.34 | U30410 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Output | `AW 51..98` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.35 | U30510 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Input | `EW 156..203` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.35 | U30510 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Output | `AW 144..191` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.36 | U30610 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Input | `EW 204..251` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.36 | U30610 | DO with manufacturer telegr. 102_1 | N/A | DO with manufacturer telegr. 102 | Output | `AW 192..239` | 384 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.37 | M30710 | DS402_Extend-A | N/A | N/A | Input | `EW 4066..4087` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.37 | M30710 | DS402_Extend-A | N/A | N/A | Output | `AW 4132..4175` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.40 | M31010 | Module I/O (08 words) | N/A | Module I/O (08 words) | Input | `EW 272..287` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.40 | M31010 | Module I/O (08 words) | N/A | Module I/O (08 words) | Output | `AW 272..287` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.41 | M31110 | DS402_Extend-A | N/A | N/A | Input | `EW 4000..4021` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.41 | M31110 | DS402_Extend-A | N/A | N/A | Output | `AW 4000..4043` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.42 | M31210 | DS402_Extend-A | N/A | N/A | Input | `EW 4022..4043` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.42 | M31210 | DS402_Extend-A | N/A | N/A | Output | `AW 4044..4087` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.43 | M31310 | DS402_Extend-A | N/A | N/A | Input | `EW 4044..4065` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.43 | M31310 | DS402_Extend-A | N/A | N/A | Output | `AW 4088..4131` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.44 | M31410 | Module I/O (08 words) | N/A | Module I/O (08 words) | Input | `EW 288..303` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.44 | M31410 | Module I/O (08 words) | N/A | Module I/O (08 words) | Output | `AW 288..303` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.45 | M31510 | Module I/O (08 words) | N/A | Module I/O (08 words) | Input | `EW 304..319` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.45 | M31510 | Module I/O (08 words) | N/A | Module I/O (08 words) | Output | `AW 304..319` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.46 | M31610 | DS402_Extend-A | N/A | N/A | Input | `EW 4088..4109` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.46 | M31610 | DS402_Extend-A | N/A | N/A | Output | `AW 4176..4219` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.47 | M31710 | DS402_Extend-A | N/A | N/A | Input | `EW 4110..4131` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.47 | M31710 | DS402_Extend-A | N/A | N/A | Output | `AW 4220..4263` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.48 | M31810 | DS402_Extend-A | N/A | N/A | Input | `EW 4132..4153` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.48 | M31810 | DS402_Extend-A | N/A | N/A | Output | `AW 4264..4307` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.49 | M31910 | DS402_Extend-A | N/A | N/A | Input | `EW 4154..4175` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.49 | M31910 | DS402_Extend-A | N/A | N/A | Output | `AW 4308..4351` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.70 | M34010 | DS402_Extend-A | N/A | N/A | Input | `EW 4176..4197` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.70 | M34010 | DS402_Extend-A | N/A | N/A | Output | `AW 4352..4395` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.71 | M34110 | DS402_Extend-A | N/A | N/A | Input | `EW 4198..4219` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.71 | M34110 | DS402_Extend-A | N/A | N/A | Output | `AW 4396..4439` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.72 | M34210 | DS402_Extend-A | N/A | N/A | Input | `EW 4220..4241` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.72 | M34210 | DS402_Extend-A | N/A | N/A | Output | `AW 4440..4483` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.73 | M34310 | DS402_Extend-A | N/A | N/A | Input | `EW 4242..4263` | 176 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.73 | M34310 | DS402_Extend-A | N/A | N/A | Output | `AW 4484..4527` | 352 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.74 | M34410 | Module I/O (08 words) | N/A | Module I/O (08 words) | Input | `EW 362..377` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.74 | M34410 | Module I/O (08 words) | N/A | Module I/O (08 words) | Output | `AW 340..355` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.170 | E44010-Encoder | EO Encoder Multiturn V2.x_1 | N/A | N/A | Input | `EW 140..155` | 128 |
|
||||
| PN/IE_1 | Ethernet/Profinet | 10.1.30.170 | E44010-Encoder | EO Encoder Multiturn V2.x_1 | N/A | N/A | Output | `AW 140..143` | 32 |
|
|
@ -1,38 +1,14 @@
|
|||
--- Log de Ejecución: x1_export_CAx.py ---
|
||||
Grupo: IO_adaptation
|
||||
Directorio de Trabajo: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Inicio: 2025-06-06 16:30:18
|
||||
Fin: 2025-06-06 16:33:55
|
||||
Duración: 0:03:37.052535
|
||||
Inicio: 2025-06-06 18:25:09
|
||||
Fin: 2025-06-06 18:25:20
|
||||
Duración: 0:00:11.673530
|
||||
Estado: SUCCESS (Código de Salida: 0)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
--- TIA Portal Project CAx Exporter and Analyzer ---
|
||||
|
||||
Selected Project: D:/Trabajo/VM/44 - 98050 - Fiera/InLavoro/PLC/98050_PLC_01/98050_PLC_01.ap19
|
||||
Using Output Directory (Working Directory): D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Detected TIA Portal version: 19.0 (from extension .ap19)
|
||||
Will export CAx data to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
Will generate summary to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Summary.md
|
||||
Export log file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.log
|
||||
|
||||
Connecting to TIA Portal V19.0...
|
||||
2025-06-06 16:30:25,861 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||
2025-06-06 16:30:25,895 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
|
||||
Connected.
|
||||
Opening project: 98050_PLC_01.ap19...
|
||||
2025-06-06 16:32:34,404 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_01\98050_PLC_01.ap19
|
||||
Project opened.
|
||||
Exporting CAx data for the project to D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml...
|
||||
CAx data exported successfully.
|
||||
|
||||
Closing TIA Portal...
|
||||
2025-06-06 16:33:52,008 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
|
||||
TIA Portal closed.
|
||||
Parsing AML file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
Markdown summary written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Summary.md
|
||||
|
||||
Script finished.
|
||||
No project file selected. Exiting.
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
Ninguno
|
||||
|
|
|
@ -1,67 +1,76 @@
|
|||
--- Log de Ejecución: x2_process_CAx.py ---
|
||||
Grupo: IO_adaptation
|
||||
Directorio de Trabajo: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Inicio: 2025-06-06 16:34:01
|
||||
Fin: 2025-06-06 16:34:09
|
||||
Duración: 0:00:08.316649
|
||||
Inicio: 2025-06-07 14:29:10
|
||||
Fin: 2025-06-07 14:29:16
|
||||
Duración: 0:00:06.140835
|
||||
Estado: SUCCESS (Código de Salida: 0)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
--- AML (CAx Export) to Hierarchical JSON and Obsidian MD Converter (v31.1 - Corrected IO Summary Table Initialization) ---
|
||||
--- AML (CAx Export) to Hierarchical JSON and Obsidian MD Converter (v32.2 - Simplified IO Address Format (Separate Start/End)) ---
|
||||
Using configured working directory: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Using Working Directory for Output: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Input AML: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
Input AML: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01.aml
|
||||
Output Directory: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
Output JSON: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.hierarchical.json
|
||||
Output IO Debug Tree MD: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export_IO_Upward_Debug.md
|
||||
Processing AML file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
Output JSON: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01.hierarchical.json
|
||||
Output IO Debug Tree MD: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_IO_Upward_Debug.md
|
||||
Processing AML file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01.aml
|
||||
Pass 1: Found 363 InternalElement(s). Populating device dictionary...
|
||||
Pass 2: Identifying PLCs and Networks (Refined v2)...
|
||||
Identified Network: PN/IE_1 (fcbafb48-c53d-4af5-8143-794df99be4d6) Type: Unknown
|
||||
Identified Network: PN/IE_1 (6ce86626-0043-4a58-b675-cc13ac87121c) Type: Ethernet/Profinet
|
||||
Identified PLC: A40510 (fc0d3bac-267e-488a-8dcf-7dc8599d80e8) - Type: CPU 1514SP T-2 PN OrderNo: 6ES7 514-2VN03-0AB0
|
||||
Pass 3: Processing InternalLinks (Robust Network Mapping & IO)...
|
||||
Found 103 InternalLink(s).
|
||||
Mapping Device/Node 'E1' (NodeID:ffef8b7b-b6bf-46aa-b3b5-f5083c420563, Addr:10.1.30.11) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:221c248d-83d8-464c-9425-c949086b34e7, Addr:10.1.30.58) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:5c3bf795-ecec-47e7-a962-4d54c95b9887, Addr:10.1.30.59) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:4100c829-d889-489a-971c-a69207333e2f, Addr:10.1.30.60) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:a6aed467-1a4c-4a5a-bf3d-e7ce46e94a9f, Addr:10.1.30.61) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b3d71b04-f99a-4af8-aa69-2ca02a30d391, Addr:10.1.30.62) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:eb59daf8-f59c-435e-bfe4-8e7afd0937bd, Addr:10.1.30.63) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:3e931f82-08a5-44de-89a5-b907f5aaeb24, Addr:10.1.30.64) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:f673d611-7a73-4a8e-912d-ea16c294d51b, Addr:10.1.30.65) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:09a2569a-4948-4830-9dd8-315ae638e391, Addr:10.1.30.66) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:719a9643-c903-454e-8325-0f03cf871c35, Addr:10.1.30.31) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:ef8c3829-2363-43b3-ae20-7e903c2517a5, Addr:10.1.30.32) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:7cece978-67aa-4707-b23c-375ebddfc2bc, Addr:10.1.30.170) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:ad86d223-8f60-41c0-8208-c88d68e42154, Addr:10.1.30.33) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:fa86bb0c-6142-41ce-ba6c-565e1e1f810e, Addr:10.1.30.34) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:441a0b56-5830-4595-ab8d-b073b6db8545, Addr:10.1.30.35) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:8dbc0c6b-a8f5-491c-832b-4af0757c259d, Addr:10.1.30.36) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:d06cdd21-62c3-4cff-9389-12a4e21869a2, Addr:10.1.30.40) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:f63db18d-dc19-48b8-afaa-be557db4d13e, Addr:10.1.30.44) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:21589a71-d4dd-4534-b457-deb72f3f7660, Addr:10.1.30.41) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:230ddec2-fa03-44d9-8350-0c0eeb463400, Addr:10.1.30.42) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b2647d59-ed85-44c5-813f-167c41ea1ca1, Addr:10.1.30.43) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:9933a2a3-47db-448a-a724-ed583d5994bb, Addr:10.1.30.37) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:9b45edaa-0640-4e86-ba59-1b991c1a85ca, Addr:10.1.30.45) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:a5fbeadb-a8ab-42e0-b58f-296e44633ce5, Addr:10.1.30.46) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:67d1d9ad-cf1d-43aa-9dc4-9e574cda5e5b, Addr:10.1.30.47) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:08689cb8-1b00-403f-bc20-c5911a20e655, Addr:10.1.30.48) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:a06a0e94-b651-4510-bbe0-8a0c3b717283, Addr:10.1.30.49) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:cf339d67-1758-4462-85f0-9e6a002d1c3c, Addr:10.1.30.70) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:d80a9fcf-5e0d-45a9-b617-362a9cb4121a, Addr:10.1.30.71) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:7c28d11a-9673-4957-931d-8840a589da85, Addr:10.1.30.72) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:ad792043-fdb8-4695-a10a-aa2cad996cf0, Addr:10.1.30.74) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b30940b8-6cb5-4143-854f-9b569f081703, Addr:10.1.30.73) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'E1' (NodeID:ab796923-4471-4a60-98f4-f8ea5920b3b9, Addr:10.1.30.11) to Network 'PN/IE_1'
|
||||
--> Found PLC in children: A40510 (ID: fc0d3bac-267e-488a-8dcf-7dc8599d80e8)
|
||||
--> Associating Network 'PN/IE_1' with PLC 'A40510' (via Node 'E1' Addr: 10.1.30.11)
|
||||
Mapping Device/Node 'IE1' (NodeID:c53ae31d-bee4-47ba-950d-15c31d2599b9, Addr:10.1.30.58) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:1a8366c9-7d4c-4e49-b0a3-77d445eabc8b, Addr:10.1.30.59) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:deeda41b-54d0-4c86-82c3-a311f753021e, Addr:10.1.30.60) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:8a916a9d-895e-4a12-9190-516ef6a8f191, Addr:10.1.30.61) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:c51471e0-9621-4ef7-b050-09bf4d695ea1, Addr:10.1.30.62) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:77ca312c-3dd0-46f6-bd64-5da69a99cf6f, Addr:10.1.30.63) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:011019c6-5925-4544-87aa-27288c3aa70c, Addr:10.1.30.64) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:5ea1e894-b51c-44f9-8aff-80c58c6cb7ef, Addr:10.1.30.65) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:612916a9-7a26-4712-9de2-d3c7894db862, Addr:10.1.30.66) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:7be7cf9f-f7af-418c-8fe4-96b4a97a581d, Addr:10.1.30.31) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:135492a8-02ab-4236-92ce-7a5585538297, Addr:10.1.30.32) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b7a06147-6428-4d95-ba0d-834fad49a1ae, Addr:10.1.30.170) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:7ce83bdf-dc4d-40f0-85c8-a246dd2c44be, Addr:10.1.30.33) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:e9a07506-2869-4c34-8541-ee021d1623f0, Addr:10.1.30.34) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:3dd0f886-c3d0-4628-804b-ae18cf5931e8, Addr:10.1.30.35) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:67c3fa72-a956-4363-9a4d-e3300d7d1429, Addr:10.1.30.36) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b35e73e8-a9ad-47e2-8ad5-00442c7a2df7, Addr:10.1.30.40) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:867d0580-06cd-4722-a66f-8c5559b624f5, Addr:10.1.30.44) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:6f1b4062-80ac-4d1a-b351-546c9d0157e2, Addr:10.1.30.41) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:c0016a06-48cc-47af-83aa-d4a0a6cb44f6, Addr:10.1.30.42) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:07fa5e8f-ffcf-4d81-9897-5ff9f73d6125, Addr:10.1.30.43) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:5e8a4449-c958-4397-8b71-877af262333b, Addr:10.1.30.37) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:79096325-dcb8-4650-bc44-2c9735a93f52, Addr:10.1.30.45) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:957e5975-eafe-477c-a682-bebf330a2868, Addr:10.1.30.46) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:379ccd79-27b4-4b53-a552-3da783bc5b25, Addr:10.1.30.47) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:b53dd719-e8af-431f-b837-a0903ffb3a76, Addr:10.1.30.48) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:0f28d88a-5208-4fc4-8ee1-f1bb33a947e8, Addr:10.1.30.49) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:d1f8ea18-50d2-410e-9966-136d8a79471d, Addr:10.1.30.70) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:1c86a627-5646-45e7-9c21-5d18d6544568, Addr:10.1.30.71) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:e80a9939-59d7-44e0-9a46-1fade44e1b78, Addr:10.1.30.72) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:8c51fa26-883a-468c-8c36-c0e1b31852e4, Addr:10.1.30.74) to Network 'PN/IE_1'
|
||||
Mapping Device/Node 'IE1' (NodeID:17be2ccc-dede-4187-ba77-1ad8499a7349, Addr:10.1.30.73) to Network 'PN/IE_1'
|
||||
Data extraction and structuring complete.
|
||||
Generating JSON output: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.hierarchical.json
|
||||
Generating JSON output: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01.hierarchical.json
|
||||
JSON data written successfully.
|
||||
|
||||
IO upward debug tree written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export_IO_Upward_Debug.md
|
||||
IO upward debug tree written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_IO_Upward_Debug.md
|
||||
|
||||
Found 1 PLC(s). Generating individual hardware trees...
|
||||
Generating Hardware Tree for PLC 'A40510' (ID: fc0d3bac-267e-488a-8dcf-7dc8599d80e8) at: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_CAx_Export_Hardware_Tree.md
|
||||
Markdown tree summary written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_CAx_Export_Hardware_Tree.md
|
||||
Generating Hardware Tree for PLC 'A40510' (ID: fc0d3bac-267e-488a-8dcf-7dc8599d80e8) at: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_Hardware_Tree.md
|
||||
Markdown tree summary written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_Hardware_Tree.md
|
||||
IO summary table written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Hardware.md
|
||||
IO summary table generated in separate file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Hardware.md
|
||||
Generating Excel IO Report for PLC 'A40510' (ID: fc0d3bac-267e-488a-8dcf-7dc8599d80e8) at: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_IO_Report.xlsx
|
||||
Generating Excel IO report for PLC: A40510
|
||||
Excel IO report saved to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_IO_Report.xlsx
|
||||
Total rows in report: 33
|
||||
|
||||
Script finished.
|
||||
|
||||
|
|
|
@ -24,8 +24,29 @@ if __name__ == "__main__":
|
|||
working_dir = configs.get("working_directory", "")
|
||||
""""
|
||||
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
try:
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load configuration (frontend not running): {e}")
|
||||
configs = {}
|
||||
working_directory = None
|
||||
|
||||
# Validate working directory with .debug fallback for standalone execution
|
||||
if not working_directory or not os.path.isdir(working_directory):
|
||||
print("Working directory not set or invalid in configuration.")
|
||||
print("Using .debug directory as fallback for direct script execution.")
|
||||
|
||||
# Fallback to .debug directory under script location
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
debug_dir = os.path.join(script_dir, ".debug")
|
||||
|
||||
# Create .debug directory if it doesn't exist
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
working_directory = debug_dir
|
||||
print(f"Using debug directory: {working_directory}")
|
||||
else:
|
||||
print(f"Using configured working directory: {working_directory}")
|
||||
|
||||
# Acceder a la configuración específica del grupo
|
||||
group_config = configs.get("level2", {})
|
||||
|
@ -38,6 +59,34 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
|
||||
### Debug Directory Fallback
|
||||
|
||||
Cuando los scripts se ejecutan directamente (no desde el frontend), utilizan un sistema de fallback para determinar el directorio de trabajo:
|
||||
|
||||
1. **Con Frontend**: Los scripts utilizan el `working_directory` configurado dinámicamente a través de `load_configuration()`
|
||||
2. **Ejecución Directa (Debug)**: Si no hay configuración válida, los scripts crean automáticamente un directorio `.debug` en la ubicación del script
|
||||
|
||||
**Estructura del directorio de debug:**
|
||||
```
|
||||
IO_adaptation/
|
||||
├── .debug/ # ← Directorio creado automáticamente para debug
|
||||
│ ├── *.aml # Archivos AML procesados
|
||||
│ ├── *.hierarchical.json # Datos estructurados extraídos
|
||||
│ ├── *_IO_Upward_Debug.md # Archivo de debug de conexiones IO
|
||||
│ ├── Hardware.md # Tabla resumen de IO
|
||||
│ └── <PLC_Name>/
|
||||
│ └── Documentation/
|
||||
│ └── *_Hardware_Tree.md # Árbol de hardware por PLC
|
||||
├── x2_process_CAx.py # Script principal
|
||||
└── example/ # Archivos de ejemplo
|
||||
```
|
||||
|
||||
**Ventajas del directorio .debug:**
|
||||
- ✅ Separación clara entre archivos de debug y archivos de producción
|
||||
- ✅ Fácil limpieza (se puede eliminar todo el directorio `.debug`)
|
||||
- ✅ No interfiere con archivos del proyecto principal
|
||||
- ✅ Creación automática sin configuración manual
|
||||
|
||||
### Directory structure for Tia Portal scripts
|
||||
|
||||
<working_directory>/
|
||||
|
|
|
@ -29,10 +29,46 @@
|
|||
"long_description": "# Script de Adaptación IO para PLCTags\n\n## Descripción\n\nEste script automatiza la adaptación de tags PLC entre hardware físico Siemens TIA Portal y software master, actualizando las direcciones lógicas según un archivo de mapeo en formato Markdown.\n\n## Funcionamiento\n\n### Entradas\n- **Archivo Excel**: Exportación de tags desde TIA Portal\n- **Archivo Markdown**: Tabla de adaptación IO con mapeo entre hardware y master tags\n\n### Proceso\n1. **Análisis inicial**:\n - Lee la tabla Markdown de adaptación IO\n - Identifica las columnas de IO y Master Tag\n - Crea un diccionario de mapeo entre tags y direcciones IO\n\n2. **Procesamiento de tags**:\n - Filtra tags en los paths \"Inputs\", \"Outputs\", \"IO Not in Hardware\\InputsMaster\" y \"IO Not in Hardware\\OutputsMaster\"\n - Actualiza las direcciones lógicas de los tags encontrados en el mapeo\n - Reasigna paths según el tipo de señal (%E -> \"Inputs\", %A -> \"Outputs\")\n\n3. **Gestión de tags sin hardware**:\n - Asigna direcciones de memoria (%Mxxxx.x) para tags sin correspondencia\n - Tags de entrada: Asignados a \"IO Not in Hardware\\InputsMaster\" desde %M3600.0\n - Tags de salida: Asignados a \"IO Not in Hardware\\OutputsMaster\" desde %M3800.0\n\n4. **Manejo de direcciones**:\n - Convierte formatos (I0.0 → %E0.0, PEW100 → %EW100, etc.)\n - Gestiona bits incrementalmente (0.0, 0.1, ..., 0.7, luego 1.0)\n - Alinea words cada 2 bytes (%MW3600, %MW3602, etc.)\n\n### Salidas\n- **Archivo Excel actualizado**: Con las nuevas direcciones lógicas y paths\n- **Archivo de log**: Registro detallado del proceso con estadísticas\n\n## Uso\n1. Ejecute el script\n2. Seleccione el archivo Excel exportado de TIA Portal\n3. Seleccione el archivo Markdown con la tabla de adaptación\n4. El script generará automáticamente el archivo Excel actualizado\n\n## Detección de tipos\n- **Entradas**: Identificadas por prefijos (DI_, P_AI_, etc.)\n- **Salidas**: Identificadas por prefijos (DO_, P_AO_, etc.)\n- **Bits vs Words**: Determinados por el tipo de dato (Bool vs Word/Int)",
|
||||
"hidden": false
|
||||
},
|
||||
"x7_update_CAx.py": {
|
||||
"display_name": "7: Actualizar AML desde Excel modificado",
|
||||
"short_description": "Actualiza un archivo AML original basándose en las modificaciones hechas en el archivo Excel de IOs.",
|
||||
"long_description": "Este script permite hacer un 'round trip' de datos: extrae información del AML al Excel, permite ediciones manuales en el Excel, y luego aplica esos cambios de vuelta al AML original.\n***\n**Pipeline:**\n\n1. **Entrada:** Solicita el archivo AML original y el archivo Excel modificado (generado por x2_process_CAx.py)\n2. **Validación:** Genera un Excel de referencia desde el AML y compara con el Excel modificado para validar que la estructura base sea la misma (mismos nodos y nombres)\n3. **Detección de cambios:** Identifica modificaciones en:\n - Direcciones IP/nodos\n - Direcciones IO (inputs/outputs)\n - Tipos de dispositivos\n - Números de orden\n - Versiones de firmware\n4. **Aplicación:** Localiza los elementos correspondientes en el XML del AML y aplica los cambios detectados\n5. **Salida:** Genera un nuevo archivo AML con sufijo '_updated' que contiene todas las modificaciones\n\n**ID único:** Utiliza PLC+Device Name como identificador único para mapear cambios entre Excel y AML.\n\n**Restricciones:** Solo procede si el Excel modificado mantiene la misma cantidad de nodos y nombres que el Excel original.",
|
||||
"hidden": false
|
||||
},
|
||||
"x0_documentation.py": {
|
||||
"display_name": "0:Documentación",
|
||||
"short_description": "Descripción del flujo de trabajo",
|
||||
"long_description": "### Flujo de trabajo:\n***\n1. Se usa [x1] para exportar los datos de configuración desde el proyecto de Tia Portal, incluidos los IO configurados en el hardware. Esto genera un archivo AML.\n2. Con [x2] se procesa el archivo AML y se convierte en markdown. Si hay un solo PLC se genera el archivo [Hardware.md]\n3. Se deben procesar los IO **desde el esquema eléctrico y agregarlos** a [Hardware.md]\n4. Se debe exportar todos los tags como un archivo excel desde el Tia Portal.\n5. Con [x3] se convierte y se filtran los tags según los path definidos en [io_paths_config] y se genera un archivo Master IO Tags.md\n6. [x4] Genera el prompt a usar con Claude usando MCP para que pueda acceder a los archivos originales y evitar tener que hacer uploads.\n7. Una vez que se genera el archivo procesado por el LLM se puede usar [x5] que convierte las adaptaciones a un archivo que luego se puede importar en Tia Portal\n8. Importar en Tia Portal el archivo [PLCTags_Updated.xlsx]",
|
||||
"long_description": "### Flujo de trabajo:\n***\n1. Se usa [x1] para exportar los datos de configuración desde el proyecto de Tia Portal, incluidos los IO configurados en el hardware. Esto genera un archivo AML.\n2. Con [x2] se procesa el archivo AML y se convierte en markdown. Si hay un solo PLC se genera el archivo [Hardware.md]. **Además genera un archivo Excel con los IOs por nodos del PLC.**\n3. Se deben procesar los IO **desde el esquema eléctrico y agregarlos** a [Hardware.md]\n4. Se debe exportar todos los tags como un archivo excel desde el Tia Portal.\n5. Con [x3] se convierte y se filtran los tags según los path definidos en [io_paths_config] y se genera un archivo Master IO Tags.md\n6. [x4] Genera el prompt a usar con Claude usando MCP para que pueda acceder a los archivos originales y evitar tener que hacer uploads.\n7. Una vez que se genera el archivo procesado por el LLM se puede usar [x5] que convierte las adaptaciones a un archivo que luego se puede importar en Tia Portal\n8. Importar en Tia Portal el archivo [PLCTags_Updated.xlsx]\n\n**Nuevo flujo alternativo con Excel:**\nDesp¹és del paso 2, se puede usar [x7] para:\n- Modificar manualmente el archivo Excel de IOs generado\n- Aplicar los cambios de vuelta al archivo AML original\n- Generar un AML actualizado con las modificaciones",
|
||||
"hidden": false
|
||||
},
|
||||
"analyze_u32810_specific.py": {
|
||||
"display_name": "analyze_u32810_specific",
|
||||
"short_description": "Sin descripción corta.",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
},
|
||||
"compare_u32810_telegrams.py": {
|
||||
"display_name": "compare_u32810_telegrams",
|
||||
"short_description": "Sin descripción corta.",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
},
|
||||
"comprehensive_address_search.py": {
|
||||
"display_name": "comprehensive_address_search",
|
||||
"short_description": "Sin descripción corta.",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
},
|
||||
"find_address_differences.py": {
|
||||
"display_name": "find_address_differences",
|
||||
"short_description": "Sin descripción corta.",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
},
|
||||
"find_u32810_addresses.py": {
|
||||
"display_name": "find_u32810_addresses",
|
||||
"short_description": "Sin descripción corta.",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia",
|
||||
"history": [
|
||||
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia",
|
||||
"D:\\Proyectos\\Scripts\\ParamManagerScripts\\backend\\script_groups\\IO_adaptation\\example",
|
||||
"C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\TAGsIO\\v2"
|
||||
]
|
||||
}
|
|
@ -12,6 +12,7 @@ import json
|
|||
from pathlib import Path
|
||||
import re
|
||||
import math # Needed for ceil
|
||||
import pandas as pd # For Excel generation
|
||||
|
||||
script_root = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
@ -221,6 +222,7 @@ def extract_aml_data(root):
|
|||
is_subnet_by_role = any("SUBNET" in rc.upper() for rc in role_classes)
|
||||
if is_subnet_by_role:
|
||||
is_network = True
|
||||
# Check role classes first
|
||||
for rc in role_classes:
|
||||
rc_upper = rc.upper()
|
||||
if "PROFINET" in rc_upper or "ETHERNET" in rc_upper:
|
||||
|
@ -229,12 +231,23 @@ def extract_aml_data(root):
|
|||
elif "PROFIBUS" in rc_upper:
|
||||
net_type = "Profibus"
|
||||
break
|
||||
|
||||
# If still unknown, check the Type attribute (crucial for PROFINET)
|
||||
if net_type == "Unknown":
|
||||
type_attr = device.get("attributes", {}).get("Type", "")
|
||||
if type_attr.upper() == "ETHERNET":
|
||||
net_type = "Ethernet/Profinet"
|
||||
elif type_attr.upper() == "PROFIBUS":
|
||||
net_type = "Profibus"
|
||||
|
||||
# Finally, check device name as fallback
|
||||
if net_type == "Unknown":
|
||||
if "PROFIBUS" in device["name"].upper():
|
||||
net_type = "Profibus"
|
||||
elif (
|
||||
"ETHERNET" in device["name"].upper()
|
||||
or "PROFINET" in device["name"].upper()
|
||||
or "PN/IE" in device["name"].upper() # Add common PROFINET naming pattern
|
||||
):
|
||||
net_type = "Ethernet/Profinet"
|
||||
if is_network:
|
||||
|
@ -328,6 +341,33 @@ def extract_aml_data(root):
|
|||
and interface_info["parent_id"] in data["plcs"]
|
||||
):
|
||||
potential_plc_id = interface_info["parent_id"]
|
||||
# Enhanced PLC search: look for PLCs in the entire hierarchy
|
||||
if not potential_plc_id:
|
||||
# Search for PLCs in the hierarchy starting from linked_device_id
|
||||
current_search_id = linked_device_id
|
||||
search_depth = 0
|
||||
max_search_depth = 10
|
||||
|
||||
while current_search_id and search_depth < max_search_depth:
|
||||
# Check current device and all its children for PLCs
|
||||
device_to_check = data["devices"].get(current_search_id)
|
||||
if device_to_check:
|
||||
# Check if current device has PLCs as children
|
||||
for child_id in device_to_check.get("children_ids", []):
|
||||
if child_id in data["plcs"]:
|
||||
potential_plc_id = child_id
|
||||
print(f" --> Found PLC in children: {data['plcs'][child_id].get('name', 'Unknown PLC')} (ID: {child_id})")
|
||||
break
|
||||
|
||||
if potential_plc_id:
|
||||
break
|
||||
|
||||
# Move up to parent
|
||||
current_search_id = device_to_check.get("parent_id")
|
||||
search_depth += 1
|
||||
else:
|
||||
break
|
||||
|
||||
if potential_plc_id:
|
||||
plc_object = data["plcs"][potential_plc_id]
|
||||
if "connected_networks" not in plc_object:
|
||||
|
@ -497,6 +537,337 @@ def generate_io_summary_file(all_plc_io_for_table, md_file_path, plc_name, proje
|
|||
return hardware_file_path
|
||||
|
||||
|
||||
# --- generate_io_excel_report function ---
|
||||
def generate_io_excel_report(project_data, excel_file_path, target_plc_id, output_root_path):
|
||||
"""
|
||||
Genera un archivo Excel con información detallada de IOs por nodos del PLC.
|
||||
"""
|
||||
|
||||
plc_info = project_data.get("plcs", {}).get(target_plc_id)
|
||||
if not plc_info:
|
||||
print(f"PLC ID '{target_plc_id}' not found in project data.")
|
||||
return
|
||||
|
||||
plc_name = plc_info.get('name', target_plc_id)
|
||||
print(f"Generating Excel IO report for PLC: {plc_name}")
|
||||
|
||||
# Lista para almacenar todas las filas del Excel
|
||||
excel_rows = []
|
||||
|
||||
# Procesar las redes conectadas al PLC
|
||||
plc_networks = plc_info.get("connected_networks", {})
|
||||
|
||||
if not plc_networks:
|
||||
# Si no hay redes, crear una fila básica del PLC
|
||||
excel_rows.append({
|
||||
'PLC Name': plc_name,
|
||||
'Network Path': 'No networks connected',
|
||||
'Network Type': 'N/A',
|
||||
'Device Address': 'N/A',
|
||||
'Device Name': plc_name,
|
||||
'Device Type': plc_info.get("type_name", "N/A"),
|
||||
'Order Number': plc_info.get("order_number", "N/A"),
|
||||
'Firmware Version': plc_info.get("firmware_version", "N/A"),
|
||||
'Position': plc_info.get("position", "N/A"),
|
||||
'IO Input Start Address': 'N/A',
|
||||
'IO Input End Address': 'N/A',
|
||||
'IO Output Start Address': 'N/A',
|
||||
'IO Output End Address': 'N/A',
|
||||
'Total Input Bits': 0,
|
||||
'Total Output Bits': 0,
|
||||
'Module Name': 'N/A',
|
||||
'Module Type': 'N/A',
|
||||
'Module Order Number': 'N/A'
|
||||
})
|
||||
else:
|
||||
# Procesar cada red conectada
|
||||
for net_id, plc_addr_on_net in plc_networks.items():
|
||||
net_info = project_data.get("networks", {}).get(net_id)
|
||||
if not net_info:
|
||||
continue
|
||||
|
||||
network_name = net_info.get('name', net_id)
|
||||
network_type = net_info.get('type', 'Unknown')
|
||||
devices_on_net = net_info.get("devices_on_net", {})
|
||||
|
||||
# Identificar nodos que pertenecen al PLC para excluirlos de la lista de dispositivos
|
||||
plc_interface_and_node_ids = set()
|
||||
for node in plc_info.get("network_nodes", []):
|
||||
plc_interface_and_node_ids.add(node["id"])
|
||||
interface_id_lookup = project_data["devices"].get(node["id"], {}).get("parent_id")
|
||||
if interface_id_lookup:
|
||||
plc_interface_and_node_ids.add(interface_id_lookup)
|
||||
plc_interface_and_node_ids.add(target_plc_id)
|
||||
|
||||
# Filtrar dispositivos que no son interfaces del PLC
|
||||
other_devices = [
|
||||
(node_id, node_addr)
|
||||
for node_id, node_addr in devices_on_net.items()
|
||||
if node_id not in plc_interface_and_node_ids
|
||||
]
|
||||
|
||||
if not other_devices:
|
||||
# Si no hay otros dispositivos, crear fila solo para el PLC en esta red
|
||||
excel_rows.append({
|
||||
'PLC Name': plc_name,
|
||||
'Network Path': f"{network_name} -> {plc_name}",
|
||||
'Network Type': network_type,
|
||||
'Device Address': plc_addr_on_net,
|
||||
'Device Name': plc_name,
|
||||
'Device Type': plc_info.get("type_name", "N/A"),
|
||||
'Order Number': plc_info.get("order_number", "N/A"),
|
||||
'Firmware Version': plc_info.get("firmware_version", "N/A"),
|
||||
'Position': plc_info.get("position", "N/A"),
|
||||
'IO Input Start Address': 'N/A',
|
||||
'IO Input End Address': 'N/A',
|
||||
'IO Output Start Address': 'N/A',
|
||||
'IO Output End Address': 'N/A',
|
||||
'Total Input Bits': 0,
|
||||
'Total Output Bits': 0,
|
||||
'Module Name': 'PLC Main Unit',
|
||||
'Module Type': plc_info.get("type_name", "N/A"),
|
||||
'Module Order Number': plc_info.get("order_number", "N/A")
|
||||
})
|
||||
else:
|
||||
# Procesar cada dispositivo en la red
|
||||
for node_id, node_addr in other_devices:
|
||||
node_info = project_data.get("devices", {}).get(node_id)
|
||||
if not node_info:
|
||||
continue
|
||||
|
||||
# Determinar la estructura jerárquica del dispositivo
|
||||
interface_id = node_info.get("parent_id")
|
||||
interface_info = None
|
||||
actual_device_id = None
|
||||
actual_device_info = None
|
||||
|
||||
if interface_id:
|
||||
interface_info = project_data.get("devices", {}).get(interface_id)
|
||||
if interface_info:
|
||||
actual_device_id = interface_info.get("parent_id")
|
||||
if actual_device_id:
|
||||
actual_device_info = project_data.get("devices", {}).get(actual_device_id)
|
||||
|
||||
# Determinar qué información mostrar
|
||||
display_info = actual_device_info if actual_device_info else (interface_info if interface_info else node_info)
|
||||
display_id = actual_device_id if actual_device_info else (interface_id if interface_info else node_id)
|
||||
|
||||
device_name = display_info.get("name", display_id)
|
||||
device_type = display_info.get("type_name", "N/A")
|
||||
device_order = display_info.get("order_number", "N/A")
|
||||
device_position = display_info.get("position", "N/A")
|
||||
firmware_version = display_info.get("firmware_version", "N/A")
|
||||
|
||||
# Construir el path de red
|
||||
network_path = f"{network_name} ({network_type}) -> {device_name} @ {node_addr}"
|
||||
|
||||
# Buscar IOs recursivamente
|
||||
io_search_root_id = display_id
|
||||
io_search_root_info = project_data.get("devices", {}).get(io_search_root_id)
|
||||
|
||||
aggregated_io_addresses = []
|
||||
|
||||
# Buscar IOs en la estructura padre si existe
|
||||
parent_structure_id = io_search_root_info.get("parent_id") if io_search_root_info else None
|
||||
|
||||
if parent_structure_id:
|
||||
# Buscar IOs en dispositivos hermanos bajo la misma estructura padre
|
||||
for dev_scan_id, dev_scan_info in project_data.get("devices", {}).items():
|
||||
if dev_scan_info.get("parent_id") == parent_structure_id:
|
||||
module_context = {
|
||||
"id": dev_scan_id,
|
||||
"name": dev_scan_info.get("name", dev_scan_id),
|
||||
"order_number": dev_scan_info.get("order_number", "N/A"),
|
||||
"type_name": dev_scan_info.get("type_name", "N/A")
|
||||
}
|
||||
io_from_sibling = find_io_recursively(dev_scan_id, project_data, module_context)
|
||||
aggregated_io_addresses.extend(io_from_sibling)
|
||||
elif io_search_root_id:
|
||||
# Buscar IOs directamente en el dispositivo
|
||||
module_context = {
|
||||
"id": io_search_root_id,
|
||||
"name": io_search_root_info.get("name", io_search_root_id),
|
||||
"order_number": io_search_root_info.get("order_number", "N/A"),
|
||||
"type_name": io_search_root_info.get("type_name", "N/A")
|
||||
}
|
||||
aggregated_io_addresses = find_io_recursively(io_search_root_id, project_data, module_context)
|
||||
|
||||
# Procesar IOs por módulo
|
||||
if aggregated_io_addresses:
|
||||
# Agrupar IOs por módulo
|
||||
ios_by_module = {}
|
||||
for addr_info in aggregated_io_addresses:
|
||||
module_id = addr_info.get("module_id")
|
||||
if module_id not in ios_by_module:
|
||||
ios_by_module[module_id] = {
|
||||
'module_info': {
|
||||
'name': addr_info.get('module_name', '?'),
|
||||
'type': addr_info.get('module_type_name', 'N/A'),
|
||||
'order': addr_info.get('module_order_number', 'N/A'),
|
||||
'position': addr_info.get('module_pos', 'N/A')
|
||||
},
|
||||
'inputs': [],
|
||||
'outputs': []
|
||||
}
|
||||
|
||||
# Clasificar IO como input u output
|
||||
io_type = addr_info.get("type", "").lower()
|
||||
if io_type == "input":
|
||||
ios_by_module[module_id]['inputs'].append(addr_info)
|
||||
elif io_type == "output":
|
||||
ios_by_module[module_id]['outputs'].append(addr_info)
|
||||
|
||||
# Crear una fila por cada módulo con IOs
|
||||
for module_id, module_data in ios_by_module.items():
|
||||
module_info = module_data['module_info']
|
||||
|
||||
# Calcular direcciones de entrada - formato simplificado
|
||||
input_start_addr = 'N/A'
|
||||
input_end_addr = 'N/A'
|
||||
total_input_bits = 0
|
||||
|
||||
for addr_info in module_data['inputs']:
|
||||
start_str = addr_info.get("start", "?")
|
||||
length_str = addr_info.get("length", "?")
|
||||
try:
|
||||
start_byte = int(start_str)
|
||||
length_bits = int(length_str)
|
||||
length_bytes = math.ceil(length_bits / 8.0)
|
||||
if length_bits > 0 and length_bytes == 0:
|
||||
length_bytes = 1
|
||||
end_byte = start_byte + length_bytes - 1
|
||||
|
||||
# Para múltiples rangos, tomar el primer inicio y último fin
|
||||
if input_start_addr == 'N/A':
|
||||
input_start_addr = start_byte
|
||||
input_end_addr = end_byte
|
||||
else:
|
||||
input_start_addr = min(input_start_addr, start_byte)
|
||||
input_end_addr = max(input_end_addr, end_byte)
|
||||
|
||||
total_input_bits += length_bits
|
||||
except:
|
||||
# En caso de error, mantener N/A
|
||||
pass
|
||||
|
||||
# Calcular direcciones de salida - formato simplificado
|
||||
output_start_addr = 'N/A'
|
||||
output_end_addr = 'N/A'
|
||||
total_output_bits = 0
|
||||
|
||||
for addr_info in module_data['outputs']:
|
||||
start_str = addr_info.get("start", "?")
|
||||
length_str = addr_info.get("length", "?")
|
||||
try:
|
||||
start_byte = int(start_str)
|
||||
length_bits = int(length_str)
|
||||
length_bytes = math.ceil(length_bits / 8.0)
|
||||
if length_bits > 0 and length_bytes == 0:
|
||||
length_bytes = 1
|
||||
end_byte = start_byte + length_bytes - 1
|
||||
|
||||
# Para múltiples rangos, tomar el primer inicio y último fin
|
||||
if output_start_addr == 'N/A':
|
||||
output_start_addr = start_byte
|
||||
output_end_addr = end_byte
|
||||
else:
|
||||
output_start_addr = min(output_start_addr, start_byte)
|
||||
output_end_addr = max(output_end_addr, end_byte)
|
||||
|
||||
total_output_bits += length_bits
|
||||
except:
|
||||
# En caso de error, mantener N/A
|
||||
pass
|
||||
|
||||
excel_rows.append({
|
||||
'PLC Name': plc_name,
|
||||
'Network Path': network_path,
|
||||
'Network Type': network_type,
|
||||
'Device Address': node_addr,
|
||||
'Device Name': device_name,
|
||||
'Device Type': device_type,
|
||||
'Order Number': device_order,
|
||||
'Firmware Version': firmware_version,
|
||||
'Position': device_position,
|
||||
'IO Input Start Address': input_start_addr,
|
||||
'IO Input End Address': input_end_addr,
|
||||
'IO Output Start Address': output_start_addr,
|
||||
'IO Output End Address': output_end_addr,
|
||||
'Total Input Bits': total_input_bits,
|
||||
'Total Output Bits': total_output_bits,
|
||||
'Module Name': module_info['name'],
|
||||
'Module Type': module_info['type'],
|
||||
'Module Order Number': module_info['order']
|
||||
})
|
||||
else:
|
||||
# Dispositivo sin IOs
|
||||
excel_rows.append({
|
||||
'PLC Name': plc_name,
|
||||
'Network Path': network_path,
|
||||
'Network Type': network_type,
|
||||
'Device Address': node_addr,
|
||||
'Device Name': device_name,
|
||||
'Device Type': device_type,
|
||||
'Order Number': device_order,
|
||||
'Firmware Version': firmware_version,
|
||||
'Position': device_position,
|
||||
'IO Input Start Address': 'N/A',
|
||||
'IO Input End Address': 'N/A',
|
||||
'IO Output Start Address': 'N/A',
|
||||
'IO Output End Address': 'N/A',
|
||||
'Total Input Bits': 0,
|
||||
'Total Output Bits': 0,
|
||||
'Module Name': 'N/A',
|
||||
'Module Type': 'N/A',
|
||||
'Module Order Number': 'N/A'
|
||||
})
|
||||
|
||||
# Crear DataFrame y guardar Excel
|
||||
if excel_rows:
|
||||
df = pd.DataFrame(excel_rows)
|
||||
|
||||
# Agregar columna de ID único para compatibilidad con x7_update_CAx
|
||||
df['Unique_ID'] = df['PLC Name'] + "+" + df['Device Name']
|
||||
|
||||
# Reordenar columnas para mejor legibilidad
|
||||
column_order = [
|
||||
'PLC Name', 'Network Path', 'Network Type', 'Device Address', 'Device Name',
|
||||
'Device Type', 'Order Number', 'Firmware Version', 'Position',
|
||||
'Module Name', 'Module Type', 'Module Order Number',
|
||||
'IO Input Start Address', 'IO Input End Address', 'IO Output Start Address', 'IO Output End Address',
|
||||
'Total Input Bits', 'Total Output Bits', 'Unique_ID' # Agregar al final para compatibilidad
|
||||
]
|
||||
df = df.reindex(columns=column_order)
|
||||
|
||||
try:
|
||||
# Guardar como Excel con formato
|
||||
with pd.ExcelWriter(excel_file_path, engine='openpyxl') as writer:
|
||||
df.to_excel(writer, sheet_name='IO Report', index=False)
|
||||
|
||||
# Ajustar ancho de columnas
|
||||
worksheet = writer.sheets['IO Report']
|
||||
for column in worksheet.columns:
|
||||
max_length = 0
|
||||
column_letter = column[0].column_letter
|
||||
for cell in column:
|
||||
try:
|
||||
if len(str(cell.value)) > max_length:
|
||||
max_length = len(str(cell.value))
|
||||
except:
|
||||
pass
|
||||
adjusted_width = min(max_length + 2, 50) # Máximo 50 caracteres
|
||||
worksheet.column_dimensions[column_letter].width = adjusted_width
|
||||
|
||||
print(f"Excel IO report saved to: {excel_file_path}")
|
||||
print(f"Total rows in report: {len(excel_rows)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR saving Excel file {excel_file_path}: {e}")
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("No data to write to Excel file.")
|
||||
|
||||
|
||||
# --- generate_markdown_tree function ---
|
||||
def generate_markdown_tree(project_data, md_file_path, target_plc_id, output_root_path):
|
||||
"""(Modified) Generates hierarchical Markdown for a specific PLC."""
|
||||
|
@ -1151,25 +1522,34 @@ def sanitize_filename(name):
|
|||
|
||||
# --- Main Execution ---
|
||||
if __name__ == "__main__":
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
try:
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load configuration (frontend not running): {e}")
|
||||
configs = {}
|
||||
working_directory = None
|
||||
|
||||
script_version = "v31.1 - Corrected IO Summary Table Initialization" # Updated version
|
||||
script_version = "v32.2 - Simplified IO Address Format (Separate Start/End)" # Updated version
|
||||
print(
|
||||
f"--- AML (CAx Export) to Hierarchical JSON and Obsidian MD Converter ({script_version}) ---"
|
||||
)
|
||||
|
||||
# Validate working directory
|
||||
# Validate working directory with .debug fallback
|
||||
if not working_directory or not os.path.isdir(working_directory):
|
||||
print("ERROR: Working directory not set or invalid in configuration.")
|
||||
print("Attempting to use script's directory as fallback.")
|
||||
# Fallback to script's directory or current directory if needed
|
||||
working_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
if not os.path.isdir(working_directory):
|
||||
working_directory = os.getcwd()
|
||||
print(f"Using fallback directory: {working_directory}")
|
||||
# Optionally, prompt user to select a working directory here if critical
|
||||
# output_dir = select_output_directory() # Keep this if you want user selection on failure
|
||||
print("Working directory not set or invalid in configuration.")
|
||||
print("Using .debug directory as fallback for direct script execution.")
|
||||
|
||||
# Fallback to .debug directory under script location
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
debug_dir = os.path.join(script_dir, ".debug")
|
||||
|
||||
# Create .debug directory if it doesn't exist
|
||||
os.makedirs(debug_dir, exist_ok=True)
|
||||
working_directory = debug_dir
|
||||
print(f"Using debug directory: {working_directory}")
|
||||
else:
|
||||
print(f"Using configured working directory: {working_directory}")
|
||||
|
||||
# Use working_directory as the output directory
|
||||
output_dir = working_directory
|
||||
|
@ -1227,6 +1607,12 @@ if __name__ == "__main__":
|
|||
print(f" Generating Hardware Tree for PLC '{plc_name_original}' (ID: {plc_id}) at: {output_plc_md_file.resolve()}")
|
||||
# Pass output_path as the root directory for Hardware.md placement
|
||||
generate_markdown_tree(project_data, str(output_plc_md_file), plc_id, str(output_path))
|
||||
|
||||
# Generate Excel IO report for this PLC
|
||||
excel_io_filename = f"{input_path.stem}_IO_Report.xlsx"
|
||||
output_excel_file = plc_doc_dir / excel_io_filename
|
||||
print(f" Generating Excel IO Report for PLC '{plc_name_original}' (ID: {plc_id}) at: {output_excel_file.resolve()}")
|
||||
generate_io_excel_report(project_data, str(output_excel_file), plc_id, str(output_path))
|
||||
else:
|
||||
print("\nFailed to process AML data. Halting before generating PLC-specific trees.")
|
||||
|
||||
|
|
|
@ -0,0 +1,619 @@
|
|||
"""
|
||||
x7_update_CAx.py : Script que actualiza un archivo AML original basándose en las modificaciones
|
||||
hechas en el archivo Excel de IOs generado por x2_process_CAx.py
|
||||
|
||||
Pipeline:
|
||||
1. Lee el AML original
|
||||
2. Lee el Excel modificado
|
||||
3. Genera Excel de referencia desde el AML para comparar
|
||||
4. Valida que la estructura base sea la misma (mismos nodos, nombres)
|
||||
5. Identifica cambios en IOs, direcciones IP, etc.
|
||||
6. Aplica los cambios al AML original
|
||||
7. Genera un nuevo archivo AML con sufijo "_updated"
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox
|
||||
import traceback
|
||||
from lxml import etree as ET
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
import re
|
||||
import math
|
||||
import tempfile
|
||||
|
||||
script_root = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
)
|
||||
sys.path.append(script_root)
|
||||
from backend.script_utils import load_configuration
|
||||
|
||||
# Import functions from x2_process_CAx
|
||||
from x2_process_CAx import (
|
||||
extract_aml_data,
|
||||
generate_io_excel_report,
|
||||
sanitize_filename
|
||||
)
|
||||
|
||||
|
||||
def select_aml_file(title="Select Original AML File", initial_dir=None):
|
||||
"""Abre un diálogo para seleccionar un archivo AML."""
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
file_path = filedialog.askopenfilename(
|
||||
title=title,
|
||||
filetypes=[("AML Files", "*.aml"), ("All Files", "*.*")],
|
||||
initialdir=initial_dir
|
||||
)
|
||||
root.destroy()
|
||||
if not file_path:
|
||||
return None
|
||||
return file_path
|
||||
|
||||
|
||||
def select_excel_file(title="Select Modified Excel File", initial_dir=None):
|
||||
"""Abre un diálogo para seleccionar un archivo Excel."""
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
file_path = filedialog.askopenfilename(
|
||||
title=title,
|
||||
filetypes=[("Excel Files", "*.xlsx"), ("Excel Files", "*.xls"), ("All Files", "*.*")],
|
||||
initialdir=initial_dir
|
||||
)
|
||||
root.destroy()
|
||||
if not file_path:
|
||||
return None
|
||||
return file_path
|
||||
|
||||
|
||||
def generate_reference_excel_from_aml(aml_file_path, temp_dir):
|
||||
"""
|
||||
Genera un Excel de referencia desde el AML para comparar con el Excel modificado.
|
||||
Retorna el path al Excel generado y los project_data.
|
||||
"""
|
||||
print("Generando Excel de referencia desde AML original...")
|
||||
|
||||
# Extraer datos del AML
|
||||
try:
|
||||
parser = ET.XMLParser(remove_blank_text=True, huge_tree=True)
|
||||
tree = ET.parse(aml_file_path, parser)
|
||||
root = tree.getroot()
|
||||
project_data = extract_aml_data(root)
|
||||
except Exception as e:
|
||||
print(f"ERROR procesando archivo AML {aml_file_path}: {e}")
|
||||
return None, None
|
||||
|
||||
if not project_data or not project_data.get("plcs"):
|
||||
print("No se encontraron PLCs en el archivo AML.")
|
||||
return None, None
|
||||
|
||||
# Generar Excel para cada PLC y combinar en uno solo
|
||||
all_excel_rows = []
|
||||
|
||||
for plc_id, plc_data in project_data.get("plcs", {}).items():
|
||||
# Crear archivo temporal para este PLC
|
||||
temp_excel_path = os.path.join(temp_dir, f"temp_plc_{plc_id}.xlsx")
|
||||
|
||||
# Generar Excel para este PLC (reutilizamos la función existente)
|
||||
generate_io_excel_report(project_data, temp_excel_path, plc_id, temp_dir)
|
||||
|
||||
# Leer el Excel generado y agregar a la lista combinada
|
||||
if os.path.exists(temp_excel_path):
|
||||
try:
|
||||
df_plc = pd.read_excel(temp_excel_path, sheet_name='IO Report')
|
||||
# Agregar columna de ID único
|
||||
df_plc['Unique_ID'] = df_plc['PLC Name'] + "+" + df_plc['Device Name']
|
||||
all_excel_rows.append(df_plc)
|
||||
os.remove(temp_excel_path) # Limpiar archivo temporal
|
||||
except Exception as e:
|
||||
print(f"ERROR leyendo Excel temporal para PLC {plc_id}: {e}")
|
||||
|
||||
if not all_excel_rows:
|
||||
print("No se pudieron generar datos de Excel de referencia.")
|
||||
return None, None
|
||||
|
||||
# Combinar todos los DataFrames
|
||||
combined_df = pd.concat(all_excel_rows, ignore_index=True)
|
||||
|
||||
# Guardar Excel de referencia
|
||||
reference_excel_path = os.path.join(temp_dir, "reference_excel.xlsx")
|
||||
combined_df.to_excel(reference_excel_path, sheet_name='IO Report', index=False)
|
||||
|
||||
return reference_excel_path, project_data
|
||||
|
||||
|
||||
def compare_excel_files(reference_excel_path, modified_excel_path):
|
||||
"""
|
||||
Compara el Excel de referencia con el Excel modificado.
|
||||
Retorna (is_valid, changes_dict) donde:
|
||||
- is_valid: True si la estructura básica es la misma
|
||||
- changes_dict: diccionario con los cambios detectados
|
||||
"""
|
||||
print("Comparando archivos Excel...")
|
||||
|
||||
try:
|
||||
# Leer ambos archivos
|
||||
df_ref = pd.read_excel(reference_excel_path, sheet_name='IO Report')
|
||||
df_mod = pd.read_excel(modified_excel_path, sheet_name='IO Report')
|
||||
|
||||
# Agregar columna de ID único si no existe
|
||||
if 'Unique_ID' not in df_ref.columns:
|
||||
df_ref['Unique_ID'] = df_ref['PLC Name'] + "+" + df_ref['Device Name']
|
||||
if 'Unique_ID' not in df_mod.columns:
|
||||
df_mod['Unique_ID'] = df_mod['PLC Name'] + "+" + df_mod['Device Name']
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR leyendo archivos Excel: {e}")
|
||||
return False, {}
|
||||
|
||||
# Validar estructura básica
|
||||
if len(df_ref) != len(df_mod):
|
||||
print(f"ERROR: Número de filas diferente. Referencia: {len(df_ref)}, Modificado: {len(df_mod)}")
|
||||
return False, {}
|
||||
|
||||
# Verificar que todos los IDs únicos coincidan
|
||||
ref_ids = set(df_ref['Unique_ID'].tolist())
|
||||
mod_ids = set(df_mod['Unique_ID'].tolist())
|
||||
|
||||
if ref_ids != mod_ids:
|
||||
missing_in_mod = ref_ids - mod_ids
|
||||
extra_in_mod = mod_ids - ref_ids
|
||||
print("ERROR: Los IDs únicos no coinciden entre archivos.")
|
||||
if missing_in_mod:
|
||||
print(f" Faltantes en modificado: {missing_in_mod}")
|
||||
if extra_in_mod:
|
||||
print(f" Extras en modificado: {extra_in_mod}")
|
||||
return False, {}
|
||||
|
||||
print("Estructura básica validada correctamente.")
|
||||
|
||||
# Detectar cambios
|
||||
changes = {}
|
||||
columns_to_monitor = [
|
||||
'Device Address', 'IO Input Start Address', 'IO Input End Address',
|
||||
'IO Output Start Address', 'IO Output End Address',
|
||||
'Network Type', 'Device Type', 'Order Number', 'Firmware Version'
|
||||
]
|
||||
|
||||
for index, row_ref in df_ref.iterrows():
|
||||
unique_id = row_ref['Unique_ID']
|
||||
row_mod = df_mod[df_mod['Unique_ID'] == unique_id].iloc[0]
|
||||
|
||||
row_changes = {}
|
||||
for col in columns_to_monitor:
|
||||
if col in df_ref.columns and col in df_mod.columns:
|
||||
ref_val = str(row_ref[col]).strip() if pd.notna(row_ref[col]) else 'N/A'
|
||||
mod_val = str(row_mod[col]).strip() if pd.notna(row_mod[col]) else 'N/A'
|
||||
|
||||
if ref_val != mod_val:
|
||||
row_changes[col] = {
|
||||
'original': ref_val,
|
||||
'modified': mod_val
|
||||
}
|
||||
|
||||
if row_changes:
|
||||
changes[unique_id] = {
|
||||
'plc_name': row_ref['PLC Name'],
|
||||
'device_name': row_ref['Device Name'],
|
||||
'changes': row_changes
|
||||
}
|
||||
|
||||
print(f"Detectados {len(changes)} dispositivos con cambios.")
|
||||
for unique_id, change_info in changes.items():
|
||||
print(f" {unique_id}: {list(change_info['changes'].keys())}")
|
||||
|
||||
return True, changes
|
||||
|
||||
|
||||
def find_device_in_aml(project_data, plc_name, device_name):
|
||||
"""
|
||||
Encuentra el device_id correspondiente en los datos del AML basándose en PLC y device name.
|
||||
"""
|
||||
# Buscar el PLC por nombre
|
||||
target_plc_id = None
|
||||
for plc_id, plc_data in project_data.get("plcs", {}).items():
|
||||
if plc_data.get('name', plc_id) == plc_name:
|
||||
target_plc_id = plc_id
|
||||
break
|
||||
|
||||
if not target_plc_id:
|
||||
return None
|
||||
|
||||
# Buscar el dispositivo en las redes del PLC
|
||||
plc_info = project_data["plcs"][target_plc_id]
|
||||
plc_networks = plc_info.get("connected_networks", {})
|
||||
|
||||
for net_id, plc_addr_on_net in plc_networks.items():
|
||||
net_info = project_data.get("networks", {}).get(net_id)
|
||||
if not net_info:
|
||||
continue
|
||||
|
||||
devices_on_net = net_info.get("devices_on_net", {})
|
||||
|
||||
# Identificar nodos que pertenecen al PLC
|
||||
plc_interface_and_node_ids = set()
|
||||
for node in plc_info.get("network_nodes", []):
|
||||
plc_interface_and_node_ids.add(node["id"])
|
||||
interface_id_lookup = project_data["devices"].get(node["id"], {}).get("parent_id")
|
||||
if interface_id_lookup:
|
||||
plc_interface_and_node_ids.add(interface_id_lookup)
|
||||
plc_interface_and_node_ids.add(target_plc_id)
|
||||
|
||||
# Buscar en dispositivos de la red
|
||||
for node_id, node_addr in devices_on_net.items():
|
||||
if node_id in plc_interface_and_node_ids:
|
||||
continue
|
||||
|
||||
node_info = project_data.get("devices", {}).get(node_id)
|
||||
if not node_info:
|
||||
continue
|
||||
|
||||
# Determinar información del dispositivo
|
||||
interface_id = node_info.get("parent_id")
|
||||
interface_info = None
|
||||
actual_device_id = None
|
||||
actual_device_info = None
|
||||
|
||||
if interface_id:
|
||||
interface_info = project_data.get("devices", {}).get(interface_id)
|
||||
if interface_info:
|
||||
actual_device_id = interface_info.get("parent_id")
|
||||
if actual_device_id:
|
||||
actual_device_info = project_data.get("devices", {}).get(actual_device_id)
|
||||
|
||||
display_info = actual_device_info if actual_device_info else (interface_info if interface_info else node_info)
|
||||
display_name = display_info.get("name", "Unknown")
|
||||
|
||||
if display_name == device_name:
|
||||
return {
|
||||
'node_id': node_id,
|
||||
'display_id': actual_device_id if actual_device_info else (interface_id if interface_info else node_id),
|
||||
'node_info': node_info,
|
||||
'display_info': display_info,
|
||||
'network_id': net_id
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def apply_changes_to_aml(aml_tree, project_data, changes_dict):
|
||||
"""
|
||||
Aplica los cambios detectados al árbol XML del AML.
|
||||
"""
|
||||
print("Aplicando cambios al archivo AML...")
|
||||
|
||||
root = aml_tree.getroot()
|
||||
changes_applied = 0
|
||||
|
||||
for unique_id, change_info in changes_dict.items():
|
||||
plc_name = change_info['plc_name']
|
||||
device_name = change_info['device_name']
|
||||
changes = change_info['changes']
|
||||
|
||||
print(f" Procesando cambios para: {unique_id}")
|
||||
|
||||
# Encontrar el dispositivo en los datos del AML
|
||||
device_info = find_device_in_aml(project_data, plc_name, device_name)
|
||||
if not device_info:
|
||||
print(f" ERROR: No se pudo encontrar el dispositivo en el AML")
|
||||
continue
|
||||
|
||||
# Encontrar el elemento XML correspondiente
|
||||
device_id = device_info['node_id']
|
||||
xml_element = root.xpath(f".//*[@ID='{device_id}']")
|
||||
|
||||
if not xml_element:
|
||||
print(f" ERROR: No se pudo encontrar el elemento XML con ID {device_id}")
|
||||
continue
|
||||
|
||||
device_element = xml_element[0]
|
||||
|
||||
# Aplicar cambios específicos
|
||||
for field, change_data in changes.items():
|
||||
original_val = change_data['original']
|
||||
modified_val = change_data['modified']
|
||||
|
||||
if field == 'Device Address':
|
||||
# Cambiar dirección de red del dispositivo
|
||||
if apply_network_address_change(device_element, modified_val):
|
||||
print(f" ✓ Dirección de red actualizada: {original_val} -> {modified_val}")
|
||||
changes_applied += 1
|
||||
else:
|
||||
print(f" ✗ Error actualizando dirección de red")
|
||||
|
||||
elif field in ['IO Input Start Address', 'IO Input End Address', 'IO Output Start Address', 'IO Output End Address']:
|
||||
# Cambiar direcciones IO específicas
|
||||
if apply_io_address_change(device_element, field, original_val, modified_val, project_data, device_id):
|
||||
print(f" ✓ {field} actualizada: {original_val} -> {modified_val}")
|
||||
changes_applied += 1
|
||||
else:
|
||||
print(f" ✗ Error actualizando {field}")
|
||||
|
||||
elif field in ['Device Type', 'Order Number', 'Firmware Version']:
|
||||
# Cambiar atributos del dispositivo
|
||||
if apply_device_attribute_change(device_element, field, modified_val):
|
||||
print(f" ✓ {field} actualizado: {original_val} -> {modified_val}")
|
||||
changes_applied += 1
|
||||
else:
|
||||
print(f" ✗ Error actualizando {field}")
|
||||
|
||||
print(f"Total de cambios aplicados: {changes_applied}")
|
||||
return changes_applied > 0
|
||||
|
||||
|
||||
def apply_network_address_change(device_element, new_address):
|
||||
"""
|
||||
Aplica cambio de dirección de red a un elemento del dispositivo.
|
||||
"""
|
||||
try:
|
||||
# Buscar atributo NetworkAddress
|
||||
addr_attr = device_element.xpath("./*[local-name()='Attribute'][@Name='NetworkAddress']")
|
||||
if addr_attr:
|
||||
value_elem = addr_attr[0].xpath("./*[local-name()='Value']")
|
||||
if value_elem:
|
||||
value_elem[0].text = new_address
|
||||
return True
|
||||
|
||||
# Si no existe, crear el atributo
|
||||
attr_elem = ET.SubElement(device_element, "Attribute")
|
||||
attr_elem.set("Name", "NetworkAddress")
|
||||
value_elem = ET.SubElement(attr_elem, "Value")
|
||||
value_elem.text = new_address
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error aplicando cambio de dirección: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def apply_device_attribute_change(device_element, field, new_value):
|
||||
"""
|
||||
Aplica cambio de atributo del dispositivo.
|
||||
"""
|
||||
try:
|
||||
# Mapear campos a nombres de atributos XML
|
||||
attr_mapping = {
|
||||
'Device Type': 'TypeName',
|
||||
'Order Number': 'OrderNumber',
|
||||
'Firmware Version': 'FirmwareVersion'
|
||||
}
|
||||
|
||||
attr_name = attr_mapping.get(field)
|
||||
if not attr_name:
|
||||
return False
|
||||
|
||||
# Buscar atributo existente
|
||||
attr_elem = device_element.xpath(f"./*[local-name()='Attribute'][@Name='{attr_name}']")
|
||||
if attr_elem:
|
||||
value_elem = attr_elem[0].xpath("./*[local-name()='Value']")
|
||||
if value_elem:
|
||||
value_elem[0].text = new_value
|
||||
return True
|
||||
|
||||
# Si no existe, crear el atributo
|
||||
attr_elem = ET.SubElement(device_element, "Attribute")
|
||||
attr_elem.set("Name", attr_name)
|
||||
value_elem = ET.SubElement(attr_elem, "Value")
|
||||
value_elem.text = new_value
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error aplicando cambio de atributo {field}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def apply_io_address_change(device_element, field, original_val, modified_val, project_data, device_id):
|
||||
"""
|
||||
Aplica cambios a las direcciones IO individuales. Ahora es más simple
|
||||
porque las direcciones están separadas en campos específicos.
|
||||
"""
|
||||
try:
|
||||
# Validar que el nuevo valor sea numérico o N/A
|
||||
if modified_val != 'N/A' and modified_val != '':
|
||||
try:
|
||||
new_addr_value = int(modified_val)
|
||||
except ValueError:
|
||||
print(f" Error: Valor de dirección no válido: {modified_val}")
|
||||
return False
|
||||
else:
|
||||
# Si es N/A, no hay dirección IO para este tipo
|
||||
return True
|
||||
|
||||
# Determinar tipo de IO y si es start o end
|
||||
is_input = "Input" in field
|
||||
is_start = "Start" in field
|
||||
io_type = "Input" if is_input else "Output"
|
||||
|
||||
# Buscar la estructura IO en el dispositivo
|
||||
# Esto requiere encontrar los elementos Address y sus sub-atributos
|
||||
address_elements = device_element.xpath("./*[local-name()='Attribute'][@Name='Address']")
|
||||
|
||||
if not address_elements:
|
||||
print(f" No se encontró elemento Address en el dispositivo")
|
||||
return False
|
||||
|
||||
# Buscar dentro del Address el sub-atributo correcto
|
||||
address_element = address_elements[0]
|
||||
io_subelements = address_element.xpath(f"./*[local-name()='Attribute']")
|
||||
|
||||
target_io_element = None
|
||||
for io_sub in io_subelements:
|
||||
# Verificar si es el tipo correcto (Input/Output)
|
||||
type_val = io_sub.xpath("./*[local-name()='Attribute'][@Name='IoType']/*[local-name()='Value']/text()")
|
||||
if type_val and type_val[0].lower() == io_type.lower():
|
||||
target_io_element = io_sub
|
||||
break
|
||||
|
||||
if not target_io_element:
|
||||
print(f" No se encontró elemento {io_type} en Address")
|
||||
return False
|
||||
|
||||
# Actualizar StartAddress o calcular Length según el campo
|
||||
if is_start:
|
||||
# Actualizar StartAddress
|
||||
start_attr = target_io_element.xpath("./*[local-name()='Attribute'][@Name='StartAddress']")
|
||||
if start_attr:
|
||||
value_elem = start_attr[0].xpath("./*[local-name()='Value']")
|
||||
if value_elem:
|
||||
value_elem[0].text = str(new_addr_value)
|
||||
return True
|
||||
else:
|
||||
# Crear StartAddress si no existe
|
||||
start_attr_elem = ET.SubElement(target_io_element, "Attribute")
|
||||
start_attr_elem.set("Name", "StartAddress")
|
||||
value_elem = ET.SubElement(start_attr_elem, "Value")
|
||||
value_elem.text = str(new_addr_value)
|
||||
return True
|
||||
else:
|
||||
# Es End Address - necesitamos calcular Length basándose en Start y End
|
||||
# Primero obtener StartAddress
|
||||
start_attr = target_io_element.xpath("./*[local-name()='Attribute'][@Name='StartAddress']/*[local-name()='Value']/text()")
|
||||
if start_attr:
|
||||
try:
|
||||
start_value = int(start_attr[0])
|
||||
# Length en bytes = (end - start + 1)
|
||||
length_bytes = new_addr_value - start_value + 1
|
||||
if length_bytes > 0:
|
||||
# Convertir a bits (asumiendo 8 bits por byte)
|
||||
length_bits = length_bytes * 8
|
||||
|
||||
# Actualizar Length
|
||||
length_attr = target_io_element.xpath("./*[local-name()='Attribute'][@Name='Length']")
|
||||
if length_attr:
|
||||
value_elem = length_attr[0].xpath("./*[local-name()='Value']")
|
||||
if value_elem:
|
||||
value_elem[0].text = str(length_bits)
|
||||
return True
|
||||
else:
|
||||
# Crear Length si no existe
|
||||
length_attr_elem = ET.SubElement(target_io_element, "Attribute")
|
||||
length_attr_elem.set("Name", "Length")
|
||||
value_elem = ET.SubElement(length_attr_elem, "Value")
|
||||
value_elem.text = str(length_bits)
|
||||
return True
|
||||
else:
|
||||
print(f" Error: End address ({new_addr_value}) debe ser mayor que start address ({start_value})")
|
||||
return False
|
||||
except ValueError:
|
||||
print(f" Error: No se pudo convertir start address: {start_attr[0]}")
|
||||
return False
|
||||
else:
|
||||
print(f" Error: No se encontró StartAddress para calcular Length")
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error aplicando cambio de dirección IO {field}: {e}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def save_updated_aml(aml_tree, original_aml_path):
|
||||
"""
|
||||
Guarda el árbol XML modificado como un nuevo archivo AML con sufijo "_updated".
|
||||
"""
|
||||
original_path = Path(original_aml_path)
|
||||
updated_path = original_path.parent / f"{original_path.stem}_updated{original_path.suffix}"
|
||||
|
||||
try:
|
||||
# Escribir el XML actualizado
|
||||
aml_tree.write(
|
||||
str(updated_path),
|
||||
pretty_print=True,
|
||||
xml_declaration=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
print(f"Archivo AML actualizado guardado en: {updated_path}")
|
||||
return str(updated_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR guardando archivo AML actualizado: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal del script."""
|
||||
try:
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
except Exception as e:
|
||||
print(f"Warning: No se pudo cargar configuración: {e}")
|
||||
configs = {}
|
||||
working_directory = None
|
||||
|
||||
script_version = "v1.1 - Simplified IO Address Handling (Start/End Separated)"
|
||||
print(f"--- Actualizador de AML desde Excel Modificado ({script_version}) ---")
|
||||
|
||||
# Validar directorio de trabajo
|
||||
if not working_directory or not os.path.isdir(working_directory):
|
||||
print("Directorio de trabajo no configurado. Usando directorio actual.")
|
||||
working_directory = os.getcwd()
|
||||
|
||||
print(f"Directorio de trabajo: {working_directory}")
|
||||
|
||||
# 1. Seleccionar archivo AML original
|
||||
print("\n1. Seleccione el archivo AML original:")
|
||||
aml_file_path = select_aml_file(initial_dir=working_directory)
|
||||
if not aml_file_path:
|
||||
print("No se seleccionó archivo AML. Saliendo.")
|
||||
return
|
||||
|
||||
# 2. Seleccionar archivo Excel modificado
|
||||
print("\n2. Seleccione el archivo Excel modificado:")
|
||||
excel_file_path = select_excel_file(initial_dir=working_directory)
|
||||
if not excel_file_path:
|
||||
print("No se seleccionó archivo Excel. Saliendo.")
|
||||
return
|
||||
|
||||
print(f"\nArchivo AML original: {aml_file_path}")
|
||||
print(f"Archivo Excel modificado: {excel_file_path}")
|
||||
|
||||
# 3. Crear directorio temporal para archivos intermedios
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
print(f"\nUsando directorio temporal: {temp_dir}")
|
||||
|
||||
# 4. Generar Excel de referencia desde AML
|
||||
reference_excel_path, project_data = generate_reference_excel_from_aml(aml_file_path, temp_dir)
|
||||
if not reference_excel_path or not project_data:
|
||||
print("ERROR: No se pudo generar Excel de referencia.")
|
||||
return
|
||||
|
||||
# 5. Comparar archivos Excel
|
||||
is_valid, changes_dict = compare_excel_files(reference_excel_path, excel_file_path)
|
||||
if not is_valid:
|
||||
print("ERROR: El archivo Excel modificado no es compatible con el AML original.")
|
||||
return
|
||||
|
||||
if not changes_dict:
|
||||
print("No se detectaron cambios en el Excel. No hay nada que actualizar.")
|
||||
return
|
||||
|
||||
# 6. Cargar y parsear AML original
|
||||
print("\nCargando archivo AML original...")
|
||||
try:
|
||||
parser = ET.XMLParser(remove_blank_text=True, huge_tree=True)
|
||||
aml_tree = ET.parse(aml_file_path, parser)
|
||||
except Exception as e:
|
||||
print(f"ERROR parseando archivo AML: {e}")
|
||||
return
|
||||
|
||||
# 7. Aplicar cambios al AML
|
||||
success = apply_changes_to_aml(aml_tree, project_data, changes_dict)
|
||||
if not success:
|
||||
print("No se pudieron aplicar cambios al AML.")
|
||||
return
|
||||
|
||||
# 8. Guardar AML actualizado
|
||||
updated_aml_path = save_updated_aml(aml_tree, aml_file_path)
|
||||
if updated_aml_path:
|
||||
print(f"\n¡Proceso completado exitosamente!")
|
||||
print(f"Archivo AML actualizado: {updated_aml_path}")
|
||||
else:
|
||||
print("ERROR: No se pudo guardar el archivo AML actualizado.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
87
data/log.txt
87
data/log.txt
|
@ -1,82 +1,5 @@
|
|||
[16:30:18] Iniciando ejecución de x1_export_CAx.py en D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia...
|
||||
[16:30:19] --- TIA Portal Project CAx Exporter and Analyzer ---
|
||||
[16:30:24] Selected Project: D:/Trabajo/VM/44 - 98050 - Fiera/InLavoro/PLC/98050_PLC_01/98050_PLC_01.ap19
|
||||
[16:30:24] Using Output Directory (Working Directory): D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
[16:30:24] Detected TIA Portal version: 19.0 (from extension .ap19)
|
||||
[16:30:24] Will export CAx data to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
[16:30:24] Will generate summary to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Summary.md
|
||||
[16:30:24] Export log file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.log
|
||||
[16:30:24] Connecting to TIA Portal V19.0...
|
||||
[16:30:25] 2025-06-06 16:30:25,861 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||
[16:30:25] 2025-06-06 16:30:25,895 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
|
||||
[16:32:34] Connected.
|
||||
[16:32:34] Opening project: 98050_PLC_01.ap19...
|
||||
[16:32:34] 2025-06-06 16:32:34,404 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_01\98050_PLC_01.ap19
|
||||
[16:33:05] Project opened.
|
||||
[16:33:05] Exporting CAx data for the project to D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml...
|
||||
[16:33:52] CAx data exported successfully.
|
||||
[16:33:52] Closing TIA Portal...
|
||||
[16:33:52] 2025-06-06 16:33:52,008 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
|
||||
[16:33:52] TIA Portal closed.
|
||||
[16:33:52] Parsing AML file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
[16:33:52] Markdown summary written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Summary.md
|
||||
[16:33:52] Script finished.
|
||||
[16:33:55] Ejecución de x1_export_CAx.py finalizada (success). Duración: 0:03:37.052535.
|
||||
[16:33:55] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\log_x1_export_CAx.txt
|
||||
[16:34:01] Iniciando ejecución de x2_process_CAx.py en D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia...
|
||||
[16:34:01] --- AML (CAx Export) to Hierarchical JSON and Obsidian MD Converter (v31.1 - Corrected IO Summary Table Initialization) ---
|
||||
[16:34:01] Using Working Directory for Output: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
[16:34:09] Input AML: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
[16:34:09] Output Directory: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
[16:34:09] Output JSON: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.hierarchical.json
|
||||
[16:34:09] Output IO Debug Tree MD: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export_IO_Upward_Debug.md
|
||||
[16:34:09] Processing AML file: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.aml
|
||||
[16:34:09] Pass 1: Found 363 InternalElement(s). Populating device dictionary...
|
||||
[16:34:09] Pass 2: Identifying PLCs and Networks (Refined v2)...
|
||||
[16:34:09] Identified Network: PN/IE_1 (fcbafb48-c53d-4af5-8143-794df99be4d6) Type: Unknown
|
||||
[16:34:09] Identified PLC: A40510 (fc0d3bac-267e-488a-8dcf-7dc8599d80e8) - Type: CPU 1514SP T-2 PN OrderNo: 6ES7 514-2VN03-0AB0
|
||||
[16:34:09] Pass 3: Processing InternalLinks (Robust Network Mapping & IO)...
|
||||
[16:34:09] Found 103 InternalLink(s).
|
||||
[16:34:09] Mapping Device/Node 'E1' (NodeID:ffef8b7b-b6bf-46aa-b3b5-f5083c420563, Addr:10.1.30.11) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:221c248d-83d8-464c-9425-c949086b34e7, Addr:10.1.30.58) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:5c3bf795-ecec-47e7-a962-4d54c95b9887, Addr:10.1.30.59) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:4100c829-d889-489a-971c-a69207333e2f, Addr:10.1.30.60) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:a6aed467-1a4c-4a5a-bf3d-e7ce46e94a9f, Addr:10.1.30.61) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:b3d71b04-f99a-4af8-aa69-2ca02a30d391, Addr:10.1.30.62) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:eb59daf8-f59c-435e-bfe4-8e7afd0937bd, Addr:10.1.30.63) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:3e931f82-08a5-44de-89a5-b907f5aaeb24, Addr:10.1.30.64) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:f673d611-7a73-4a8e-912d-ea16c294d51b, Addr:10.1.30.65) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:09a2569a-4948-4830-9dd8-315ae638e391, Addr:10.1.30.66) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:719a9643-c903-454e-8325-0f03cf871c35, Addr:10.1.30.31) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:ef8c3829-2363-43b3-ae20-7e903c2517a5, Addr:10.1.30.32) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:7cece978-67aa-4707-b23c-375ebddfc2bc, Addr:10.1.30.170) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:ad86d223-8f60-41c0-8208-c88d68e42154, Addr:10.1.30.33) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:fa86bb0c-6142-41ce-ba6c-565e1e1f810e, Addr:10.1.30.34) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:441a0b56-5830-4595-ab8d-b073b6db8545, Addr:10.1.30.35) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:8dbc0c6b-a8f5-491c-832b-4af0757c259d, Addr:10.1.30.36) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:d06cdd21-62c3-4cff-9389-12a4e21869a2, Addr:10.1.30.40) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:f63db18d-dc19-48b8-afaa-be557db4d13e, Addr:10.1.30.44) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:21589a71-d4dd-4534-b457-deb72f3f7660, Addr:10.1.30.41) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:230ddec2-fa03-44d9-8350-0c0eeb463400, Addr:10.1.30.42) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:b2647d59-ed85-44c5-813f-167c41ea1ca1, Addr:10.1.30.43) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:9933a2a3-47db-448a-a724-ed583d5994bb, Addr:10.1.30.37) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:9b45edaa-0640-4e86-ba59-1b991c1a85ca, Addr:10.1.30.45) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:a5fbeadb-a8ab-42e0-b58f-296e44633ce5, Addr:10.1.30.46) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:67d1d9ad-cf1d-43aa-9dc4-9e574cda5e5b, Addr:10.1.30.47) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:08689cb8-1b00-403f-bc20-c5911a20e655, Addr:10.1.30.48) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:a06a0e94-b651-4510-bbe0-8a0c3b717283, Addr:10.1.30.49) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:cf339d67-1758-4462-85f0-9e6a002d1c3c, Addr:10.1.30.70) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:d80a9fcf-5e0d-45a9-b617-362a9cb4121a, Addr:10.1.30.71) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:7c28d11a-9673-4957-931d-8840a589da85, Addr:10.1.30.72) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:ad792043-fdb8-4695-a10a-aa2cad996cf0, Addr:10.1.30.74) to Network 'PN/IE_1'
|
||||
[16:34:09] Mapping Device/Node 'IE1' (NodeID:b30940b8-6cb5-4143-854f-9b569f081703, Addr:10.1.30.73) to Network 'PN/IE_1'
|
||||
[16:34:09] Data extraction and structuring complete.
|
||||
[16:34:09] Generating JSON output: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export.hierarchical.json
|
||||
[16:34:09] JSON data written successfully.
|
||||
[16:34:09] IO upward debug tree written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\98050_PLC_01_CAx_Export_IO_Upward_Debug.md
|
||||
[16:34:09] Found 1 PLC(s). Generating individual hardware trees...
|
||||
[16:34:09] Generating Hardware Tree for PLC 'A40510' (ID: fc0d3bac-267e-488a-8dcf-7dc8599d80e8) at: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_CAx_Export_Hardware_Tree.md
|
||||
[16:34:09] Markdown tree summary written to: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\A40510\Documentation\98050_PLC_01_CAx_Export_Hardware_Tree.md
|
||||
[16:34:09] Script finished.
|
||||
[16:34:09] Ejecución de x2_process_CAx.py finalizada (success). Duración: 0:00:08.316649.
|
||||
[16:34:09] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\log_x2_process_CAx.txt
|
||||
[15:36:49] Iniciando ejecución de x7_update_CAx.py en D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia...
|
||||
[15:36:49] --- Actualizador de AML desde Excel Modificado (v1.1 - Simplified IO Address Handling (Start/End Separated)) ---
|
||||
[15:36:49] Directorio de trabajo: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia
|
||||
[15:36:49] 1. Seleccione el archivo AML original:
|
||||
[15:36:57] 2. Seleccione el archivo Excel modificado:
|
||||
|
|
Loading…
Reference in New Issue