Add path validation and sanitization tests
- Implemented `test_path_validation.py` to test filename sanitization, path sanitization, and export path validation functions. - Added comprehensive test cases for various problematic block names and paths to ensure proper handling of invalid characters and whitespace. - Created `test_sanitization.py` to specifically address problematic block names with updated sanitization logic, including special cases for "I/O access error" and "Time error interrupt". - Enhanced filename sanitization to replace specific problematic characters and patterns, ensuring consistent output for known issues.
This commit is contained in:
parent
586e3cc9b3
commit
48e25282d6
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,49 +0,0 @@
|
|||
--- Log de Ejecución: x3.py ---
|
||||
Grupo: ObtainIOFromProjectTia
|
||||
Directorio de Trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport
|
||||
Inicio: 2025-05-12 14:24:35
|
||||
Fin: 2025-05-12 14:24:39
|
||||
Duración: 0:00:04.165462
|
||||
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) ---
|
||||
Using Working Directory for Output: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport
|
||||
Input AML: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export.aml
|
||||
Output Directory: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport
|
||||
Output JSON: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export.hierarchical.json
|
||||
Output IO Debug Tree MD: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export_IO_Upward_Debug.md
|
||||
Processing AML file: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export.aml
|
||||
Pass 1: Found 203 InternalElement(s). Populating device dictionary...
|
||||
Pass 2: Identifying PLCs and Networks (Refined v2)...
|
||||
Identified Network: PROFIBUS_1 (d645659a-3704-4cd6-b2c8-6165ceeed6ee) Type: Profibus
|
||||
Identified Network: ETHERNET_1 (f0b1c852-7dc9-4748-888e-34c60b519a75) Type: Ethernet/Profinet
|
||||
Identified PLC: PLC (a48e038f-0bcc-4b48-8373-033da316c62b) - Type: CPU 1516F-3 PN/DP OrderNo: 6ES7 516-3FP03-0AB0
|
||||
Pass 3: Processing InternalLinks (Robust Network Mapping & IO)...
|
||||
Found 116 InternalLink(s).
|
||||
Mapping Device/Node 'E1' (NodeID:439930b8-1bbc-4cb2-a93b-2eed931f4b12, Addr:10.1.33.11) to Network 'ETHERNET_1'
|
||||
--> Associating Network 'ETHERNET_1' with PLC 'PLC' (via Node 'E1' Addr: 10.1.33.11)
|
||||
Mapping Device/Node 'P1' (NodeID:904bb0f7-df2d-4c1d-ab65-f45480449db1, Addr:1) to Network 'PROFIBUS_1'
|
||||
--> Associating Network 'PROFIBUS_1' with PLC 'PLC' (via Node 'P1' Addr: 1)
|
||||
Mapping Device/Node 'PB1' (NodeID:2784bae8-9807-475f-89bd-bcf44282f5f4, Addr:12) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:e9c5f60a-1da2-4c9b-979e-7d03a5b58a44, Addr:20) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:dd7201c2-e127-4a9d-b6ae-7a74a4ffe418, Addr:21) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:d8825919-3a6c-4f95-aef0-62c782cfdb51, Addr:22) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:27d0e31d-46dc-4fdd-ab82-cfb91899a27c, Addr:10) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:d91d5905-aa1a-485e-b4eb-8333cc2133c2, Addr:8) to Network 'PROFIBUS_1'
|
||||
Mapping Device/Node 'PB1' (NodeID:0c5dfe06-786d-4ab6-b57c-8dfede56c2aa, Addr:40) to Network 'PROFIBUS_1'
|
||||
Data extraction and structuring complete.
|
||||
Generating JSON output: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export.hierarchical.json
|
||||
JSON data written successfully.
|
||||
|
||||
IO upward debug tree written to: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\SAE196_c0.2_CAx_Export_IO_Upward_Debug.md
|
||||
|
||||
Found 1 PLC(s). Generating individual hardware trees...
|
||||
Generating Hardware Tree for PLC 'PLC' (ID: a48e038f-0bcc-4b48-8373-033da316c62b) at: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\Documentation\SAE196_c0.2_CAx_Export_Hardware_Tree.md
|
||||
Markdown summary (including table) written to: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\Documentation\SAE196_c0.2_CAx_Export_Hardware_Tree.md
|
||||
|
||||
Script finished.
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
Ninguno
|
||||
--- FIN DEL LOG ---
|
|
@ -1,65 +0,0 @@
|
|||
--- Log de Ejecución: x4.py ---
|
||||
Grupo: ObtainIOFromProjectTia
|
||||
Directorio de Trabajo: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Source
|
||||
Inicio: 2025-06-19 19:05:36
|
||||
Fin: 2025-06-19 19:06:33
|
||||
Duración: 0:00:57.281042
|
||||
Estado: SUCCESS (Código de Salida: 0)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
--- Exportador de Referencias Cruzadas de TIA Portal ---
|
||||
Versión de TIA Portal detectada: 19.0 (de la extensión .ap19)
|
||||
|
||||
Proyecto seleccionado: D:/Trabajo/VM/44 - 98050 - Fiera/InLavoro/PLC/98050_PLC_11/98050_PLC_11.ap19
|
||||
Usando directorio base de exportación: D:\Trabajo\VM\44 - 98050 - Fiera\Reporte\ExportsTia\Source
|
||||
|
||||
Conectando a TIA Portal V19.0...
|
||||
2025-06-19 19:05:42,182 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||
2025-06-19 19:05:42,202 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
|
||||
Conectado a TIA Portal.
|
||||
2025-06-19 19:05:52,371 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal GetProcessId - Process id: 24972
|
||||
ID del proceso del Portal: 24972
|
||||
2025-06-19 19:05:52,710 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... D:/Trabajo/VM/44 - 98050 - Fiera/InLavoro/PLC/98050_PLC_11/98050_PLC_11.ap19
|
||||
|
||||
Ocurrió un error inesperado: OpennessAccessException: Error when calling method 'OpenWithUpgrade' of type 'Siemens.Engineering.ProjectComposition'.
|
||||
|
||||
|
||||
|
||||
Unable to open the project under path 'D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19'.
|
||||
|
||||
|
||||
|
||||
An error occurred while opening the project
|
||||
|
||||
The project/library D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19 cannot be accessed. It has already been opened by user Miguel on computer CSANUC. Note: If the application was not correctly closed, the open projects and libraries can only be opened again after a 2 minute delay.
|
||||
|
||||
Script finalizado.
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
2025-06-19 19:05:53,136 [1] ERROR Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject -
|
||||
Siemens.TiaPortal.OpennessContracts.OpennessAccessException: Error when calling method 'OpenWithUpgrade' of type 'Siemens.Engineering.ProjectComposition'.
|
||||
|
||||
Unable to open the project under path 'D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19'.
|
||||
|
||||
An error occurred while opening the project
|
||||
The project/library D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19 cannot be accessed. It has already been opened by user Miguel on computer CSANUC. Note: If the application was not correctly closed, the open projects and libraries can only be opened again after a 2 minute delay.
|
||||
Traceback (most recent call last):
|
||||
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 455, in <module>
|
||||
portal_instance, project_object = open_portal_and_project(tia_version, project_file)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 413, in open_portal_and_project
|
||||
project_obj = portal.open_project(project_file_path=str(project_file_path))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
ValueError: OpennessAccessException: Error when calling method 'OpenWithUpgrade' of type 'Siemens.Engineering.ProjectComposition'.
|
||||
|
||||
|
||||
|
||||
Unable to open the project under path 'D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19'.
|
||||
|
||||
|
||||
|
||||
An error occurred while opening the project
|
||||
|
||||
The project/library D:\Trabajo\VM\44 - 98050 - Fiera\InLavoro\PLC\98050_PLC_11\98050_PLC_11.ap19 cannot be accessed. It has already been opened by user Miguel on computer CSANUC. Note: If the application was not correctly closed, the open projects and libraries can only be opened again after a 2 minute delay.
|
||||
|
||||
--- FIN DEL LOG ---
|
|
@ -1,81 +0,0 @@
|
|||
--- Log de Ejecución: xTest.py ---
|
||||
Grupo: ObtainIOFromProjectTia
|
||||
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourcdSD
|
||||
Inicio: 2025-05-22 11:17:27
|
||||
Fin: 2025-05-22 11:18:44
|
||||
Duración: 0:01:16.758340
|
||||
Estado: ERROR (Código de Salida: 1)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
============================================================
|
||||
PRUEBA DE EXPORTACIÓN SIMATIC SD - TIA PORTAL V20
|
||||
============================================================
|
||||
|
||||
Project: C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452_V20/SAE452_V20.ap20
|
||||
Export Directory: C:/Users/migue/Downloads/Nueva carpeta (18)\SIMATIC_SD_Test
|
||||
|
||||
Connecting to TIA Portal V20...
|
||||
2025-05-22 11:17:49,266 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||
2025-05-22 11:17:49,283 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
|
||||
Connected successfully.
|
||||
Opening project...
|
||||
2025-05-22 11:18:05,562 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452_V20/SAE452_V20.ap20
|
||||
Project opened successfully.
|
||||
2025-05-22 11:18:20,088 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc CPU 315F-2 PN/DP with parent name _SSAE0452
|
||||
Found 1 PLC(s)
|
||||
|
||||
Testing with PLC: CPU 315F-2 PN/DP
|
||||
Found 410 program blocks
|
||||
|
||||
--- Testing Block 1/3: ISOonTCP_or_TCP_Protocol ---
|
||||
Programming Language: STL
|
||||
Available methods on block:
|
||||
- export
|
||||
- export_cross_references
|
||||
✗ ExportAsDocuments method NOT found
|
||||
Available methods containing 'export':
|
||||
- export
|
||||
- export_cross_references
|
||||
|
||||
--- Testing Block 2/3: PIDControl ---
|
||||
Compiling block...
|
||||
2025-05-22 11:18:24,970 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Compile the PLC program block PIDControl. Result:
|
||||
2025-05-22 11:18:31,184 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Warning: CPU 315F-2 PN/DP > General warnings > Inputs or outputs are used that do not exist in the configured hardware.
|
||||
2025-05-22 11:18:31,185 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Warning: CPU 315F-2 PN/DP > Compiling finished (errors: 0; warnings: 1)
|
||||
Programming Language: LAD
|
||||
Available methods on block:
|
||||
- export
|
||||
- export_cross_references
|
||||
✗ ExportAsDocuments method NOT found
|
||||
Available methods containing 'export':
|
||||
- export
|
||||
- export_cross_references
|
||||
|
||||
--- Testing Block 3/3: DETAIL_DP_DIAG ---
|
||||
Programming Language: STL
|
||||
Available methods on block:
|
||||
- export
|
||||
- export_cross_references
|
||||
✗ ExportAsDocuments method NOT found
|
||||
Available methods containing 'export':
|
||||
- export
|
||||
- export_cross_references
|
||||
|
||||
============================================================
|
||||
PRUEBA COMPLETADA
|
||||
============================================================
|
||||
|
||||
No se crearon archivos en C:/Users/migue/Downloads/Nueva carpeta (18)\SIMATIC_SD_Test
|
||||
|
||||
Closing TIA Portal...
|
||||
2025-05-22 11:18:31,209 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
|
||||
|
||||
Press Enter to exit...
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
Traceback (most recent call last):
|
||||
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\xTest.py", line 215, in <module>
|
||||
input("\nPress Enter to exit...")
|
||||
EOFError: EOF when reading a line
|
||||
|
||||
--- FIN DEL LOG ---
|
|
@ -22,5 +22,11 @@
|
|||
"short_description": "Test específico para exportación SIMATIC SD usando ExportAsDocuments()",
|
||||
"long_description": "Script de prueba experimental para validar la funcionalidad de exportación SIMATIC SD utilizando el método ExportAsDocuments() de la API de TIA Portal Openness.\n***\n**Propósito:**\n\n1. **Validación de API:** Prueba diferentes métodos de exportación SIMATIC SD\n2. **Comparación de métodos:** Evalúa ExportAsDocuments() vs Export() estándar\n3. **Debugging:** Identifica problemas y limitaciones en la exportación SD\n4. **Desarrollo:** Base para mejoras en scripts de producción\n\n**Estado:** Script experimental - usar solo para pruebas y desarrollo\n\n**Nota:** Este script es parte del proceso de desarrollo y optimización de los métodos de exportación SIMATIC SD.",
|
||||
"hidden": false
|
||||
},
|
||||
"test_simatic_sd_compatibility.py": {
|
||||
"display_name": "test_simatic_sd_compatibility",
|
||||
"short_description": "Test script to verify SIMATIC SD compatibility detection",
|
||||
"long_description": "",
|
||||
"hidden": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
"""
|
||||
Test script for path validation and sanitization functions
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""Sanitizes a filename by removing/replacing invalid characters and whitespace."""
|
||||
# Replace spaces and other problematic characters with underscores
|
||||
sanitized = re.sub(r'[<>:"/\\|?*\s]+', "_", name)
|
||||
# Remove leading/trailing underscores and dots
|
||||
sanitized = sanitized.strip("_.")
|
||||
# Ensure it's not empty
|
||||
if not sanitized:
|
||||
sanitized = "unknown"
|
||||
return sanitized
|
||||
|
||||
|
||||
def sanitize_path(path):
|
||||
"""Sanitizes a path by ensuring it doesn't contain problematic whitespace."""
|
||||
# Normalize the path and remove any trailing/leading whitespace
|
||||
normalized = os.path.normpath(path.strip())
|
||||
return normalized
|
||||
|
||||
|
||||
def validate_export_path(path):
|
||||
"""Validates that an export path is suitable for TIA Portal."""
|
||||
if not path:
|
||||
return False, "La ruta está vacía"
|
||||
|
||||
# Check for problematic characters or patterns
|
||||
if any(char in path for char in '<>"|?*'):
|
||||
return False, f"La ruta contiene caracteres no válidos: {path}"
|
||||
|
||||
# Check for excessive whitespace
|
||||
if path != path.strip():
|
||||
return False, f"La ruta contiene espacios al inicio o final: '{path}'"
|
||||
|
||||
# Check for multiple consecutive spaces
|
||||
if " " in path:
|
||||
return False, f"La ruta contiene espacios múltiples consecutivos: '{path}'"
|
||||
|
||||
# Check path length (Windows limitation)
|
||||
if len(path) > 250:
|
||||
return False, f"La ruta es demasiado larga ({len(path)} caracteres): {path}"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
# Test cases
|
||||
test_names = [
|
||||
"IO access error",
|
||||
"CreatesAnyPointer",
|
||||
"WriteMemArea_G",
|
||||
"Block with spaces",
|
||||
"Block<>with:invalid|chars",
|
||||
" Block with leading and trailing spaces ",
|
||||
"Block with multiple spaces",
|
||||
"",
|
||||
]
|
||||
|
||||
test_paths = [
|
||||
"C:\\normal\\path",
|
||||
"C:\\path with spaces\\subdir",
|
||||
" C:\\path with leading space",
|
||||
"C:\\path with trailing space ",
|
||||
"C:\\path with multiple spaces\\subdir",
|
||||
"C:\\path<with>invalid:chars|in\\subdir",
|
||||
"",
|
||||
]
|
||||
|
||||
print("=== Testing sanitize_filename ===")
|
||||
for name in test_names:
|
||||
sanitized = sanitize_filename(name)
|
||||
print(f"'{name}' -> '{sanitized}'")
|
||||
|
||||
print("\n=== Testing sanitize_path ===")
|
||||
for path in test_paths:
|
||||
sanitized = sanitize_path(path)
|
||||
print(f"'{path}' -> '{sanitized}'")
|
||||
|
||||
print("\n=== Testing validate_export_path ===")
|
||||
for path in test_paths:
|
||||
sanitized = sanitize_path(path)
|
||||
is_valid, msg = validate_export_path(sanitized)
|
||||
print(f"'{sanitized}' -> Valid: {is_valid}, Message: {msg}")
|
||||
|
||||
print("\n=== Test specific problematic block names ===")
|
||||
problematic_blocks = ["IO access error", "CreatesAnyPointer", "WriteMemArea_G"]
|
||||
base_path = "D:\\Export\\Test"
|
||||
|
||||
for block_name in problematic_blocks:
|
||||
sanitized_name = sanitize_filename(block_name)
|
||||
full_path = sanitize_path(
|
||||
os.path.join(base_path, sanitized_name, "ProgramBlocks_XML")
|
||||
)
|
||||
is_valid, msg = validate_export_path(full_path)
|
||||
print(f"Block: '{block_name}' -> '{sanitized_name}'")
|
||||
print(f" Full path: '{full_path}'")
|
||||
print(f" Valid: {is_valid}, Message: {msg}")
|
||||
print()
|
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
Test script for updated sanitization function
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""Sanitizes a filename by removing/replacing invalid characters and whitespace."""
|
||||
# Handle specific problematic cases first
|
||||
if name == "I/O access error":
|
||||
return "IO_access_error"
|
||||
elif name == "Time error interrupt":
|
||||
return "Time_error_interrupt"
|
||||
elif name.startswith("I/O_"):
|
||||
return name.replace("I/O_", "IO_").replace("/", "_")
|
||||
|
||||
# Replace spaces and other problematic characters with underscores
|
||||
sanitized = re.sub(r'[<>:"/\\|?*\s]+', "_", name)
|
||||
# Remove leading/trailing underscores and dots
|
||||
sanitized = sanitized.strip("_.")
|
||||
# Ensure it's not empty
|
||||
if not sanitized:
|
||||
sanitized = "unknown"
|
||||
return sanitized
|
||||
|
||||
|
||||
# Test the problematic block names from the log
|
||||
problematic_blocks = [
|
||||
"IO access error",
|
||||
"Time error interrupt",
|
||||
"I/O_FLT1",
|
||||
"I/O_FLT2",
|
||||
"CreatesAnyPointer",
|
||||
"WriteMemArea_G",
|
||||
"ComGetPut_G",
|
||||
"Sys_Plc_G",
|
||||
"Sys_PLC_D",
|
||||
"RACK_FLT",
|
||||
"Startup",
|
||||
"PROG_ERR",
|
||||
]
|
||||
|
||||
print("=== Testing updated sanitize_filename function ===")
|
||||
for block_name in problematic_blocks:
|
||||
sanitized = sanitize_filename(block_name)
|
||||
has_spaces = " " in block_name
|
||||
has_slash = "/" in block_name
|
||||
print(f"'{block_name}' -> '{sanitized}' [Spaces: {has_spaces}, Slash: {has_slash}]")
|
|
@ -1,70 +0,0 @@
|
|||
"""
|
||||
Test script to verify SIMATIC SD compatibility detection
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# --- TIA Scripting Import Handling ---
|
||||
if os.getenv("TIA_SCRIPTING"):
|
||||
sys.path.append(os.getenv("TIA_SCRIPTING"))
|
||||
|
||||
try:
|
||||
import siemens_tia_scripting as ts
|
||||
|
||||
print("✓ TIA Scripting import successful")
|
||||
print(
|
||||
f"Available programming languages: {[lang for lang in dir(ts.Enums.ProgrammingLanguage) if not lang.startswith('_')]}"
|
||||
)
|
||||
print(
|
||||
f"Available export formats: {[fmt for fmt in dir(ts.Enums.ExportFormats) if not fmt.startswith('_')]}"
|
||||
)
|
||||
print(
|
||||
f"Available block types: {[bt for bt in dir(ts.Enums.BlockType) if not bt.startswith('_')]}"
|
||||
)
|
||||
|
||||
# Check if SIMATIC SD is available
|
||||
try:
|
||||
simatic_sd_format = ts.Enums.ExportFormats.SimaticSD
|
||||
print(f"✓ SIMATIC SD format available: {simatic_sd_format}")
|
||||
except AttributeError:
|
||||
print("✗ SIMATIC SD format NOT available in this TIA Scripting version")
|
||||
|
||||
except ImportError as e:
|
||||
print(f"✗ Failed to import TIA Scripting: {e}")
|
||||
print(
|
||||
"This is expected if TIA Portal is not installed or TIA_SCRIPTING env var not set"
|
||||
)
|
||||
|
||||
|
||||
def analyze_simatic_sd_requirements():
|
||||
"""Analyze and display SIMATIC SD requirements"""
|
||||
print("\n=== SIMATIC SD FORMAT REQUIREMENTS ===")
|
||||
print("Based on official Siemens documentation:")
|
||||
print()
|
||||
print("✓ SUPPORTED:")
|
||||
print(" • Programming Language: LAD (Ladder) ONLY")
|
||||
print(
|
||||
" • Block Types: FB (Function Block), FC (Function), OB (Organization Block)"
|
||||
)
|
||||
print(" • TIA Portal Version: V20 or later")
|
||||
print(" • Target PLCs: S7-1200, S7-1500")
|
||||
print()
|
||||
print("✗ NOT SUPPORTED:")
|
||||
print(" • SCL (Structured Control Language)")
|
||||
print(" • STL (Statement List)")
|
||||
print(" • FBD (Function Block Diagram)")
|
||||
print(" • Graph programming")
|
||||
print(" • CFC (Continuous Function Chart)")
|
||||
print(" • Complex LAD elements (some advanced functions)")
|
||||
print()
|
||||
print("📋 COMMON CAUSES FOR XML-ONLY EXPORT:")
|
||||
print(" 1. Block programmed in SCL/STL/FBD instead of LAD")
|
||||
print(" 2. Block contains unsupported LAD elements")
|
||||
print(" 3. Block is not compiled/consistent")
|
||||
print(" 4. TIA Portal version < V20")
|
||||
print(" 5. Wrong block type (not FB/FC/OB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
analyze_simatic_sd_requirements()
|
|
@ -7,6 +7,8 @@ from tkinter import filedialog
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path # Import Path
|
||||
|
||||
script_root = os.path.dirname(
|
||||
|
@ -17,14 +19,12 @@ from backend.script_utils import load_configuration
|
|||
|
||||
# --- Configuration ---
|
||||
# Supported TIA Portal versions mapping (extension -> version)
|
||||
SUPPORTED_TIA_VERSIONS = {
|
||||
".ap18": "18.0",
|
||||
".ap19": "19.0",
|
||||
".ap20": "20.0"
|
||||
}
|
||||
SUPPORTED_TIA_VERSIONS = {".ap18": "18.0", ".ap19": "19.0", ".ap20": "20.0"}
|
||||
|
||||
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 ---
|
||||
# Check if the TIA_SCRIPTING environment variable is set
|
||||
|
@ -46,7 +46,7 @@ try:
|
|||
except ImportError:
|
||||
print("ERROR: Failed to import 'siemens_tia_scripting'.")
|
||||
print("Ensure:")
|
||||
print(f"1. TIA Portal Openness for V{TIA_PORTAL_VERSION} is installed.")
|
||||
print("1. TIA Portal Openness is installed.")
|
||||
print(
|
||||
"2. The 'siemens_tia_scripting' Python module is installed (pip install ...) or"
|
||||
)
|
||||
|
@ -64,11 +64,121 @@ except Exception as e:
|
|||
|
||||
# --- Functions ---
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""Sanitizes a filename by removing/replacing invalid characters and whitespace."""
|
||||
import re
|
||||
|
||||
# Handle specific problematic cases first
|
||||
if name == "I/O access error":
|
||||
return "IO_access_error"
|
||||
elif name == "Time error interrupt":
|
||||
return "Time_error_interrupt"
|
||||
elif name.startswith("I/O_"):
|
||||
return name.replace("I/O_", "IO_").replace("/", "_")
|
||||
|
||||
# Replace spaces and other problematic characters with underscores
|
||||
sanitized = re.sub(r'[<>:"/\\|?*\s]+', "_", name)
|
||||
# Remove leading/trailing underscores and dots
|
||||
sanitized = sanitized.strip("_.")
|
||||
# Ensure it's not empty
|
||||
if not sanitized:
|
||||
sanitized = "unknown"
|
||||
return sanitized
|
||||
|
||||
|
||||
def sanitize_path(path):
|
||||
"""Sanitizes a path by ensuring it doesn't contain problematic whitespace."""
|
||||
# Normalize the path and remove any trailing/leading whitespace
|
||||
normalized = os.path.normpath(path.strip())
|
||||
return normalized
|
||||
|
||||
|
||||
def validate_export_path(path):
|
||||
"""Validates that an export path is suitable for TIA Portal."""
|
||||
if not path:
|
||||
return False, "La ruta está vacía"
|
||||
|
||||
# Check for problematic characters or patterns
|
||||
if any(char in path for char in '<>"|?*'):
|
||||
return False, f"La ruta contiene caracteres no válidos: {path}"
|
||||
|
||||
# Check for excessive whitespace
|
||||
if path != path.strip():
|
||||
return False, f"La ruta contiene espacios al inicio o final: '{path}'"
|
||||
|
||||
# Check for multiple consecutive spaces
|
||||
if " " in path:
|
||||
return False, f"La ruta contiene espacios múltiples consecutivos: '{path}'"
|
||||
|
||||
# Check path length (Windows limitation)
|
||||
if len(path) > 250:
|
||||
return False, f"La ruta es demasiado larga ({len(path)} caracteres): {path}"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def create_temp_export_dir():
|
||||
"""Creates a temporary directory for export that doesn't contain spaces."""
|
||||
# Create a temporary directory with a safe name
|
||||
temp_base = tempfile.gettempdir()
|
||||
temp_export = os.path.join(temp_base, "TIA_Export_Temp")
|
||||
|
||||
# Ensure the temp directory exists and is clean
|
||||
if os.path.exists(temp_export):
|
||||
shutil.rmtree(temp_export)
|
||||
os.makedirs(temp_export, exist_ok=True)
|
||||
|
||||
return temp_export
|
||||
|
||||
|
||||
def copy_temp_to_final(temp_dir, final_dir):
|
||||
"""Copies files from temporary directory to final destination."""
|
||||
try:
|
||||
print(f"\nCopiando archivos exportados desde directorio temporal...")
|
||||
print(f" Origen: {temp_dir}")
|
||||
print(f" Destino: {final_dir}")
|
||||
|
||||
# Ensure final directory exists
|
||||
os.makedirs(final_dir, exist_ok=True)
|
||||
|
||||
# Copy all contents from temp to final directory
|
||||
for item in os.listdir(temp_dir):
|
||||
src_path = os.path.join(temp_dir, item)
|
||||
dst_path = os.path.join(final_dir, item)
|
||||
|
||||
if os.path.isdir(src_path):
|
||||
if os.path.exists(dst_path):
|
||||
shutil.rmtree(dst_path)
|
||||
shutil.copytree(src_path, dst_path)
|
||||
print(f" Directorio copiado: {item}")
|
||||
else:
|
||||
shutil.copy2(src_path, dst_path)
|
||||
print(f" Archivo copiado: {item}")
|
||||
|
||||
print(" Copia completada exitosamente.")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ERROR durante la copia: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def cleanup_temp_dir(temp_dir):
|
||||
"""Cleans up the temporary directory."""
|
||||
try:
|
||||
if os.path.exists(temp_dir):
|
||||
shutil.rmtree(temp_dir)
|
||||
print(f"Directorio temporal limpiado: {temp_dir}")
|
||||
except Exception as e:
|
||||
print(f"ADVERTENCIA: No se pudo limpiar el directorio temporal {temp_dir}: {e}")
|
||||
|
||||
|
||||
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]
|
||||
version_major = version.split(".")[0]
|
||||
filetypes.append((f"TIA Portal V{version_major} Projects", f"*{ext}"))
|
||||
|
||||
# Add option to show all supported files
|
||||
|
@ -77,6 +187,7 @@ def get_supported_filetypes():
|
|||
|
||||
return filetypes
|
||||
|
||||
|
||||
def detect_tia_version(project_file_path):
|
||||
"""Detects TIA Portal version based on file extension."""
|
||||
file_path = Path(project_file_path)
|
||||
|
@ -84,21 +195,25 @@ def detect_tia_version(project_file_path):
|
|||
|
||||
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})")
|
||||
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())}")
|
||||
print(
|
||||
f"ADVERTENCIA: Extensión de archivo no reconocida '{file_extension}'. Extensiones soportadas: {list(SUPPORTED_TIA_VERSIONS.keys())}"
|
||||
)
|
||||
# Default to version 18.0 for backward compatibility
|
||||
print("Usando por defecto TIA Portal V18.0")
|
||||
return "18.0"
|
||||
|
||||
|
||||
def select_project_file():
|
||||
"""Opens a dialog to select a TIA Portal project file."""
|
||||
root = tk.Tk()
|
||||
root.withdraw() # Hide the main tkinter window
|
||||
file_path = filedialog.askopenfilename(
|
||||
title="Select TIA Portal Project File",
|
||||
filetypes=get_supported_filetypes()
|
||||
title="Select TIA Portal Project File", filetypes=get_supported_filetypes()
|
||||
)
|
||||
root.destroy()
|
||||
if not file_path:
|
||||
|
@ -122,18 +237,40 @@ def select_export_directory():
|
|||
def export_plc_data(plc, export_base_dir):
|
||||
"""Exports Blocks, UDTs, and Tag Tables from a given PLC."""
|
||||
plc_name = plc.get_name()
|
||||
plc_name_sanitized = sanitize_filename(plc_name)
|
||||
print(f"\n--- Procesando PLC: {plc_name} ---")
|
||||
if plc_name != plc_name_sanitized:
|
||||
print(f" Nombre sanitizado para directorios: {plc_name_sanitized}")
|
||||
|
||||
# Define base export path for this PLC
|
||||
plc_export_dir = os.path.join(export_base_dir, plc_name)
|
||||
plc_export_dir = sanitize_path(os.path.join(export_base_dir, plc_name_sanitized))
|
||||
|
||||
# Validate PLC export directory
|
||||
is_valid, validation_msg = validate_export_path(plc_export_dir)
|
||||
if not is_valid:
|
||||
print(f"ERROR: Directorio de exportación del PLC no válido - {validation_msg}")
|
||||
return
|
||||
|
||||
os.makedirs(plc_export_dir, exist_ok=True)
|
||||
|
||||
# --- Export Program Blocks ---
|
||||
blocks_exported = 0
|
||||
blocks_skipped = 0
|
||||
print(f"\n[PLC: {plc_name}] Exportando bloques de programa...")
|
||||
xml_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_XML")
|
||||
scl_blocks_path = os.path.join(plc_export_dir, "ProgramBlocks_SCL")
|
||||
xml_blocks_path = sanitize_path(os.path.join(plc_export_dir, "ProgramBlocks_XML"))
|
||||
scl_blocks_path = sanitize_path(os.path.join(plc_export_dir, "ProgramBlocks_SCL"))
|
||||
|
||||
# Validate block export paths
|
||||
xml_valid, xml_msg = validate_export_path(xml_blocks_path)
|
||||
scl_valid, scl_msg = validate_export_path(scl_blocks_path)
|
||||
|
||||
if not xml_valid:
|
||||
print(f" ERROR: Ruta XML no válida - {xml_msg}")
|
||||
return
|
||||
if not scl_valid:
|
||||
print(f" ERROR: Ruta SCL no válida - {scl_msg}")
|
||||
return
|
||||
|
||||
os.makedirs(xml_blocks_path, exist_ok=True)
|
||||
os.makedirs(scl_blocks_path, exist_ok=True)
|
||||
print(f" Destino XML: {xml_blocks_path}")
|
||||
|
@ -157,23 +294,152 @@ def export_plc_data(plc, export_base_dir):
|
|||
continue
|
||||
|
||||
print(f" Exportando {block_name} como XML...")
|
||||
block.export(
|
||||
target_directory_path=xml_blocks_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
try:
|
||||
print(f" Destino: {xml_blocks_path}")
|
||||
|
||||
# Check if this is a system block that might need special handling
|
||||
is_system_block = any(
|
||||
keyword in block_name.lower()
|
||||
for keyword in [
|
||||
"interrupt",
|
||||
"error",
|
||||
"startup",
|
||||
"i/o",
|
||||
"rack_flt",
|
||||
"prog_err",
|
||||
"time error",
|
||||
"io access",
|
||||
"createsan",
|
||||
]
|
||||
)
|
||||
|
||||
# Try creating a sanitized filename for problematic blocks
|
||||
if is_system_block or " " in block_name or "/" in block_name:
|
||||
print(
|
||||
f" Detectado bloque con nombre problemático: '{block_name}'"
|
||||
)
|
||||
|
||||
# Create a temporary export directory with sanitized name
|
||||
sanitized_block_name = sanitize_filename(block_name)
|
||||
temp_block_dir = os.path.join(
|
||||
xml_blocks_path, sanitized_block_name
|
||||
)
|
||||
os.makedirs(temp_block_dir, exist_ok=True)
|
||||
|
||||
print(f" Usando directorio sanitizado: {temp_block_dir}")
|
||||
|
||||
block.export(
|
||||
target_directory_path=temp_block_dir,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||
keep_folder_structure=False, # Disable folder structure for problematic blocks
|
||||
)
|
||||
|
||||
# Rename files to use original block name in metadata
|
||||
for file in os.listdir(temp_block_dir):
|
||||
if file.endswith(".xml"):
|
||||
original_path = os.path.join(temp_block_dir, file)
|
||||
# Move file to main directory with original name preserved in content
|
||||
target_path = os.path.join(xml_blocks_path, file)
|
||||
if os.path.exists(target_path):
|
||||
os.remove(target_path)
|
||||
shutil.move(original_path, target_path)
|
||||
|
||||
# Remove temporary directory
|
||||
if os.path.exists(temp_block_dir):
|
||||
os.rmdir(temp_block_dir)
|
||||
|
||||
else:
|
||||
# Normal export for regular blocks
|
||||
block.export(
|
||||
target_directory_path=xml_blocks_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
|
||||
except Exception as xml_ex:
|
||||
print(
|
||||
f" ERROR en exportación XML para {block_name}: {xml_ex}"
|
||||
)
|
||||
print(f" Ruta problemática: '{xml_blocks_path}'")
|
||||
print(f" Tipo de bloque: {type(block).__name__}")
|
||||
|
||||
# Skip this block and continue with others
|
||||
blocks_skipped += 1
|
||||
continue
|
||||
|
||||
# If we get here, XML export was successful
|
||||
# Now try SCL export if applicable
|
||||
try:
|
||||
prog_language = block.get_property(name="ProgrammingLanguage")
|
||||
if prog_language == "SCL":
|
||||
print(f" Exportando {block_name} como SCL...")
|
||||
block.export(
|
||||
target_directory_path=scl_blocks_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.ExternalSource,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
try:
|
||||
print(f" Destino: {scl_blocks_path}")
|
||||
|
||||
# Use same logic for SCL export
|
||||
is_system_block = any(
|
||||
keyword in block_name.lower()
|
||||
for keyword in [
|
||||
"interrupt",
|
||||
"error",
|
||||
"startup",
|
||||
"i/o",
|
||||
"rack_flt",
|
||||
"prog_err",
|
||||
"time error",
|
||||
"io access",
|
||||
"createsan",
|
||||
]
|
||||
)
|
||||
|
||||
if (
|
||||
is_system_block
|
||||
or " " in block_name
|
||||
or "/" in block_name
|
||||
):
|
||||
sanitized_block_name = sanitize_filename(block_name)
|
||||
temp_block_dir = os.path.join(
|
||||
scl_blocks_path, sanitized_block_name
|
||||
)
|
||||
os.makedirs(temp_block_dir, exist_ok=True)
|
||||
|
||||
block.export(
|
||||
target_directory_path=temp_block_dir,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.ExternalSource,
|
||||
keep_folder_structure=False,
|
||||
)
|
||||
|
||||
# Move files to main directory
|
||||
for file in os.listdir(temp_block_dir):
|
||||
if file.endswith(".scl"):
|
||||
original_path = os.path.join(
|
||||
temp_block_dir, file
|
||||
)
|
||||
target_path = os.path.join(
|
||||
scl_blocks_path, file
|
||||
)
|
||||
if os.path.exists(target_path):
|
||||
os.remove(target_path)
|
||||
shutil.move(original_path, target_path)
|
||||
|
||||
if os.path.exists(temp_block_dir):
|
||||
os.rmdir(temp_block_dir)
|
||||
else:
|
||||
block.export(
|
||||
target_directory_path=scl_blocks_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
export_format=ts.Enums.ExportFormats.ExternalSource,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
except Exception as scl_ex:
|
||||
print(
|
||||
f" ERROR en exportación SCL para {block_name}: {scl_ex}"
|
||||
)
|
||||
print(f" Ruta problemática: '{scl_blocks_path}'")
|
||||
# Don't raise, just continue
|
||||
except Exception as prop_ex:
|
||||
print(
|
||||
f" No se pudo obtener el lenguaje de programación para {block_name}. Omitiendo SCL. Error: {prop_ex}"
|
||||
|
@ -194,7 +460,14 @@ def export_plc_data(plc, export_base_dir):
|
|||
udts_exported = 0
|
||||
udts_skipped = 0
|
||||
print(f"\n[PLC: {plc_name}] Exportando tipos de datos PLC (UDTs)...")
|
||||
udt_export_path = os.path.join(plc_export_dir, "PlcDataTypes")
|
||||
udt_export_path = sanitize_path(os.path.join(plc_export_dir, "PlcDataTypes"))
|
||||
|
||||
# Validate UDT export path
|
||||
udt_valid, udt_msg = validate_export_path(udt_export_path)
|
||||
if not udt_valid:
|
||||
print(f" ERROR: Ruta UDT no válida - {udt_msg}")
|
||||
return
|
||||
|
||||
os.makedirs(udt_export_path, exist_ok=True)
|
||||
print(f" Destino: {udt_export_path}")
|
||||
|
||||
|
@ -216,11 +489,19 @@ def export_plc_data(plc, export_base_dir):
|
|||
continue
|
||||
|
||||
print(f" Exportando {udt_name}...")
|
||||
udt.export(
|
||||
target_directory_path=udt_export_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
try:
|
||||
print(f" Destino: {udt_export_path}")
|
||||
udt.export(
|
||||
target_directory_path=udt_export_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
except Exception as udt_export_ex:
|
||||
print(
|
||||
f" ERROR en exportación UDT para {udt_name}: {udt_export_ex}"
|
||||
)
|
||||
print(f" Ruta problemática: '{udt_export_path}'")
|
||||
raise udt_export_ex
|
||||
udts_exported += 1
|
||||
except Exception as udt_ex:
|
||||
print(f" ERROR exportando UDT {udt_name}: {udt_ex}")
|
||||
|
@ -236,7 +517,14 @@ def export_plc_data(plc, export_base_dir):
|
|||
tags_exported = 0
|
||||
tags_skipped = 0
|
||||
print(f"\n[PLC: {plc_name}] Exportando tablas de variables PLC...")
|
||||
tags_export_path = os.path.join(plc_export_dir, "PlcTags")
|
||||
tags_export_path = sanitize_path(os.path.join(plc_export_dir, "PlcTags"))
|
||||
|
||||
# Validate tags export path
|
||||
tags_valid, tags_msg = validate_export_path(tags_export_path)
|
||||
if not tags_valid:
|
||||
print(f" ERROR: Ruta Tags no válida - {tags_msg}")
|
||||
return
|
||||
|
||||
os.makedirs(tags_export_path, exist_ok=True)
|
||||
print(f" Destino: {tags_export_path}")
|
||||
|
||||
|
@ -248,14 +536,24 @@ def export_plc_data(plc, export_base_dir):
|
|||
print(f" Procesando tabla de variables: {table_name}...")
|
||||
try:
|
||||
print(f" Exportando {table_name}...")
|
||||
table.export(
|
||||
target_directory_path=tags_export_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
try:
|
||||
print(f" Destino: {tags_export_path}")
|
||||
table.export(
|
||||
target_directory_path=tags_export_path,
|
||||
export_options=EXPORT_OPTIONS,
|
||||
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||
)
|
||||
except Exception as table_export_ex:
|
||||
print(
|
||||
f" ERROR en exportación tabla para {table_name}: {table_export_ex}"
|
||||
)
|
||||
print(f" Ruta problemática: '{tags_export_path}'")
|
||||
raise table_export_ex
|
||||
tags_exported += 1
|
||||
except Exception as table_ex:
|
||||
print(f" ERROR exportando tabla de variables {table_name}: {table_ex}")
|
||||
print(
|
||||
f" ERROR exportando tabla de variables {table_name}: {table_ex}"
|
||||
)
|
||||
tags_skipped += 1
|
||||
print(
|
||||
f" Resumen de exportación de tablas de variables: Exportados={tags_exported}, Omitidos/Errores={tags_skipped}"
|
||||
|
@ -279,12 +577,22 @@ if __name__ == "__main__":
|
|||
# Validate working directory
|
||||
if not working_directory or not os.path.isdir(working_directory):
|
||||
print("ERROR: Directorio de trabajo no configurado o inválido.")
|
||||
print("Por favor configure el directorio de trabajo usando la aplicación principal.")
|
||||
print(
|
||||
"Por favor configure el directorio de trabajo usando la aplicación principal."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# 1. Select Project File, Export Directory comes from config
|
||||
project_file = select_project_file()
|
||||
export_dir = working_directory # Use working directory from config
|
||||
export_dir = sanitize_path(
|
||||
working_directory
|
||||
) # Use working directory from config with sanitization
|
||||
|
||||
# Validate export directory
|
||||
is_valid, validation_msg = validate_export_path(export_dir)
|
||||
if not is_valid:
|
||||
print(f"ERROR: Directorio de exportación no válido - {validation_msg}")
|
||||
sys.exit(1)
|
||||
|
||||
# 2. Detect TIA Portal version from project file
|
||||
tia_version = detect_tia_version(project_file)
|
||||
|
@ -309,7 +617,9 @@ if __name__ == "__main__":
|
|||
print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
|
||||
project_object = portal_instance.open_project(project_file_path=project_file)
|
||||
if project_object is None:
|
||||
print("El proyecto podría estar ya abierto, intentando obtener el manejador...")
|
||||
print(
|
||||
"El proyecto podría estar ya abierto, intentando obtener el manejador..."
|
||||
)
|
||||
project_object = portal_instance.get_project()
|
||||
if project_object is None:
|
||||
raise Exception("No se pudo abrir u obtener el proyecto especificado.")
|
||||
|
@ -320,7 +630,9 @@ if __name__ == "__main__":
|
|||
if not plcs:
|
||||
print("No se encontraron dispositivos PLC en el proyecto.")
|
||||
else:
|
||||
print(f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación...")
|
||||
print(
|
||||
f"Se encontraron {len(plcs)} PLC(s). Iniciando proceso de exportación..."
|
||||
)
|
||||
|
||||
# 6. Iterate and Export Data for each PLC
|
||||
for plc_device in plcs:
|
||||
|
|
23786
data/log.txt
23786
data/log.txt
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue