Add test script for verifying SIMATIC SD compatibility detection

- Implemented a new test script `test_simatic_sd_compatibility.py` to check the availability of SIMATIC SD format in TIA Scripting.
- Included detailed analysis of SIMATIC SD requirements based on official Siemens documentation.
- Provided feedback on supported and unsupported programming languages and block types.
- Added error handling for TIA Scripting import and environment variable checks.
This commit is contained in:
Miguel 2025-08-23 13:53:13 +02:00
parent affab8a646
commit 586e3cc9b3
4 changed files with 3340 additions and 54 deletions

View File

@ -5,22 +5,22 @@
"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 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.",
"hidden": false "hidden": false
}, },
"x4.py": {
"display_name": "2: Exportar Referencias Cruzadas desde Tia Portal",
"short_description": "Script para exportar las referencias cruzadas",
"long_description": "",
"hidden": false
},
"x2.py": { "x2.py": {
"display_name": "2: Exportar Lógica desde TIA Portal V20 en SIMATIC SD format", "display_name": "2: Exportar Lógica desde TIA Portal V20 en SIMATIC SD format",
"short_description": "export_logic_from_tia_v20_simatic_sd : Script para exportar el software de un PLC desde TIA Portal V20", "short_description": "Script para exportar el software de un PLC desde TIA Portal V20 en el nuevo formato SIMATIC SD",
"long_description": "", "long_description": "Script especializado para exportar bloques de PLC desde TIA Portal V20 utilizando el nuevo formato SIMATIC SD (Structured Data). Este formato proporciona una representación más estructurada y moderna de los datos del PLC.\n***\n**Características principales:**\n\n1. **Formato SIMATIC SD:** Utiliza el nuevo formato de exportación disponible en TIA Portal V20+\n2. **Detección automática:** Verifica compatibilidad con el formato SIMATIC SD antes de la exportación\n3. **Comparación dual:** Exporta tanto en formato SIMATIC SD como en XML tradicional para comparación\n4. **Estructura organizada:** Crea carpetas separadas para bloques, UDTs y tablas de variables\n5. **Timestamp único:** Evita conflictos con exports anteriores usando timestamp en nombres de carpetas\n\n**Estructura de exportación:**\n- `01_ProgramBlocks_SD/` - Bloques en formato SIMATIC SD\n- `02_ProgramBlocks_XML_Compare/` - Bloques en XML para comparación\n- `03_PlcDataTypes_SD/` - UDTs en formato SIMATIC SD\n- `04_PlcDataTypes_XML_Compare/` - UDTs en XML para comparación\n- `05_PlcTags_SD/` - Tablas de variables en formato SIMATIC SD\n- `06_PlcTags_XML_Compare/` - Tablas de variables en XML para comparación\n\n**Compatibilidad:** Requiere TIA Portal V20 o superior. Puede ejecutarse después de x1.py sin conflictos.",
"hidden": false
},
"x4.py": {
"display_name": "3: Exportar Referencias Cruzadas desde TIA Portal",
"short_description": "Script para exportar las referencias cruzadas de un proyecto TIA Portal",
"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",
"hidden": false "hidden": false
}, },
"xTest.py": { "xTest.py": {
"display_name": "xTest", "display_name": "xTest - Pruebas SIMATIC SD ExportAsDocuments",
"short_description": "Test específico para exportación SIMATIC SD usando ExportAsDocuments()", "short_description": "Test específico para exportación SIMATIC SD usando ExportAsDocuments()",
"long_description": "", "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 "hidden": false
} }
} }

View File

