# processors/process_call.py # -*- coding: utf-8 -*- import sympy import traceback # Asumiendo que estas funciones ahora existen y están adaptadas from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name from .symbol_manager import SymbolManager # Necesitamos pasar el symbol_manager # Definir sufijo globalmente o importar SCL_SUFFIX = "_sympy_processed" def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data): instr_uid = instruction["instruction_uid"] instr_type_original = instruction.get("type", "") # Tipo antes de añadir sufijo if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original: return False block_name = instruction.get("block_name", f"UnknownCall_{instr_uid}") block_type = instruction.get("block_type") # FC, FB instance_db = instruction.get("instance_db") # Nombre del DB de instancia (para FB) # Formatear nombres SCL (para la llamada final) block_name_scl = format_variable_name(block_name) instance_db_scl = format_variable_name(instance_db) if instance_db else None # --- Manejo de EN --- en_input = instruction["inputs"].get("en") sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true if sympy_en_expr is None: # print(f"DEBUG Call {instr_uid}: EN dependency not ready.") return False # Dependencia EN no resuelta # --- Procesar Parámetros de Entrada --- scl_call_params = [] processed_inputs = {"en"} dependencies_resolved = True # Ordenar para consistencia input_pin_names = sorted(instruction.get("inputs", {}).keys()) for pin_name in input_pin_names: if pin_name not in processed_inputs: source_info = instruction["inputs"][pin_name] # Obtener la representación de la fuente (puede ser SymPy o Constante/String) source_sympy_or_const = get_sympy_representation(source_info, network_id, sympy_map, symbol_manager) if source_sympy_or_const is None: # print(f"DEBUG Call {instr_uid}: Input param '{pin_name}' dependency not ready.") dependencies_resolved = False break # Salir si una dependencia no está lista # Convertir la expresión/constante a SCL para la llamada # Simplificar ANTES de convertir? Probablemente no necesario para parámetros de entrada # a menos que queramos optimizar el valor pasado. Por ahora, convertir directo. param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager) # El nombre del pin SÍ necesita formateo pin_name_scl = format_variable_name(pin_name) scl_call_params.append(f"{pin_name_scl} := {param_scl_value}") processed_inputs.add(pin_name) if not dependencies_resolved: return False # --- Construcción de la Llamada SCL (similar a antes) --- scl_call_body = "" param_string = ", ".join(scl_call_params) if block_type == "FB": if not instance_db_scl: print(f"Error: Call FB '{block_name_scl}' (UID {instr_uid}) sin instancia.") instruction["scl"] = f"// ERROR: FB Call {block_name_scl} sin instancia" instruction["type"] = f"Call_FB_error" return True scl_call_body = f"{instance_db_scl}({param_string});" elif block_type == "FC": scl_call_body = f"{block_name_scl}({param_string});" else: print(f"Advertencia: Tipo de bloque no soportado para Call UID {instr_uid}: {block_type}") scl_call_body = f"// ERROR: Call a bloque tipo '{block_type}' no soportado: {block_name_scl}" instruction["type"] = f"Call_{block_type}_error" # Marcar como error # --- Aplicar Condición EN (usando la expresión SymPy EN) --- scl_final = "" if sympy_en_expr != sympy.true: # Simplificar la condición EN ANTES de convertirla a SCL try: #simplified_en_expr = sympy.simplify_logic(sympy_en_expr, force=True) simplified_en_expr = sympy.logic.boolalg.to_dnf(sympy_en_expr, simplify=True) except Exception as e: print(f"Error simplifying EN for Call {instr_uid}: {e}") simplified_en_expr = sympy_en_expr # Fallback en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager) indented_call = "\n".join([f" {line}" for line in scl_call_body.splitlines()]) scl_final = f"IF {en_condition_scl} THEN\n{indented_call}\nEND_IF;" else: scl_final = scl_call_body # --- Actualizar Instrucción y Mapa SymPy --- instruction["scl"] = scl_final # Guardar el SCL final generado instruction["type"] = (f"Call_{block_type}{SCL_SUFFIX}" if "_error" not in instruction["type"] else instruction["type"]) # Actualizar sympy_map con el estado ENO (es la expresión SymPy de EN) map_key_eno = (network_id, instr_uid, "eno") sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO # Propagar valores de salida (requiere info de interfaz o heurística) # Si se sabe que hay una salida 'MyOutput', se podría añadir su SCL al mapa # Ejemplo MUY simplificado: # for pin_name, dest_list in instruction.get("outputs", {}).items(): # if pin_name != 'eno' and dest_list: # Asumir que hay un destino # map_key_out = (network_id, instr_uid, pin_name) # if block_type == "FB" and instance_db_scl: # sympy_map[map_key_out] = f"{instance_db_scl}.{format_variable_name(pin_name)}" # Guardar el *string* de acceso SCL # # Para FCs es más complejo, necesitaría asignación explícita a temp # # else: # FC output -> necesita temp var # # temp_var = generate_temp_var_name(...) # # sympy_map[map_key_out] = temp_var return True # --- Processor Information Function --- def get_processor_info(): """Devuelve la información para las llamadas a FC y FB.""" return [ {'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6}, {'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6} ]