Simatic_XML_Parser_to_SCL/json_to_scl.py

259 lines
12 KiB
Python

import json
import os
# --- Funciones Procesadoras por Tipo de Instrucción ---
# Cada función recibe el diccionario de la instrucción del JSON.
# Por ahora, solo imprimen información.
def process_contact(instruction):
"""Procesa una instrucción 'Contact'."""
print(f" [Contact] UID: {instruction['instruction_uid']}")
operand = instruction['inputs'].get('operand', {})
print(f" - Checks: {operand.get('scope', '?')} UID: {operand.get('uid', '?')}") # Adaptar si 'unknown_access' se resuelve
in_source = instruction['inputs'].get('in', {})
if in_source.get('type') == 'powerrail':
print(" - Input: Power Rail")
elif in_source.get('type') == 'connection':
print(f" - Input: From instruction {in_source.get('source_instruction_uid', '?')} (Pin: {in_source.get('source_pin', '?')})")
else:
print(f" - Input: {in_source}")
def process_coil(instruction):
"""Procesa una instrucción 'Coil'."""
print(f" [Coil] UID: {instruction['instruction_uid']}")
operand = instruction['inputs'].get('operand', {})
print(f" - Assigns to: {operand.get('scope', '?')} UID: {operand.get('uid', '?')}")
in_source = instruction['inputs'].get('in', {})
if in_source.get('type') == 'connection':
print(f" - Condition from: instruction {in_source.get('source_instruction_uid', '?')} (Pin: {in_source.get('source_pin', '?')})")
else:
print(f" - Condition: {in_source}")
def process_add(instruction):
"""Procesa una instrucción 'Add'."""
print(f" [Add] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
print(f" - Input 1: {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2: {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail (Direct)") # Si Add pudiera conectarse directo
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en: # Si 'en' no está presente o no es conexión/powerrail (poco común en Add)
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)") # Asumir si no hay pin 'en'
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
def process_move(instruction):
"""Procesa una instrucción 'Move'."""
print(f" [Move] UID: {instruction['instruction_uid']}")
in_val = instruction['inputs'].get('in', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out1', []) # Asumiendo pin 'out1' para Move
print(f" - Input Value: {in_val.get('scope', '?')} UID: {in_val.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
def process_eq(instruction):
"""Procesa una instrucción 'Eq' (Equal)."""
print(f" [Compare EQ] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
pre = instruction['inputs'].get('pre', {}) # Condición previa (usualmente PowerRail o conexión)
print(f" - Input 1: {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2: {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if pre.get('type') == 'powerrail':
print(" - Pre-condition: Power Rail")
elif pre.get('type') == 'connection':
print(f" - Pre-condition: instruction {pre.get('source_instruction_uid', '?')} (Pin: {pre.get('source_pin', '?')})")
else:
print(f" - Pre-condition: {pre}")
# La salida 'out' de Eq usualmente va a otra instrucción (Contact, Coil, Enable pin)
# Lo veremos cuando procesemos la instrucción destino
def process_mod(instruction):
"""Procesa una instrucción 'Mod' (Modulo)."""
print(f" [Modulo] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
eno_outputs = instruction['outputs'].get('eno', []) # Mod también puede tener ENO
print(f" - Input 1 (Dividend): {in1.get('scope', '?')} UID: {in1.get('uid', '?')}")
print(f" - Input 2 (Divisor): {in2.get('scope', '?')} UID: {in2.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output (Remainder) to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
# ENO normalmente se conecta a pines 'en' o 'pre' de la siguiente instrucción
def process_convert(instruction):
"""Procesa una instrucción 'Convert'."""
print(f" [Convert] UID: {instruction['instruction_uid']}")
in_val = instruction['inputs'].get('in', {})
en = instruction['inputs'].get('en', {})
outputs = instruction['outputs'].get('out', [])
# Podríamos extraer los tipos de datos de TemplateValue si estuvieran en el JSON
# template_vals = instruction.get('template_values', {})
print(f" - Input Value: {in_val.get('scope', '?')} UID: {in_val.get('uid', '?')}")
if en.get('type') == 'powerrail':
print(" - Enabled by: Power Rail")
elif en.get('type') == 'connection':
print(f" - Enabled by: instruction {en.get('source_instruction_uid', '?')} (Pin: {en.get('source_pin', '?')})")
elif en:
print(f" - Enabled by: {en}")
else:
print(" - Enabled by: Power Rail (Implícito, sin EN)")
for output in outputs:
print(f" - Output to: {output.get('scope', '?')} UID: {output.get('uid', '?')}")
# print(f" (Expected DestType: {template_vals.get('DestType', '?')})")
def process_or(instruction):
"""Procesa una instrucción 'O' (OR)."""
# Las instrucciones 'O' en LAD suelen representar la unión de ramas paralelas.
# Este parser simple solo muestra las entradas directas. Reconstruir la lógica OR completa requeriría más análisis.
print(f" [OR Logic] UID: {instruction['instruction_uid']}")
in1 = instruction['inputs'].get('in1', {})
in2 = instruction['inputs'].get('in2', {})
# Podría haber in3, in4... si Cardinality > 2
if in1.get('type') == 'connection':
print(f" - Input 1 from: instruction {in1.get('source_instruction_uid', '?')} (Pin: {in1.get('source_pin', '?')})")
else:
print(f" - Input 1: {in1}")
if in2.get('type') == 'connection':
print(f" - Input 2 from: instruction {in2.get('source_instruction_uid', '?')} (Pin: {in2.get('source_pin', '?')})")
else:
print(f" - Input 2: {in2}")
# La salida 'out' de O usualmente va a otra instrucción (Contact, Coil, Enable pin)
def process_pbox(instruction):
"""Procesa una instrucción 'PBox'."""
# PBox puede ser muchas cosas (Rising Edge, Falling Edge, Set, Reset, etc.)
# Necesitaríamos más información o convenciones para saber qué hace exactamente.
print(f" [PBox - Special?] UID: {instruction['instruction_uid']}")
inputs = instruction.get('inputs', {})
outputs = instruction.get('outputs', {})
for pin, source in inputs.items():
if source.get('type') == 'connection':
print(f" - Input Pin '{pin}' from: instruction {source.get('source_instruction_uid', '?')} (Pin: {source.get('source_pin', '?')})")
elif source.get('type') == 'powerrail':
print(f" - Input Pin '{pin}': Power Rail")
else:
print(f" - Input Pin '{pin}': {source.get('scope', '?')} UID: {source.get('uid', '?')}")
# La salida de PBox la veremos en el destino
def process_unknown(instruction):
"""Procesa una instrucción de tipo desconocido."""
print(f" [Unknown Type: {instruction.get('type', 'N/A')}] UID: {instruction['instruction_uid']}")
print(f" - Inputs: {instruction.get('inputs')}")
print(f" - Outputs: {instruction.get('outputs')}")
# --- Mapeo de Tipos a Funciones ---
instruction_handlers = {
"Contact": process_contact,
"Coil": process_coil,
"Add": process_add,
"Move": process_move,
"Eq": process_eq,
"Mod": process_mod,
"Convert": process_convert,
"O": process_or, # 'O' representa un OR lógico en FlgNet
"PBox": process_pbox, # Tipo genérico, tratar como desconocido por ahora
# Añade más tipos aquí si aparecen
}
# --- Función Principal de Procesamiento ---
def process_logic_data(data):
"""Itera sobre el JSON cargado y procesa cada instrucción."""
print("=" * 40)
print(f"Processing Block: {data.get('block_name')} ({data.get('block_number')})")
print(f"Language: {data.get('language')}")
print("-" * 40)
# Opcional: Imprimir interfaz
print("Interface:")
for section, members in data.get('interface', {}).items():
if members:
print(f" {section}:")
for member in members:
print(f" - {member['name']} ({member['datatype']})")
print("-" * 40)
# Procesar Redes
print("Networks:")
for network in data.get('networks', []):
print(f"\nNetwork ID: {network.get('id')} - Title: '{network.get('title', '')}'")
if 'error' in network:
print(f" ERROR en esta red: {network['error']}")
continue
if not network.get('logic'):
print(" (No logic instructions found in JSON for this network)")
continue
for instruction in network.get('logic', []):
instruction_type = instruction.get('type')
# Obtener el handler adecuado, o el default si no se encuentra
handler = instruction_handlers.get(instruction_type, process_unknown)
try:
handler(instruction)
except Exception as e:
print(f" ERROR procesando instrucción UID {instruction.get('instruction_uid')} (Tipo: {instruction_type}): {e}")
# Considerar imprimir más detalles del error o de la instrucción
# import traceback
# traceback.print_exc()
# --- Ejecución ---
if __name__ == "__main__":
json_file = 'BlenderRun_ProdTime_simplified.json' # El archivo generado por el script anterior
if not os.path.exists(json_file):
print(f"Error: Archivo JSON no encontrado en {json_file}")
print("Asegúrate de haber ejecutado el script de conversión XML a JSON primero.")
else:
try:
with open(json_file, 'r', encoding='utf-8') as f:
logic_data = json.load(f)
process_logic_data(logic_data)
except json.JSONDecodeError as e:
print(f"Error: El archivo JSON ({json_file}) no es válido: {e}")
except Exception as e:
print(f"Ocurrió un error inesperado al cargar o procesar el JSON: {e}")
import traceback
traceback.print_exc()