Agregado de los parametros de las FC

This commit is contained in:
Miguel 2025-04-20 13:52:26 +02:00
parent 66c5a076ab
commit 0e68e32b8a
3 changed files with 1036 additions and 787 deletions

View File

@ -11,14 +11,17 @@ 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
# Get original type before potential suffix/error was added by x1 or previous passes
# This requires storing the original type perhaps, or removing known suffixes
# Let's assume 'block_type' (FC/FB) and 'block_name' are correct from x1
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)
# Check if already processed
if instruction.get("type", "").endswith(SCL_SUFFIX) or "_error" in instruction.get("type", ""):
return False
# 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
@ -33,91 +36,140 @@ def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManag
# --- Procesar Parámetros de Entrada ---
scl_call_params = []
processed_inputs = {"en"}
processed_inputs = {"en"} # Track processed pins to avoid duplicates if 'en' is also listed elsewhere
dependencies_resolved = True
# Ordenar para consistencia
# Iterar sobre las entradas que x1 debería haber poblado
# Ordenar por nombre de pin para consistencia en la llamada SCL
input_pin_names = sorted(instruction.get("inputs", {}).keys())
for pin_name in input_pin_names:
if pin_name not in processed_inputs:
if pin_name not in processed_inputs: # Skip 'en' if already handled
source_info = instruction["inputs"][pin_name]
# Obtener la representación de la fuente (puede ser SymPy o Constante/String)
# Get the representation of the source (SymPy, constant, or SCL 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
break # Exit if one dependency is not ready
# 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.
# Convert the expression/constant to SCL for the call
# Simplification of inputs is generally not needed here, convert directly
param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
# El nombre del pin SÍ necesita formateo
# Parameter pin name needs formatting for SCL
pin_name_scl = format_variable_name(pin_name)
# Special check for DB_ANY or ANY_POINTER - pass name directly without :=
# We need the original parameter type info for this, which is not in the simplified JSON.
# WORKAROUND: Check if param_scl_value looks like a DB name ("DB_NAME")
# This is heuristic and might be wrong. Ideally, x1 should pass type info.
# For now, we assume standard 'Param := Value' syntax.
# if param_scl_value.startswith('"') and param_scl_value.endswith('"') and block_type == "FC": # Heuristic for DB_ANY?
# scl_call_params.append(f"{pin_name_scl} := {param_scl_value}") # Still use := for clarity? TIA might infer
# else:
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) ---
# --- Construcción de la Llamada SCL (con parámetros) ---
scl_call_body = ""
param_string = ", ".join(scl_call_params)
param_string = ", ".join(scl_call_params) # Join parameters with commas
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
instruction["type"] = f"Call_FB_error" # Mark with error
return True # Processed (with error)
# FB Call: InstanceName(Param1 := Value1, Param2 := Value2);
scl_call_body = f"{instance_db_scl}({param_string});"
elif block_type == "FC":
# FC Call: BlockName(Param1 := Value1, Param2 := Value2);
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
# Mark instruction type with error
instruction["type"] = f"Call_{block_type or 'Unknown'}_error" # Add specific type if known
# --- 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
# Simplify the EN condition before converting to 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}")
print(f"Error simplifying EN for Call {instr_uid} ({block_name_scl}): {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;"
# Avoid IF TRUE/FALSE blocks
if en_condition_scl == "TRUE":
scl_final = scl_call_body
elif en_condition_scl == "FALSE":
scl_final = f"// Call {block_name_scl} (UID {instr_uid}) condition simplified to FALSE."
# Also update type to avoid further processing?
# instruction["type"] = f"Call_{block_type}{SCL_SUFFIX}_Optimized"
else:
# Indent the call body within the IF block
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:
# No IF needed if EN is always TRUE
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)
# Update instruction type to mark as processed (unless already marked as error)
if "_error" not in instruction.get("type", ""):
instruction["type"] = f"Call_{block_type}{SCL_SUFFIX}"
# Propagar 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:
# --- Propagar Valores de Salida (Importante pero complejo) ---
# Esto requiere conocer la interfaz del bloque llamado (que no tenemos aquí directamente)
# O asumir convenciones estándar (ej. FCs tienen Ret_Val, FBs tienen outputs en su instancia)
# Heurística simple: Si es un FC, intentar propagar Ret_Val si existe en outputs
# Si es un FB, las salidas se acceden a través de la instancia (e.g., "MyInstance".Output1)
# Por ahora, dejaremos la propagación de salidas más avanzada para una mejora futura
# o requerirá pasar información de la interfaz del bloque llamado.
# Ejemplo básico (necesita mejorar):
# 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)
# pin_name_scl = format_variable_name(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
# # Salida de FB: "Instancia".NombrePin
# output_scl_access = f"{instance_db_scl}.{pin_name_scl}"
# # Podríamos guardar el string SCL o crear/obtener un Symbol
# sympy_out_symbol = symbol_manager.get_symbol(output_scl_access)
# sympy_map[map_key_out] = sympy_out_symbol if sympy_out_symbol else output_scl_access # Prefiere Symbol
# elif block_type == "FC":
# # Salida de FC: Requiere asignar a una variable (temporal o de interfaz)
# # Esto se complica porque el destino está en 'dest_list'
# if len(dest_list) == 1 and dest_list[0].get("type") == "variable":
# target_var_name = format_variable_name(dest_list[0].get("name"))
# # Guardar el nombre del destino SCL que contendrá el valor
# sympy_map[map_key_out] = target_var_name
# # Necesitaríamos modificar scl_final para incluir la asignación:
# # target_var_name := FC_Call(...); (requiere reestructurar la generación SCL)
# else:
# # Múltiples destinos o destino no variable es complejo para FC outputs
# sympy_map[map_key_out] = f"/* TODO: Assign FC output {pin_name_scl} */"
return True
@ -125,7 +177,8 @@ def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManag
# --- Processor Information Function ---
def get_processor_info():
"""Devuelve la información para las llamadas a FC y FB."""
# Asegurarse que los type_name coincidan con los usados en x1 y x2
return [
{'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6},
{'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6}
{'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6}, # Prioridad alta
{'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6} # Prioridad alta
]

184
paste.py
View File

@ -0,0 +1,184 @@
# 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"]
# Get original type before potential suffix/error was added by x1 or previous passes
# This requires storing the original type perhaps, or removing known suffixes
# Let's assume 'block_type' (FC/FB) and 'block_name' are correct from x1
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)
# Check if already processed
if instruction.get("type", "").endswith(SCL_SUFFIX) or "_error" in instruction.get("type", ""):
return False
# 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"} # Track processed pins to avoid duplicates if 'en' is also listed elsewhere
dependencies_resolved = True
# Iterar sobre las entradas que x1 debería haber poblado
# Ordenar por nombre de pin para consistencia en la llamada SCL
input_pin_names = sorted(instruction.get("inputs", {}).keys())
for pin_name in input_pin_names:
if pin_name not in processed_inputs: # Skip 'en' if already handled
source_info = instruction["inputs"][pin_name]
# Get the representation of the source (SymPy, constant, or SCL 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 # Exit if one dependency is not ready
# Convert the expression/constant to SCL for the call
# Simplification of inputs is generally not needed here, convert directly
param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
# Parameter pin name needs formatting for SCL
pin_name_scl = format_variable_name(pin_name)
# Special check for DB_ANY or ANY_POINTER - pass name directly without :=
# We need the original parameter type info for this, which is not in the simplified JSON.
# WORKAROUND: Check if param_scl_value looks like a DB name ("DB_NAME")
# This is heuristic and might be wrong. Ideally, x1 should pass type info.
# For now, we assume standard 'Param := Value' syntax.
# if param_scl_value.startswith('"') and param_scl_value.endswith('"') and block_type == "FC": # Heuristic for DB_ANY?
# scl_call_params.append(f"{pin_name_scl} := {param_scl_value}") # Still use := for clarity? TIA might infer
# else:
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 (con parámetros) ---
scl_call_body = ""
param_string = ", ".join(scl_call_params) # Join parameters with commas
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" # Mark with error
return True # Processed (with error)
# FB Call: InstanceName(Param1 := Value1, Param2 := Value2);
scl_call_body = f"{instance_db_scl}({param_string});"
elif block_type == "FC":
# FC Call: BlockName(Param1 := Value1, Param2 := Value2);
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}"
# Mark instruction type with error
instruction["type"] = f"Call_{block_type or 'Unknown'}_error" # Add specific type if known
# --- Aplicar Condición EN (usando la expresión SymPy EN) ---
scl_final = ""
if sympy_en_expr != sympy.true:
# Simplify the EN condition before converting to 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} ({block_name_scl}): {e}")
simplified_en_expr = sympy_en_expr # Fallback
en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
# Avoid IF TRUE/FALSE blocks
if en_condition_scl == "TRUE":
scl_final = scl_call_body
elif en_condition_scl == "FALSE":
scl_final = f"// Call {block_name_scl} (UID {instr_uid}) condition simplified to FALSE."
# Also update type to avoid further processing?
# instruction["type"] = f"Call_{block_type}{SCL_SUFFIX}_Optimized"
else:
# Indent the call body within the IF block
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:
# No IF needed if EN is always TRUE
scl_final = scl_call_body
# --- Actualizar Instrucción y Mapa SymPy ---
instruction["scl"] = scl_final # Guardar el SCL final generado
# Update instruction type to mark as processed (unless already marked as error)
if "_error" not in instruction.get("type", ""):
instruction["type"] = f"Call_{block_type}{SCL_SUFFIX}"
# Propagar 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 (Importante pero complejo) ---
# Esto requiere conocer la interfaz del bloque llamado (que no tenemos aquí directamente)
# O asumir convenciones estándar (ej. FCs tienen Ret_Val, FBs tienen outputs en su instancia)
# Heurística simple: Si es un FC, intentar propagar Ret_Val si existe en outputs
# Si es un FB, las salidas se acceden a través de la instancia (e.g., "MyInstance".Output1)
# Por ahora, dejaremos la propagación de salidas más avanzada para una mejora futura
# o requerirá pasar información de la interfaz del bloque llamado.
# Ejemplo básico (necesita mejorar):
# 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)
# pin_name_scl = format_variable_name(pin_name)
# if block_type == "FB" and instance_db_scl:
# # Salida de FB: "Instancia".NombrePin
# output_scl_access = f"{instance_db_scl}.{pin_name_scl}"
# # Podríamos guardar el string SCL o crear/obtener un Symbol
# sympy_out_symbol = symbol_manager.get_symbol(output_scl_access)
# sympy_map[map_key_out] = sympy_out_symbol if sympy_out_symbol else output_scl_access # Prefiere Symbol
# elif block_type == "FC":
# # Salida de FC: Requiere asignar a una variable (temporal o de interfaz)
# # Esto se complica porque el destino está en 'dest_list'
# if len(dest_list) == 1 and dest_list[0].get("type") == "variable":
# target_var_name = format_variable_name(dest_list[0].get("name"))
# # Guardar el nombre del destino SCL que contendrá el valor
# sympy_map[map_key_out] = target_var_name
# # Necesitaríamos modificar scl_final para incluir la asignación:
# # target_var_name := FC_Call(...); (requiere reestructurar la generación SCL)
# else:
# # Múltiples destinos o destino no variable es complejo para FC outputs
# sympy_map[map_key_out] = f"/* TODO: Assign FC output {pin_name_scl} */"
return True
# --- Processor Information Function ---
def get_processor_info():
"""Devuelve la información para las llamadas a FC y FB."""
# Asegurarse que los type_name coincidan con los usados en x1 y x2
return [
{'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6}, # Prioridad alta
{'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6} # Prioridad alta
]

File diff suppressed because it is too large Load Diff