# 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 # Detectar si es una dirección indirecta (comienza con %) is_address_ref = isinstance( source_sympy_or_const, str ) and source_sympy_or_const.startswith("%") # El nombre del pin SÍ necesita formateo pin_name_scl = format_variable_name(pin_name) # Para direcciones indirectas, usar => en lugar de := if is_address_ref: # Es un parámetro InOut con dirección indirecta scl_call_params.append(f"{pin_name_scl} => {source_sympy_or_const}") else: # Es un parámetro normal (In) param_scl_value = sympy_expr_to_scl( source_sympy_or_const, symbol_manager ) 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 --- scl_call_body = "" param_string = ", ".join(scl_call_params) # Verificar si hay una asignación de valor de retorno para FC ret_val_assignment = None if block_type == "FC": ret_val_outputs = instruction.get("outputs", {}).get("Ret_Val", []) if ret_val_outputs and len(ret_val_outputs) > 0: # Hay una variable que debe recibir el valor de retorno ret_val_dest = ret_val_outputs[0] # Tomar el primer destino if ret_val_dest.get("scope") in ["LocalVariable", "GlobalVariable"]: ret_val_var_name = ret_val_dest.get("name", "").strip('"') ret_val_assignment = format_variable_name(ret_val_var_name) 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": if ret_val_assignment: # FC con valor de retorno asignado scl_call_body = f"{ret_val_assignment} := {block_name_scl}({param_string});" else: # FC sin valor de retorno o valor de retorno no utilizado 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 para FC con valor de retorno if block_type == "FC" and ret_val_assignment: # Para FC con valor de retorno, registrar la variable de destino en el mapa map_key_ret = (network_id, instr_uid, "Ret_Val") # Crear un símbolo SymPy para la variable de destino ret_val_symbol = sympy.symbols(ret_val_assignment.replace('"', "")) sympy_map[map_key_ret] = ret_val_symbol # Para FB, propagar valores de salida (requiere info de interfaz o heurística) if block_type == "FB" and instance_db_scl: for pin_name, dest_list in instruction.get("outputs", {}).items(): if ( pin_name not in ["eno", "Ret_Val"] and dest_list ): # FB outputs específicos map_key_out = (network_id, instr_uid, pin_name) fb_output_access = f"{instance_db_scl}.{format_variable_name(pin_name)}" sympy_map[map_key_out] = ( fb_output_access # Guardar el string de acceso SCL ) 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}, ]