Simatic_XML_Parser_to_SCL/processors/process_call.py

142 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
from .processor_utils import get_scl_representation, format_variable_name,get_target_scl_name
# TODO: Import necessary functions from processor_utils
# Example: from .processor_utils import get_scl_representation, format_variable_name
# Or: import processors.processor_utils as utils
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
SCL_SUFFIX = "_scl"
# --- Function code starts ---
def process_call(instruction, network_id, scl_map, access_map, data):
instr_uid = instruction["instruction_uid"]
instr_type = instruction.get("type", "") # Usar get con default
if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type:
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
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")
en_scl = (
get_scl_representation(en_input, network_id, scl_map, access_map)
if en_input
else "TRUE"
)
if en_scl is None:
return False # Dependencia EN no resuelta
# --- Procesar Parámetros de Entrada/Salida ---
# Necesitamos iterar sobre los pines definidos en la interfaz del bloque llamado.
# Esta información no está directamente en la instrucción 'Call' del JSON simplificado.
# ¡Limitación! Sin la interfaz del bloque llamado, solo podemos manejar EN/ENO
# y asumir una llamada sin parámetros o con parámetros conectados implícitamente.
# Solución temporal: Buscar conexiones en 'inputs' y 'outputs' que NO sean 'en'/'eno'
# y construir la llamada basándose en eso. Esto es muy heurístico.
scl_call_params = []
processed_inputs = {"en"} # Marcar 'en' como ya procesado
for pin_name, source_info in instruction.get("inputs", {}).items():
if pin_name not in processed_inputs:
param_scl = get_scl_representation(
source_info, network_id, scl_map, access_map
)
if param_scl is None:
# print(f"DEBUG: Call {instr_uid} esperando parámetro de entrada {pin_name}")
return False # Dependencia de parámetro no resuelta
# Formatear si es variable
param_scl_formatted = (
format_variable_name(param_scl)
if source_info.get("type") == "variable"
else param_scl
)
scl_call_params.append(
f"{format_variable_name(pin_name)} := {param_scl_formatted}"
)
processed_inputs.add(pin_name)
# Procesar parámetros de salida (asignaciones después de la llamada o pasados como VAR_IN_OUT/VAR_OUTPUT)
# Esto es aún más complejo. SCL normalmente asigna salidas después o usa punteros/referencias.
# Simplificación: Asumir que las salidas se manejan por asignación posterior si es necesario,
# o que son VAR_OUTPUT y se acceden como instancia.salida.
# Por ahora, no generamos asignaciones explícitas para las salidas aquí.
# --- Construcción de la Llamada SCL ---
scl_call_body = ""
param_string = ", ".join(scl_call_params)
if block_type == "FB":
if not instance_db_scl:
print(
f"Error: Llamada a FB '{block_name_scl}' (UID {instr_uid}) sin DB de instancia especificado."
)
instruction["scl"] = f"// ERROR: FB Call {block_name_scl} sin instancia"
instruction["type"] = "Call_FB_error"
return True # Procesado con error
# Llamada a FB con DB de instancia
scl_call_body = f"{instance_db_scl}({param_string});"
elif block_type == "FC":
# Llamada a 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 parcial
# --- Aplicar Condición EN ---
scl_final = ""
if en_scl != "TRUE":
# Indentar la llamada dentro del IF
indented_call = "\n".join([f" {line}" for line in scl_call_body.splitlines()])
scl_final = f"IF {en_scl} THEN\n{indented_call}\nEND_IF;"
else:
scl_final = scl_call_body
# --- Actualizar JSON y Mapa SCL ---
instruction["scl"] = scl_final
instruction["type"] = (
f"Call_{block_type}_scl"
if "_error" not in instruction["type"]
else instruction["type"]
)
# Actualizar scl_map con el estado ENO (igual a EN para llamadas simples sin manejo explícito de ENO)
map_key_eno = (network_id, instr_uid, "eno")
scl_map[map_key_eno] = en_scl
# Propagar valores de salida (si pudiéramos determinarlos)
# Ejemplo: Si supiéramos que hay una salida 'Out1' de tipo INT
# map_key_out1 = (network_id, instr_uid, "Out1")
# if block_type == "FB" and instance_db_scl:
# scl_map[map_key_out1] = f"{instance_db_scl}.Out1" # Acceso a salida de instancia
# else:
# # Para FCs, necesitaríamos una variable temporal o asignación explícita
# temp_out1 = generate_temp_var_name(network_id, instr_uid, "Out1")
# # Modificar scl_call_body para incluir la asignación: Out1 => temp_out1
# scl_map[map_key_out1] = temp_out1
return True
# --- Procesador de Temporizadores (TON, TOF) ---
# --- Function code ends ---
# --- Processor Information Function ---
def get_processor_info():
"""Devuelve la información para las llamadas a FC y FB."""
# Esta función maneja tanto FC como FB. El despachador en x2_process.py
# usará 'call_fc' o 'call_fb' como clave basada en block_type.
return [
{'type_name': 'call_fc', 'processor_func': process_call, 'priority': 6},
{'type_name': 'call_fb', 'processor_func': process_call, 'priority': 6}
]