Simatic_XML_Parser_to_SCL/ToUpload/processors/process_se.py

112 lines
5.6 KiB
Python

# processors/process_se.py
# -*- coding: utf-8 -*-
import sympy
import traceback
# Usar las nuevas utilidades
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
from .symbol_manager import SymbolManager, extract_plc_variable_name
SCL_SUFFIX = "_sympy_processed"
def process_se(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
"""
Genera SCL para Temporizador de Pulso (Se -> TP) o SdCoil (-> TON).
Usa SymPy para entradas y almacena Symbol para salida Q.
"""
instr_uid = instruction["instruction_uid"]
# Obtener tipo original (antes de añadir sufijo) para determinar comportamiento
instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX,"").replace("_error","") # Se o SdCoil
current_type = instruction.get("type","") # Tipo actual para chequeo inicial
if current_type.endswith(SCL_SUFFIX) or "_error" in current_type:
return False
# Determinar el tipo de instrucción SCL y pines de entrada/salida correctos
scl_timer_type = "TP"
pin_in = "s" # Pin de entrada para Se
pin_time = "tv" # Pin de valor de tiempo para Se
pin_instance = "timer" # Pin donde se conecta la instancia para Se
pin_out_q = "q" # Pin de salida Q para Se
pin_out_time = "rt" # Pin de tiempo restante para Se -> TP.ET
# Ajustar pines si el tipo original era SdCoil
if instr_type_original == "SdCoil":
scl_timer_type = "TON" # SdCoil es funcionalmente un TON
pin_in = "in" # SdCoil usa 'in'
pin_time = "value" # SdCoil usa 'value'
pin_instance = "operand" # SdCoil usa 'operand' como instancia/variable de salida
pin_out_q = "out" # SdCoil usa 'out' como pin de salida Q
pin_out_time = None # SdCoil no tiene salida ET explícita
# 1. Obtener Inputs usando los nombres de pin correctos
s_info = instruction["inputs"].get(pin_in)
tv_info = instruction["inputs"].get(pin_time)
timer_instance_info = instruction["inputs"].get(pin_instance)
# Obtener representaciones (SymPy o Constante/String)
sympy_s_expr = get_sympy_representation(s_info, network_id, sympy_map, symbol_manager)
sympy_or_const_tv = get_sympy_representation(tv_info, network_id, sympy_map, symbol_manager)
# Obtener el nombre PLC original de la INSTANCIA
instance_plc_name = extract_plc_variable_name(timer_instance_info)
# 2. Verificar dependencias
if sympy_s_expr is None or sympy_or_const_tv is None:
# print(f"DEBUG {instr_type_original} {instr_uid}: Input/TV dependency not ready")
return False
if instance_plc_name is None:
print(f"Error: {instr_type_original} {instr_uid} sin variable de instancia en pin '{pin_instance}'.")
instance_plc_name = f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder
print(f"Advertencia: Usando placeholder '{instance_plc_name}'. ¡Declarar en SCL!")
# 3. Formatear nombre de instancia para SCL
instance_name_scl = format_variable_name(instance_plc_name)
# 4. Convertir entradas SymPy/Constante a SCL strings (simplificando la entrada IN)
try:
# Simplificar la expresión de entrada booleana
simplified_s_expr = sympy.simplify_logic(sympy_s_expr, force=True)
simplified_s_expr = sympy.logic.boolalg.to_dnf(sympy_s_expr, simplify=True)
except Exception as e:
print(f"Error simplifying '{pin_in}' input for {instr_type_original} {instr_uid}: {e}")
simplified_s_expr = sympy_s_expr # Fallback
s_scl = sympy_expr_to_scl(simplified_s_expr, symbol_manager)
# tv normalmente es constante, sympy_expr_to_scl debería manejarlo
tv_scl = sympy_expr_to_scl(sympy_or_const_tv, symbol_manager)
# 5. Generar la llamada SCL
# Ignoramos 'r' (reset) de Se si existiera
scl_call = f"{instance_name_scl}(IN := {s_scl}, PT := {tv_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};"
# 6. Actualizar instrucción con el SCL final
instruction["scl"] = scl_call
instruction["type"] = instr_type_original + SCL_SUFFIX # Marcar como procesado
# 7. Actualizar sympy_map para las salidas (Q y ET si aplica)
# Usar los nombres de pin originales determinados al principio
map_key_q = (network_id, instr_uid, pin_out_q) # pin_out_q es 'q' o 'out'
q_output_scl_access = f"{instance_name_scl}.Q" # Siempre accedemos a .Q del FB SCL
# *** OBTENER/CREAR Y ALMACENAR SYMBOL para la salida booleana Q ***
sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access)
if sympy_q_symbol:
sympy_map[map_key_q] = sympy_q_symbol # Almacenar el OBJETO SYMBOL
else:
# Manejar error si no se pudo crear el símbolo
print(f"Error: No se pudo crear símbolo para {q_output_scl_access} en {instr_type_original} {instr_uid}")
sympy_map[map_key_q] = None # Indicar error/irresoluble
# Almacenar ET solo si corresponde (para Se, no para SdCoil)
if pin_out_time: # pin_out_time es 'rt' o None
map_key_rt = (network_id, instr_uid, pin_out_time)
# ET es TIME, no booleano. Almacenar el string SCL de acceso está bien.
sympy_map[map_key_rt] = f"{instance_name_scl}.ET" # Salida ET del FB SCL
return True
# --- Processor Information Function ---
def get_processor_info():
"""Devuelve la info para Se (-> TP) y SdCoil (-> TON, manejado aquí)."""
return [
{'type_name': 'se', 'processor_func': process_se, 'priority': 5},
# Asegurarse que x1.py mapea SdCoil a este procesador o a uno específico
{'type_name': 'sdcoil', 'processor_func': process_se, 'priority': 5}
]