""" export_simatic_sd_tia20 : Script para exportar bloques de PLC desde TIA Portal 20 en formato SIMATIC SD. Basado en la nueva funcionalidad de exportación SIMATIC SD disponible en TIA Portal V20. """ import tkinter as tk from tkinter import filedialog import os import sys import traceback 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 --- TIA_PORTAL_VERSION = "20.0" # Target TIA Portal version for SIMATIC SD support 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 verify_export_format(export_path, expected_format="SIMATIC_SD"): """ Verifies what format was actually exported by examining file extensions. Returns (actual_format, file_extensions, file_count) """ if not os.path.exists(export_path): return "NO_FILES", [], 0 files = [ f for f in os.listdir(export_path) if os.path.isfile(os.path.join(export_path, f)) ] if not files: return "EMPTY_FOLDER", [], 0 extensions = [os.path.splitext(f)[1].lower() for f in files] extension_counts = {} for ext in extensions: extension_counts[ext] = extension_counts.get(ext, 0) + 1 # Determine actual format based on file extensions if all(ext == ".xml" for ext in extensions): actual_format = "XML_ONLY" elif any(ext in [".sd", ".simatic"] for ext in extensions): actual_format = "SIMATIC_SD" elif ".xml" in extensions and len(set(extensions)) > 1: actual_format = "MIXED" else: actual_format = "UNKNOWN" return actual_format, extension_counts, len(files) def check_simatic_sd_block_compatibility(block): """ Checks if a block is compatible with SIMATIC SD export format. Returns (is_compatible, reason) """ try: # Check if block is consistent if not block.is_consistent(): return False, "Block is not consistent/compiled" # Check programming language - SIMATIC SD only supports LAD try: prog_lang = block.get_programming_language() if prog_lang != ts.Enums.ProgrammingLanguage.LAD: return ( False, f"Language {prog_lang} not supported (SIMATIC SD requires LAD)", ) except Exception: return False, "Could not determine programming language" # Check block type - SIMATIC SD typically supports FB, FC, OB try: block_type = block.get_block_type() supported_types = [ ts.Enums.BlockType.FB, # Function Block ts.Enums.BlockType.FC, # Function ts.Enums.BlockType.OB, # Organization Block ] if block_type not in supported_types: return False, f"Block type {block_type} may not be supported" except Exception: # If we can't determine type, assume it might work pass return True, "Block appears compatible with SIMATIC SD" except Exception as e: return False, f"Error checking compatibility: {e}" def verify_export_format(export_path, expected_format="SimaticSD"): """ Verifies if the exported files are actually in the expected format. For SIMATIC SD, looks for specific keywords like RUNG, END_RUNG, wire# Returns (is_correct_format, file_count, sample_files, format_details) """ if not os.path.exists(export_path): return False, 0, [], "Directory does not exist" files = [ f for f in os.listdir(export_path) if os.path.isfile(os.path.join(export_path, f)) ] if not files: return False, 0, [], "No files found" # Check first few files for format sample_files = files[:3] format_details = [] for file_name in sample_files: file_path = os.path.join(export_path, file_name) try: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: content = f.read(2000) # Read first 2KB file_info = {"file": file_name, "size": len(content)} if expected_format == "SimaticSD": # SIMATIC SD specific keywords and structure sd_keywords = ["RUNG", "END_RUNG", "wire#", "NETWORK", "TITLE", "LAD"] xml_indicators = [" 0 file_info["is_simatic_sd"] = ( len(found_sd_keywords) > 0 and not file_info["is_xml"] ) file_info["first_100_chars"] = ( content[:100].replace("\n", " ").replace("\r", "") ) else: # XML format file_info["is_xml"] = ( content.strip().startswith(" 0: print(f" 🔍 UNKNOWN format - needs manual inspection") else: print(f" {description}: Folder not found") print(f"\n🔍 SIMATIC SD DIAGNOSIS FOR {plc_name}:") if simatic_sd_working: print( f" ✅ SIMATIC SD is working: {total_sd_files} files in true SD format" ) else: print(f" ❌ SIMATIC SD NOT working: All 'SD' exports are actually XML") print(f" 📊 Total XML fallbacks: {total_xml_fallbacks}") if total_xml_fallbacks > 0: print(f"\n 💡 POSSIBLE CAUSES:") print(f" • TIA Portal version doesn't fully support SIMATIC SD") print(f" • TIA Scripting version incompatible with SIMATIC SD") print(f" • Project blocks contain unsupported LAD elements") print( f" • SIMATIC SD enum exists but falls back to XML silently" ) except ts.TiaException as tia_ex: print(f"\nTIA Portal Openness Error: {tia_ex}") traceback.print_exc() except FileNotFoundError: print(f"\nERROR: Project file not found at {project_file}") except Exception as e: print(f"\nAn unexpected error occurred: {e}") traceback.print_exc() finally: # 6. Cleanup if portal_instance: try: print("\nClosing TIA Portal...") portal_instance.close_portal() print("TIA Portal closed.") except Exception as close_ex: print(f"Error during TIA Portal cleanup: {close_ex}") print("\nScript finished.")