112 lines
5.6 KiB
Python
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}
|
|
] |