# 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} ]