ParamManagerScripts/backend/script_groups/XML Parser to SCL/processors/process_call.py

190 lines
7.8 KiB
Python

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