feat: Update TIA Portal version support and enhance export functionality in x1.py and x4.py; remove obsolete test scripts
This commit is contained in:
parent
48e25282d6
commit
18f6cdaa4f
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"x1.py": {
|
"x1.py": {
|
||||||
"display_name": "1: Exportar Lógica desde TIA Portal v18,v19,v20 en XML",
|
"display_name": "1: Exportar Lógica desde TIA Portal v15,v16,v17,v18,v19,v20 en XML",
|
||||||
"short_description": "Exporta la lógica del PLC desde TIA Portal en archivos XML y SCL.",
|
"short_description": "Exporta la lógica del PLC desde TIA Portal (V15 a V20) en archivos XML y SCL.",
|
||||||
"long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal.\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.",
|
"long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal (V15, V16, V17, V18, V19, V20) y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal (V15 a V20).\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.",
|
||||||
"hidden": false
|
"hidden": false
|
||||||
},
|
},
|
||||||
"x2.py": {
|
"x2.py": {
|
||||||
|
@ -12,9 +12,9 @@
|
||||||
"hidden": false
|
"hidden": false
|
||||||
},
|
},
|
||||||
"x4.py": {
|
"x4.py": {
|
||||||
"display_name": "3: Exportar Referencias Cruzadas desde TIA Portal",
|
"display_name": "3: Exportar Referencias Cruzadas desde TIA Portal v17,v18,v19,v20",
|
||||||
"short_description": "Script para exportar las referencias cruzadas de un proyecto TIA Portal",
|
"short_description": "Script para exportar las referencias cruzadas de un proyecto TIA Portal (V17 a V20)",
|
||||||
"long_description": "Este script exporta las referencias cruzadas (cross-references) de un proyecto TIA Portal, proporcionando información detallada sobre las interconexiones entre variables, bloques y componentes del sistema.\n***\n**Funcionalidad:**\n\n1. **Referencias cruzadas:** Extrae información sobre dónde se utilizan las variables y bloques\n2. **Análisis de dependencias:** Identifica relaciones entre componentes del proyecto\n3. **Documentación:** Genera reportes útiles para mantenimiento y debugging\n4. **Formato estructurado:** Exporta en formato legible para análisis posterior\n\n**Casos de uso:**\n- Documentación de proyecto\n- Análisis de impacto de cambios\n- Debugging y mantenimiento\n- Auditorías de código",
|
"long_description": "Este script exporta las referencias cruzadas (cross-references) de un proyecto TIA Portal (V17, V18, V19, V20), proporcionando información detallada sobre las interconexiones entre variables, bloques y componentes del sistema.\n***\n**Funcionalidad:**\n\n1. **Referencias cruzadas:** Extrae información sobre dónde se utilizan las variables y bloques\n2. **Análisis de dependencias:** Identifica relaciones entre componentes del proyecto\n3. **Documentación:** Genera reportes útiles para mantenimiento y debugging\n4. **Formato estructurado:** Exporta en formato legible para análisis posterior\n\n**Casos de uso:**\n- Documentación de proyecto\n- Análisis de impacto de cambios\n- Debugging y mantenimiento\n- Auditorías de código\n\n**NOTA:** La exportación de referencias cruzadas requiere TIA Portal V17.0 o superior. Las versiones V15 y V16 no soportan esta funcionalidad en la API de Openness.",
|
||||||
"hidden": false
|
"hidden": false
|
||||||
},
|
},
|
||||||
"xTest.py": {
|
"xTest.py": {
|
||||||
|
@ -28,5 +28,23 @@
|
||||||
"short_description": "Test script to verify SIMATIC SD compatibility detection",
|
"short_description": "Test script to verify SIMATIC SD compatibility detection",
|
||||||
"long_description": "",
|
"long_description": "",
|
||||||
"hidden": false
|
"hidden": false
|
||||||
|
},
|
||||||
|
"test_path_validation.py": {
|
||||||
|
"display_name": "test_path_validation",
|
||||||
|
"short_description": "Test script for path validation and sanitization functions",
|
||||||
|
"long_description": "",
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
"test_sanitization.py": {
|
||||||
|
"display_name": "test_sanitization",
|
||||||
|
"short_description": "Test script for updated sanitization function",
|
||||||
|
"long_description": "",
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
"test_block_validation.py": {
|
||||||
|
"display_name": "test_block_validation",
|
||||||
|
"short_description": "Test script para verificar la función is_block_exportable",
|
||||||
|
"long_description": "",
|
||||||
|
"hidden": false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
"""
|
||||||
|
Test script para verificar la función is_block_exportable
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Mock class para simular un bloque de TIA Portal
|
||||||
|
class MockBlock:
|
||||||
|
def __init__(self, programming_language):
|
||||||
|
self.programming_language = programming_language
|
||||||
|
|
||||||
|
def get_property(self, name):
|
||||||
|
if name == "ProgrammingLanguage":
|
||||||
|
return self.programming_language
|
||||||
|
raise Exception(f"Property {name} not found")
|
||||||
|
|
||||||
|
|
||||||
|
# Importar la función desde x1.py
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append(os.path.dirname(__file__))
|
||||||
|
from x1 import is_block_exportable
|
||||||
|
|
||||||
|
# Testear diferentes tipos de bloques
|
||||||
|
test_cases = [
|
||||||
|
("LAD", True, "LAD blocks should be exportable"),
|
||||||
|
("FBD", True, "FBD blocks should be exportable"),
|
||||||
|
("STL", True, "STL blocks should be exportable"),
|
||||||
|
("SCL", True, "SCL blocks should be exportable"),
|
||||||
|
("ProDiag_OB", False, "ProDiag_OB blocks should not be exportable"),
|
||||||
|
("ProDiag", False, "ProDiag blocks should not be exportable"),
|
||||||
|
("GRAPH", False, "GRAPH blocks should not be exportable"),
|
||||||
|
]
|
||||||
|
|
||||||
|
print("=== Test de validación de bloques ===")
|
||||||
|
for prog_lang, expected_exportable, description in test_cases:
|
||||||
|
block = MockBlock(prog_lang)
|
||||||
|
is_exportable, detected_lang, reason = is_block_exportable(block)
|
||||||
|
|
||||||
|
status = "✓ PASS" if is_exportable == expected_exportable else "✗ FAIL"
|
||||||
|
print(f"{status} - {description}")
|
||||||
|
print(f" Lenguaje: {detected_lang}, Exportable: {is_exportable}, Razón: {reason}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
# Test con bloque que genera excepción
|
||||||
|
class MockBlockError:
|
||||||
|
def get_property(self, name):
|
||||||
|
raise Exception("Cannot access property")
|
||||||
|
|
||||||
|
|
||||||
|
print("=== Test de manejo de errores ===")
|
||||||
|
error_block = MockBlockError()
|
||||||
|
is_exportable, detected_lang, reason = is_block_exportable(error_block)
|
||||||
|
print(f"Bloque con error - Exportable: {is_exportable}, Lenguaje: {detected_lang}")
|
||||||
|
print(f"Razón: {reason}")
|
|
@ -1,103 +0,0 @@
|
||||||
"""
|
|
||||||
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()
|
|
|
@ -1,49 +0,0 @@
|
||||||
"""
|
|
||||||
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}]")
|
|
|
@ -19,7 +19,14 @@ from backend.script_utils import load_configuration
|
||||||
|
|
||||||
# --- Configuration ---
|
# --- Configuration ---
|
||||||
# Supported TIA Portal versions mapping (extension -> version)
|
# Supported TIA Portal versions mapping (extension -> version)
|
||||||
SUPPORTED_TIA_VERSIONS = {".ap18": "18.0", ".ap19": "19.0", ".ap20": "20.0"}
|
SUPPORTED_TIA_VERSIONS = {
|
||||||
|
".ap15": "15.0",
|
||||||
|
".ap16": "16.0",
|
||||||
|
".ap17": "17.0",
|
||||||
|
".ap18": "18.0",
|
||||||
|
".ap19": "19.0",
|
||||||
|
".ap20": "20.0",
|
||||||
|
}
|
||||||
|
|
||||||
EXPORT_OPTIONS = None # Use default export options
|
EXPORT_OPTIONS = None # Use default export options
|
||||||
KEEP_FOLDER_STRUCTURE = (
|
KEEP_FOLDER_STRUCTURE = (
|
||||||
|
@ -65,6 +72,36 @@ except Exception as e:
|
||||||
# --- Functions ---
|
# --- Functions ---
|
||||||
|
|
||||||
|
|
||||||
|
def is_block_exportable(block):
|
||||||
|
"""
|
||||||
|
Checks if a block can be exported based on its programming language.
|
||||||
|
Returns (is_exportable, programming_language, reason)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
prog_language = block.get_property(name="ProgrammingLanguage")
|
||||||
|
|
||||||
|
# List of known unsupported programming languages
|
||||||
|
unsupported_languages = [
|
||||||
|
"ProDiag_OB", # ProDiag Organization Blocks
|
||||||
|
"ProDiag", # ProDiag Function Blocks
|
||||||
|
"GRAPH", # GRAPH (Sequential Control)
|
||||||
|
]
|
||||||
|
|
||||||
|
if prog_language in unsupported_languages:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
prog_language,
|
||||||
|
f"Programming language '{prog_language}' is not supported for export",
|
||||||
|
)
|
||||||
|
|
||||||
|
return True, prog_language, "OK"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# If we can't determine the programming language, assume it might be exportable
|
||||||
|
# but warn about it
|
||||||
|
return True, "Unknown", f"Could not determine programming language: {e}"
|
||||||
|
|
||||||
|
|
||||||
def sanitize_filename(name):
|
def sanitize_filename(name):
|
||||||
"""Sanitizes a filename by removing/replacing invalid characters and whitespace."""
|
"""Sanitizes a filename by removing/replacing invalid characters and whitespace."""
|
||||||
import re
|
import re
|
||||||
|
@ -188,6 +225,17 @@ def get_supported_filetypes():
|
||||||
return filetypes
|
return filetypes
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_project_path(project_path):
|
||||||
|
"""Normalizes a TIA Portal project path to avoid path-related issues."""
|
||||||
|
# Convert forward slashes to backslashes for Windows
|
||||||
|
normalized = project_path.replace("/", "\\")
|
||||||
|
# Use os.path.normpath to clean up the path
|
||||||
|
normalized = os.path.normpath(normalized)
|
||||||
|
# Ensure it's an absolute path
|
||||||
|
normalized = os.path.abspath(normalized)
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
def detect_tia_version(project_file_path):
|
def detect_tia_version(project_file_path):
|
||||||
"""Detects TIA Portal version based on file extension."""
|
"""Detects TIA Portal version based on file extension."""
|
||||||
file_path = Path(project_file_path)
|
file_path = Path(project_file_path)
|
||||||
|
@ -203,9 +251,9 @@ def detect_tia_version(project_file_path):
|
||||||
print(
|
print(
|
||||||
f"ADVERTENCIA: Extensión de archivo no reconocida '{file_extension}'. Extensiones soportadas: {list(SUPPORTED_TIA_VERSIONS.keys())}"
|
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
|
# Default to version 15.0 for backward compatibility
|
||||||
print("Usando por defecto TIA Portal V18.0")
|
print("Usando por defecto TIA Portal V15.0")
|
||||||
return "18.0"
|
return "15.0"
|
||||||
|
|
||||||
|
|
||||||
def select_project_file():
|
def select_project_file():
|
||||||
|
@ -282,6 +330,18 @@ def export_plc_data(plc, export_base_dir):
|
||||||
for block in program_blocks:
|
for block in program_blocks:
|
||||||
block_name = block.get_name()
|
block_name = block.get_name()
|
||||||
print(f" Procesando bloque: {block_name}...")
|
print(f" Procesando bloque: {block_name}...")
|
||||||
|
|
||||||
|
# Check if block is exportable
|
||||||
|
is_exportable, prog_language, reason = is_block_exportable(block)
|
||||||
|
|
||||||
|
if not is_exportable:
|
||||||
|
print(f" ADVERTENCIA: {reason}. Omitiendo bloque {block_name}.")
|
||||||
|
blocks_skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if prog_language == "Unknown":
|
||||||
|
print(f" ADVERTENCIA: {reason}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not block.is_consistent():
|
if not block.is_consistent():
|
||||||
print(f" Compilando bloque {block_name}...")
|
print(f" Compilando bloque {block_name}...")
|
||||||
|
@ -364,6 +424,15 @@ def export_plc_data(plc, export_base_dir):
|
||||||
)
|
)
|
||||||
print(f" Ruta problemática: '{xml_blocks_path}'")
|
print(f" Ruta problemática: '{xml_blocks_path}'")
|
||||||
print(f" Tipo de bloque: {type(block).__name__}")
|
print(f" Tipo de bloque: {type(block).__name__}")
|
||||||
|
print(f" Lenguaje de programación: {prog_language}")
|
||||||
|
|
||||||
|
# Check if it's a ProDiag related error
|
||||||
|
if "ProDiag" in str(
|
||||||
|
xml_ex
|
||||||
|
) or "not supported during import and export" in str(xml_ex):
|
||||||
|
print(
|
||||||
|
f" Este bloque usa un lenguaje no soportado para exportación. Omitiendo."
|
||||||
|
)
|
||||||
|
|
||||||
# Skip this block and continue with others
|
# Skip this block and continue with others
|
||||||
blocks_skipped += 1
|
blocks_skipped += 1
|
||||||
|
@ -372,7 +441,6 @@ def export_plc_data(plc, export_base_dir):
|
||||||
# If we get here, XML export was successful
|
# If we get here, XML export was successful
|
||||||
# Now try SCL export if applicable
|
# Now try SCL export if applicable
|
||||||
try:
|
try:
|
||||||
prog_language = block.get_property(name="ProgrammingLanguage")
|
|
||||||
if prog_language == "SCL":
|
if prog_language == "SCL":
|
||||||
print(f" Exportando {block_name} como SCL...")
|
print(f" Exportando {block_name} como SCL...")
|
||||||
try:
|
try:
|
||||||
|
@ -440,9 +508,13 @@ def export_plc_data(plc, export_base_dir):
|
||||||
)
|
)
|
||||||
print(f" Ruta problemática: '{scl_blocks_path}'")
|
print(f" Ruta problemática: '{scl_blocks_path}'")
|
||||||
# Don't raise, just continue
|
# Don't raise, just continue
|
||||||
except Exception as prop_ex:
|
else:
|
||||||
print(
|
print(
|
||||||
f" No se pudo obtener el lenguaje de programación para {block_name}. Omitiendo SCL. Error: {prop_ex}"
|
f" Bloque {block_name} no es SCL (lenguaje: {prog_language}). Omitiendo exportación SCL."
|
||||||
|
)
|
||||||
|
except Exception as scl_check_ex:
|
||||||
|
print(
|
||||||
|
f" Error verificando lenguaje para exportación SCL: {scl_check_ex}"
|
||||||
)
|
)
|
||||||
|
|
||||||
blocks_exported += 1
|
blocks_exported += 1
|
||||||
|
@ -584,6 +656,8 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# 1. Select Project File, Export Directory comes from config
|
# 1. Select Project File, Export Directory comes from config
|
||||||
project_file = select_project_file()
|
project_file = select_project_file()
|
||||||
|
# Normalize the project file path to avoid TIA Portal path issues
|
||||||
|
project_file = normalize_project_path(project_file)
|
||||||
export_dir = sanitize_path(
|
export_dir = sanitize_path(
|
||||||
working_directory
|
working_directory
|
||||||
) # Use working directory from config with sanitization
|
) # Use working directory from config with sanitization
|
||||||
|
@ -615,7 +689,17 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# 4. Open Project
|
# 4. Open Project
|
||||||
print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
|
print(f"Abriendo proyecto: {os.path.basename(project_file)}...")
|
||||||
project_object = portal_instance.open_project(project_file_path=project_file)
|
print(f"Ruta completa del proyecto: {project_file}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
project_object = portal_instance.open_project(
|
||||||
|
project_file_path=project_file
|
||||||
|
)
|
||||||
|
except Exception as open_ex:
|
||||||
|
print(f"Error al abrir el proyecto: {open_ex}")
|
||||||
|
print("Intentando obtener proyecto ya abierto...")
|
||||||
|
project_object = None
|
||||||
|
|
||||||
if project_object is None:
|
if project_object is None:
|
||||||
print(
|
print(
|
||||||
"El proyecto podría estar ya abierto, intentando obtener el manejador..."
|
"El proyecto podría estar ya abierto, intentando obtener el manejador..."
|
||||||
|
@ -640,8 +724,21 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
print("\nProceso de exportación completado.")
|
print("\nProceso de exportación completado.")
|
||||||
|
|
||||||
except ts.TiaException as tia_ex:
|
except ValueError as val_ex:
|
||||||
print(f"\nError de TIA Portal Openness: {tia_ex}")
|
# Handle TIA Portal Openness exceptions (they come as ValueError)
|
||||||
|
if "OpennessAccessException" in str(val_ex):
|
||||||
|
print(f"\nError de TIA Portal Openness: {val_ex}")
|
||||||
|
print("Posibles causas:")
|
||||||
|
print("- El proyecto puede estar corrupto o en un formato incompatible")
|
||||||
|
print(
|
||||||
|
"- El proyecto puede requerir actualización a una versión más reciente"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"- Verificar que la ruta del proyecto no contenga caracteres especiales"
|
||||||
|
)
|
||||||
|
print("- Asegurarse de que TIA Portal esté instalado correctamente")
|
||||||
|
else:
|
||||||
|
print(f"\nError de valor: {val_ex}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
|
print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
|
||||||
|
|
|
@ -19,7 +19,18 @@ from backend.script_utils import load_configuration
|
||||||
|
|
||||||
# --- Configuration ---
|
# --- Configuration ---
|
||||||
# Supported TIA Portal versions mapping (extension -> version)
|
# Supported TIA Portal versions mapping (extension -> version)
|
||||||
SUPPORTED_TIA_VERSIONS = {".ap18": "18.0", ".ap19": "19.0", ".ap20": "20.0"}
|
SUPPORTED_TIA_VERSIONS = {
|
||||||
|
".ap15": "15.0",
|
||||||
|
".ap16": "16.0",
|
||||||
|
".ap17": "17.0",
|
||||||
|
".ap18": "18.0",
|
||||||
|
".ap19": "19.0",
|
||||||
|
".ap20": "20.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cross-references export support was introduced in TIA Portal V17+
|
||||||
|
# Earlier versions don't support the export_cross_references() method
|
||||||
|
CROSS_REFERENCES_SUPPORTED_VERSIONS = ["17.0", "18.0", "19.0", "20.0"]
|
||||||
|
|
||||||
# Filter for cross-references. Based on documentation:
|
# Filter for cross-references. Based on documentation:
|
||||||
# 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects'
|
# 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects'
|
||||||
|
@ -83,6 +94,34 @@ except Exception as e:
|
||||||
# --- Functions ---
|
# --- Functions ---
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_project_path(project_path):
|
||||||
|
"""Normalizes a project path to ensure it's compatible with TIA Portal."""
|
||||||
|
if not project_path:
|
||||||
|
return project_path
|
||||||
|
|
||||||
|
# Convert to Path object for easier manipulation
|
||||||
|
path_obj = Path(project_path)
|
||||||
|
|
||||||
|
# Resolve to absolute path and normalize
|
||||||
|
try:
|
||||||
|
normalized = path_obj.resolve()
|
||||||
|
# Convert back to string with Windows-style separators
|
||||||
|
normalized_str = str(normalized).replace("/", "\\")
|
||||||
|
|
||||||
|
print(f" Ruta original: {project_path}")
|
||||||
|
print(f" Ruta normalizada: {normalized_str}")
|
||||||
|
|
||||||
|
return normalized_str
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ADVERTENCIA: Error al normalizar ruta: {e}")
|
||||||
|
return str(project_path)
|
||||||
|
|
||||||
|
|
||||||
|
def is_cross_references_supported(tia_version):
|
||||||
|
"""Check if cross-references export is supported in the given TIA Portal version."""
|
||||||
|
return tia_version in CROSS_REFERENCES_SUPPORTED_VERSIONS
|
||||||
|
|
||||||
|
|
||||||
def get_supported_filetypes():
|
def get_supported_filetypes():
|
||||||
"""Returns the supported file types for TIA Portal projects."""
|
"""Returns the supported file types for TIA Portal projects."""
|
||||||
filetypes = []
|
filetypes = []
|
||||||
|
@ -112,9 +151,9 @@ def detect_tia_version(project_file_path):
|
||||||
print(
|
print(
|
||||||
f"ADVERTENCIA: Extensión de archivo no reconocida '{file_extension}'. Extensiones soportadas: {list(SUPPORTED_TIA_VERSIONS.keys())}"
|
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
|
# Default to version 15.0 for backward compatibility
|
||||||
print("Usando por defecto TIA Portal V18.0")
|
print("Usando por defecto TIA Portal V15.0")
|
||||||
return "18.0"
|
return "15.0"
|
||||||
|
|
||||||
|
|
||||||
def select_project_file():
|
def select_project_file():
|
||||||
|
@ -249,19 +288,31 @@ def export_plc_cross_references(
|
||||||
print(f" Exportación completada en {elapsed_time:.2f} segundos")
|
print(f" Exportación completada en {elapsed_time:.2f} segundos")
|
||||||
blocks_cr_exported += 1
|
blocks_cr_exported += 1
|
||||||
exported_blocks.add(norm_block)
|
exported_blocks.add(norm_block)
|
||||||
except RuntimeError as block_ex:
|
except Exception as block_ex:
|
||||||
|
error_msg = str(block_ex)
|
||||||
|
if (
|
||||||
|
"NotSupportedException" in error_msg
|
||||||
|
and "not supported in this API version" in error_msg
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f" ERROR: Método export_cross_references() no soportado en esta versión de TIA Portal para el bloque {block_name}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" Esta funcionalidad requiere TIA Portal V17.0 o superior."
|
||||||
|
)
|
||||||
|
elif "RuntimeError" in str(type(block_ex)):
|
||||||
print(
|
print(
|
||||||
f" ERROR TIA al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
f" ERROR TIA al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
||||||
)
|
)
|
||||||
problematic_blocks.add(norm_block)
|
else:
|
||||||
blocks_cr_skipped += 1
|
|
||||||
except Exception as block_ex:
|
|
||||||
print(
|
print(
|
||||||
f" ERROR GENERAL al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
f" ERROR GENERAL al exportar referencias cruzadas para el bloque {block_name}: {block_ex}"
|
||||||
)
|
)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
problematic_blocks.add(norm_block) # Always mark as problematic
|
|
||||||
|
problematic_blocks.add(norm_block)
|
||||||
blocks_cr_skipped += 1
|
blocks_cr_skipped += 1
|
||||||
|
|
||||||
if _is_disposed_exception(block_ex):
|
if _is_disposed_exception(block_ex):
|
||||||
# Escalamos para que el script pueda re-abrir el Portal y omitir el bloque
|
# Escalamos para que el script pueda re-abrir el Portal y omitir el bloque
|
||||||
raise PortalDisposedException(block_ex, failed_block=block_name)
|
raise PortalDisposedException(block_ex, failed_block=block_name)
|
||||||
|
@ -496,13 +547,39 @@ def open_portal_and_project(tia_version: str, project_file_path: str):
|
||||||
print("Conectado a TIA Portal.")
|
print("Conectado a TIA Portal.")
|
||||||
print(f"ID del proceso del Portal: {portal.get_process_id()}")
|
print(f"ID del proceso del Portal: {portal.get_process_id()}")
|
||||||
|
|
||||||
project_obj = portal.open_project(project_file_path=str(project_file_path))
|
# Normalize the project path
|
||||||
|
normalized_path = normalize_project_path(project_file_path)
|
||||||
|
|
||||||
|
print(f"Abriendo proyecto: {Path(normalized_path).name}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
project_obj = portal.open_project(project_file_path=normalized_path)
|
||||||
if project_obj is None:
|
if project_obj is None:
|
||||||
|
print(
|
||||||
|
"El proyecto podría estar ya abierto, intentando obtener el manejador..."
|
||||||
|
)
|
||||||
project_obj = portal.get_project()
|
project_obj = portal.get_project()
|
||||||
if project_obj is None:
|
if project_obj is None:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"No se pudo abrir u obtener el proyecto especificado tras la reapertura."
|
"No se pudo abrir u obtener el proyecto especificado tras la reapertura."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print("Proyecto abierto exitosamente.")
|
||||||
|
return portal, project_obj
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = str(e)
|
||||||
|
print(f"ERROR al abrir proyecto: {error_msg}")
|
||||||
|
|
||||||
|
if "path" in error_msg.lower() and "cannot be" in error_msg.lower():
|
||||||
|
print(
|
||||||
|
f" Problema con formato de ruta. Ruta utilizada: '{normalized_path}'"
|
||||||
|
)
|
||||||
|
print(f" Ruta original: '{project_file_path}'")
|
||||||
|
|
||||||
|
# Re-raise with more context
|
||||||
|
raise Exception(f"Error al abrir proyecto TIA Portal: {error_msg}")
|
||||||
|
|
||||||
return portal, project_obj
|
return portal, project_obj
|
||||||
|
|
||||||
|
|
||||||
|
@ -535,6 +612,23 @@ if __name__ == "__main__":
|
||||||
# 2. Detect TIA Portal version from project file
|
# 2. Detect TIA Portal version from project file
|
||||||
tia_version = detect_tia_version(project_file)
|
tia_version = detect_tia_version(project_file)
|
||||||
|
|
||||||
|
# Check if cross-references export is supported in this version
|
||||||
|
if not is_cross_references_supported(tia_version):
|
||||||
|
print(
|
||||||
|
f"\nADVERTENCIA: La exportación de referencias cruzadas no está soportada en TIA Portal V{tia_version}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Las referencias cruzadas están soportadas desde TIA Portal V17.0 en adelante."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"Versiones soportadas para referencias cruzadas:",
|
||||||
|
", ".join([f"V{v}" for v in CROSS_REFERENCES_SUPPORTED_VERSIONS]),
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"\nEl script se cerrará. Por favor use TIA Portal V17.0 o superior para exportar referencias cruzadas."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# 3. Define Export Directory using working_directory and subfolder
|
# 3. Define Export Directory using working_directory and subfolder
|
||||||
export_base_dir = Path(working_directory)
|
export_base_dir = Path(working_directory)
|
||||||
try:
|
try:
|
||||||
|
@ -640,7 +734,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
print("\nProceso de exportación de referencias cruzadas completado.")
|
print("\nProceso de exportación de referencias cruzadas completado.")
|
||||||
|
|
||||||
except RuntimeError as tia_ex:
|
except Exception as tia_ex:
|
||||||
print(f"\nError de TIA Portal Openness: {tia_ex}")
|
print(f"\nError de TIA Portal Openness: {tia_ex}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
|
18
data/log.txt
18
data/log.txt
|
@ -1,5 +1,13 @@
|
||||||
[16:23:41] Iniciando ejecución de x1.py en D:\Trabajo\VM\45 - HENKEL - VM Auto Changeover\ExportTia...
|
[16:48:57] Iniciando ejecución de x4.py en D:\Trabajo\VM\45 - HENKEL - VM Auto Changeover\ExportTia...
|
||||||
[16:23:41] --- Exportador de datos TIA Portal (Bloques, UDTs, Variables) ---
|
[16:48:57] --- Exportador de Referencias Cruzadas de TIA Portal ---
|
||||||
[16:24:03] No se seleccionó ningún archivo de proyecto. Saliendo.
|
[16:48:57] Configuración:
|
||||||
[16:24:04] Ejecución de x1.py finalizada (success). Duración: 0:00:22.833498.
|
[16:48:57] - Tiempo esperado por bloque: 120 segundos (para logging)
|
||||||
[16:24:04] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\.log\log_x1.txt
|
[16:48:57] - Máximo intentos de reapertura: 5
|
||||||
|
[16:48:57] - Filtro de referencias cruzadas: 1
|
||||||
|
[16:49:01] Versión de TIA Portal detectada: 16.0 (de la extensión .ap16)
|
||||||
|
[16:49:01] ADVERTENCIA: La exportación de referencias cruzadas no está soportada en TIA Portal V16.0
|
||||||
|
[16:49:01] Las referencias cruzadas están soportadas desde TIA Portal V17.0 en adelante.
|
||||||
|
[16:49:01] Versiones soportadas para referencias cruzadas: V17.0, V18.0, V19.0, V20.0
|
||||||
|
[16:49:01] El script se cerrará. Por favor use TIA Portal V17.0 o superior para exportar referencias cruzadas.
|
||||||
|
[16:49:02] Ejecución de x4.py finalizada (error). Duración: 0:00:05.217693.
|
||||||
|
[16:49:02] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\.log\log_x4.txt
|
||||||
|
|
Loading…
Reference in New Issue