@ -0,0 +1,70 @@
"""
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()

View File

@ -61,6 +61,154 @@ except Exception as e:
# --- Functions --- # --- 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 = ["<?xml", "<Document", "<SW.Blocks"]
found_sd_keywords = [kw for kw in sd_keywords if kw in content]
found_xml_indicators = [xi for xi in xml_indicators if xi in content]
file_info["sd_keywords"] = found_sd_keywords
file_info["xml_indicators"] = found_xml_indicators
file_info["is_xml"] = len(found_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("<?xml") or "<Document" in content
)
file_info["first_100_chars"] = (
content[:100].replace("\n", " ").replace("\r", "")
)
format_details.append(file_info)
except Exception as e:
format_details.append({"file": file_name, "error": str(e)})
# Determine overall result for SIMATIC SD
if expected_format == "SimaticSD":
is_correct_format = any(
f.get("is_simatic_sd", False) for f in format_details if "error" not in f
)
else:
is_correct_format = any(
f.get("is_xml", False) for f in format_details if "error" not in f
)
return is_correct_format, len(files), sample_files, format_details
def select_project_file(): def select_project_file():
"""Opens a dialog to select a TIA Portal project file.""" """Opens a dialog to select a TIA Portal project file."""
root = tk.Tk() root = tk.Tk()
@ -98,11 +246,35 @@ def check_simatic_sd_support():
try: try:
# Check if SimaticSD is available in ExportFormats enum # Check if SimaticSD is available in ExportFormats enum
simatic_sd_format = ts.Enums.ExportFormats.SimaticSD simatic_sd_format = ts.Enums.ExportFormats.SimaticSD
print(f"✓ SIMATIC SD format supported (enum value: {simatic_sd_format})") print(f"✓ SIMATIC SD format enum found (value: {simatic_sd_format})")
# Try to get more information about available formats
try:
all_formats = [
attr for attr in dir(ts.Enums.ExportFormats) if not attr.startswith("_")
]
print(f" Available export formats: {all_formats}")
except Exception:
pass
return True return True
except AttributeError: except AttributeError:
print("✗ ERROR: SIMATIC SD format not available in this TIA Scripting version.") print("✗ ERROR: SIMATIC SD format not available in this TIA Scripting version.")
print("Please ensure you are using TIA Portal V20 or later with compatible TIA Scripting.") print(
"Please ensure you are using TIA Portal V20 or later with compatible TIA Scripting."
)
return False
def check_tia_portal_version():
"""Check TIA Portal version and compatibility."""
print("\n=== TIA PORTAL VERSION CHECK ===")
try:
# This will be filled when we connect to TIA Portal
print("TIA Portal version check will be performed after connection...")
return True
except Exception as e:
print(f"Could not check TIA Portal version: {e}")
return False return False
@ -113,18 +285,22 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
# Define base export path for this PLC with timestamp to avoid conflicts # Define base export path for this PLC with timestamp to avoid conflicts
import datetime import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
plc_export_dir = os.path.join(export_base_dir, f"{plc_name}_SimaticSD_{timestamp}") plc_export_dir = os.path.join(export_base_dir, f"{plc_name}_SimaticSD_{timestamp}")
os.makedirs(plc_export_dir, exist_ok=True) os.makedirs(plc_export_dir, exist_ok=True)
# --- Export Program Blocks in SIMATIC SD Format --- # --- Export Program Blocks in SIMATIC SD Format ---
blocks_exported = 0 blocks_exported_sd = 0
blocks_exported_xml = 0
blocks_skipped = 0 blocks_skipped = 0
blocks_not_lad = 0
print(f"\n[PLC: {plc_name}] Exporting Program Blocks (SIMATIC SD)...") print(f"\n[PLC: {plc_name}] Exporting Program Blocks (SIMATIC SD)...")
print(" NOTE: SIMATIC SD format only supports LAD (Ladder) programming language!")
sd_blocks_path = os.path.join(plc_export_dir, "01_ProgramBlocks_SD") sd_blocks_path = os.path.join(plc_export_dir, "01_ProgramBlocks_SD")
os.makedirs(sd_blocks_path, exist_ok=True) os.makedirs(sd_blocks_path, exist_ok=True)
print(f" SIMATIC SD Target: {sd_blocks_path}") print(f" SIMATIC SD Target: {sd_blocks_path}")
# Also create a separate SimaticML export for comparison # Also create a separate SimaticML export for comparison
xml_blocks_path = os.path.join(plc_export_dir, "02_ProgramBlocks_XML_Compare") xml_blocks_path = os.path.join(plc_export_dir, "02_ProgramBlocks_XML_Compare")
os.makedirs(xml_blocks_path, exist_ok=True) os.makedirs(xml_blocks_path, exist_ok=True)
@ -146,17 +322,86 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
blocks_skipped += 1 blocks_skipped += 1
continue continue
print(f" Exporting {block_name} as SIMATIC SD...") # Check programming language - CRITICAL for SIMATIC SD
is_compatible, compatibility_reason = (
check_simatic_sd_block_compatibility(block)
)
print(f" Compatibility check: {compatibility_reason}")
if not is_compatible:
print(f" Block {block_name} not compatible with SIMATIC SD.")
print(f" Exporting XML only: {compatibility_reason}")
blocks_not_lad += 1
# Export only in XML for incompatible 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,
)
blocks_exported_xml += 1
print(
f" ✓ Exported {block_name} in XML (incompatible with SIMATIC SD)"
)
continue
# Try SIMATIC SD export for LAD blocks
print(f" Exporting LAD block {block_name} as SIMATIC SD...")
try: try:
block.export( block.export(
target_directory_path=sd_blocks_path, target_directory_path=sd_blocks_path,
export_options=EXPORT_OPTIONS, export_options=EXPORT_OPTIONS,
export_format=ts.Enums.ExportFormats.SimaticSD, # New SIMATIC SD format export_format=ts.Enums.ExportFormats.SimaticSD, # SIMATIC SD format
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) )
blocks_exported += 1
print(f" ✓ Successfully exported {block_name} in SIMATIC SD") # Verify if the export was actually in SIMATIC SD format
print(f" Verifying SIMATIC SD format for {block_name}...")
is_sd, file_count, sample_files, format_details = (
verify_export_format(sd_blocks_path, "SimaticSD")
)
if is_sd:
blocks_exported_sd += 1
print(
f" ✓ Successfully exported {block_name} in REAL SIMATIC SD format"
)
# Show sample of SD content
for detail in format_details:
if detail.get("is_simatic_sd") and detail.get(
"sd_keywords"
):
print(
f" 🎯 SD Keywords found: {', '.join(detail['sd_keywords'])}"
)
break
else:
print(
f" ❌ FAILED: Export claimed SD but files are actually XML!"
)
print(f" 📋 Format analysis:")
for detail in format_details:
if "error" not in detail:
print(f" File: {detail['file']}")
print(
f" SIMATIC SD: {detail.get('is_simatic_sd', False)}"
)
print(
f" XML format: {detail.get('is_xml', False)}"
)
if detail.get("sd_keywords"):
print(
f" SD keywords: {detail['sd_keywords']}"
)
if detail.get("xml_indicators"):
print(
f" XML indicators: {detail['xml_indicators']}"
)
print(
f" Content start: {detail.get('first_100_chars', '')[:50]}..."
)
# Also export same block in XML for comparison # Also export same block in XML for comparison
print(f" Exporting {block_name} as XML for comparison...") print(f" Exporting {block_name} as XML for comparison...")
block.export( block.export(
@ -165,31 +410,45 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
export_format=ts.Enums.ExportFormats.SimaticML, # Traditional XML format export_format=ts.Enums.ExportFormats.SimaticML, # Traditional XML format
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) )
blocks_exported_xml += 1
print(f" + Also exported {block_name} in XML for comparison") print(f" + Also exported {block_name} in XML for comparison")
except Exception as export_ex: except Exception as export_ex:
print(f" ERROR during export: {export_ex}") print(f" ERROR during SIMATIC SD export: {export_ex}")
print(
f" This is likely because SIMATIC SD has specific requirements:"
)
print(f" - Block must be in LAD (Ladder) format")
print(
f" - Block must be compatible with SIMATIC SD specification"
)
# Try to export only in XML if SD fails # Try to export only in XML if SD fails
try: try:
print(f" Attempting fallback XML export for {block_name}...") print(
f" Attempting fallback XML export for {block_name}..."
)
block.export( block.export(
target_directory_path=xml_blocks_path, target_directory_path=xml_blocks_path,
export_options=EXPORT_OPTIONS, export_options=EXPORT_OPTIONS,
export_format=ts.Enums.ExportFormats.SimaticML, export_format=ts.Enums.ExportFormats.SimaticML,
keep_folder_structure=KEEP_FOLDER_STRUCTURE, keep_folder_structure=KEEP_FOLDER_STRUCTURE,
) )
print(f" ✓ Fallback XML export successful for {block_name}") print(
blocks_exported += 1 f" ✓ Fallback XML export successful for {block_name}"
)
blocks_exported_xml += 1
except Exception as fallback_ex: except Exception as fallback_ex:
print(f" ERROR: Both SD and XML export failed: {fallback_ex}") print(
f" ERROR: Both SD and XML export failed: {fallback_ex}"
)
blocks_skipped += 1 blocks_skipped += 1
except Exception as block_ex: except Exception as block_ex:
print(f" ERROR exporting block {block_name}: {block_ex}") print(f" ERROR exporting block {block_name}: {block_ex}")
blocks_skipped += 1 blocks_skipped += 1
print( print(
f" Program Blocks Export Summary: Exported={blocks_exported}, Skipped/Errors={blocks_skipped}" f" Program Blocks Export Summary: SIMATIC SD={blocks_exported_sd}, XML={blocks_exported_xml}, Non-LAD={blocks_not_lad}, Skipped/Errors={blocks_skipped}"
) )
except Exception as e: except Exception as e:
print(f" ERROR processing Program Blocks: {e}") print(f" ERROR processing Program Blocks: {e}")
@ -243,11 +502,11 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
except Exception as udt_export_ex: except Exception as udt_export_ex:
print(f" ERROR during UDT export: {udt_export_ex}") print(f" ERROR during UDT export: {udt_export_ex}")
udts_skipped += 1 udts_skipped += 1
except Exception as udt_ex: except Exception as udt_ex:
print(f" ERROR exporting UDT {udt_name}: {udt_ex}") print(f" ERROR exporting UDT {udt_name}: {udt_ex}")
udts_skipped += 1 udts_skipped += 1
print( print(
f" UDT Export Summary: Exported={udts_exported}, Skipped/Errors={udts_skipped}" f" UDT Export Summary: Exported={udts_exported}, Skipped/Errors={udts_skipped}"
) )
@ -293,11 +552,11 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
except Exception as tag_export_ex: except Exception as tag_export_ex:
print(f" ERROR during Tag Table export: {tag_export_ex}") print(f" ERROR during Tag Table export: {tag_export_ex}")
tags_skipped += 1 tags_skipped += 1
except Exception as table_ex: except Exception as table_ex:
print(f" ERROR exporting Tag Table {table_name}: {table_ex}") print(f" ERROR exporting Tag Table {table_name}: {table_ex}")
tags_skipped += 1 tags_skipped += 1
print( print(
f" Tag Table Export Summary: Exported={tags_exported}, Skipped/Errors={tags_skipped}" f" Tag Table Export Summary: Exported={tags_exported}, Skipped/Errors={tags_skipped}"
) )
@ -311,16 +570,18 @@ def export_plc_data_simatic_sd(plc, export_base_dir):
def export_additional_formats(plc, export_base_dir): def export_additional_formats(plc, export_base_dir):
"""Optional: Export in traditional formats alongside SIMATIC SD for comparison.""" """Optional: Export in traditional formats alongside SIMATIC SD for comparison."""
plc_name = plc.get_name() plc_name = plc.get_name()
print(f"\n[Optional] Exporting traditional formats for comparison - PLC: {plc_name}") print(
f"\n[Optional] Exporting traditional formats for comparison - PLC: {plc_name}"
)
# Create comparison directory # Create comparison directory
comparison_dir = os.path.join(export_base_dir, plc_name, "Comparison_Formats") comparison_dir = os.path.join(export_base_dir, plc_name, "Comparison_Formats")
os.makedirs(comparison_dir, exist_ok=True) os.makedirs(comparison_dir, exist_ok=True)
# Export a few blocks in SimaticML for comparison # Export a few blocks in SimaticML for comparison
xml_comparison_path = os.path.join(comparison_dir, "SimaticML_Sample") xml_comparison_path = os.path.join(comparison_dir, "SimaticML_Sample")
os.makedirs(xml_comparison_path, exist_ok=True) os.makedirs(xml_comparison_path, exist_ok=True)
try: try:
program_blocks = plc.get_program_blocks() program_blocks = plc.get_program_blocks()
# Export first 3 blocks in SimaticML for comparison # Export first 3 blocks in SimaticML for comparison
@ -347,7 +608,7 @@ if __name__ == "__main__":
print("--- TIA Portal 20 SIMATIC SD Exporter ---") print("--- TIA Portal 20 SIMATIC SD Exporter ---")
print("Exporting Blocks, UDTs, and Tags in SIMATIC SD Format") print("Exporting Blocks, UDTs, and Tags in SIMATIC SD Format")
# Check SIMATIC SD support first # Check SIMATIC SD support first
if not check_simatic_sd_support(): if not check_simatic_sd_support():
sys.exit(1) sys.exit(1)
@ -378,6 +639,24 @@ if __name__ == "__main__":
print("Connected to TIA Portal V20.") print("Connected to TIA Portal V20.")
print(f"Portal Process ID: {portal_instance.get_process_id()}") print(f"Portal Process ID: {portal_instance.get_process_id()}")
# Get TIA Portal version information
try:
portal_version = portal_instance.get_version()
print(f"TIA Portal Version: {portal_version}")
# Check if this version really supports SIMATIC SD
version_parts = portal_version.split(".")
major_version = int(version_parts[0]) if version_parts else 0
if major_version < 20:
print(
f"⚠️ WARNING: TIA Portal V{major_version} may not fully support SIMATIC SD (requires V20+)"
)
else:
print(f"✓ TIA Portal V{major_version} should support SIMATIC SD")
except Exception as ver_ex:
print(f"Could not get TIA Portal version: {ver_ex}")
# 3. Open Project # 3. Open Project
print(f"Opening project: {os.path.basename(project_file)}...") print(f"Opening project: {os.path.basename(project_file)}...")
project_object = portal_instance.open_project(project_file_path=project_file) project_object = portal_instance.open_project(project_file_path=project_file)
@ -397,29 +676,41 @@ if __name__ == "__main__":
# 5. Iterate and Export Data for each PLC in SIMATIC SD format # 5. Iterate and Export Data for each PLC in SIMATIC SD format
import datetime # Add this import for timestamp import datetime # Add this import for timestamp
for plc_device in plcs: for plc_device in plcs:
export_plc_data_simatic_sd( export_plc_data_simatic_sd(plc=plc_device, export_base_dir=export_dir)
plc=plc_device, export_base_dir=export_dir
)
print("\n🎉 SIMATIC SD Export process completed successfully!") print("\n🎉 SIMATIC SD Export process completed successfully!")
print("\nExported files structure:") print("\nExported files structure:")
print("├── [PLC_Name]_SimaticSD_[timestamp]/") print("├── [PLC_Name]_SimaticSD_[timestamp]/")
print("│ ├── 01_ProgramBlocks_SD/ # SIMATIC SD format") print(
"│ ├── 01_ProgramBlocks_SD/ # SIMATIC SD format (LAD blocks only)"
)
print("│ ├── 02_ProgramBlocks_XML_Compare/ # Traditional XML for comparison") print("│ ├── 02_ProgramBlocks_XML_Compare/ # Traditional XML for comparison")
print("│ ├── 03_PlcDataTypes_SD/") print("│ ├── 03_PlcDataTypes_SD/")
print("│ ├── 04_PlcDataTypes_XML_Compare/") print("│ ├── 04_PlcDataTypes_XML_Compare/")
print("│ ├── 05_PlcTags_SD/") print("│ ├── 05_PlcTags_SD/")
print("│ └── 06_PlcTags_XML_Compare/") print("│ └── 06_PlcTags_XML_Compare/")
print("\nNow you can compare the differences between SIMATIC SD and traditional XML formats!") print("\n📋 IMPORTANT SIMATIC SD LIMITATIONS:")
print(" • SIMATIC SD format ONLY supports LAD (Ladder) programming language")
print(" • SCL, STL, FBD blocks are exported as XML only")
print(" • Only FB, FC, OB block types are typically supported")
print(" • Complex LAD elements may still fall back to XML")
print(
"\nNow you can compare the differences between SIMATIC SD and traditional XML formats!"
)
# Add file analysis # Add file analysis
print("\n=== FILE ANALYSIS ===") print("\n=== FILE FORMAT ANALYSIS ===")
for plc_device in plcs: for plc_device in plcs:
plc_name = plc_device.get_name() plc_name = plc_device.get_name()
import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
plc_export_dir = os.path.join(export_dir, f"{plc_name}_SimaticSD_{timestamp}") plc_export_dir = os.path.join(
export_dir, f"{plc_name}_SimaticSD_{timestamp}"
)
print(f"\nAnalyzing exported files for PLC: {plc_name}") print(f"\nAnalyzing exported files for PLC: {plc_name}")
folders_to_check = [ folders_to_check = [
("01_ProgramBlocks_SD", "SIMATIC SD Blocks"), ("01_ProgramBlocks_SD", "SIMATIC SD Blocks"),
@ -427,20 +718,56 @@ if __name__ == "__main__":
("03_PlcDataTypes_SD", "SIMATIC SD UDTs"), ("03_PlcDataTypes_SD", "SIMATIC SD UDTs"),
("04_PlcDataTypes_XML_Compare", "XML UDTs"), ("04_PlcDataTypes_XML_Compare", "XML UDTs"),
("05_PlcTags_SD", "SIMATIC SD Tags"), ("05_PlcTags_SD", "SIMATIC SD Tags"),
("06_PlcTags_XML_Compare", "XML Tags") ("06_PlcTags_XML_Compare", "XML Tags"),
] ]
simatic_sd_working = False
total_sd_files = 0
total_xml_fallbacks = 0
for folder_name, description in folders_to_check: for folder_name, description in folders_to_check:
folder_path = os.path.join(plc_export_dir, folder_name) folder_path = os.path.join(plc_export_dir, folder_name)
if os.path.exists(folder_path): if os.path.exists(folder_path):
files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))] actual_format, extensions, file_count = verify_export_format(
print(f" {description}: {len(files)} files") folder_path
if files: )
extensions = set(os.path.splitext(f)[1].lower() for f in files) print(f" {description}: {file_count} files")
print(f" File extensions: {', '.join(extensions) if extensions else 'No extensions'}") print(f" Format detected: {actual_format}")
print(f" File extensions: {extensions}")
if "SD" in folder_name: # This should be SIMATIC SD folder
if actual_format == "XML_ONLY":
print(
f" ⚠️ WARNING: Expected SIMATIC SD but got XML only!"
)
total_xml_fallbacks += file_count
elif actual_format == "SIMATIC_SD":
simatic_sd_working = True
total_sd_files += file_count
elif actual_format == "UNKNOWN" and file_count > 0:
print(f" 🔍 UNKNOWN format - needs manual inspection")
else: else:
print(f" {description}: Folder not found") 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: except ts.TiaException as tia_ex:
print(f"\nTIA Portal Openness Error: {tia_ex}") print(f"\nTIA Portal Openness Error: {tia_ex}")
traceback.print_exc() traceback.print_exc()
@ -459,4 +786,4 @@ if __name__ == "__main__":
except Exception as close_ex: except Exception as close_ex:
print(f"Error during TIA Portal cleanup: {close_ex}") print(f"Error during TIA Portal cleanup: {close_ex}")
print("\nScript finished.") print("\nScript finished.")

File diff suppressed because it is too large Load Diff