Implement SyrBrix Autocorrection Function Block and Syrup Line MFM Preparation Logic
- Added the SyrBrix Autocorrection function block (FB1809) to handle syrup brix and density measurements for sugar-based beverages. - Implemented logic for syrup autocorrection requests, latching, and running states. - Introduced a new function block for Syrup Line MFM Preparation (FB1813) to manage syrup line preparation and related timers. - Enhanced network logic for syrup preparation, including handling of manual drain and fuzzy logic for syrup density checks. - Updated variable handling and added necessary comments for clarity and maintainability.
This commit is contained in:
parent
b1ee3a0eae
commit
c907112293
|
@ -4,3 +4,127 @@ Para generar un resultado directo desde un xml a 1 scl
|
|||
```bash
|
||||
C:/Users/migue/miniconda3/envs/tia_scripting/python.exe "d:/Proyectos/Scripts/ParamManagerScripts/backend/script_groups/XML Parser to SCL/x0_main.py" --plc-dir "D:\Trabajo\VM\45 - HENKEL - VM Auto Changeover\ExportTia" --source-xml "D:\Trabajo\VM\45 - HENKEL - VM Auto Changeover\ExportTia\PLC_TL27_Q1\ProgramBlocks_XML\FB HMI Interlock.xml" --dest-scl "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\XML Parser to SCL\.example\FB_HMI_Interlock.scl"
|
||||
```
|
||||
|
||||
## Flujo de Trabajo para Resolución de Errores de Conversión
|
||||
|
||||
### 1. Identificación de Errores
|
||||
|
||||
**Comando para buscar errores en SCL generados:**
|
||||
```bash
|
||||
# Buscar TODOs sin implementar
|
||||
grep -r "TODO:" *.scl
|
||||
|
||||
# Buscar líneas duplicadas P_TRIG
|
||||
grep -r "P_TRIG.*- Mem:" *.scl
|
||||
|
||||
# Buscar acceso directo a memoria
|
||||
grep -r "%DB[0-9]*\.DBX[0-9]*" *.scl
|
||||
|
||||
# Buscar instancias no declaradas
|
||||
grep -r "#.*_INSTANCE_.*(" *.scl
|
||||
```
|
||||
|
||||
### 2. Categorización de Errores Comunes
|
||||
|
||||
#### **A. Errores de Edge Detection (P_TRIG/N_TRIG)**
|
||||
- **Síntoma**: Líneas duplicadas con comentarios `// P_TRIG(...) - Mem: "variable"`
|
||||
- **Causa**: El procesador de Coils genera tanto la asignación como el comentario
|
||||
- **Solución**: Crear procesador específico para edge detection o mejorar process_coil.py
|
||||
|
||||
#### **B. Instancias de Timer No Declaradas**
|
||||
- **Síntoma**: `#TP_INSTANCE_X(...); // TODO: Declarar #TP_INSTANCE_X : TP;`
|
||||
- **Causa**: Los timers temporales no se declaran automáticamente en VAR_TEMP
|
||||
- **Solución**: Mejorar process_timer.py para auto-declarar instancias temporales
|
||||
|
||||
#### **C. Acceso Directo a Memoria**
|
||||
- **Síntoma**: `%DB960.DBX56.1 := ...`
|
||||
- **Causa**: El symbol_manager no resuelve direcciones absolutas a variables simbólicas
|
||||
- **Solución**: Mejorar symbol_manager.py para mapear direcciones absolutas
|
||||
|
||||
#### **D. Lógica Agrupada Incorrecta**
|
||||
- **Síntoma**: Lógica compleja en una sola línea, difícil de leer
|
||||
- **Causa**: El agrupador de IFs es demasiado agresivo
|
||||
- **Solución**: Ajustar process_group_ifs en x2_process.py
|
||||
|
||||
### 3. Metodología de Corrección
|
||||
|
||||
#### **Paso 1: Análisis de Archivos SCL Problemáticos**
|
||||
```bash
|
||||
# Revisar archivo específico
|
||||
python x0_main.py --source-xml "path/to/file.xml" --dest-scl "output.scl"
|
||||
# Examinar output.scl para identificar patrones de error
|
||||
```
|
||||
|
||||
#### **Paso 2: Identificar el Procesador Responsable**
|
||||
- **Coils**: `processors/process_coil.py` - Para asignaciones y edge detection
|
||||
- **Timers**: `processors/process_timer.py` - Para TON, TOF, TP
|
||||
- **Contacts**: `processors/process_contact.py` - Para lectura de variables
|
||||
- **Direcciones de memoria**: `processors/symbol_manager.py` - Para resolución simbólica
|
||||
|
||||
#### **Paso 3: Implementar Corrección**
|
||||
1. **Crear nuevo procesador** (si es necesario):
|
||||
```python
|
||||
# processors/process_ptrig.py
|
||||
def get_processor_info():
|
||||
return {
|
||||
"type_name": "ptrig",
|
||||
"processor_func": process_ptrig_instruction,
|
||||
"priority": 5 # Ejecutar antes que Coils
|
||||
}
|
||||
```
|
||||
|
||||
2. **Mejorar procesador existente**:
|
||||
- Agregar lógica específica para el caso problemático
|
||||
- Actualizar manejo de dependencias
|
||||
- Mejorar almacenamiento en sympy_map
|
||||
|
||||
#### **Paso 4: Validación**
|
||||
```bash
|
||||
# Re-ejecutar conversión
|
||||
python x0_main.py --source-xml "problema.xml" --dest-scl "test.scl"
|
||||
|
||||
# Verificar que el error se resolvió
|
||||
grep -c "TODO:" test.scl # Debe ser 0
|
||||
grep -c "P_TRIG.*- Mem:" test.scl # Debe ser 0
|
||||
```
|
||||
|
||||
### 4. Patrones de Testing
|
||||
|
||||
#### **A. Testing Incremental**
|
||||
```bash
|
||||
# Test con archivo pequeño conocido
|
||||
python x0_main.py --source-xml "simple_test.xml" --dest-scl "simple_out.scl"
|
||||
|
||||
# Test con archivo complejo original
|
||||
python x0_main.py --source-xml "complex_original.xml" --dest-scl "complex_out.scl"
|
||||
```
|
||||
|
||||
#### **B. Comparación de Resultados**
|
||||
```bash
|
||||
# Comparar antes/después
|
||||
diff old_output.scl new_output.scl
|
||||
```
|
||||
|
||||
### 5. Herramientas de Debugging
|
||||
|
||||
#### **A. Activar Debug en x2_process.py**
|
||||
```python
|
||||
# Agregar prints de debug en procesadores específicos
|
||||
print(f"DEBUG: Processing {instr_type} - UID: {instr_uid}")
|
||||
```
|
||||
|
||||
#### **B. Revisar Logs de Conversión**
|
||||
```bash
|
||||
# Buscar patrones en logs
|
||||
grep "ERROR\|WARNING\|TODO" log_*.txt
|
||||
```
|
||||
|
||||
### 6. Checklist de Calidad
|
||||
|
||||
Antes de considerar un error "resuelto":
|
||||
- [ ] No hay comentarios TODO en el SCL
|
||||
- [ ] No hay líneas duplicadas de P_TRIG
|
||||
- [ ] No hay acceso directo a memoria (%DB.DBX)
|
||||
- [ ] Todas las variables de timer están declaradas
|
||||
- [ ] El código SCL es sintácticamente válido
|
||||
- [ ] La lógica funcional es equivalente al LAD original
|
|
@ -160,7 +160,21 @@ def _generate_scl_temp_vars(data, declared_temps):
|
|||
scl_output.append("VAR_TEMP") # Only add if no temps were declared before
|
||||
for temp_name in additional_temps:
|
||||
scl_name = format_variable_name(temp_name)
|
||||
inferred_type = "Bool"
|
||||
# ENHANCED: Infer correct type based on variable name pattern
|
||||
inferred_type = "Bool" # Default
|
||||
if "_INSTANCE_" in temp_name:
|
||||
if "TP_INSTANCE_" in temp_name:
|
||||
inferred_type = "TP"
|
||||
elif "TON_INSTANCE_" in temp_name:
|
||||
inferred_type = "TON"
|
||||
elif "TOF_INSTANCE_" in temp_name:
|
||||
inferred_type = "TOF"
|
||||
elif "CTU_INSTANCE_" in temp_name:
|
||||
inferred_type = "CTU"
|
||||
elif "CTD_INSTANCE_" in temp_name:
|
||||
inferred_type = "CTD"
|
||||
elif "CTUD_INSTANCE_" in temp_name:
|
||||
inferred_type = "CTUD"
|
||||
scl_output.append(
|
||||
f" {scl_name} : {inferred_type}; // Auto-generated temporary"
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
Manejador de UIDs duplicados en el JSON
|
||||
"""
|
||||
|
||||
|
||||
def detect_and_resolve_duplicate_uids(data):
|
||||
"""
|
||||
Detecta y resuelve UIDs duplicados en el JSON de datos.
|
||||
|
@ -26,7 +27,9 @@ def detect_and_resolve_duplicate_uids(data):
|
|||
# Segunda pasada: renombrar duplicados
|
||||
for uid, instances in uid_counts.items():
|
||||
if len(instances) > 1:
|
||||
print(f"INFO: UID duplicado encontrado: {uid} ({len(instances)} instancias)")
|
||||
print(
|
||||
f"INFO: UID duplicado encontrado: {uid} ({len(instances)} instancias)"
|
||||
)
|
||||
|
||||
# Mantener la primera instancia, renombrar las demás
|
||||
for i, (network_id, instruction) in enumerate(instances[1:], 1):
|
||||
|
@ -47,6 +50,7 @@ def detect_and_resolve_duplicate_uids(data):
|
|||
|
||||
return modifications_made > 0
|
||||
|
||||
|
||||
def update_uid_references(data, old_uid, new_uid, target_network_id):
|
||||
"""
|
||||
Actualiza las referencias al UID antiguo en conexiones de otras instrucciones
|
||||
|
@ -62,4 +66,6 @@ def update_uid_references(data, old_uid, new_uid, target_network_id):
|
|||
if isinstance(pin_info, dict):
|
||||
if pin_info.get("source_instruction_uid") == old_uid:
|
||||
pin_info["source_instruction_uid"] = new_uid
|
||||
print(f" - Actualizada referencia en instrucción {instruction.get('instruction_uid')} pin {pin_name}")
|
||||
print(
|
||||
f" - Actualizada referencia en instrucción {instruction.get('instruction_uid')} pin {pin_name}"
|
||||
)
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
# processors/process_coil.py
|
||||
import sympy
|
||||
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
|
||||
from .processor_utils import (
|
||||
get_sympy_representation,
|
||||
sympy_expr_to_scl,
|
||||
get_target_scl_name,
|
||||
format_variable_name,
|
||||
)
|
||||
from .symbol_manager import SymbolManager, extract_plc_variable_name
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed"
|
||||
|
||||
|
||||
def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
|
||||
"""Genera la asignación SCL para Coil, simplificando la entrada SymPy."""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
|
@ -15,10 +21,14 @@ def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
|
|||
|
||||
# Get input expression from SymPy map
|
||||
coil_input_info = instruction["inputs"].get("in")
|
||||
sympy_expr_in = get_sympy_representation(coil_input_info, network_id, sympy_map, symbol_manager)
|
||||
sympy_expr_in = get_sympy_representation(
|
||||
coil_input_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
|
||||
# Get target variable SCL name
|
||||
target_scl_name = get_target_scl_name(instruction, "operand", network_id, default_to_temp=False) # Coil must have explicit target
|
||||
target_scl_name = get_target_scl_name(
|
||||
instruction, "operand", network_id, default_to_temp=False
|
||||
) # Coil must have explicit target
|
||||
|
||||
# Check dependencies
|
||||
if sympy_expr_in is None:
|
||||
|
@ -28,16 +38,16 @@ def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
|
|||
print(f"Error: Coil {instr_uid} operando no es variable o falta info.")
|
||||
instruction["scl"] = f"// ERROR: Coil {instr_uid} operando no es variable."
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True # Processed with error
|
||||
return True # Processed with error
|
||||
|
||||
# *** Perform Simplification ***
|
||||
try:
|
||||
#simplified_expr = sympy.simplify_logic(sympy_expr_in, force=False)
|
||||
#simplified_expr = sympy_expr_in
|
||||
# simplified_expr = sympy.simplify_logic(sympy_expr_in, force=False)
|
||||
# simplified_expr = sympy_expr_in
|
||||
simplified_expr = sympy.logic.boolalg.to_dnf(sympy_expr_in, simplify=True)
|
||||
except Exception as e:
|
||||
print(f"Error during SymPy simplification for Coil {instr_uid}: {e}")
|
||||
simplified_expr = sympy_expr_in # Fallback to original expression
|
||||
simplified_expr = sympy_expr_in # Fallback to original expression
|
||||
|
||||
# *** Convert simplified expression back to SCL string ***
|
||||
condition_scl = sympy_expr_to_scl(simplified_expr, symbol_manager)
|
||||
|
@ -46,28 +56,52 @@ def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
|
|||
scl_assignment = f"{target_scl_name} := {condition_scl};"
|
||||
scl_final = scl_assignment
|
||||
|
||||
# --- Handle Edge Detector Memory Update (Logic similar to before) ---
|
||||
# Check if input comes from PBox/NBox and append memory update
|
||||
# --- Handle Edge Detector Memory Update (FIXED: Avoid Duplication) ---
|
||||
# Check if input comes from PBox/NBox and incorporate memory update logic
|
||||
mem_update_scl_combined = None
|
||||
if isinstance(coil_input_info, dict) and coil_input_info.get("type") == "connection":
|
||||
if (
|
||||
isinstance(coil_input_info, dict)
|
||||
and coil_input_info.get("type") == "connection"
|
||||
):
|
||||
source_uid = coil_input_info.get("source_instruction_uid")
|
||||
source_pin = coil_input_info.get("source_pin")
|
||||
source_instruction = None
|
||||
network_logic = next((net["logic"] for net in data["networks"] if net["id"] == network_id), [])
|
||||
network_logic = next(
|
||||
(net["logic"] for net in data["networks"] if net["id"] == network_id), []
|
||||
)
|
||||
for instr in network_logic:
|
||||
if instr.get("instruction_uid") == source_uid:
|
||||
source_instruction = instr
|
||||
break
|
||||
if source_instruction:
|
||||
# Check for the original type before suffix was added
|
||||
orig_source_type = source_instruction.get("type", "").replace(SCL_SUFFIX, '').replace('_error', '')
|
||||
if orig_source_type in ["PBox", "NBox"] and '_edge_mem_update_scl' in source_instruction:
|
||||
mem_update_scl_combined = source_instruction.get('_edge_mem_update_scl')
|
||||
# Check for the original type before suffix was added
|
||||
orig_source_type = (
|
||||
source_instruction.get("type", "")
|
||||
.replace(SCL_SUFFIX, "")
|
||||
.replace("_error", "")
|
||||
)
|
||||
if (
|
||||
orig_source_type in ["PBox", "NBox"]
|
||||
and "_edge_mem_update_scl" in source_instruction
|
||||
):
|
||||
mem_update_scl_combined = source_instruction.get("_edge_mem_update_scl")
|
||||
if mem_update_scl_combined:
|
||||
scl_final = f"{scl_assignment}\n{mem_update_scl_combined}"
|
||||
# Clear the source SCL?
|
||||
source_instruction['scl'] = f"// Edge Logic handled by Coil {instr_uid}"
|
||||
# FIXED: Extract just the memory assignment part (before comment)
|
||||
mem_assignment_part = mem_update_scl_combined.split(" //")[
|
||||
0
|
||||
].strip()
|
||||
comment_part = (
|
||||
" //" + mem_update_scl_combined.split(" //", 1)[1]
|
||||
if " //" in mem_update_scl_combined
|
||||
else ""
|
||||
)
|
||||
|
||||
# Add memory update BEFORE the coil assignment (correct P_TRIG order)
|
||||
scl_final = f"{mem_assignment_part}{comment_part}\n{scl_assignment}"
|
||||
# Mark the source as handled
|
||||
source_instruction["scl"] = (
|
||||
f"// Edge Logic handled by Coil {instr_uid}"
|
||||
)
|
||||
|
||||
# Update instruction
|
||||
instruction["scl"] = scl_final
|
||||
|
@ -80,7 +114,8 @@ def process_coil(instruction, network_id, sympy_map, symbol_manager, data):
|
|||
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador Coil."""
|
||||
return {'type_name': 'coil', 'processor_func': process_coil, 'priority': 3}
|
||||
return {"type_name": "coil", "processor_func": process_coil, "priority": 3}
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
# processors/process_contact.py
|
||||
import sympy
|
||||
from .processor_utils import get_sympy_representation, format_variable_name # Use new util
|
||||
from .symbol_manager import SymbolManager, extract_plc_variable_name # Need symbol manager access
|
||||
from .processor_utils import (
|
||||
get_sympy_representation,
|
||||
format_variable_name,
|
||||
) # Use new util
|
||||
from .symbol_manager import (
|
||||
SymbolManager,
|
||||
extract_plc_variable_name,
|
||||
) # Need symbol manager access
|
||||
|
||||
# Define SCL_SUFFIX or import if needed globally
|
||||
SCL_SUFFIX = "_sympy_processed" # Indicate processing type
|
||||
SCL_SUFFIX = "_sympy_processed" # Indicate processing type
|
||||
|
||||
def process_contact(instruction, network_id, sympy_map, symbol_manager, data): # Pass symbol_manager
|
||||
|
||||
def process_contact(
|
||||
instruction, network_id, sympy_map, symbol_manager, data
|
||||
): # Pass symbol_manager
|
||||
"""Genera la expresión SymPy para Contact (normal o negado)."""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type_original = instruction.get("type", "Contact")
|
||||
|
@ -19,12 +28,16 @@ def process_contact(instruction, network_id, sympy_map, symbol_manager, data): #
|
|||
|
||||
# Get incoming SymPy expression (RLO)
|
||||
in_input = instruction["inputs"].get("in")
|
||||
sympy_expr_in = get_sympy_representation(in_input, network_id, sympy_map, symbol_manager)
|
||||
sympy_expr_in = get_sympy_representation(
|
||||
in_input, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
|
||||
# Get operand SymPy Symbol
|
||||
operand_info = instruction["inputs"].get("operand")
|
||||
operand_plc_name = extract_plc_variable_name(operand_info)
|
||||
sympy_symbol_operand = symbol_manager.get_symbol(operand_plc_name) if operand_plc_name else None
|
||||
sympy_symbol_operand = (
|
||||
symbol_manager.get_symbol(operand_plc_name) if operand_plc_name else None
|
||||
)
|
||||
|
||||
# Enhanced robustness: Handle cases where operand parsing fails
|
||||
if operand_plc_name is None and operand_info:
|
||||
|
@ -36,7 +49,9 @@ def process_contact(instruction, network_id, sympy_map, symbol_manager, data): #
|
|||
|
||||
# If still no success, mark as error instead of hanging
|
||||
if sympy_symbol_operand is None:
|
||||
print(f"Error: Contact {instr_uid} - no se pudo extraer operando de {operand_info}")
|
||||
print(
|
||||
f"Error: Contact {instr_uid} - no se pudo extraer operando de {operand_info}"
|
||||
)
|
||||
instruction["scl"] = f"// ERROR: Contact {instr_uid} operando inválido"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
@ -48,37 +63,40 @@ def process_contact(instruction, network_id, sympy_map, symbol_manager, data): #
|
|||
sympy_expr_in = sympy.true
|
||||
else:
|
||||
# print(f"DEBUG Contact {instr_uid}: Input dependency not ready")
|
||||
return False # Dependencies not ready
|
||||
return False # Dependencies not ready
|
||||
|
||||
if sympy_symbol_operand is None:
|
||||
# print(f"DEBUG Contact {instr_uid}: Operand dependency not ready")
|
||||
return False # Dependencies not ready
|
||||
return False # Dependencies not ready
|
||||
|
||||
# Apply negation using SymPy
|
||||
current_term = sympy.Not(sympy_symbol_operand) if is_negated else sympy_symbol_operand
|
||||
current_term = (
|
||||
sympy.Not(sympy_symbol_operand) if is_negated else sympy_symbol_operand
|
||||
)
|
||||
|
||||
# Combine with previous RLO using SymPy
|
||||
# Simplify common cases: TRUE AND X -> X, FALSE AND X -> FALSE
|
||||
if sympy_expr_in == sympy.true:
|
||||
sympy_expr_out = current_term
|
||||
sympy_expr_out = current_term
|
||||
elif sympy_expr_in == sympy.false:
|
||||
sympy_expr_out = sympy.false
|
||||
sympy_expr_out = sympy.false
|
||||
else:
|
||||
sympy_expr_out = sympy.And(sympy_expr_in, current_term)
|
||||
sympy_expr_out = sympy.And(sympy_expr_in, current_term)
|
||||
|
||||
# Store the resulting SymPy expression object in the map
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
sympy_map[map_key_out] = sympy_expr_out
|
||||
|
||||
# Mark instruction as processed (SCL field is now less relevant here)
|
||||
instruction["scl"] = f"// SymPy Contact: {sympy_expr_out}" # Optional debug comment
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX # Use the new suffix
|
||||
instruction["scl"] = f"// SymPy Contact: {sympy_expr_out}" # Optional debug comment
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX # Use the new suffix
|
||||
# Contact doesn't usually have ENO, it modifies the RLO ('out')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador Contact."""
|
||||
# Ensure 'data' argument is added if needed by the processor function signature change
|
||||
return {'type_name': 'contact', 'processor_func': process_contact, 'priority': 1}
|
||||
return {"type_name": "contact", "processor_func": process_contact, "priority": 1}
|
||||
|
|
|
@ -3,18 +3,27 @@
|
|||
import sympy
|
||||
import traceback
|
||||
import re
|
||||
|
||||
# Usar las nuevas utilidades
|
||||
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
|
||||
from .processor_utils import (
|
||||
get_sympy_representation,
|
||||
sympy_expr_to_scl,
|
||||
get_target_scl_name,
|
||||
format_variable_name,
|
||||
)
|
||||
from .symbol_manager import SymbolManager
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed"
|
||||
|
||||
def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
|
||||
def process_math(
|
||||
instruction, network_id, sympy_map, symbol_manager: SymbolManager, data
|
||||
):
|
||||
"""
|
||||
Genera SCL para operaciones matemáticas (SUB, MUL, DIV, CEIL), simplificando EN.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type_original = instruction.get("type", "") # SUB, MUL, DIV, CEIL
|
||||
instr_type_original = instruction.get("type", "") # SUB, MUL, DIV, CEIL
|
||||
if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original:
|
||||
return False
|
||||
|
||||
|
@ -22,28 +31,44 @@ def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
|||
op_map = {"SUB": "-", "MUL": "*", "DIV": "/", "CEIL": "CEIL"}
|
||||
scl_operator = op_map.get(instr_type_original.upper())
|
||||
if not scl_operator:
|
||||
instruction["scl"] = f"// ERROR: Operación matemática no soportada: {instr_type_original}"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
instruction["scl"] = (
|
||||
f"// ERROR: Operación matemática no soportada: {instr_type_original}"
|
||||
)
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# Obtener EN (SymPy), IN1, IN2 (SymPy o Constante/String)
|
||||
en_input = instruction["inputs"].get("en")
|
||||
in1_info = instruction["inputs"].get("in1")
|
||||
in2_info = instruction["inputs"].get("in2")
|
||||
in_info = instruction["inputs"].get("in") # Para funciones de un solo operando como CEIL
|
||||
in_info = instruction["inputs"].get(
|
||||
"in"
|
||||
) # Para funciones de un solo operando como CEIL
|
||||
|
||||
sympy_en_expr = get_sympy_representation(en_input, network_id, sympy_map, symbol_manager) if en_input else sympy.true
|
||||
sympy_en_expr = (
|
||||
get_sympy_representation(en_input, network_id, sympy_map, symbol_manager)
|
||||
if en_input
|
||||
else sympy.true
|
||||
)
|
||||
|
||||
# Para CEIL solo necesitamos un operando
|
||||
if instr_type_original.upper() == "CEIL":
|
||||
op1_sympy_or_const = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
op1_sympy_or_const = get_sympy_representation(
|
||||
in_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
op2_sympy_or_const = None
|
||||
else:
|
||||
op1_sympy_or_const = get_sympy_representation(in1_info, network_id, sympy_map, symbol_manager)
|
||||
op2_sympy_or_const = get_sympy_representation(in2_info, network_id, sympy_map, symbol_manager)
|
||||
op1_sympy_or_const = get_sympy_representation(
|
||||
in1_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
op2_sympy_or_const = get_sympy_representation(
|
||||
in2_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
|
||||
# Obtener destino SCL
|
||||
target_scl_name = get_target_scl_name(instruction, "out", network_id, default_to_temp=True)
|
||||
target_scl_name = get_target_scl_name(
|
||||
instruction, "out", network_id, default_to_temp=True
|
||||
)
|
||||
|
||||
# Verificar dependencias
|
||||
if sympy_en_expr is None or op1_sympy_or_const is None or target_scl_name is None:
|
||||
|
@ -62,8 +87,12 @@ def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
|||
|
||||
# Añadir paréntesis si contienen operadores (más seguro)
|
||||
# La función sympy_expr_to_scl debería idealmente manejar esto, pero doble chequeo simple:
|
||||
op1_scl_formatted = f"({op1_scl})" if re.search(r'[+\-*/ ]', op1_scl) else op1_scl
|
||||
op2_scl_formatted = f"({op2_scl})" if re.search(r'[+\-*/ ]', op2_scl) else op2_scl
|
||||
op1_scl_formatted = (
|
||||
f"({op1_scl})" if re.search(r"[+\-*/ ]", op1_scl) else op1_scl
|
||||
)
|
||||
op2_scl_formatted = (
|
||||
f"({op2_scl})" if re.search(r"[+\-*/ ]", op2_scl) else op2_scl
|
||||
)
|
||||
|
||||
scl_core = f"{target_scl_name} := {op1_scl_formatted} {scl_operator} {op2_scl_formatted};"
|
||||
|
||||
|
@ -71,11 +100,13 @@ def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
|||
scl_final = ""
|
||||
if sympy_en_expr != sympy.true:
|
||||
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)
|
||||
# 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 {instr_type_original} {instr_uid}: {e}")
|
||||
simplified_en_expr = sympy_en_expr # Fallback
|
||||
simplified_en_expr = sympy_en_expr # Fallback
|
||||
en_condition_scl = sympy_expr_to_scl(simplified_en_expr, symbol_manager)
|
||||
|
||||
indented_core = "\n".join([f" {line}" for line in scl_core.splitlines()])
|
||||
|
@ -84,23 +115,24 @@ def process_math(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
|||
scl_final = scl_core
|
||||
|
||||
# Actualizar instrucción y mapa
|
||||
instruction["scl"] = scl_final # SCL final generado
|
||||
instruction["scl"] = scl_final # SCL final generado
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
|
||||
# Propagar valor de salida (nombre SCL del destino) y ENO (expresión SymPy)
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
|
||||
sympy_map[map_key_out] = target_scl_name # Guardar nombre del destino
|
||||
map_key_eno = (network_id, instr_uid, "eno")
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
sympy_map[map_key_eno] = sympy_en_expr # Guardar la expresión SymPy para ENO
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve info para SUB, MUL, DIV, CEIL."""
|
||||
return [
|
||||
{'type_name': 'sub', 'processor_func': process_math, 'priority': 4},
|
||||
{'type_name': 'mul', 'processor_func': process_math, 'priority': 4},
|
||||
{'type_name': 'div', 'processor_func': process_math, 'priority': 4},
|
||||
{'type_name': 'ceil', 'processor_func': process_math, 'priority': 4}
|
||||
{"type_name": "sub", "processor_func": process_math, "priority": 4},
|
||||
{"type_name": "mul", "processor_func": process_math, "priority": 4},
|
||||
{"type_name": "div", "processor_func": process_math, "priority": 4},
|
||||
{"type_name": "ceil", "processor_func": process_math, "priority": 4},
|
||||
]
|
|
@ -2,11 +2,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sympy
|
||||
import traceback
|
||||
|
||||
# Usar las nuevas utilidades
|
||||
from .processor_utils import get_sympy_representation
|
||||
from .symbol_manager import SymbolManager
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed" # Nuevo sufijo
|
||||
SCL_SUFFIX = "_sympy_processed" # Nuevo sufijo
|
||||
|
||||
|
||||
def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""Genera la expresión SymPy para la operación lógica O (OR)."""
|
||||
|
@ -16,7 +18,9 @@ def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager,
|
|||
return False
|
||||
|
||||
# Buscar todas las entradas 'in', 'in1', 'in2', ...
|
||||
input_pins = sorted([pin for pin in instruction.get("inputs", {}) if pin.startswith("in")])
|
||||
input_pins = sorted(
|
||||
[pin for pin in instruction.get("inputs", {}) if pin.startswith("in")]
|
||||
)
|
||||
|
||||
if not input_pins:
|
||||
print(f"Error: O {instr_uid} sin pines de entrada (inX).")
|
||||
|
@ -30,7 +34,9 @@ def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager,
|
|||
|
||||
for pin in input_pins:
|
||||
input_info = instruction["inputs"][pin]
|
||||
sympy_expr = get_sympy_representation(input_info, network_id, sympy_map, symbol_manager)
|
||||
sympy_expr = get_sympy_representation(
|
||||
input_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
|
||||
if sympy_expr is None:
|
||||
all_resolved = False
|
||||
|
@ -40,12 +46,12 @@ def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager,
|
|||
|
||||
# Optimización: No incluir FALSE en un OR
|
||||
if sympy_expr != sympy.false:
|
||||
sympy_parts.append(sympy_expr)
|
||||
sympy_parts.append(sympy_expr)
|
||||
|
||||
if not all_resolved:
|
||||
# More detailed debug info
|
||||
# print(f"DEBUG: O {instr_uid} esperando pines {missing_deps}")
|
||||
return False # Esperar dependencias
|
||||
return False # Esperar dependencias
|
||||
|
||||
# Enhanced handling: If all inputs are FALSE or no valid inputs, result is FALSE
|
||||
if not sympy_parts:
|
||||
|
@ -65,18 +71,18 @@ def process_o(instruction, network_id, sympy_map, symbol_manager: SymbolManager,
|
|||
# print(f"DEBUG: O {instr_uid} simplification failed: {e}")
|
||||
pass
|
||||
|
||||
|
||||
# Guardar la expresión SymPy resultante en el mapa para 'out'
|
||||
map_key_out = (network_id, instr_uid, "out")
|
||||
sympy_map[map_key_out] = result_sympy_expr
|
||||
|
||||
# Marcar como procesado, SCL principal es solo comentario
|
||||
instruction["scl"] = f"// SymPy O: {result_sympy_expr}" # Comentario opcional
|
||||
instruction["scl"] = f"// SymPy O: {result_sympy_expr}" # Comentario opcional
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
# La instrucción 'O' no tiene ENO propio, propaga el resultado por 'out'
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para la operación lógica O (OR)."""
|
||||
return {'type_name': 'o', 'processor_func': process_o, 'priority': 1}
|
||||
return {"type_name": "o", "processor_func": process_o, "priority": 1}
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sympy
|
||||
import traceback
|
||||
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, get_target_scl_name, format_variable_name
|
||||
from .processor_utils import (
|
||||
get_sympy_representation,
|
||||
sympy_expr_to_scl,
|
||||
get_target_scl_name,
|
||||
format_variable_name,
|
||||
)
|
||||
from .symbol_manager import SymbolManager
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed"
|
||||
|
||||
|
||||
def process_sr(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
"""
|
||||
Genera SCL para Set/Reset flip-flop (Sr).
|
||||
|
@ -23,7 +29,9 @@ def process_sr(instruction, network_id, sympy_map, symbol_manager: SymbolManager
|
|||
|
||||
# Si no tiene conexiones, marcar como procesado sin generar código
|
||||
if not inputs and not outputs:
|
||||
instruction["scl"] = "// Sr flip-flop sin conexiones - procesado como placeholder"
|
||||
instruction["scl"] = (
|
||||
"// Sr flip-flop sin conexiones - procesado como placeholder"
|
||||
)
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
return True
|
||||
|
||||
|
@ -33,7 +41,8 @@ def process_sr(instruction, network_id, sympy_map, symbol_manager: SymbolManager
|
|||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve la información para el procesador Sr."""
|
||||
return {'type_name': 'sr', 'processor_func': process_sr, 'priority': 4}
|
||||
return {"type_name": "sr", "processor_func": process_sr, "priority": 4}
|
||||
|
|
|
@ -2,44 +2,66 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sympy
|
||||
import traceback
|
||||
|
||||
# Usar las nuevas utilidades
|
||||
from .processor_utils import get_sympy_representation, sympy_expr_to_scl, format_variable_name, get_target_scl_name
|
||||
from .processor_utils import (
|
||||
get_sympy_representation,
|
||||
sympy_expr_to_scl,
|
||||
format_variable_name,
|
||||
get_target_scl_name,
|
||||
)
|
||||
from .symbol_manager import SymbolManager, extract_plc_variable_name
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed"
|
||||
|
||||
def process_timer(instruction, network_id, sympy_map, symbol_manager: SymbolManager, data):
|
||||
|
||||
def process_timer(
|
||||
instruction, network_id, sympy_map, symbol_manager: SymbolManager, data
|
||||
):
|
||||
"""
|
||||
Genera SCL para Temporizadores (TON, TOF) directamente.
|
||||
Requiere datos de instancia.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
instr_type_original = instruction.get("type", "").replace(SCL_SUFFIX,"").replace("_error","") # TON o TOF
|
||||
if instruction.get("type","").endswith(SCL_SUFFIX) or "_error" in instruction.get("type",""):
|
||||
instr_type_original = (
|
||||
instruction.get("type", "").replace(SCL_SUFFIX, "").replace("_error", "")
|
||||
) # TON o TOF
|
||||
if instruction.get("type", "").endswith(SCL_SUFFIX) or "_error" in instruction.get(
|
||||
"type", ""
|
||||
):
|
||||
return False
|
||||
|
||||
scl_timer_type = instr_type_original.upper()
|
||||
if scl_timer_type not in ["TON", "TOF", "TP"]:
|
||||
instruction["scl"] = f"// ERROR: Tipo de temporizador directo no soportado: {instr_type_original}"
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
instruction["scl"] = (
|
||||
f"// ERROR: Tipo de temporizador directo no soportado: {instr_type_original}"
|
||||
)
|
||||
instruction["type"] = instr_type_original + "_error"
|
||||
return True
|
||||
|
||||
# 1. Obtener Inputs: IN, PT, y nombre de instancia (implícito o explícito)
|
||||
in_info = instruction["inputs"].get("IN")
|
||||
pt_info = instruction["inputs"].get("PT")
|
||||
# Buscar instancia: ¿está en inputs? ¿o como instance_db?
|
||||
instance_plc_name = instruction.get("instance_db") # Buscar primero aquí
|
||||
instance_plc_name = instruction.get("instance_db") # Buscar primero aquí
|
||||
if not instance_plc_name:
|
||||
# Si no, buscar un input llamado 'timer' o similar? No estándar.
|
||||
# Asumir que debe estar declarado como STAT si no hay instance_db
|
||||
instance_plc_name = instruction.get("instance_name") # Nombre directo?
|
||||
instance_plc_name = instruction.get("instance_name") # Nombre directo?
|
||||
if not instance_plc_name:
|
||||
instance_plc_name = f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder final
|
||||
print(f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_plc_name}'.")
|
||||
instance_plc_name = (
|
||||
f"#{scl_timer_type}_INSTANCE_{instr_uid}" # Placeholder final
|
||||
)
|
||||
print(
|
||||
f"Advertencia: No se encontró nombre/instancia para {instr_type_original} UID {instr_uid}. Usando placeholder '{instance_plc_name}'."
|
||||
)
|
||||
|
||||
|
||||
sympy_in_expr = get_sympy_representation(in_info, network_id, sympy_map, symbol_manager)
|
||||
sympy_or_const_pt = get_sympy_representation(pt_info, network_id, sympy_map, symbol_manager)
|
||||
sympy_in_expr = get_sympy_representation(
|
||||
in_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
sympy_or_const_pt = get_sympy_representation(
|
||||
pt_info, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
|
||||
# Verificar dependencias
|
||||
if sympy_in_expr is None or sympy_or_const_pt is None or instance_plc_name is None:
|
||||
|
@ -53,24 +75,32 @@ def process_timer(instruction, network_id, sympy_map, symbol_manager: SymbolMana
|
|||
pt_scl = sympy_expr_to_scl(sympy_or_const_pt, symbol_manager)
|
||||
|
||||
# Generar la llamada SCL
|
||||
scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};"
|
||||
# ENHANCED: Only add TODO comment if it's not an auto-generated placeholder
|
||||
if instance_plc_name.startswith("#") and "_INSTANCE_" in instance_plc_name:
|
||||
# It's an auto-generated placeholder, will be declared automatically in VAR_TEMP
|
||||
scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl});"
|
||||
else:
|
||||
# It's a named instance that might need manual declaration
|
||||
scl_call = f"{instance_name_scl}(IN := {in_scl}, PT := {pt_scl}); // TODO: Declarar {instance_name_scl} : {scl_timer_type};"
|
||||
|
||||
# Actualizar instrucción
|
||||
instruction["scl"] = scl_call # SCL final generado
|
||||
instruction["scl"] = scl_call # SCL final generado
|
||||
instruction["type"] = instr_type_original + SCL_SUFFIX
|
||||
|
||||
# 7. Actualizar sympy_map para las salidas Q, ET y ENO
|
||||
map_key_q = (network_id, instr_uid, "Q") # Pin estándar SCL
|
||||
map_key_q = (network_id, instr_uid, "Q") # Pin estándar SCL
|
||||
# *** Store SymPy Symbol for boolean output Q ***
|
||||
q_output_scl_access = f"{instance_name_scl}.Q" # String for SCL access
|
||||
sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access) # Get/Create Symbol
|
||||
q_output_scl_access = f"{instance_name_scl}.Q" # String for SCL access
|
||||
sympy_q_symbol = symbol_manager.get_symbol(q_output_scl_access) # Get/Create Symbol
|
||||
if sympy_q_symbol:
|
||||
sympy_map[map_key_q] = sympy_q_symbol # Store the SYMBOL
|
||||
sympy_map[map_key_q] = sympy_q_symbol # Store the SYMBOL
|
||||
else:
|
||||
print(f"Error: Could not create symbol for {q_output_scl_access} in {instr_type_original} {instr_uid}")
|
||||
print(
|
||||
f"Error: Could not create symbol for {q_output_scl_access} in {instr_type_original} {instr_uid}"
|
||||
)
|
||||
sympy_map[map_key_q] = None
|
||||
|
||||
map_key_et = (network_id, instr_uid, "ET") # Pin estándar SCL
|
||||
map_key_et = (network_id, instr_uid, "ET") # Pin estándar SCL
|
||||
# ET is TIME, store SCL access string
|
||||
sympy_map[map_key_et] = f"{instance_name_scl}.ET"
|
||||
|
||||
|
@ -83,16 +113,21 @@ def process_timer(instruction, network_id, sympy_map, symbol_manager: SymbolMana
|
|||
sympy_map[map_key_eno] = sympy.true
|
||||
|
||||
# *** Also handle common aliases ***
|
||||
map_key_out = (network_id, instr_uid, "out") # Some connections might look for "out"
|
||||
map_key_out = (
|
||||
network_id,
|
||||
instr_uid,
|
||||
"out",
|
||||
) # Some connections might look for "out"
|
||||
sympy_map[map_key_out] = sympy_q_symbol # Map "out" to Q output
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# --- Processor Information Function ---
|
||||
def get_processor_info():
|
||||
"""Devuelve info para TON, TOF y TP directos."""
|
||||
return [
|
||||
{'type_name': 'ton', 'processor_func': process_timer, 'priority': 5},
|
||||
{'type_name': 'tof', 'processor_func': process_timer, 'priority': 5},
|
||||
{'type_name': 'tp', 'processor_func': process_timer, 'priority': 5}
|
||||
{"type_name": "ton", "processor_func": process_timer, "priority": 5},
|
||||
{"type_name": "tof", "processor_func": process_timer, "priority": 5},
|
||||
{"type_name": "tp", "processor_func": process_timer, "priority": 5},
|
||||
]
|
|
@ -4,7 +4,8 @@ import re
|
|||
import sympy
|
||||
from .symbol_manager import SymbolManager, extract_plc_variable_name
|
||||
|
||||
SCL_SUFFIX = "_sympy_processed" # <<< AÑADE ESTA LÍNEA
|
||||
SCL_SUFFIX = "_sympy_processed" # <<< AÑADE ESTA LÍNEA
|
||||
|
||||
|
||||
def format_variable_name(name):
|
||||
"""Limpia el nombre de la variable para SCL."""
|
||||
|
@ -21,18 +22,21 @@ def format_variable_name(name):
|
|||
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
||||
return prefix + name
|
||||
|
||||
|
||||
def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager):
|
||||
"""Gets the SymPy expression object representing the source."""
|
||||
if not source_info:
|
||||
print("Warning: get_sympy_representation called with None source_info.")
|
||||
return None # Or raise error
|
||||
return None # Or raise error
|
||||
|
||||
# Handle lists (OR branches) - Recursively call and combine with sympy.Or
|
||||
if isinstance(source_info, list):
|
||||
sympy_parts = []
|
||||
all_resolved = True
|
||||
for sub_source in source_info:
|
||||
sub_sympy = get_sympy_representation(sub_source, network_id, sympy_map, symbol_manager)
|
||||
sub_sympy = get_sympy_representation(
|
||||
sub_source, network_id, sympy_map, symbol_manager
|
||||
)
|
||||
if sub_sympy is None:
|
||||
all_resolved = False
|
||||
break
|
||||
|
@ -41,7 +45,7 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
|||
if not all_resolved:
|
||||
return None
|
||||
if not sympy_parts:
|
||||
return sympy.false # Empty OR is false
|
||||
return sympy.false # Empty OR is false
|
||||
# Return sympy.Or only if there are multiple parts
|
||||
return sympy.Or(*sympy_parts) if len(sympy_parts) > 1 else sympy_parts[0]
|
||||
|
||||
|
@ -56,8 +60,8 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
|||
if plc_name:
|
||||
return symbol_manager.get_symbol(plc_name)
|
||||
else:
|
||||
print(f"Error: Variable source without name: {source_info}")
|
||||
return None # Error case
|
||||
print(f"Error: Variable source without name: {source_info}")
|
||||
return None # Error case
|
||||
elif source_type == "constant":
|
||||
# Represent constants directly if possible, otherwise maybe as symbols?
|
||||
# For boolean simplification, only TRUE/FALSE matter significantly.
|
||||
|
@ -70,7 +74,7 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
|||
# Or just return their string representation if they won't be simplified anyway?
|
||||
# Let's return their string value for now, processors will handle it.
|
||||
# This might need refinement if constants need symbolic handling.
|
||||
return str(value) # Or maybe symbol_manager.get_symbol(str(value))?
|
||||
return str(value) # Or maybe symbol_manager.get_symbol(str(value))?
|
||||
elif source_type == "connection":
|
||||
map_key = (
|
||||
network_id,
|
||||
|
@ -78,7 +82,9 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
|||
source_info.get("source_pin"),
|
||||
)
|
||||
# Return the SymPy object from the map
|
||||
return sympy_map.get(map_key) # Returns None if not found (dependency not ready)
|
||||
return sympy_map.get(
|
||||
map_key
|
||||
) # Returns None if not found (dependency not ready)
|
||||
# Nueva lógica para manejar parámetros de tipo Address
|
||||
elif source_type == "unknown_structure" and source_scope == "Address":
|
||||
# Crear una representación especial para direcciones indirectas
|
||||
|
@ -112,16 +118,20 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
|||
return addr_ref
|
||||
elif source_type == "unknown_source":
|
||||
print(f"Warning: Referring to unknown source UID: {source_info.get('uid')}")
|
||||
return None # Cannot resolve
|
||||
return None # Cannot resolve
|
||||
else:
|
||||
print(f"Warning: Unknown source type: {source_info}")
|
||||
return None # Cannot resolve
|
||||
return None # Cannot resolve
|
||||
|
||||
|
||||
def sympy_expr_to_scl(expr, symbol_manager, format_prec=5):
|
||||
"""Converts a SymPy expression to an SCL string using the symbol map."""
|
||||
if expr is None: return "/* ERROR: None expression */"
|
||||
if expr == sympy.true: return "TRUE"
|
||||
if expr == sympy.false: return "FALSE"
|
||||
if expr is None:
|
||||
return "/* ERROR: None expression */"
|
||||
if expr == sympy.true:
|
||||
return "TRUE"
|
||||
if expr == sympy.false:
|
||||
return "FALSE"
|
||||
|
||||
# Use sympy's string printer with custom settings if needed
|
||||
# For boolean, standard printing might be okay, but need to substitute symbols
|
||||
|
@ -131,27 +141,29 @@ def sympy_expr_to_scl(expr, symbol_manager, format_prec=5):
|
|||
|
||||
# Substitute symbols back to their py_id strings first
|
||||
# Need to handle the structure (And, Or, Not)
|
||||
scl_str = sympy.sstr(expr, order=None) # Basic string representation
|
||||
scl_str = sympy.sstr(expr, order=None) # Basic string representation
|
||||
|
||||
# Now, carefully replace py_id back to PLC names using regex
|
||||
# Sort keys by length descending to replace longer IDs first
|
||||
for py_id in sorted(inverse_map.keys(), key=len, reverse=True):
|
||||
# Use word boundaries to avoid replacing parts of other IDs
|
||||
scl_str = re.sub(r'\b' + re.escape(py_id) + r'\b', inverse_map[py_id], scl_str)
|
||||
scl_str = re.sub(
|
||||
r"\b" + re.escape(py_id) + r"\b", inverse_map[py_id], scl_str
|
||||
)
|
||||
|
||||
# Replace SymPy operators/functions with SCL equivalents
|
||||
scl_str = scl_str.replace('&', ' AND ')
|
||||
scl_str = scl_str.replace('|', ' OR ')
|
||||
scl_str = scl_str.replace('^', ' XOR ') # If XOR is used
|
||||
scl_str = scl_str.replace('~', 'NOT ')
|
||||
scl_str = scl_str.replace("&", " AND ")
|
||||
scl_str = scl_str.replace("|", " OR ")
|
||||
scl_str = scl_str.replace("^", " XOR ") # If XOR is used
|
||||
scl_str = scl_str.replace("~", "NOT ")
|
||||
# Add spaces around operators if needed after substitution
|
||||
scl_str = re.sub(r'AND', ' AND ', scl_str)
|
||||
scl_str = re.sub(r'OR', ' OR ', scl_str)
|
||||
scl_str = re.sub(r'XOR', ' XOR ', scl_str)
|
||||
scl_str = re.sub(r'NOT', 'NOT ', scl_str) # Space after NOT
|
||||
scl_str = re.sub(r"AND", " AND ", scl_str)
|
||||
scl_str = re.sub(r"OR", " OR ", scl_str)
|
||||
scl_str = re.sub(r"XOR", " XOR ", scl_str)
|
||||
scl_str = re.sub(r"NOT", "NOT ", scl_str) # Space after NOT
|
||||
|
||||
# Clean up potential double spaces, etc.
|
||||
scl_str = re.sub(r'\s+', ' ', scl_str).strip()
|
||||
scl_str = re.sub(r"\s+", " ", scl_str).strip()
|
||||
# Handle parentheses potentially added by sstr - maybe remove redundant ones?
|
||||
# Be careful not to break operator precedence.
|
||||
|
||||
|
@ -162,6 +174,7 @@ def sympy_expr_to_scl(expr, symbol_manager, format_prec=5):
|
|||
traceback.print_exc()
|
||||
return f"/* ERROR converting SymPy: {expr} */"
|
||||
|
||||
|
||||
def get_scl_representation(source_info, network_id, scl_map, access_map):
|
||||
if not source_info:
|
||||
return None
|
||||
|
@ -254,6 +267,7 @@ def get_scl_representation(source_info, network_id, scl_map, access_map):
|
|||
print(f"Advertencia: Tipo de fuente desconocido: {source_info}")
|
||||
return f"_ERR_INVALID_SRC_TYPE_"
|
||||
|
||||
|
||||
def format_variable_name(name):
|
||||
"""Limpia el nombre de la variable para SCL."""
|
||||
if not name:
|
||||
|
@ -282,6 +296,7 @@ def format_variable_name(name):
|
|||
|
||||
return prefix + name
|
||||
|
||||
|
||||
def generate_temp_var_name(network_id, instr_uid, pin_name):
|
||||
net_id_clean = str(network_id).replace("-", "_")
|
||||
instr_uid_clean = str(instr_uid).replace("-", "_")
|
||||
|
@ -289,13 +304,19 @@ def generate_temp_var_name(network_id, instr_uid, pin_name):
|
|||
# Usar # para variables temporales SCL estándar
|
||||
return f"#_temp_{net_id_clean}_{instr_uid_clean}_{pin_name_clean}"
|
||||
|
||||
|
||||
def get_target_scl_name(instruction, pin_name, network_id, default_to_temp=True):
|
||||
"""Gets the SCL formatted name for a target variable.
|
||||
Handles instruction outputs AND specific inputs like Coil operand.
|
||||
Handles instruction outputs AND specific inputs like Coil operand.
|
||||
"""
|
||||
instr_uid = instruction["instruction_uid"]
|
||||
# Ahora SCL_SUFFIX está definido en este módulo
|
||||
instr_type_upper = instruction.get("type", "").upper().replace(SCL_SUFFIX.upper(), "").replace("_ERROR", "") # Check original type
|
||||
instr_type_upper = (
|
||||
instruction.get("type", "")
|
||||
.upper()
|
||||
.replace(SCL_SUFFIX.upper(), "")
|
||||
.replace("_ERROR", "")
|
||||
) # Check original type
|
||||
target_info = None
|
||||
|
||||
# Special handling for inputs that represent the target variable
|
||||
|
@ -308,8 +329,12 @@ def get_target_scl_name(instruction, pin_name, network_id, default_to_temp=True)
|
|||
# Default: Assume pin_name refers to an output pin
|
||||
output_pin_data = instruction.get("outputs", {}).get(pin_name)
|
||||
# Check if it's a list and has one connection (standard case)
|
||||
if (output_pin_data and isinstance(output_pin_data, list) and len(output_pin_data) == 1):
|
||||
target_info = output_pin_data[0]
|
||||
if (
|
||||
output_pin_data
|
||||
and isinstance(output_pin_data, list)
|
||||
and len(output_pin_data) == 1
|
||||
):
|
||||
target_info = output_pin_data[0]
|
||||
# Add handling for direct output assignment if your JSON structure supports it
|
||||
|
||||
target_scl = None
|
||||
|
@ -317,9 +342,11 @@ def get_target_scl_name(instruction, pin_name, network_id, default_to_temp=True)
|
|||
if target_info.get("type") == "variable":
|
||||
plc_name = target_info.get("name")
|
||||
if plc_name:
|
||||
target_scl = format_variable_name(plc_name) # Use existing util
|
||||
target_scl = format_variable_name(plc_name) # Use existing util
|
||||
else:
|
||||
print(f"Error: Target variable for {instr_uid}.{pin_name} has no name (UID: {target_info.get('uid')}).")
|
||||
print(
|
||||
f"Error: Target variable for {instr_uid}.{pin_name} has no name (UID: {target_info.get('uid')})."
|
||||
)
|
||||
elif target_info.get("type") == "unknown_structure":
|
||||
# Handle direct memory addresses like DB960.X448.0
|
||||
area = target_info.get("Area")
|
||||
|
@ -353,24 +380,29 @@ def get_target_scl_name(instruction, pin_name, network_id, default_to_temp=True)
|
|||
target_scl = f"%{area}D{byte_offset}"
|
||||
else:
|
||||
target_scl = f"%{area}{byte_offset}"
|
||||
print(f"INFO: Converted direct address to SCL: {target_scl} for {instr_uid}.{pin_name}")
|
||||
print(
|
||||
f"INFO: Converted direct address to SCL: {target_scl} for {instr_uid}.{pin_name}"
|
||||
)
|
||||
else:
|
||||
print(f"Error: Incomplete address info for {instr_uid}.{pin_name}: Area={area}, Block={block_number}, Offset={bit_offset}")
|
||||
print(
|
||||
f"Error: Incomplete address info for {instr_uid}.{pin_name}: Area={area}, Block={block_number}, Offset={bit_offset}"
|
||||
)
|
||||
elif target_info.get("type") == "constant":
|
||||
print(f"Advertencia: Attempt to write to constant target {instr_uid}.{pin_name} (UID: {target_info.get('uid')}).")
|
||||
print(
|
||||
f"Advertencia: Attempt to write to constant target {instr_uid}.{pin_name} (UID: {target_info.get('uid')})."
|
||||
)
|
||||
# else: # Handle other target types if needed
|
||||
# print(f"Advertencia: Target {instr_uid}.{pin_name} is not a variable: {target_info.get('type')}.")
|
||||
# else: # No target info found for the specified pin
|
||||
# print(f"DEBUG: No target info found for {instr_uid}.{pin_name}")
|
||||
# else: # No target info found for the specified pin
|
||||
# print(f"DEBUG: No target info found for {instr_uid}.{pin_name}")
|
||||
pass
|
||||
|
||||
|
||||
# Handle default_to_temp logic
|
||||
if target_scl:
|
||||
return target_scl
|
||||
elif default_to_temp:
|
||||
# Generate temp only if no explicit target was found AND default is allowed
|
||||
print(f"INFO: Generating temp var for {instr_uid}.{pin_name}") # Be informative
|
||||
print(f"INFO: Generating temp var for {instr_uid}.{pin_name}") # Be informative
|
||||
return generate_temp_var_name(network_id, instr_uid, pin_name)
|
||||
else:
|
||||
# No target found and default temps not allowed
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import sympy
|
||||
import re
|
||||
|
||||
|
||||
class SymbolManager:
|
||||
def __init__(self):
|
||||
# plc_name -> py_id (e.g., '"DB".Var' -> 'v0_')
|
||||
|
@ -12,25 +13,27 @@ class SymbolManager:
|
|||
self.py_id_to_plc = {}
|
||||
self.counter = 0
|
||||
# Pre-define common keywords/constants to avoid mapping them
|
||||
self.reserved_names = {"TRUE", "FALSE"} # Add others if needed
|
||||
self.reserved_names = {"TRUE", "FALSE"} # Add others if needed
|
||||
|
||||
def _generate_py_id(self):
|
||||
py_id = f"v{self.counter}_"
|
||||
self.counter += 1
|
||||
# Extremely unlikely collision, but check anyway
|
||||
while py_id in self.py_id_to_symbol:
|
||||
py_id = f"v{self.counter}_"
|
||||
self.counter += 1
|
||||
py_id = f"v{self.counter}_"
|
||||
self.counter += 1
|
||||
return py_id
|
||||
|
||||
def get_symbol(self, plc_var_name):
|
||||
"""Gets/Creates a SymPy Symbol for a PLC variable name."""
|
||||
if plc_var_name is None:
|
||||
print("Warning: Attempted to get symbol for None PLC name.")
|
||||
return None # Or handle error appropriately
|
||||
return None # Or handle error appropriately
|
||||
if plc_var_name.upper() in self.reserved_names:
|
||||
print(f"Warning: Attempted to create symbol for reserved name: {plc_var_name}")
|
||||
return None # Or handle differently (e.g., return sympy.true/false?)
|
||||
print(
|
||||
f"Warning: Attempted to create symbol for reserved name: {plc_var_name}"
|
||||
)
|
||||
return None # Or handle differently (e.g., return sympy.true/false?)
|
||||
|
||||
if plc_var_name not in self.plc_to_py_id:
|
||||
py_id = self._generate_py_id()
|
||||
|
@ -51,6 +54,7 @@ class SymbolManager:
|
|||
"""Returns the map needed for postprocessing (py_id -> plc_name)."""
|
||||
return self.py_id_to_plc.copy()
|
||||
|
||||
|
||||
# Helper function to extract PLC variable name from JSON operand info
|
||||
def extract_plc_variable_name(operand_info):
|
||||
if operand_info and operand_info.get("type") == "variable":
|
||||
|
@ -88,4 +92,4 @@ def extract_plc_variable_name(operand_info):
|
|||
return f"%{area}D{byte_offset}"
|
||||
else:
|
||||
return f"%{area}{byte_offset}"
|
||||
return None # Not a variable or info missing
|
||||
return None # Not a variable or info missing
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
// FB1809
|
||||
// Block Type: FB
|
||||
// Block Name (Original): SyrBrix Autocorrection
|
||||
// Block Number: 1809
|
||||
// Original Network Languages: LAD
|
||||
// Block Comment:
|
||||
// Syrup Autocorrection means that the measured syrup brix AND syrup density from
|
||||
// meter, are used instead
|
||||
// OF the one from the recipe, TO calculate the volumetric ratio.
|
||||
// Activated only IF the beverage is sugar based.
|
||||
|
||||
FUNCTION_BLOCK "SyrBrix_Autocorrection"
|
||||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
i_Value : Real;
|
||||
i_Num : Int;
|
||||
i_Enable : Bool;
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
FilterOut : Real;
|
||||
END_VAR
|
||||
|
||||
VAR_STAT
|
||||
mProdSyrFact : Real;
|
||||
mSyrMFMFact : Real;
|
||||
mAuxONS : Bool;
|
||||
mAuxONS1 : Bool;
|
||||
mAuxONS2 : Bool;
|
||||
mSyrBrix_AutoCorrReqTPON : Bool;
|
||||
Syrup_Fact_Fltd : "LowPassFilter";
|
||||
SyrupMFM_Fact_Fltd : "LowPassFilter";
|
||||
mSyrBrix_AutoCorrReqTP : "TP:v1.0";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
Latch_ONS : Bool;
|
||||
sec2_ONS : Bool;
|
||||
mProdSyrFactAcq : Bool;
|
||||
mZeroTest : Bool;
|
||||
mZeroTest1 : Bool;
|
||||
SyrBrix : Real;
|
||||
SyrBrixMaxCorr : Real;
|
||||
SyrBrixMaxValveOp : Real;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
|
||||
// Network 1: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Qualifier" := "gBlenderProdMode" AND "Blender_Variables_Pers"."gSugarBeverage" AND "Procedure_Variables"."First_Production"."Done" AND "gBlenderSuppliesOk" AND NOT "Blender_Variables_Pers"."gWaterRecipe" AND NOT "gBlenderRinseMode";
|
||||
|
||||
// Network 2: SyrBrix_AutoCorrReq (Original Language: LAD)
|
||||
|
||||
// PBox SymPy processed, logic in consumer
|
||||
"SyrAutoCorrReq" := "HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch"; // P_TRIG("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch") - Mem: "SyrAutoCorrReq"
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Request" := ("Procedure_Variables"."Syr_RunOut"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch") OR ("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "SyrAutoCorrReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
// Network 3: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Reset" := NOT "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 4: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Wait" := NOT "gBlenderEnToRamp" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mPDS_SYR_PA_Data"."Input_From_mPDS"."gPAmPDS_CommErr") OR ("gFTP302_Fault" AND NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter");
|
||||
|
||||
// Network 5: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Latch" := ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done") OR ("Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done");
|
||||
|
||||
// Network 6: MIX - (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 28_dup4
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch"; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
|
||||
"Latch_ONS" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "SyrAutoCorrLatch";
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch"; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Running" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Wait";
|
||||
|
||||
// Network 7: (Original Language: LAD)
|
||||
|
||||
SyrBrix_SyrupCorrPerc();
|
||||
|
||||
// Network 8: MIX - Blender Variables - Persistent (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 26_dup1
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S"; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
|
||||
"sec2_ONS" := "AUX Blink_2.0S" AND NOT "SyrAutoCorrBlink2Sec";
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S"; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
|
||||
// Network 9: (Original Language: LAD)
|
||||
|
||||
"mProdSyrFactAcq" := "gBlenderEnToRamp" AND "gPV_SyrBrixOk" AND "sec2_ONS" AND "Procedure_Variables"."First_Production"."Done" AND "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 10: (Original Language: LAD)
|
||||
|
||||
"mZeroTest" := Eq("mProdSyrFact", 0) OR Eq("Blender_Variables"."gProdRunSyrFact", 0);
|
||||
|
||||
// Network 11: (Original Language: LAD)
|
||||
|
||||
"mZeroTest1" := Eq("mSyrMFMFact", 0) OR Eq("Blender_Variables"."gProdRunSyrMFMFact", 0);
|
||||
|
||||
// Network 12: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during the production, so when the
|
||||
// Surup Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Blender_Variables"."gMeterSyrBrix", 0.0), IN0 := "Blender_Variables"."gMeterSyrBrix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mProdSyrFactAcq" AND NOT "mZeroTest" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 13: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during production, so when the Syrup
|
||||
// Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Profibus_Variables"."gFTP302_Brix", 0.0), IN0 := "Profibus_Variables"."gFTP302_Brix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "mZeroTest1" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 14: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" OR "gBlenderCIPMode" OR "gBlenderRinseMode" THEN
|
||||
"mProdSyrFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 15: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest1" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" THEN
|
||||
"mSyrMFMFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrMFMFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 16: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"Syrup_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 17: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"SyrupMFM_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 18: (Original Language: LAD)
|
||||
// ??
|
||||
|
||||
IF "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE" THEN
|
||||
"HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor" := "Blender_Variables"."gProdRunSyrFact" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
END_IF;
|
||||
|
||||
"HMI_Variables_Cmd"."Recipe_Updates"."SyrFactUpdate" := "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE";
|
||||
|
||||
// Network 19: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
"gMinRatio" := "HMI_PID"."RMP302"."Out" > 80.0;
|
||||
|
||||
// Network 20: MIX - Maximum Syrup Brix Autocorr Acheaved maximum autocorr per (Original Language: LAD)
|
||||
// Creato due parametri per la Massima Correzzione.
|
||||
// per la Produzione con Brix Sciroppo inferiori a 15 la massima correzzione
|
||||
// passada 40 a 10. W.O.28/01/2025
|
||||
|
||||
"M1743.5" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" <= 15.0;
|
||||
|
||||
IF "AUX TRUE" AND NOT "M_validat_27_01_25" AND NOT "M1743.5" THEN
|
||||
"SyrBrixMaxCorr" := 40.0;
|
||||
END_IF;
|
||||
|
||||
IF "AUX TRUE" AND "M1743.5" AND NOT "M_validat_27_01_25" THEN
|
||||
"SyrBrixMaxCorr" := 10.0;
|
||||
END_IF;
|
||||
|
||||
"gMaxSyrAutoCorrDone" := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
_HMI_Alarms___gH_Status__8_ := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
// Network 21: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMinRatio" := FALSE;
|
||||
END_IF;
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMaxSyrAutoCorrDone" := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 22: (Original Language: LAD)
|
||||
|
||||
"HMI_Variables_Status"."Procedures"."SyrBrixAutoRun" := "Procedure_Variables"."SyrAuto_Corr"."Latch";
|
||||
|
||||
// Network 23: Syrup Autocorrection Running (Original Language: LAD)
|
||||
// Message Syrup Autocorrection Running TO HMI
|
||||
|
||||
_HMI_Alarms___gH_Status__4_ := "Procedure_Variables"."SyrAuto_Corr"."Running";
|
||||
|
||||
// Network 24: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Done" := ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMinRatio" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMaxSyrAutoCorrDone" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "Latch_ONS" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
END_FUNCTION_BLOCK
|
|
@ -0,0 +1,219 @@
|
|||
// FB1809
|
||||
// Block Type: FB
|
||||
// Block Name (Original): SyrBrix Autocorrection
|
||||
// Block Number: 1809
|
||||
// Original Network Languages: LAD
|
||||
// Block Comment:
|
||||
// Syrup Autocorrection means that the measured syrup brix AND syrup density from
|
||||
// meter, are used instead
|
||||
// OF the one from the recipe, TO calculate the volumetric ratio.
|
||||
// Activated only IF the beverage is sugar based.
|
||||
|
||||
FUNCTION_BLOCK "SyrBrix_Autocorrection"
|
||||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
i_Value : Real;
|
||||
i_Num : Int;
|
||||
i_Enable : Bool;
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
FilterOut : Real;
|
||||
END_VAR
|
||||
|
||||
VAR_STAT
|
||||
mProdSyrFact : Real;
|
||||
mSyrMFMFact : Real;
|
||||
mAuxONS : Bool;
|
||||
mAuxONS1 : Bool;
|
||||
mAuxONS2 : Bool;
|
||||
mSyrBrix_AutoCorrReqTPON : Bool;
|
||||
Syrup_Fact_Fltd : "LowPassFilter";
|
||||
SyrupMFM_Fact_Fltd : "LowPassFilter";
|
||||
mSyrBrix_AutoCorrReqTP : "TP:v1.0";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
Latch_ONS : Bool;
|
||||
sec2_ONS : Bool;
|
||||
mProdSyrFactAcq : Bool;
|
||||
mZeroTest : Bool;
|
||||
mZeroTest1 : Bool;
|
||||
SyrBrix : Real;
|
||||
SyrBrixMaxCorr : Real;
|
||||
SyrBrixMaxValveOp : Real;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
|
||||
// Network 1: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Qualifier" := "gBlenderProdMode" AND "Blender_Variables_Pers"."gSugarBeverage" AND "Procedure_Variables"."First_Production"."Done" AND "gBlenderSuppliesOk" AND NOT "Blender_Variables_Pers"."gWaterRecipe" AND NOT "gBlenderRinseMode";
|
||||
|
||||
// Network 2: SyrBrix_AutoCorrReq (Original Language: LAD)
|
||||
|
||||
// PBox SymPy processed, logic in consumer
|
||||
"SyrAutoCorrReq" := "HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch"; // P_TRIG("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch") - Mem: "SyrAutoCorrReq"
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Request" := ("Procedure_Variables"."Syr_RunOut"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch") OR ("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "SyrAutoCorrReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
// Network 3: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Reset" := NOT "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 4: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Wait" := NOT "gBlenderEnToRamp" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mPDS_SYR_PA_Data"."Input_From_mPDS"."gPAmPDS_CommErr") OR ("gFTP302_Fault" AND NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter");
|
||||
|
||||
// Network 5: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Latch" := ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done") OR ("Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done");
|
||||
|
||||
// Network 6: MIX - (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 28_dup4
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch"; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch";; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
"Latch_ONS" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "SyrAutoCorrLatch";
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Running" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Wait";
|
||||
|
||||
// Network 7: (Original Language: LAD)
|
||||
|
||||
SyrBrix_SyrupCorrPerc();
|
||||
|
||||
// Network 8: MIX - Blender Variables - Persistent (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 26_dup1
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S"; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S";; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
"sec2_ONS" := "AUX Blink_2.0S" AND NOT "SyrAutoCorrBlink2Sec";
|
||||
|
||||
// Network 9: (Original Language: LAD)
|
||||
|
||||
"mProdSyrFactAcq" := "gBlenderEnToRamp" AND "gPV_SyrBrixOk" AND "sec2_ONS" AND "Procedure_Variables"."First_Production"."Done" AND "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 10: (Original Language: LAD)
|
||||
|
||||
"mZeroTest" := Eq("mProdSyrFact", 0) OR Eq("Blender_Variables"."gProdRunSyrFact", 0);
|
||||
|
||||
// Network 11: (Original Language: LAD)
|
||||
|
||||
"mZeroTest1" := Eq("mSyrMFMFact", 0) OR Eq("Blender_Variables"."gProdRunSyrMFMFact", 0);
|
||||
|
||||
// Network 12: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during the production, so when the
|
||||
// Surup Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Blender_Variables"."gMeterSyrBrix", 0.0), IN0 := "Blender_Variables"."gMeterSyrBrix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mProdSyrFactAcq" AND NOT "mZeroTest" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 13: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during production, so when the Syrup
|
||||
// Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Profibus_Variables"."gFTP302_Brix", 0.0), IN0 := "Profibus_Variables"."gFTP302_Brix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "mZeroTest1" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 14: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" OR "gBlenderCIPMode" OR "gBlenderRinseMode" THEN
|
||||
"mProdSyrFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 15: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest1" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" THEN
|
||||
"mSyrMFMFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrMFMFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 16: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"Syrup_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 17: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"SyrupMFM_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 18: (Original Language: LAD)
|
||||
// ??
|
||||
|
||||
IF "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE" THEN
|
||||
"HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor" := "Blender_Variables"."gProdRunSyrFact" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
END_IF;
|
||||
|
||||
"HMI_Variables_Cmd"."Recipe_Updates"."SyrFactUpdate" := "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE";
|
||||
|
||||
// Network 19: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
"gMinRatio" := "HMI_PID"."RMP302"."Out" > 80.0;
|
||||
|
||||
// Network 20: MIX - Maximum Syrup Brix Autocorr Acheaved maximum autocorr per (Original Language: LAD)
|
||||
// Creato due parametri per la Massima Correzzione.
|
||||
// per la Produzione con Brix Sciroppo inferiori a 15 la massima correzzione
|
||||
// passada 40 a 10. W.O.28/01/2025
|
||||
|
||||
"M1743.5" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" <= 15.0;
|
||||
|
||||
IF "AUX TRUE" AND NOT "M_validat_27_01_25" AND NOT "M1743.5" THEN
|
||||
"SyrBrixMaxCorr" := 40.0;
|
||||
END_IF;
|
||||
|
||||
IF "AUX TRUE" AND "M1743.5" AND NOT "M_validat_27_01_25" THEN
|
||||
"SyrBrixMaxCorr" := 10.0;
|
||||
END_IF;
|
||||
|
||||
"gMaxSyrAutoCorrDone" := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
_HMI_Alarms___gH_Status__8_ := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
// Network 21: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMinRatio" := FALSE;
|
||||
END_IF;
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMaxSyrAutoCorrDone" := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 22: (Original Language: LAD)
|
||||
|
||||
"HMI_Variables_Status"."Procedures"."SyrBrixAutoRun" := "Procedure_Variables"."SyrAuto_Corr"."Latch";
|
||||
|
||||
// Network 23: Syrup Autocorrection Running (Original Language: LAD)
|
||||
// Message Syrup Autocorrection Running TO HMI
|
||||
|
||||
_HMI_Alarms___gH_Status__4_ := "Procedure_Variables"."SyrAuto_Corr"."Running";
|
||||
|
||||
// Network 24: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Done" := ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMinRatio" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMaxSyrAutoCorrDone" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "Latch_ONS" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
END_FUNCTION_BLOCK
|
|
@ -0,0 +1,219 @@
|
|||
// FB1809
|
||||
// Block Type: FB
|
||||
// Block Name (Original): SyrBrix Autocorrection
|
||||
// Block Number: 1809
|
||||
// Original Network Languages: LAD
|
||||
// Block Comment:
|
||||
// Syrup Autocorrection means that the measured syrup brix AND syrup density from
|
||||
// meter, are used instead
|
||||
// OF the one from the recipe, TO calculate the volumetric ratio.
|
||||
// Activated only IF the beverage is sugar based.
|
||||
|
||||
FUNCTION_BLOCK "SyrBrix_Autocorrection"
|
||||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
i_Value : Real;
|
||||
i_Num : Int;
|
||||
i_Enable : Bool;
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
FilterOut : Real;
|
||||
END_VAR
|
||||
|
||||
VAR_STAT
|
||||
mProdSyrFact : Real;
|
||||
mSyrMFMFact : Real;
|
||||
mAuxONS : Bool;
|
||||
mAuxONS1 : Bool;
|
||||
mAuxONS2 : Bool;
|
||||
mSyrBrix_AutoCorrReqTPON : Bool;
|
||||
Syrup_Fact_Fltd : "LowPassFilter";
|
||||
SyrupMFM_Fact_Fltd : "LowPassFilter";
|
||||
mSyrBrix_AutoCorrReqTP : "TP:v1.0";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
Latch_ONS : Bool;
|
||||
sec2_ONS : Bool;
|
||||
mProdSyrFactAcq : Bool;
|
||||
mZeroTest : Bool;
|
||||
mZeroTest1 : Bool;
|
||||
SyrBrix : Real;
|
||||
SyrBrixMaxCorr : Real;
|
||||
SyrBrixMaxValveOp : Real;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
|
||||
// Network 1: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Qualifier" := "gBlenderProdMode" AND "Blender_Variables_Pers"."gSugarBeverage" AND "Procedure_Variables"."First_Production"."Done" AND "gBlenderSuppliesOk" AND NOT "Blender_Variables_Pers"."gWaterRecipe" AND NOT "gBlenderRinseMode";
|
||||
|
||||
// Network 2: SyrBrix_AutoCorrReq (Original Language: LAD)
|
||||
|
||||
// PBox SymPy processed, logic in consumer
|
||||
"SyrAutoCorrReq" := "HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch"; // P_TRIG("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch") - Mem: "SyrAutoCorrReq"
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Request" := ("Procedure_Variables"."Syr_RunOut"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch") OR ("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_SyrAutoReq" AND NOT "SyrAutoCorrReq" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
// Network 3: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Reset" := NOT "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 4: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Wait" := NOT "gBlenderEnToRamp" OR ("HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mPDS_SYR_PA_Data"."Input_From_mPDS"."gPAmPDS_CommErr") OR ("gFTP302_Fault" AND NOT "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter");
|
||||
|
||||
// Network 5: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Latch" := ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done") OR ("Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "AUX Start CPU" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Done" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Reset" AND NOT "Procedure_Variables"."Syr_RunOut"."Done");
|
||||
|
||||
// Network 6: MIX - (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 28_dup4
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch"; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
|
||||
"SyrAutoCorrLatch" := "Procedure_Variables"."SyrAuto_Corr"."Latch"; // P_TRIG("Procedure_Variables"."SyrAuto_Corr"."Latch") - Mem: "SyrAutoCorrLatch"
|
||||
"Latch_ONS" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "SyrAutoCorrLatch";
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Running" := "Procedure_Variables"."SyrAuto_Corr"."Latch" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Wait";
|
||||
|
||||
// Network 7: (Original Language: LAD)
|
||||
|
||||
SyrBrix_SyrupCorrPerc();
|
||||
|
||||
// Network 8: MIX - Blender Variables - Persistent (Original Language: LAD)
|
||||
|
||||
// Edge Logic handled by Coil 26_dup1
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S"; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
|
||||
"SyrAutoCorrBlink2Sec" := "AUX Blink_2.0S"; // P_TRIG("AUX Blink_2.0S") - Mem: "SyrAutoCorrBlink2Sec"
|
||||
"sec2_ONS" := "AUX Blink_2.0S" AND NOT "SyrAutoCorrBlink2Sec";
|
||||
|
||||
// Network 9: (Original Language: LAD)
|
||||
|
||||
"mProdSyrFactAcq" := "gBlenderEnToRamp" AND "gPV_SyrBrixOk" AND "sec2_ONS" AND "Procedure_Variables"."First_Production"."Done" AND "Procedure_Variables"."SyrAuto_Corr"."Qualifier";
|
||||
|
||||
// Network 10: (Original Language: LAD)
|
||||
|
||||
"mZeroTest" := Eq("mProdSyrFact", 0) OR Eq("Blender_Variables"."gProdRunSyrFact", 0);
|
||||
|
||||
// Network 11: (Original Language: LAD)
|
||||
|
||||
"mZeroTest1" := Eq("mSyrMFMFact", 0) OR Eq("Blender_Variables"."gProdRunSyrMFMFact", 0);
|
||||
|
||||
// Network 12: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during the production, so when the
|
||||
// Surup Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Blender_Variables"."gMeterSyrBrix", 0.0), IN0 := "Blender_Variables"."gMeterSyrBrix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_SyrBrixMeter" AND "mProdSyrFactAcq" AND NOT "mZeroTest" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 13: (Original Language: LAD)
|
||||
// This segment calculates the Syrup Factor during production, so when the Syrup
|
||||
// Run Out starts OR the Autocorrection is activated, the actual syrup brix
|
||||
// doesn't change, the actual ratio doesn't change too
|
||||
|
||||
SEL_R(G := Eq("Profibus_Variables"."gFTP302_Brix", 0.0), IN0 := "Profibus_Variables"."gFTP302_Brix", IN1 := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix");
|
||||
|
||||
"SyrBrix" := "SyrBrix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
|
||||
"SyrBrix" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" / "SyrBrix";
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "mZeroTest1" THEN
|
||||
SEL_R(G := "Procedure_Variables"."Syr_RunOut"."Latch" OR "Procedure_Variables"."SyrAuto_Corr"."Latch", IN0 := "SyrBrix", IN1 := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 14: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" OR "gBlenderCIPMode" OR "gBlenderRinseMode" THEN
|
||||
"mProdSyrFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 15: (Original Language: LAD)
|
||||
|
||||
IF "mZeroTest1" OR "Procedure_Variables"."First_Production"."Latch" OR "gSyrBrixOutSpec_Fault" THEN
|
||||
"mSyrMFMFact" := 1.0;
|
||||
"Blender_Variables"."gProdRunSyrMFMFact" := 1.0;
|
||||
END_IF;
|
||||
|
||||
// Network 16: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"Syrup_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mProdSyrFact");
|
||||
END_IF;
|
||||
|
||||
// Network 17: (Original Language: LAD)
|
||||
|
||||
IF "mProdSyrFactAcq" AND NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"SyrupMFM_Fact_Fltd"(i_Enable := "AUX TRUE", i_Num := 12, i_Value := "mSyrMFMFact");
|
||||
END_IF;
|
||||
|
||||
// Network 18: (Original Language: LAD)
|
||||
// ??
|
||||
|
||||
IF "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE" THEN
|
||||
"HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor" := "Blender_Variables"."gProdRunSyrFact" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
END_IF;
|
||||
|
||||
"HMI_Variables_Cmd"."Recipe_Updates"."SyrFactUpdate" := "Procedure_Variables"."SyrAuto_Corr"."Request" AND "AUX FALSE";
|
||||
|
||||
// Network 19: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
"gMinRatio" := "HMI_PID"."RMP302"."Out" > 80.0;
|
||||
|
||||
// Network 20: MIX - Maximum Syrup Brix Autocorr Acheaved maximum autocorr per (Original Language: LAD)
|
||||
// Creato due parametri per la Massima Correzzione.
|
||||
// per la Produzione con Brix Sciroppo inferiori a 15 la massima correzzione
|
||||
// passada 40 a 10. W.O.28/01/2025
|
||||
|
||||
"M1743.5" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" <= 15.0;
|
||||
|
||||
IF "AUX TRUE" AND NOT "M_validat_27_01_25" AND NOT "M1743.5" THEN
|
||||
"SyrBrixMaxCorr" := 40.0;
|
||||
END_IF;
|
||||
|
||||
IF "AUX TRUE" AND "M1743.5" AND NOT "M_validat_27_01_25" THEN
|
||||
"SyrBrixMaxCorr" := 10.0;
|
||||
END_IF;
|
||||
|
||||
"gMaxSyrAutoCorrDone" := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
_HMI_Alarms___gH_Status__8_ := "HMI_Variables_Status"."Analog_Values"."SyrupBrixCorrection" > "SyrBrixMaxCorr";
|
||||
|
||||
// Network 21: MIX - Maximum Syrup Brix Autocorr Acheaved - minimum ratio - (Original Language: LAD)
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMinRatio" := FALSE;
|
||||
END_IF;
|
||||
|
||||
IF NOT "Procedure_Variables"."SyrAuto_Corr"."Latch" THEN
|
||||
"gMaxSyrAutoCorrDone" := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 22: (Original Language: LAD)
|
||||
|
||||
"HMI_Variables_Status"."Procedures"."SyrBrixAutoRun" := "Procedure_Variables"."SyrAuto_Corr"."Latch";
|
||||
|
||||
// Network 23: Syrup Autocorrection Running (Original Language: LAD)
|
||||
// Message Syrup Autocorrection Running TO HMI
|
||||
|
||||
_HMI_Alarms___gH_Status__4_ := "Procedure_Variables"."SyrAuto_Corr"."Running";
|
||||
|
||||
// Network 24: (Original Language: LAD)
|
||||
|
||||
"Procedure_Variables"."SyrAuto_Corr"."Done" := ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMinRatio" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Running" AND "gMaxSyrAutoCorrDone" AND "Procedure_Variables"."Syr_RunOut"."Latch") OR ("Procedure_Variables"."SyrAuto_Corr"."Request" AND NOT "Latch_ONS" AND NOT "Procedure_Variables"."Syr_RunOut"."Latch");
|
||||
|
||||
END_FUNCTION_BLOCK
|
|
@ -0,0 +1,269 @@
|
|||
// FB1813
|
||||
// Block Type: FB
|
||||
// Block Name (Original): Syrup Line MFM Prep DAR
|
||||
// Block Number: 1813
|
||||
// Original Network Languages: LAD, STL
|
||||
|
||||
FUNCTION_BLOCK "Syrup_Line_MFM_Prep_DAR"
|
||||
{ S7_Optimized_Access := 'TRUE' }
|
||||
VERSION : 0.1
|
||||
|
||||
VAR_INPUT
|
||||
IN : Bool;
|
||||
PT : Time;
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
Q : Bool;
|
||||
ET : Time;
|
||||
END_VAR
|
||||
|
||||
VAR_STAT
|
||||
mStepNum : Int;
|
||||
mTimeStep : Int;
|
||||
Real_Time : Time;
|
||||
mTransition : Bool;
|
||||
mSyrLineMFMPrepONS : Bool;
|
||||
mSyrupLineManualDrainSR : Bool;
|
||||
mQTM306_PrepReqTPON : Bool;
|
||||
mQTM306_PrepReqTP1ON : Bool;
|
||||
mDelayON_StopPumpON : Bool;
|
||||
mDelayON_SyrupMinON : Bool;
|
||||
mDelayON_PumpStatusON : Bool;
|
||||
mHVP302_TONON : Bool;
|
||||
mQTM306_Prep_TimeOutON : Bool;
|
||||
mQTM306_PrepReqTP : "TP:v1.0";
|
||||
mQTM306_PrepReqTP1 : "TP:v1.0";
|
||||
mDelayON_StopPump : "TON:v1.0";
|
||||
mDelayON_SyrupMin : "TON:v1.0";
|
||||
mDelayON_PumpStatus : "TON:v1.0";
|
||||
mHVP302_TON : "TON:v1.0";
|
||||
mQTM306_Prep_TimeOut : "TON:v1.0";
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
mDummy : Bool;
|
||||
mAux1 : Real;
|
||||
mAux2 : Real;
|
||||
mTimeOutElapsed : Bool;
|
||||
mStopPumpP2 : Bool;
|
||||
mSyrMinLevel : Bool;
|
||||
mPumpP2Running : Bool;
|
||||
mWaterCountAcheaved : Bool;
|
||||
mSyrupLineManualDrained : Bool;
|
||||
mFuzzyNetOut : Bool;
|
||||
Out_Time_DI : DInt;
|
||||
Real_Time_S5 : S5Time;
|
||||
mProcSlctd : Bool;
|
||||
mFuzzyNetAdd1 : Real;
|
||||
mFuzzyNetAdd2 : Real;
|
||||
mFuzzyNetAdd3 : Real;
|
||||
mSyrBrixAux : Real;
|
||||
mSyrBrixAux_1 : Real;
|
||||
Aux_Somma_Lt : Real;
|
||||
END_VAR
|
||||
|
||||
#_1S : Bool; // Auto-generated temporary
|
||||
#_4S : Bool; // Auto-generated temporary
|
||||
#_4S_600MS : Bool; // Auto-generated temporary
|
||||
#_5S : Bool; // Auto-generated temporary
|
||||
#TON_INSTANCE_26_dup3 : Bool; // Auto-generated temporary
|
||||
#TON_INSTANCE_26_dup5 : Bool; // Auto-generated temporary
|
||||
#TON_INSTANCE_27_dup4 : Bool; // Auto-generated temporary
|
||||
#TON_INSTANCE_30_dup5 : Bool; // Auto-generated temporary
|
||||
#TP_INSTANCE_44 : Bool; // Auto-generated temporary
|
||||
BEGIN
|
||||
|
||||
// Network 1: (Original Language: LAD)
|
||||
|
||||
%DB960.DBX56.0 := ("gBlenderProdMode" AND "gBlenderSuppliesOk" AND "System_RunOut_Variables"."FastChangeOverActivated" AND NOT "Blender_Variables_Pers"."gWaterRecipe") OR ("gBlenderProdMode" AND "gBlenderSuppliesOk" AND NOT "Blender_Variables_Pers"."gWaterRecipe" AND NOT "gBlenderRinseMode");
|
||||
|
||||
// Network 2: SyrLineMFMPrepReq (Original Language: LAD)
|
||||
|
||||
// PBox SymPy processed, logic in consumer
|
||||
"Tag_69" := "HMI_Variables_Cmd"."Commands_From_HMI"."CMD_FTP302Line_Prep" AND NOT "System_RunOut_Variables"."FastChangeOverActivated" AND NOT "Procedure_Variables"."Syr_RunOut"."FastChangeOverRinseDone"; // P_TRIG("HMI_Variables_Cmd"."Commands_From_HMI"."CMD_FTP302Line_Prep" AND NOT "System_RunOut_Variables"."FastChangeOverActivated" AND NOT "Procedure_Variables"."Syr_RunOut"."FastChangeOverRinseDone") - Mem: "Tag_69"
|
||||
|
||||
#TP_INSTANCE_44(IN := "System_RunOut_Variables"."FastChangeOverActivated" AND "Procedure_Variables"."Syr_RunOut"."FastChangeOverRinseDone" AND "HMI_Variables_Cmd"."GLOBAL_CMD"."_EnableNextRecipe" AND "System_RunOut_Variables"."NextRecipeOk" AND NOT %DB960.DBX56.6, PT := T#1S); // TODO: Declarar #TP_INSTANCE_44 : TP;
|
||||
|
||||
%DB960.DBX56.1 := ("mQTM306_PrepReqTP1ON" AND %DB960.DBX56.0) OR (%DB960.DBX56.0 AND "HMI_Variables_Cmd"."Commands_From_HMI"."CMD_FTP302Line_Prep" AND NOT "System_RunOut_Variables"."FastChangeOverActivated" AND NOT "Tag_69" AND NOT "Procedure_Variables"."Syr_RunOut"."FastChangeOverRinseDone");
|
||||
|
||||
// Network 3: (Original Language: LAD)
|
||||
|
||||
// PBox SymPy processed, logic in consumer
|
||||
"mSyrLineMFMPrepONS" := %DB960.DBX56.1 AND %DB960.DBX56.6 AND NOT "Procedure_Variables"."Blender_Run"."Latch" AND NOT "System_RunOut_Variables"."FastChangeOverActivated"; // P_TRIG(%DB960.DBX56.1 AND %DB960.DBX56.6 AND NOT "Procedure_Variables"."Blender_Run"."Latch" AND NOT "System_RunOut_Variables"."FastChangeOverActivated") - Mem: "mSyrLineMFMPrepONS"
|
||||
|
||||
%DB960.DBX56.2 := "gBlenderCIPMode" OR "Blender_Variables_Pers"."gWaterRecipe" OR ("gEmergencyPressed" AND %DB960.DBX56.3) OR ("Procedure_Variables"."SyrupLineRinse"."Latch" AND "System_RunOut_Variables"."FastChangeOverActivated") OR ("gBlenderRinseMode" AND NOT "System_RunOut_Variables"."FastChangeOverActivated") OR (%DB960.DBX56.1 AND %DB960.DBX56.6 AND NOT "Procedure_Variables"."Blender_Run"."Latch" AND NOT "System_RunOut_Variables"."FastChangeOverActivated" AND NOT "mSyrLineMFMPrepONS");
|
||||
|
||||
// Network 4: (Original Language: LAD)
|
||||
|
||||
IF ("Procedure_Variables"."Blender_Rinse"."Latch" AND NOT "AUX MASTER VALIDATION") OR ("Procedure_Variables"."Syr_RunOut"."Latch" AND NOT "AUX MASTER VALIDATION") OR ("CIP_Program_Variables"."Status"."Started" AND NOT "AUX MASTER VALIDATION") THEN
|
||||
%DB960.DBX57.0 := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 5: (Original Language: LAD)
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Reset" THEN
|
||||
"mStepNum" := 0;
|
||||
END_IF;
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Reset" THEN
|
||||
%DB960.DBX56.6 := FALSE;
|
||||
END_IF;
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Reset" THEN
|
||||
"Procedure_Variables"."FTP302Line_Preparation"."LinePrepared" := FALSE;
|
||||
END_IF;
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Reset" THEN
|
||||
"mWaterCountAcheaved" := FALSE;
|
||||
END_IF;
|
||||
|
||||
// Network 6: (Original Language: LAD)
|
||||
|
||||
%DB960.DBX56.5 := "HMI_Device"."PPP302"."Alarm" OR "HMI_Device"."SyrupRoom_SyrupPump"."Alarm" OR NOT "HMI_Digital"."PSM311"."Filtered";
|
||||
|
||||
// Network 7: (Original Language: LAD)
|
||||
|
||||
%DB960.DBX56.3 := (%DB960.DBX56.1 AND NOT "AUX Start CPU" AND NOT %DB960.DBX56.2 AND NOT %DB960.DBX56.6) OR ("Procedure_Variables"."FTP302Line_Preparation"."Latch" AND NOT "AUX Start CPU" AND NOT %DB960.DBX56.2 AND NOT %DB960.DBX56.6);
|
||||
|
||||
IF (%DB960.DBX56.1 AND NOT "AUX Start CPU" AND NOT %DB960.DBX56.2 AND NOT %DB960.DBX56.6) OR ("Procedure_Variables"."FTP302Line_Preparation"."Latch" AND NOT "AUX Start CPU" AND NOT %DB960.DBX56.2 AND NOT %DB960.DBX56.6) THEN
|
||||
"HMI_Variables_Status"."Procedures"."BlenderStateNum" := 3;
|
||||
END_IF;
|
||||
|
||||
// Network 8: (Original Language: LAD)
|
||||
|
||||
%DB960.DBX56.4 := %DB960.DBX56.3 AND NOT %DB960.DBX56.5;
|
||||
|
||||
// Network 9: MIX - (Original Language: LAD)
|
||||
|
||||
"mAux1" := "Blender_Variables"."gMinProduction" / 6.0;
|
||||
|
||||
SEL_R(G := Ne("mAux1", 0.0), IN0 := 1.0, IN1 := "mAux1");
|
||||
|
||||
"mAux2" := "HMI_Blender_Parameters"."ProcessSetup"."_SyrupRunOutLiters" * 1.5;
|
||||
|
||||
"mAux1" := "mAux2" / "mAux1";
|
||||
|
||||
"Out_Time_DI" := CEIL("mAux1");
|
||||
|
||||
"mDummy" := TRUE;
|
||||
|
||||
// Network 10: (Original Language: STL)
|
||||
// #Out_Time_DI (DINT) converted in #Real_Time_S5 (S5Time)
|
||||
// Use L#1000 IF #Out_Time_DI is in ms
|
||||
|
||||
// --- BEGIN STL Network 10 ---
|
||||
```stl
|
||||
L "Out_Time_DI"
|
||||
L 1000
|
||||
MUL_D
|
||||
T "Real_Time"
|
||||
```
|
||||
// --- END STL Network 10 ---
|
||||
|
||||
// Network 11: SyrLineMFMPrep_TimeOut (Original Language: LAD)
|
||||
|
||||
#TON_INSTANCE_26_dup3(IN := %DBX56.3, PT := "Real_Time"); // TODO: Declarar #TON_INSTANCE_26_dup3 : TON;
|
||||
|
||||
"mTimeOutElapsed" := "mQTM306_Prep_TimeOutON";
|
||||
|
||||
// Network 12: DelayON_StopPump (Original Language: LAD)
|
||||
|
||||
#TON_INSTANCE_27_dup4(IN := NOT "gIN_SyrTank_MinLvl", PT := T#4S_600MS); // TODO: Declarar #TON_INSTANCE_27_dup4 : TON;
|
||||
|
||||
"mStopPumpP2" := "mDelayON_StopPumpON";
|
||||
|
||||
// Network 13: DelayON_SyrupMin (Original Language: LAD)
|
||||
|
||||
"mSyrMinLevel" := "gIN_SyrTank_MinLvl";
|
||||
|
||||
// Network 14: DelayON_PumpStatus (Original Language: LAD)
|
||||
|
||||
#TON_INSTANCE_26_dup5(IN := "HMI_Device"."PPP302"."Out", PT := T#4S); // TODO: Declarar #TON_INSTANCE_26_dup5 : TON;
|
||||
|
||||
"mPumpP2Running" := "mDelayON_PumpStatusON";
|
||||
|
||||
// Network 15: SYRUP LINE MANUAL DRAIN (Original Language: LAD)
|
||||
// THIS PROCEDURE HAS TO BE DONE BEFORE TO SELECT THE SYRUP LINE STARTUP.
|
||||
|
||||
#TON_INSTANCE_30_dup5(IN := "gIN_HVP301_Aux", PT := T#5S); // TODO: Declarar #TON_INSTANCE_30_dup5 : TON;
|
||||
|
||||
// Network 16: MIX - HMI Variables Cmd (Original Language: LAD)
|
||||
|
||||
"mSyrupLineManualDrained" := %DB960.DBX56.3 AND "mSyrupLineManualDrainSR" AND NOT "gIN_HVP301_Aux";
|
||||
|
||||
// Network 17: (Original Language: LAD)
|
||||
|
||||
IF NOT "Procedure_Variables"."FTP302Line_Preparation"."Latch" THEN
|
||||
"Blender_Variables_Pers"."gSyrLinePrepCountInit" := "Profibus_Variables"."gFTP302_Tot";
|
||||
END_IF;
|
||||
|
||||
// Network 18: BRIX PRODUCT STARTUP THRESHOLD (Original Language: LAD)
|
||||
|
||||
"mSyrBrixAux" := "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupBrix" * "HMI_Blender_Parameters"."ProcessSetup"."_PercSyrupBrixSyrStarUp";
|
||||
|
||||
"mSyrBrixAux_1" := "mSyrBrixAux" / 100.0;
|
||||
|
||||
"Blender_Constants"."gSugaredSyrupBrixThrsd" := "mSyrBrixAux_1";
|
||||
|
||||
// Network 19: Fuzzy Net (Original Language: LAD)
|
||||
|
||||
SEL_R(G := "M_validat_27_01_25" AND "gPV_SyrDensOk" AND "HMI_Device"."PPP302"."Out" AND NOT "Blender_Variables_Pers"."gSugarBeverage", IN0 := 0.0, IN1 := 101.0);
|
||||
|
||||
IF "Blender_Variables_Pers"."gSugarBeverage" THEN
|
||||
"mAux1" := "Profibus_Variables"."gFTP302_Brix" * "HMI_Blender_Parameters"."Actual_Recipe_Parameters"."_SyrupFactor";
|
||||
END_IF;
|
||||
|
||||
SEL_R(G := "gIN_SyrTank_MinLvl" AND ("mAux1" > "Blender_Constants"."gSugaredSyrupBrixThrsd"), IN0 := 0.0, IN1 := 101.0);
|
||||
|
||||
IF NOT "M_validat_27_01_25" AND NOT "Blender_Variables_Pers"."gSugarBeverage" THEN
|
||||
"Aux_Somma_Lt" := "HMI_Blender_Parameters"."ProcessSetup"."_SyrupRunOutLiters" + %DBD784;
|
||||
END_IF;
|
||||
|
||||
IF NOT "M_validat_27_01_25" AND NOT "Blender_Variables_Pers"."gSugarBeverage" THEN
|
||||
"HMI_Variables_Status"."Analog_Values"."TP301SyrupPrimingCount" := "Profibus_Variables"."gFTP302_Tot" - "Blender_Variables_Pers"."gSyrLinePrepCountInit";
|
||||
END_IF;
|
||||
|
||||
"mWaterCountAcheaved" := NOT "mSyrupLineManualDrainSR" AND ("HMI_Variables_Status"."Analog_Values"."TP301SyrupPrimingCount" >= "Aux_Somma_Lt");
|
||||
|
||||
SEL_R(G := "HMI_Device"."PPP302"."Out" AND NOT "mSyrupLineManualDrainSR" AND ("HMI_Variables_Status"."Analog_Values"."TP301SyrupPrimingCount" >= "Aux_Somma_Lt"), IN0 := 0.0, IN1 := 101.0);
|
||||
|
||||
"mDummy" := TRUE;
|
||||
|
||||
// Network 20: Fuzzy Net (Original Language: LAD)
|
||||
|
||||
"mFuzzyNetAdd1" := "mFuzzyNetAdd1" + "mFuzzyNetAdd2";
|
||||
|
||||
"mFuzzyNetAdd1" := "mFuzzyNetAdd1" + "mFuzzyNetAdd3";
|
||||
|
||||
"mFuzzyNetOut" := "Procedure_Variables"."FTP302Line_Preparation"."Latch" AND ("mFuzzyNetAdd1" > 100.0);
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Latch" AND NOT "AUX MASTER VALIDATION" AND ("mFuzzyNetAdd1" > 100.0) THEN
|
||||
"Procedure_Variables"."FTP302Line_Preparation"."LinePrepared" := TRUE;
|
||||
END_IF;
|
||||
|
||||
// Network 21: Opeartor Run Syrup Prep (Original Language: LAD)
|
||||
|
||||
_HMI_Alarms___gH_Message__8_ := "gBlenderProdMode" AND "Procedure_Variables"."FTP302Line_Preparation"."Qualifier" AND NOT "Procedure_Variables"."FTP302Line_Preparation"."Latch" AND NOT "Blender_Variables_Pers"."gWaterRecipe" AND NOT "gBlenderRinseMode" AND NOT "Procedure_Variables"."FTP302_StartUp"."Latch" AND NOT "Procedure_Variables"."FTP302Line_Preparation"."Done" AND NOT "System_RunOut_Variables"."FastChangeOverActivated";
|
||||
|
||||
// Network 22: Syrup Tank Prep Running (Original Language: LAD)
|
||||
// Syrup Tank Prep Running Message
|
||||
|
||||
"HMI_Variables_Status"."Procedures"."TP301PrepRun" := "Procedure_Variables"."FTP302Line_Preparation"."Latch";
|
||||
|
||||
// Network 23: Syrup Tank Prep Running (Original Language: LAD)
|
||||
// Syrup Tank Prep Running Message
|
||||
|
||||
_HMI_Alarms___gH_Status__3_ := "Procedure_Variables"."FTP302Line_Preparation"."Latch" OR "Procedure_Variables"."FTP302_StartUp"."Latch";
|
||||
|
||||
// Network 24: (Original Language: LAD)
|
||||
|
||||
IF "Procedure_Variables"."FTP302Line_Preparation"."Running" THEN
|
||||
Syrup_Line_MFM_Prep_Seq(FuzzyNetOut := "mFuzzyNetOut", SyrupLineManualDrained := "mSyrupLineManualDrained", WaterCountAcheaved := "mWaterCountAcheaved", mStep := "mStepNum", mStopPumpP2 := "mStopPumpP2", mSyrMinLevel := "mDelayON_SyrupMinON", mTimer := "mTimeStep", mTransition := "mTransition");
|
||||
END_IF;
|
||||
|
||||
// Network 25: (Original Language: LAD)
|
||||
|
||||
"HMI_Variables_Status"."System_Run_Out"."TP301PrepDone" := "Procedure_Variables"."FTP302Line_Preparation"."Done";
|
||||
|
||||
END_FUNCTION_BLOCK
|
|
@ -651,9 +651,7 @@ def convert_xml_to_json(xml_filepath, json_filepath):
|
|||
except ValueError:
|
||||
# Fallback si relpath falla con diferentes unidades
|
||||
rel_path = json_filepath
|
||||
print(
|
||||
f"Conversión finalizada. JSON guardado en: '{rel_path}'"
|
||||
)
|
||||
print(f"Conversión finalizada. JSON guardado en: '{rel_path}'")
|
||||
return True # Indicar éxito
|
||||
except IOError as e:
|
||||
print(
|
||||
|
|
|
@ -312,9 +312,12 @@ def process_json_to_scl(json_filepath, output_json_filepath):
|
|||
# --- NUEVO: Manejar UIDs duplicados antes del procesamiento principal ---
|
||||
try:
|
||||
from processors.duplicate_uid_handler import detect_and_resolve_duplicate_uids
|
||||
|
||||
duplicates_resolved = detect_and_resolve_duplicate_uids(data)
|
||||
if duplicates_resolved:
|
||||
print("INFO: UIDs duplicados resueltos. Continuando con el procesamiento...")
|
||||
print(
|
||||
"INFO: UIDs duplicados resueltos. Continuando con el procesamiento..."
|
||||
)
|
||||
except ImportError:
|
||||
print("WARNING: No se pudo cargar el manejador de UIDs duplicados")
|
||||
except Exception as e:
|
||||
|
|
|
@ -4,6 +4,7 @@ LadderToSCL - Conversor de Siemens LAD/FUP XML a SCL
|
|||
Este script es parte de un conjunto de herramientas para convertir proyectos de Siemens LAD/FUP a SCL.
|
||||
|
||||
"""
|
||||
|
||||
# ToUpload/x3_generate_scl.py
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
|
@ -12,6 +13,7 @@ import re
|
|||
import argparse
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
script_root = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
)
|
||||
|
@ -131,9 +133,7 @@ def generate_scl_or_markdown(
|
|||
except ValueError:
|
||||
# Fallback si relpath falla con diferentes unidades
|
||||
rel_path = output_filepath
|
||||
print(
|
||||
f" -> Escribiendo archivo de salida final en: {rel_path}"
|
||||
)
|
||||
print(f" -> Escribiendo archivo de salida final en: {rel_path}")
|
||||
try:
|
||||
# Asegurar que el directorio de salida exista (ya debería estar hecho en __main__)
|
||||
os.makedirs(os.path.dirname(output_filepath), exist_ok=True)
|
||||
|
@ -158,7 +158,10 @@ if __name__ == "__main__":
|
|||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
except ImportError:
|
||||
print("Error: Tkinter no está instalado. No se puede mostrar el diálogo de archivo.", file=sys.stderr)
|
||||
print(
|
||||
"Error: Tkinter no está instalado. No se puede mostrar el diálogo de archivo.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
tk = None
|
||||
|
||||
input_json_file = ""
|
||||
|
@ -167,21 +170,29 @@ if __name__ == "__main__":
|
|||
if tk:
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
print("Por favor, selecciona el archivo JSON procesado de entrada (generado por x2)...")
|
||||
print(
|
||||
"Por favor, selecciona el archivo JSON procesado de entrada (generado por x2)..."
|
||||
)
|
||||
input_json_file = filedialog.askopenfilename(
|
||||
title="Selecciona el archivo JSON procesado de entrada (_processed.json)",
|
||||
filetypes=[("Processed JSON files", "*_processed.json"), ("JSON files", "*.json"), ("All files", "*.*")]
|
||||
filetypes=[
|
||||
("Processed JSON files", "*_processed.json"),
|
||||
("JSON files", "*.json"),
|
||||
("All files", "*.*"),
|
||||
],
|
||||
)
|
||||
if input_json_file:
|
||||
print(f"Archivo JSON procesado seleccionado: {input_json_file}")
|
||||
print("Por favor, selecciona el directorio raíz del proyecto XML (ej. la carpeta 'PLC')...")
|
||||
print(
|
||||
"Por favor, selecciona el directorio raíz del proyecto XML (ej. la carpeta 'PLC')..."
|
||||
)
|
||||
project_root_dir = filedialog.askdirectory(
|
||||
title="Selecciona el directorio raíz del proyecto XML"
|
||||
)
|
||||
if project_root_dir:
|
||||
print(f"Directorio raíz del proyecto seleccionado: {project_root_dir}")
|
||||
print(f"Directorio raíz del proyecto seleccionado: {project_root_dir}")
|
||||
else:
|
||||
print("No se seleccionó directorio raíz. Saliendo.", file=sys.stderr)
|
||||
print("No se seleccionó directorio raíz. Saliendo.", file=sys.stderr)
|
||||
else:
|
||||
print("No se seleccionó archivo JSON procesado. Saliendo.", file=sys.stderr)
|
||||
root.destroy()
|
||||
|
@ -191,14 +202,24 @@ if __name__ == "__main__":
|
|||
# <-- NUEVO: Leer nombre del directorio de salida desde la configuración -->
|
||||
configs = load_configuration()
|
||||
xml_parser_config = configs.get("XML Parser to SCL", {})
|
||||
cfg_scl_output_dirname = xml_parser_config.get("scl_output_dir", "scl_output") # Leer con default
|
||||
cfg_scl_output_dirname = xml_parser_config.get(
|
||||
"scl_output_dir", "scl_output"
|
||||
) # Leer con default
|
||||
# <-- FIN NUEVO -->
|
||||
|
||||
final_output_dir = os.path.join(project_root_dir, cfg_scl_output_dirname) # Usar valor leído
|
||||
final_output_dir = os.path.join(
|
||||
project_root_dir, cfg_scl_output_dirname
|
||||
) # Usar valor leído
|
||||
|
||||
print(f"(x3 - Standalone) Generando SCL/MD desde: '{os.path.relpath(input_json_file)}'")
|
||||
print(f"(x3 - Standalone) Directorio de salida final: '{os.path.relpath(final_output_dir)}'")
|
||||
print(f"(x3 - Standalone) Usando ruta raíz del proyecto: '{project_root_dir}' para buscar UDTs.")
|
||||
print(
|
||||
f"(x3 - Standalone) Generando SCL/MD desde: '{os.path.relpath(input_json_file)}'"
|
||||
)
|
||||
print(
|
||||
f"(x3 - Standalone) Directorio de salida final: '{os.path.relpath(final_output_dir)}'"
|
||||
)
|
||||
print(
|
||||
f"(x3 - Standalone) Usando ruta raíz del proyecto: '{project_root_dir}' para buscar UDTs."
|
||||
)
|
||||
|
||||
# Asegurar que el directorio de salida final exista
|
||||
try:
|
||||
|
@ -209,11 +230,11 @@ if __name__ == "__main__":
|
|||
file=sys.stderr,
|
||||
)
|
||||
# sys.exit(1) # No usar sys.exit
|
||||
success = False # Marcar como fallo para evitar la llamada
|
||||
success = False # Marcar como fallo para evitar la llamada
|
||||
else:
|
||||
success = True # Marcar como éxito para proceder
|
||||
success = True # Marcar como éxito para proceder
|
||||
|
||||
if success: # Solo intentar si se pudo crear el directorio
|
||||
if success: # Solo intentar si se pudo crear el directorio
|
||||
try:
|
||||
# Llamar a la función principal
|
||||
success = generate_scl_or_markdown(
|
||||
|
@ -223,7 +244,10 @@ if __name__ == "__main__":
|
|||
print("\nGeneración de SCL/MD completada exitosamente.")
|
||||
else:
|
||||
# La función generate_scl_or_markdown ya imprime el error
|
||||
print(f"\nError durante la generación desde '{os.path.relpath(input_json_file)}'.", file=sys.stderr)
|
||||
print(
|
||||
f"\nError durante la generación desde '{os.path.relpath(input_json_file)}'.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
# sys.exit(1) # No usar sys.exit
|
||||
except Exception as e:
|
||||
print(f"Error Crítico no manejado en x3: {e}", file=sys.stderr)
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
{
|
||||
"history": [
|
||||
{
|
||||
"id": "66baa92e",
|
||||
"group_id": "2",
|
||||
"script_name": "main.py",
|
||||
"executed_date": "2025-08-28T09:54:46.647326Z",
|
||||
"arguments": [],
|
||||
"working_directory": "D:/Proyectos/Scripts/RS485/MaselliSimulatorApp",
|
||||
"python_env": "tia_scripting",
|
||||
"executable_type": "pythonw.exe",
|
||||
"status": "running",
|
||||
"pid": 29432,
|
||||
"execution_time": null
|
||||
},
|
||||
{
|
||||
"id": "4372845c",
|
||||
"group_id": "2",
|
||||
|
|
52145
data/log.txt
52145
data/log.txt
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue