# processors/process_counter.py # -*- coding: utf-8 -*- import sympy import traceback from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name from .symbol_manager import SymbolManager SCL_SUFFIX = "_sympy_processed" def process_counter(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data): """ Genera SCL para Contadores (CTU, CTD, CTUD). Requiere datos de instancia (DB o STAT). """ instr_uid = instruction["instruction_uid"] instr_type_original = instruction.get("type", "") # CTU, CTD, CTUD if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original: return False # 1. Definir pines de entrada esperados input_pins_map = { "CTU": ["CU", "R", "PV"], "CTD": ["CD", "LD", "PV"], "CTUD": ["CU", "CD", "R", "LD", "PV"] } input_pins = input_pins_map.get(instr_type_original.upper()) if not input_pins: instruction["scl"] = f"// ERROR: Tipo de contador no soportado: {instr_type_original}" instruction["type"] = instr_type_original + "_error" return True # 2. Procesar Parámetros de Entrada scl_call_params = [] dependencies_resolved = True optional_pins = {"R", "LD"} # Estos pueden no estar conectados for pin in input_pins: pin_info = instruction["inputs"].get(pin) if pin_info: # Si el pin está definido en el JSON source_sympy_or_const = get_sympy_representation(pin_info, network_id, sympy_map, symbol_manager) if source_sympy_or_const is None: # print(f"DEBUG Counter {instr_uid}: Input param '{pin}' dependency not ready.") dependencies_resolved = False break # Convertir a SCL para la llamada (sin simplificar aquí) param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager) pin_name_scl = format_variable_name(pin) # Formatear nombre del parámetro scl_call_params.append(f"{pin_name_scl} := {param_scl_value}") elif pin not in optional_pins: # Si falta un pin requerido print(f"Error: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}.") instruction["scl"] = f"// ERROR: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}." instruction["type"] = instr_type_original + "_error" return True if not dependencies_resolved: return False # 3. Obtener Nombre de Instancia # Asumiendo que x1 o una fase previa llena 'instance_db' si es un FB multi-instancia instance_name_raw = instruction.get("instance_db") if not instance_name_raw: # Asumiendo que es STAT si no hay DB instancia explícito (requiere declaración en x3) instance_name_raw = instruction.get("instance_name") # Buscar nombre directo si x1 lo provee if not instance_name_raw: instance_name_raw = f"#CTR_INSTANCE_{instr_uid}" # Placeholder final print(f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_name_raw}'.") instance_name_scl = format_variable_name(instance_name_raw) # 4. Generar la llamada SCL param_string = ", ".join(scl_call_params) scl_call = f"{instance_name_scl}({param_string}); // TODO: Declarar {instance_name_scl} : {instr_type_original.upper()}; en VAR_STAT o VAR" # Contadores no suelen tener EN/ENO explícito en LAD, se asume siempre habilitado instruction["scl"] = scl_call # SCL final generado instruction["type"] = instr_type_original + SCL_SUFFIX # 4. Actualizar sympy_map para las salidas (QU, QD, CV) output_pins_map = { "CTU": ["QU", "CV"], "CTD": ["QD", "CV"], "CTUD": ["QU", "QD", "CV"] } output_pins = output_pins_map.get(instr_type_original.upper(), []) for pin in output_pins: map_key = (network_id, instr_uid, pin) output_scl_access = f"{instance_name_scl}.{pin.upper()}" if pin.upper() in ["QU", "QD"]: # These are boolean outputs # *** Store SymPy Symbol for boolean outputs QU/QD *** sympy_out_symbol = symbol_manager.get_symbol(output_scl_access) if sympy_out_symbol: sympy_map[map_key] = sympy_out_symbol # Store SYMBOL else: print(f"Error: Could not create symbol for {output_scl_access} in {instr_type_original} {instr_uid}") sympy_map[map_key] = None else: # For non-boolean (like CV - count value), store SCL access string sympy_map[map_key] = output_scl_access return True # --- Processor Information Function --- def get_processor_info(): """Devuelve la información para los contadores CTU, CTD, CTUD.""" return [ {'type_name': 'ctu', 'processor_func': process_counter, 'priority': 5}, {'type_name': 'ctd', 'processor_func': process_counter, 'priority': 5}, {'type_name': 'ctud', 'processor_func': process_counter, 'priority': 5} ]