ParamManagerScripts/backend/script_groups/ObtainIOFromProjectTia/x1.py

350 lines
13 KiB
Python

"""
export_logic_from_tia : Script para exportar el software de un PLC desde TIA Portal en archivos XML y SCL.
"""
import tkinter as tk
from tkinter import filedialog
import os
import sys
import traceback
from pathlib import Path # Import Path
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
# --- Configuration ---
# Supported TIA Portal versions mapping (extension -> version)
SUPPORTED_TIA_VERSIONS = {
".ap18": "18.0",
".ap19": "19.0",
".ap20": "20.0"
}
EXPORT_OPTIONS = None # Use default export options
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
if os.getenv("TIA_SCRIPTING"):
sys.path.append(os.getenv("TIA_SCRIPTING"))
else:
# Optional: Define a fallback path if the environment variable isn't set
# fallback_path = "C:\\path\\to\\your\\TIA_Scripting_binaries"
# if os.path.exists(fallback_path):
# sys.path.append(fallback_path)
pass # Allow import to fail if not found
try:
import siemens_tia_scripting as ts
EXPORT_OPTIONS = (
ts.Enums.ExportOptions.WithDefaults
) # Set default options now that 'ts' is imported
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(
"2. The 'siemens_tia_scripting' Python module is installed (pip install ...) or"
)
print(
" the path to its binaries is set in the 'TIA_SCRIPTING' environment variable."
)
print(
"3. You are using a compatible Python version (e.g., 3.12.X as per documentation)."
)
sys.exit(1)
except Exception as e:
print(f"An unexpected error occurred during import: {e}")
traceback.print_exc()
sys.exit(1)
# --- Functions ---
def get_supported_filetypes():
"""Returns the supported file types for TIA Portal projects."""
filetypes = []
for ext, version in SUPPORTED_TIA_VERSIONS.items():
version_major = version.split('.')[0]
filetypes.append((f"TIA Portal V{version_major} Projects", f"*{ext}"))
# Add option to show all supported files
all_extensions = " ".join([f"*{ext}" for ext in SUPPORTED_TIA_VERSIONS.keys()])
filetypes.insert(0, ("All TIA Portal Projects", all_extensions))
return filetypes
def detect_tia_version(project_file_path):
"""Detects TIA Portal version based on file extension."""
file_path = Path(project_file_path)
file_extension = file_path.suffix.lower()
if file_extension in SUPPORTED_TIA_VERSIONS:
detected_version = SUPPORTED_TIA_VERSIONS[file_extension]
print(f"Versión de TIA Portal detectada: {detected_version} (de la extensión {file_extension})")
return detected_version
else:
print(f"ADVERTENCIA: Extensión de archivo no reconocida '{file_extension}'. Extensiones soportadas: {list(SUPPORTED_TIA_VERSIONS.keys())}")
# Default to version 18.0 for backward compatibility
print("Usando por defecto TIA Portal V18.0")
return "18.0"
def select_project_file():
"""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()
)
root.destroy()
if not file_path:
print("No se seleccionó ningún archivo de proyecto. Saliendo.")
sys.exit(0)
return file_path
def select_export_directory():
"""Opens a dialog to select the export directory."""
root = tk.Tk()
root.withdraw() # Hide the main tkinter window
dir_path = filedialog.askdirectory(title="Select Export Directory")
root.destroy()
if not dir_path:
print("No export directory selected. Exiting.")
sys.exit(0)
return dir_path
def export_plc_data(plc, export_base_dir):
"""Exports Blocks, UDTs, and Tag Tables from a given PLC."""
plc_name = plc.get_name()
print(f"\n--- Procesando PLC: {plc_name} ---")
# Define base export path for this PLC
plc_export_dir = os.path.join(export_base_dir, plc_name)
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")
os.makedirs(xml_blocks_path, exist_ok=True)
os.makedirs(scl_blocks_path, exist_ok=True)
print(f" Destino XML: {xml_blocks_path}")
print(f" Destino SCL: {scl_blocks_path}")
try:
program_blocks = plc.get_program_blocks()
print(f" Se encontraron {len(program_blocks)} bloques de programa.")
for block in program_blocks:
block_name = block.get_name()
print(f" Procesando bloque: {block_name}...")
try:
if not block.is_consistent():
print(f" Compilando bloque {block_name}...")
block.compile()
if not block.is_consistent():
print(
f" ADVERTENCIA: Bloque {block_name} inconsistente después de compilar. Omitiendo."
)
blocks_skipped += 1
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:
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,
)
except Exception as prop_ex:
print(
f" No se pudo obtener el lenguaje de programación para {block_name}. Omitiendo SCL. Error: {prop_ex}"
)
blocks_exported += 1
except Exception as block_ex:
print(f" ERROR exportando bloque {block_name}: {block_ex}")
blocks_skipped += 1
print(
f" Resumen de exportación de bloques: Exportados={blocks_exported}, Omitidos/Errores={blocks_skipped}"
)
except Exception as e:
print(f" ERROR procesando bloques de programa: {e}")
traceback.print_exc()
# --- Export PLC Data Types (UDTs) ---
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")
os.makedirs(udt_export_path, exist_ok=True)
print(f" Destino: {udt_export_path}")
try:
udts = plc.get_user_data_types()
print(f" Se encontraron {len(udts)} UDTs.")
for udt in udts:
udt_name = udt.get_name()
print(f" Procesando UDT: {udt_name}...")
try:
if not udt.is_consistent():
print(f" Compilando UDT {udt_name}...")
udt.compile()
if not udt.is_consistent():
print(
f" ADVERTENCIA: UDT {udt_name} inconsistente después de compilar. Omitiendo."
)
udts_skipped += 1
continue
print(f" Exportando {udt_name}...")
udt.export(
target_directory_path=udt_export_path,
export_options=EXPORT_OPTIONS,
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
)
udts_exported += 1
except Exception as udt_ex:
print(f" ERROR exportando UDT {udt_name}: {udt_ex}")
udts_skipped += 1
print(
f" Resumen de exportación de UDTs: Exportados={udts_exported}, Omitidos/Errores={udts_skipped}"
)
except Exception as e:
print(f" ERROR procesando UDTs: {e}")
traceback.print_exc()
# --- Export PLC Tag Tables ---
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")
os.makedirs(tags_export_path, exist_ok=True)
print(f" Destino: {tags_export_path}")
try:
tag_tables = plc.get_plc_tag_tables()
print(f" Se encontraron {len(tag_tables)} tablas de variables.")
for table in tag_tables:
table_name = table.get_name()
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,
)
tags_exported += 1
except Exception as 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}"
)
except Exception as e:
print(f" ERROR procesando tablas de variables: {e}")
traceback.print_exc()
print(f"\n--- Finalizado el procesamiento del PLC: {plc_name} ---")
# --- Main Script ---
if __name__ == "__main__":
configs = load_configuration()
working_directory = configs.get("working_directory")
print("--- Exportador de datos TIA Portal (Bloques, UDTs, Variables) ---")
# 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.")
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
# 2. Detect TIA Portal version from project file
tia_version = detect_tia_version(project_file)
print(f"\nProyecto seleccionado: {project_file}")
print(f"Usando directorio de exportación (Directorio de trabajo): {export_dir}")
portal_instance = None
project_object = None
try:
# 3. Connect to TIA Portal with detected version
print(f"\nConectando a TIA Portal V{tia_version}...")
portal_instance = ts.open_portal(
version=tia_version,
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
)
print("Conectado a TIA Portal.")
print(f"ID del proceso del Portal: {portal_instance.get_process_id()}")
# 4. Open Project
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...")
project_object = portal_instance.get_project()
if project_object is None:
raise Exception("No se pudo abrir u obtener el proyecto especificado.")
print("Proyecto abierto exitosamente.")
# 5. Get PLCs
plcs = project_object.get_plcs()
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...")
# 6. Iterate and Export Data for each PLC
for plc_device in plcs:
export_plc_data(plc=plc_device, export_base_dir=export_dir)
print("\nProceso de exportación completado.")
except ts.TiaException as tia_ex:
print(f"\nError de TIA Portal Openness: {tia_ex}")
traceback.print_exc()
except FileNotFoundError:
print(f"\nERROR: Archivo de proyecto no encontrado en {project_file}")
except Exception as e:
print(f"\nOcurrió un error inesperado: {e}")
traceback.print_exc()
finally:
# 7. Cleanup
if portal_instance:
try:
print("\nCerrando TIA Portal...")
portal_instance.close_portal()
print("TIA Portal cerrado.")
except Exception as close_ex:
print(f"Error durante la limpieza de TIA Portal: {close_ex}")
print("\nScript finalizado.")