Mejorado simpre de parsing de SCL a SCL
This commit is contained in:
parent
c22f98d865
commit
4e3aba9a2a
|
@ -0,0 +1,105 @@
|
||||||
|
FUNCTION "BlenderCtrl_MFM Command" : Void
|
||||||
|
{ S7_Optimized_Access := 'FALSE' }
|
||||||
|
AUTHOR : 'Author'
|
||||||
|
FAMILY : TASK2
|
||||||
|
NAME : 'Name'
|
||||||
|
VERSION : 1.0
|
||||||
|
VAR_INPUT
|
||||||
|
mResetWaterTot : Bool;
|
||||||
|
mResetSyrupTot : Bool;
|
||||||
|
mResetCO2Tot : Bool;
|
||||||
|
mResetProductTot : Bool;
|
||||||
|
END_VAR
|
||||||
|
|
||||||
|
VAR_TEMP
|
||||||
|
mWaterVFMCtrl : Int;
|
||||||
|
mSyrupMFMCtrl : Int;
|
||||||
|
mCO2MFMCtrl : Int;
|
||||||
|
mProductMFMCtrl : Int;
|
||||||
|
END_VAR
|
||||||
|
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
|
||||||
|
IF #mResetWaterTot THEN
|
||||||
|
#mWaterVFMCtrl := 1 ;
|
||||||
|
ELSE
|
||||||
|
#mWaterVFMCtrl := 0 ;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
IF #mResetSyrupTot THEN
|
||||||
|
#mSyrupMFMCtrl := 1 ;
|
||||||
|
ELSE
|
||||||
|
#mSyrupMFMCtrl := 0 ;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
IF #mResetCO2Tot THEN
|
||||||
|
#mCO2MFMCtrl := 1 ;
|
||||||
|
ELSE
|
||||||
|
#mCO2MFMCtrl := 0 ;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
IF #mResetProductTot THEN
|
||||||
|
#mProductMFMCtrl := 1 ;
|
||||||
|
ELSE
|
||||||
|
#mProductMFMCtrl := 0 ;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
CASE #mWaterVFMCtrl OF
|
||||||
|
1: "P_FTN301_Tot_Ctrl" := 01; // Reset Totalizer
|
||||||
|
|
||||||
|
2: "P_FTN301_Tot_Ctrl" := 02 ; // Preset Totalizer
|
||||||
|
|
||||||
|
ELSE:
|
||||||
|
"P_FTN301_Tot_Ctrl" := 00;
|
||||||
|
END_CASE;
|
||||||
|
|
||||||
|
|
||||||
|
IF "gSyrupRoomEn" THEN
|
||||||
|
CASE #mSyrupMFMCtrl OF
|
||||||
|
1: "P_FTP302_Tot_Ctrl" := 01; (* Reset Totalizer 1*)
|
||||||
|
|
||||||
|
2: "P_FTP302_Tot_Ctrl" := 02; (* Reset Totalizer 2*)
|
||||||
|
|
||||||
|
3: "P_FTP302_Tot_Ctrl" := 03; (* Reset Totalizer 1 & 2*)
|
||||||
|
|
||||||
|
4: "P_FTP302_Tot_Ctrl" := 04; (* Zeropoint Adjust *)
|
||||||
|
|
||||||
|
5: "P_FTP302_Tot_Ctrl" := 05; (* Positive Zero Return *)
|
||||||
|
|
||||||
|
6: "P_FTP302_Tot_Ctrl" := 06; (* Negative Zero Return *)
|
||||||
|
|
||||||
|
ELSE:
|
||||||
|
"P_FTP302_Tot_Ctrl" := 00;
|
||||||
|
END_CASE;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
CASE #mCO2MFMCtrl OF
|
||||||
|
1: "P_FTM303_Tot_Ctrl" := 01; (* Reset Totalizer 1*)
|
||||||
|
|
||||||
|
2: "P_FTM303_Tot_Ctrl" := 02; (* Reset Totalizer 2 *)
|
||||||
|
|
||||||
|
3: "P_FTM303_Tot_Ctrl" := 03; (* Reset Totalizer 1 & 2*)
|
||||||
|
|
||||||
|
4: "P_FTM303_Tot_Ctrl" := 04; (* Zeropoint Adjust *)
|
||||||
|
|
||||||
|
5: "P_FTM303_Tot_Ctrl" := 05; (* Positive Zero Return *)
|
||||||
|
|
||||||
|
6: "P_FTM303_Tot_Ctrl" := 06; (* Negative Zero Return *)
|
||||||
|
ELSE:
|
||||||
|
"P_FTM303_Tot_Ctrl" := 00;
|
||||||
|
END_CASE;
|
||||||
|
|
||||||
|
(*CASE mProductMFMCtrl OF
|
||||||
|
1: gProductTotCtrl_Node17 := 01; (* Reset Totalizer 1*)
|
||||||
|
|
||||||
|
2: gProductTotCtrl_Node17 := 02 ; (* Preset Totalizer1 *)
|
||||||
|
|
||||||
|
ELSE
|
||||||
|
gProductTotCtrl_Node17 := 00;
|
||||||
|
END_CASE;*)
|
||||||
|
|
||||||
|
|
||||||
|
END_FUNCTION
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
// Block Name (Original): BlenderPIDCtrl__Loop
|
||||||
|
// Block Number: 1729
|
||||||
|
// Original Language: LAD
|
||||||
|
// Block Comment: TASK1 PID
|
||||||
|
|
||||||
|
FUNCTION_BLOCK "BlenderPIDCtrl__Loop"
|
||||||
|
{ S7_Optimized_Access := 'TRUE' }
|
||||||
|
VERSION : 0.1
|
||||||
|
|
||||||
|
VAR_STAT_STATIC
|
||||||
|
PID_1_300ms : Bool;
|
||||||
|
PID_2_300ms : Bool;
|
||||||
|
PID_3_300ms : Bool;
|
||||||
|
PID_4_300ms : Bool;
|
||||||
|
PID_5_300ms : Bool;
|
||||||
|
PID_6_300ms : Bool;
|
||||||
|
Flow_Meter_Error_RETVAL : Real;
|
||||||
|
PID_FF_Calc : "BlenderPID_PIDFFCalc";
|
||||||
|
PID_Blending_Fault : "BlenderPID_BlendingFault";
|
||||||
|
PID_Save_Integral : "BlenderPIDCtrl_SaveInteg";
|
||||||
|
PID_Monitor : "BlenderPIDCtrl_Monitor";
|
||||||
|
Read_AnalogInput : "BlenderPIDCtrl_ReadAnIn";
|
||||||
|
END_VAR
|
||||||
|
|
||||||
|
VAR_TEMP
|
||||||
|
END_VAR
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
// Network 1: Read Analoc Inputs (Original Language: LAD)
|
||||||
|
|
||||||
|
"Read_AnalogInput"();
|
||||||
|
|
||||||
|
// Network 2: MIX - OB35 scan counter (Original Language: LAD)
|
||||||
|
// PID Control Time Bit (300ms)
|
||||||
|
|
||||||
|
"PID_1_300ms" := Eq("MW1968", 1);
|
||||||
|
"PID_2_300ms" := Eq("MW1968", 2);
|
||||||
|
"PID_3_300ms" := Eq("MW1968", 3);
|
||||||
|
"PID_4_300ms" := Eq("MW1968", 4);
|
||||||
|
"PID_5_300ms" := Eq("MW1968", 5);
|
||||||
|
"PID_6_300ms" := Eq("MW1968", 6);
|
||||||
|
|
||||||
|
// Network 3: PID Call (Original Language: LAD)
|
||||||
|
// Water PID
|
||||||
|
// Syrup PID
|
||||||
|
// CO2 PID
|
||||||
|
|
||||||
|
IF "PID_1_300ms" THEN
|
||||||
|
"PID_FF_Calc"();
|
||||||
|
"PID_Blending_Fault"();
|
||||||
|
BlenderPID_FlowMeterErro();
|
||||||
|
"PID_Monitor"();
|
||||||
|
"PID_Save_Integral"();
|
||||||
|
BlenderPIDCtrl_SaveValve();
|
||||||
|
END_IF;
|
||||||
|
IF "PID_1_300ms" AND "HMI_PID"."RMM301"."Config" THEN
|
||||||
|
"PID_RMM301_Data"();
|
||||||
|
END_IF;
|
||||||
|
IF "PID_1_300ms" AND "HMI_PID"."RMP302"."Config" THEN
|
||||||
|
"PID_RMP302_Data"();
|
||||||
|
END_IF;
|
||||||
|
IF "PID_1_300ms" AND "HMI_PID"."RMM303"."Config" THEN
|
||||||
|
"PID_RMM303_Data"();
|
||||||
|
END_IF;
|
||||||
|
IF "PID_1_300ms" AND "HMI_PID"."RMM304"."Config" THEN
|
||||||
|
"PID_RMM304_Data"();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
// Network 4: PID Product Tank Pressure (Original Language: LAD)
|
||||||
|
|
||||||
|
IF "PID_2_300ms" AND "HMI_PID"."RVM301"."Config" THEN
|
||||||
|
"PID_RVM301_Data"();
|
||||||
|
BlenderPIDCtrl_PresRelea();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
// Network 5: Pid Call (Original Language: LAD)
|
||||||
|
|
||||||
|
IF "PID_4_300ms" AND "HMI_PID"."RVM319_PRD"."Config" THEN
|
||||||
|
"PID_RVM319_Data"();
|
||||||
|
END_IF;
|
||||||
|
IF "HMI_PID"."RVP303"."Config" AND "PID_4_300ms" THEN
|
||||||
|
"PID_RVP303_Data"();
|
||||||
|
END_IF;
|
||||||
|
IF "HMI_PID"."RVN302"."Config" AND "PID_4_300ms" AND NOT "HMI_PID"."RVN302"."ConfigPID" THEN
|
||||||
|
"PID_RVN302_Data"();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
// Network 6: Filling Head (Original Language: LAD)
|
||||||
|
|
||||||
|
IF "HMI_Blender_Parameters"."Processor_Options"."Blender_OPT"."_BlendFillSystem" AND "PID_5_300ms" AND "HMI_PID"."PPM303"."Config" THEN
|
||||||
|
"PID_Filling_Head_Data"();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
// Network 7: CIp Heating PID (Original Language: LAD)
|
||||||
|
|
||||||
|
IF "PID_6_300ms" AND "HMI_PID"."RVS318"."Config" THEN
|
||||||
|
"PID_RVS318_Data"();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
// Network 8: Write Analog Outputs (Original Language: LAD)
|
||||||
|
|
||||||
|
IF "AUX TRUE" THEN
|
||||||
|
BlenderPIDCtrl_WriteAnOu();
|
||||||
|
END_IF;
|
||||||
|
|
||||||
|
END_FUNCTION_BLOCK
|
109
x1_to_json.py
109
x1_to_json.py
|
@ -247,101 +247,120 @@ def parse_call(call_element):
|
||||||
|
|
||||||
def reconstruct_scl_from_tokens(st_node):
|
def reconstruct_scl_from_tokens(st_node):
|
||||||
"""
|
"""
|
||||||
Reconstruye una cadena SCL a partir de los elementos hijos
|
Reconstruye SCL desde <StructuredText>, mejorando el manejo de
|
||||||
de un nodo <StructuredText>, manejando Tokens, Access (Variables y Constantes),
|
variables, constantes literales, tokens básicos, espacios y saltos de línea.
|
||||||
saltos de línea, espacios y comentarios.
|
|
||||||
"""
|
"""
|
||||||
if st_node is None:
|
if st_node is None:
|
||||||
return "// Error: StructuredText node not found.\n"
|
return "// Error: StructuredText node not found.\n"
|
||||||
|
|
||||||
scl_parts = []
|
scl_parts = []
|
||||||
# Obtener todos los elementos hijos directos en orden
|
|
||||||
# Usamos '*' para obtener todos los hijos y luego filtramos por tag
|
|
||||||
children = st_node.xpath("./st:*", namespaces=ns)
|
children = st_node.xpath("./st:*", namespaces=ns)
|
||||||
|
|
||||||
for elem in children:
|
for elem in children:
|
||||||
# Obtener el nombre local de la etiqueta sin el namespace
|
|
||||||
tag = etree.QName(elem.tag).localname
|
tag = etree.QName(elem.tag).localname
|
||||||
|
|
||||||
if tag == "Token":
|
if tag == "Token":
|
||||||
scl_parts.append(elem.get("Text", ""))
|
scl_parts.append(elem.get("Text", ""))
|
||||||
elif tag == "Blank":
|
elif tag == "Blank":
|
||||||
|
# Añadir espacios simples, evitar múltiples si ya hay uno antes/después
|
||||||
|
if not scl_parts or not scl_parts[-1].endswith(' '):
|
||||||
scl_parts.append(" " * int(elem.get("Num", 1)))
|
scl_parts.append(" " * int(elem.get("Num", 1)))
|
||||||
|
elif int(elem.get("Num", 1)) > 1: # Añadir extras si son más de 1
|
||||||
|
scl_parts.append(" " * (int(elem.get("Num", 1))-1))
|
||||||
elif tag == "NewLine":
|
elif tag == "NewLine":
|
||||||
|
# Limpiar espacios antes del salto de línea real
|
||||||
|
if scl_parts:
|
||||||
|
scl_parts[-1] = scl_parts[-1].rstrip()
|
||||||
scl_parts.append("\n")
|
scl_parts.append("\n")
|
||||||
elif tag == "Access":
|
elif tag == "Access":
|
||||||
scope = elem.get("Scope")
|
scope = elem.get("Scope")
|
||||||
access_str = f"_{scope}_?" # Fallback
|
access_str = f"/*_ERR_Scope_{scope}_*/" # Fallback más informativo
|
||||||
|
|
||||||
if scope == "GlobalVariable" or scope == "LocalVariable":
|
if scope in ["GlobalVariable", "LocalVariable", "TempVariable", "InOutVariable", "InputVariable", "OutputVariable", "ConstantVariable"]: # Tipos comunes de variables
|
||||||
symbol_elem = elem.xpath("./st:Symbol", namespaces=ns)
|
symbol_elem = elem.xpath("./st:Symbol", namespaces=ns)
|
||||||
if symbol_elem:
|
if symbol_elem:
|
||||||
components = symbol_elem[0].xpath("./st:Component | ./st:Token[@Text='.']", namespaces=ns)
|
components = symbol_elem[0].xpath("./st:Component", namespaces=ns)
|
||||||
symbol_text_parts = []
|
symbol_text_parts = []
|
||||||
for comp in components:
|
for i, comp in enumerate(components):
|
||||||
comp_tag = etree.QName(comp.tag).localname
|
|
||||||
if comp_tag == "Component":
|
|
||||||
name = comp.get("Name", "_ERR_COMP_")
|
name = comp.get("Name", "_ERR_COMP_")
|
||||||
# Comprobar si necesita comillas (requiere BooleanAttribute)
|
# Añadir punto si no es el primer componente
|
||||||
# Simplificación: Añadir comillas si el nombre original parece tenerlas
|
if i > 0:
|
||||||
|
symbol_text_parts.append(".")
|
||||||
|
|
||||||
|
# Reconstrucción de comillas (heurística)
|
||||||
has_quotes_elem = comp.xpath("../st:BooleanAttribute[@Name='HasQuotes']/text()", namespaces=ns)
|
has_quotes_elem = comp.xpath("../st:BooleanAttribute[@Name='HasQuotes']/text()", namespaces=ns)
|
||||||
has_quotes = has_quotes_elem and has_quotes_elem[0].lower() == "true"
|
has_quotes = has_quotes_elem and has_quotes_elem[0].lower() == "true"
|
||||||
|
is_temp = name.startswith('#')
|
||||||
|
|
||||||
# Heurística: Usar comillas si HasQuotes=true o si es el primer componente y no es TEMP (#)
|
if has_quotes or (i == 0 and not is_temp): # Comillas si HasQuotes o primer componente (no temp)
|
||||||
if has_quotes or (len(symbol_text_parts) == 0 and not name.startswith('#')):
|
|
||||||
symbol_text_parts.append(f'"{name}"')
|
symbol_text_parts.append(f'"{name}"')
|
||||||
else:
|
else:
|
||||||
symbol_text_parts.append(name)
|
symbol_text_parts.append(name)
|
||||||
|
|
||||||
# Manejar índices de array (simplificado)
|
# Manejar índices de array (RECURSIVO)
|
||||||
index_access = comp.xpath("./st:Access", namespaces=ns)
|
index_access = comp.xpath("./st:Access", namespaces=ns)
|
||||||
if index_access:
|
if index_access:
|
||||||
indices_text = [reconstruct_scl_from_tokens(idx_node) for idx_node in index_access] # Llamada recursiva
|
# Llama recursivamente para obtener el texto de cada índice
|
||||||
|
indices_text = [reconstruct_scl_from_tokens(idx_node) for idx_node in index_access]
|
||||||
symbol_text_parts.append(f"[{','.join(indices_text)}]")
|
symbol_text_parts.append(f"[{','.join(indices_text)}]")
|
||||||
|
|
||||||
elif comp_tag == "Token": # Es un punto
|
access_str = "".join(symbol_text_parts)
|
||||||
# Asegurarse de no añadir puntos duplicados si ya están en las partes
|
|
||||||
if symbol_text_parts and symbol_text_parts[-1] != ".":
|
|
||||||
symbol_text_parts.append(".")
|
|
||||||
# Limpiar posibles puntos extra al inicio/final o dobles
|
|
||||||
access_str = "".join(symbol_text_parts).strip('.')
|
|
||||||
access_str = re.sub(r'\.+', '.', access_str) # Reemplazar múltiples puntos con uno solo
|
|
||||||
|
|
||||||
|
|
||||||
elif scope == "LiteralConstant":
|
elif scope == "LiteralConstant":
|
||||||
constant_elem = elem.xpath("./st:Constant", namespaces=ns)
|
constant_elem = elem.xpath("./st:Constant", namespaces=ns)
|
||||||
if constant_elem:
|
if constant_elem:
|
||||||
val_elem = constant_elem[0].xpath("./st:ConstantValue/text()", namespaces=ns)
|
val_elem = constant_elem[0].xpath("./st:ConstantValue/text()", namespaces=ns)
|
||||||
# **CORRECCIÓN CLAVE**: Extraer el valor y usarlo
|
type_elem = constant_elem[0].xpath("./st:ConstantType/text()", namespaces=ns)
|
||||||
access_str = val_elem[0] if val_elem else "_ERR_CONSTVAL_"
|
const_type = type_elem[0] if type_elem else ""
|
||||||
|
const_val = val_elem[0] if val_elem else "_ERR_CONSTVAL_"
|
||||||
|
|
||||||
|
# **CORRECCIÓN CLAVE**: Usar el valor extraído
|
||||||
|
access_str = const_val
|
||||||
|
|
||||||
|
# Opcional: añadir prefijos T#, L#, etc. si es necesario
|
||||||
|
# if const_type == "Time": access_str = f"T#{const_val}"
|
||||||
|
# elif const_type == "LTime": access_str = f"LT#{const_val}"
|
||||||
|
# ... otros tipos ...
|
||||||
else:
|
else:
|
||||||
access_str = "_ERR_NOCONST_"
|
access_str = "/*_ERR_NOCONST_*/"
|
||||||
# Añadir manejo para otros scopes si es necesario (Address, Call, etc.)
|
# --- Añadir más manejo de scopes aquí si es necesario ---
|
||||||
# elif scope == "Call": ...
|
# elif scope == "Call": access_str = reconstruct_call(elem)
|
||||||
# elif scope == "Address": ...
|
# elif scope == "Expression": access_str = reconstruct_expression(elem)
|
||||||
|
|
||||||
scl_parts.append(access_str)
|
scl_parts.append(access_str)
|
||||||
|
|
||||||
elif tag == "Comment" or tag == "LineComment":
|
elif tag == "Comment" or tag == "LineComment":
|
||||||
# Manejo de comentarios (simplificado - asume texto directo)
|
|
||||||
comment_text = "".join(elem.xpath(".//text()")).strip()
|
comment_text = "".join(elem.xpath(".//text()")).strip()
|
||||||
if tag == "Comment": # Comentario tipo (* *)
|
if tag == "Comment":
|
||||||
scl_parts.append(f"(* {comment_text} *)")
|
scl_parts.append(f"(* {comment_text} *)")
|
||||||
else: # Comentario tipo //
|
else:
|
||||||
scl_parts.append(f"// {comment_text}")
|
scl_parts.append(f"// {comment_text}")
|
||||||
# else: # Ignorar otros tipos de nodos por ahora
|
# else: Ignorar otros nodos
|
||||||
# pass
|
|
||||||
|
|
||||||
# Unir todas las partes
|
# Unir partes, limpiar espacios extra alrededor de operadores y saltos de línea
|
||||||
full_scl = "".join(scl_parts)
|
full_scl = "".join(scl_parts)
|
||||||
# Limpieza básica de formato (puede necesitar ajustes)
|
|
||||||
lines = [line.rstrip() for line in full_scl.split('\n')]
|
|
||||||
cleaned_scl = "\n".join(lines)
|
|
||||||
# Eliminar múltiples líneas vacías consecutivas (opcional)
|
|
||||||
# import re
|
|
||||||
# cleaned_scl = re.sub(r'\n\s*\n', '\n\n', cleaned_scl)
|
|
||||||
|
|
||||||
return cleaned_scl.strip() # Quitar espacios/saltos al inicio/final
|
|
||||||
|
|
||||||
|
# Re-indentar líneas después de IF/THEN, etc. (Simplificado)
|
||||||
|
output_lines = []
|
||||||
|
indent_level = 0
|
||||||
|
for line in full_scl.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line: continue # Saltar líneas vacías
|
||||||
|
|
||||||
|
# Reducir indentación antes de procesar END_IF, ELSE, etc. (simplificado)
|
||||||
|
if line.startswith(('END_IF', 'END_WHILE', 'END_FOR', 'END_CASE', 'ELSE', 'ELSIF')):
|
||||||
|
indent_level = max(0, indent_level - 1)
|
||||||
|
|
||||||
|
output_lines.append(" " * indent_level + line) # Aplicar indentación
|
||||||
|
|
||||||
|
# Aumentar indentación después de IF, WHILE, FOR, CASE, ELSE, ELSIF (simplificado)
|
||||||
|
if line.endswith('THEN') or line.endswith('DO') or line.endswith('OF') or line == 'ELSE':
|
||||||
|
indent_level += 1
|
||||||
|
# Nota: Esto no maneja bloques BEGIN/END dentro de SCL
|
||||||
|
|
||||||
|
return "\n".join(output_lines)
|
||||||
|
|
||||||
# STL (Statement List) Parser
|
# STL (Statement List) Parser
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue