Simatic_XML_Parser_to_SCL/processors/process_counter.py

110 lines
5.0 KiB
Python

# processors/process_counter.py
# -*- coding: utf-8 -*-
import sympy
import traceback
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
from .symbol_manager import SymbolManager
SCL_SUFFIX = "_sympy_processed"
def process_counter(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
"""
Genera SCL para Contadores (CTU, CTD, CTUD).
Requiere datos de instancia (DB o STAT).
"""
instr_uid = instruction["instruction_uid"]
instr_type_original = instruction.get("type", "") # CTU, CTD, CTUD
if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
return False
# 1. Definir pines de entrada esperados
input_pins_map = {
"CTU": ["CU", "R", "PV"],
"CTD": ["CD", "LD", "PV"],
"CTUD": ["CU", "CD", "R", "LD", "PV"]
}
input_pins = input_pins_map.get(instr_type_original.upper())
if not input_pins:
instruction["scl"] = f"// ERROR: Tipo de contador no soportado: {instr_type_original}"
instruction["type"] = instr_type_original + "_error"
return True
# 2. Procesar Parámetros de Entrada
scl_call_params = []
dependencies_resolved = True
optional_pins = {"R", "LD"} # Estos pueden no estar conectados
for pin in input_pins:
pin_info = instruction["inputs"].get(pin)
if pin_info: # Si el pin está definido en el JSON
source_sympy_or_const = get_sympy_representation(pin_info, network_id, sympy_map, symbol_manager)
if source_sympy_or_const is None:
# print(f"DEBUG Counter {instr_uid}: Input param '{pin}' dependency not ready.")
dependencies_resolved = False
break
# Convertir a SCL para la llamada (sin simplificar aquí)
param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
pin_name_scl = format_variable_name(pin) # Formatear nombre del parámetro
scl_call_params.append(f"{pin_name_scl} := {param_scl_value}")
elif pin not in optional_pins: # Si falta un pin requerido
print(f"Error: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}.")
instruction["scl"] = f"// ERROR: Falta entrada requerida '{pin}' para {instr_type_original} UID {instr_uid}."
instruction["type"] = instr_type_original + "_error"
return True
if not dependencies_resolved:
return False
# 3. Obtener Nombre de Instancia
# Asumiendo que x1 o una fase previa llena 'instance_db' si es un FB multi-instancia
instance_name_raw = instruction.get("instance_db")
if not instance_name_raw:
# Asumiendo que es STAT si no hay DB instancia explícito (requiere declaración en x3)
instance_name_raw = instruction.get("instance_name") # Buscar nombre directo si x1 lo provee
if not instance_name_raw:
instance_name_raw = f"#CTR_INSTANCE_{instr_uid}" # Placeholder final
print(f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_name_raw}'.")
instance_name_scl = format_variable_name(instance_name_raw)
# 4. Generar la llamada SCL
param_string = ", ".join(scl_call_params)
scl_call = f"{instance_name_scl}({param_string}); // TODO: Declarar {instance_name_scl} : {instr_type_original.upper()}; en VAR_STAT o VAR"
# Contadores no suelen tener EN/ENO explícito en LAD, se asume siempre habilitado
instruction["scl"] = scl_call # SCL final generado
instruction["type"] = instr_type_original + SCL_SUFFIX
# 4. Actualizar sympy_map para las salidas (QU, QD, CV)
output_pins_map = {
"CTU": ["QU", "CV"],
"CTD": ["QD", "CV"],
"CTUD": ["QU", "QD", "CV"]
}
output_pins = output_pins_map.get(instr_type_original.upper(), [])
for pin in output_pins:
map_key = (network_id, instr_uid, pin)
output_scl_access = f"{instance_name_scl}.{pin.upper()}"
if pin.upper() in ["QU", "QD"]: # These are boolean outputs
# *** Store SymPy Symbol for boolean outputs QU/QD ***
sympy_out_symbol = symbol_manager.get_symbol(output_scl_access)
if sympy_out_symbol:
sympy_map[map_key] = sympy_out_symbol # Store SYMBOL
else:
print(f"Error: Could not create symbol for {output_scl_access} in {instr_type_original} {instr_uid}")
sympy_map[map_key] = None
else:
# For non-boolean (like CV - count value), store SCL access string
sympy_map[map_key] = output_scl_access
return True
# --- Processor Information Function ---
def get_processor_info():
"""Devuelve la información para los contadores CTU, CTD, CTUD."""
return [
{'type_name': 'ctu', 'processor_func': process_counter, 'priority': 5},
{'type_name': 'ctd', 'processor_func': process_counter, 'priority': 5},
{'type_name': 'ctud', 'processor_func': process_counter, 'priority': 5}
]