# processors/process_timer.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_timer( instruction, network_id, sympy_map, symbol_manager: SymbolManager, data ): """ Genera SCL para Temporizadores (TON, TOF) directamente. Requiere datos de instancia. """ instr_uid = instruction["instruction_uid"] instr_type_original = ( instruction.get("type", "").replace(SCL_SUFFIX, "").replace("_error", "") ) # TON o TOF if instruction.get("type", "").endswith(SCL_SUFFIX) or "_error" in instruction.get( "type", "" ): return False scl_timer_type = instr_type_original.upper() if scl_timer_type not in ["TON", "TOF", "TP"]: instruction["scl"] = ( f"// ERROR: Tipo de temporizador directo no soportado: {instr_type_original}" ) instruction["type"] = instr_type_original + "_error" return True # 1. Obtener Inputs: IN, PT, y nombre de instancia (implícito o explícito) in_info = instruction["inputs"].get("IN") pt_info = instruction["inputs"].get("PT") # Buscar instancia: ¿está en inputs? ¿o como instance_db? instance_plc_name = instruction.get("instance_db") # Buscar primero aquí if not instance_plc_name: # Si no, buscar un input llamado 'timer' o similar? No estándar. # Asumir que debe estar declarado como STAT si no hay instance_db instance_plc_name = instruction.get("instance_name") # Nombre directo? if not instance_plc_name: instance_plc_name = ( f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder final ) print( f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_plc_name}'." ) sympy_in_expr = get_sympy_representation( in_info, network_id, sympy_map, symbol_manager ) sympy_or_const_pt = get_sympy_representation( pt_info, network_id, sympy_map, symbol_manager ) # Verificar dependencias if sympy_in_expr is None or sympy_or_const_pt is None or instance_plc_name is None: return False # Formatear nombre de instancia instance_name_scl = format_variable_name(instance_plc_name) # Convertir entradas SymPy/Constante a SCL strings in_scl = sympy_expr_to_scl(sympy_in_expr, symbol_manager) pt_scl = sympy_expr_to_scl(sympy_or_const_pt, symbol_manager) # Generar la llamada SCL # ENHANCED: Only add TODO comment if it's not an auto-generated placeholder if instance_plc_name.startswith("#") and "_INSTANCE_" in instance_plc_name: # It's an auto-generated placeholder, will be declared automatically in VAR_TEMP scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl});" else: # It's a named instance that might need manual declaration scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};" # Actualizar instrucción instruction["scl"] = scl_call # SCL final generado instruction["type"] = instr_type_original + SCL_SUFFIX # 7. Actualizar sympy_map para las salidas Q, ET y ENO map_key_q = (network_id, instr_uid, "Q") # Pin estándar SCL # *** Store SymPy Symbol for boolean output Q *** q_output_scl_access = f"{instance_name_scl}.Q" # String for SCL access sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access) # Get/Create Symbol if sympy_q_symbol: sympy_map[map_key_q] = sympy_q_symbol # Store the SYMBOL else: print( f"Error: Could not create symbol for {q_output_scl_access} in {instr_type_original} {instr_uid}" ) sympy_map[map_key_q] = None map_key_et = (network_id, instr_uid, "ET") # Pin estándar SCL # ET is TIME, store SCL access string sympy_map[map_key_et] = f"{instance_name_scl}.ET" # *** NEW: Handle ENO (Enable Output) pin *** map_key_eno = (network_id, instr_uid, "eno") # For timers, ENO is typically TRUE when the timer is properly executed # In simplified logic, we can assume ENO = TRUE for well-formed timer calls # Or we could make it conditional based on input validity # For now, let's use TRUE as a reasonable default sympy_map[map_key_eno] = sympy.true # *** Also handle common aliases *** map_key_out = ( network_id, instr_uid, "out", ) # Some connections might look for "out" sympy_map[map_key_out] = sympy_q_symbol # Map "out" to Q output return True # --- Processor Information Function --- def get_processor_info(): """Devuelve info para TON, TOF y TP directos.""" return [ {"type_name": "ton", "processor_func": process_timer, "priority": 5}, {"type_name": "tof", "processor_func": process_timer, "priority": 5}, {"type_name": "tp", "processor_func": process_timer, "priority": 5}, ]