210 lines
8.0 KiB
Python
210 lines
8.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
# /processors/processor_utils.py
|
|
# Procesador de utilidades para el procesamiento de archivos XML de Simatic
|
|
import re
|
|
|
|
# --- Copia aquí las funciones auxiliares ---
|
|
# get_scl_representation, format_variable_name,
|
|
# generate_temp_var_name, get_target_scl_name
|
|
# Asegúrate de que no dependen de variables globales de x2_process.py
|
|
# (como 'data' o 'network_access_maps' - esas se pasarán como argumentos)
|
|
|
|
# Ejemplo de una función (asegúrate de copiar todas las necesarias)
|
|
def format_variable_name(name):
|
|
"""Limpia el nombre de la variable para SCL."""
|
|
if not name:
|
|
return "_INVALID_NAME_"
|
|
if name.startswith('"') and name.endswith('"'):
|
|
return name
|
|
prefix = ""
|
|
if name.startswith("#"):
|
|
prefix = "#"
|
|
name = name[1:]
|
|
if name and name[0].isdigit():
|
|
name = "_" + name
|
|
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
|
return prefix + name
|
|
|
|
# --- Helper Functions ---
|
|
# (get_scl_representation, format_variable_name, generate_temp_var_name, get_target_scl_name - sin cambios)
|
|
def get_scl_representation(source_info, network_id, scl_map, access_map):
|
|
if not source_info:
|
|
return None
|
|
if isinstance(source_info, list):
|
|
scl_parts = []
|
|
all_resolved = True
|
|
for sub_source in source_info:
|
|
sub_scl = get_scl_representation(
|
|
sub_source, network_id, scl_map, access_map
|
|
)
|
|
if sub_scl is None:
|
|
all_resolved = False
|
|
break
|
|
if (
|
|
sub_scl in ["TRUE", "FALSE"]
|
|
or (sub_scl.startswith('"') and sub_scl.endswith('"'))
|
|
or sub_scl.isdigit()
|
|
or (sub_scl.startswith("(") and sub_scl.endswith(")"))
|
|
):
|
|
scl_parts.append(sub_scl)
|
|
else:
|
|
scl_parts.append(f"({sub_scl})")
|
|
return (
|
|
" OR ".join(scl_parts)
|
|
if len(scl_parts) > 1
|
|
else (scl_parts[0] if scl_parts else "FALSE") if all_resolved else None
|
|
)
|
|
source_type = source_info.get("type")
|
|
if source_type == "powerrail":
|
|
return "TRUE"
|
|
elif source_type == "variable":
|
|
name = source_info.get("name")
|
|
# Asegurar que los nombres de variables se formatean correctamente aquí también
|
|
return (
|
|
format_variable_name(name)
|
|
if name
|
|
else f"_ERR_VAR_NO_NAME_{source_info.get('uid')}_"
|
|
)
|
|
elif source_type == "constant":
|
|
dtype = str(source_info.get("datatype", "")).upper()
|
|
value = source_info.get("value")
|
|
try:
|
|
if dtype == "BOOL":
|
|
return str(value).upper()
|
|
elif dtype in [
|
|
"INT",
|
|
"DINT",
|
|
"SINT",
|
|
"USINT",
|
|
"UINT",
|
|
"UDINT",
|
|
"LINT",
|
|
"ULINT",
|
|
"WORD",
|
|
"DWORD",
|
|
"LWORD",
|
|
"BYTE",
|
|
]:
|
|
return str(value)
|
|
elif dtype in ["REAL", "LREAL"]:
|
|
s_val = str(value)
|
|
return s_val if "." in s_val or "e" in s_val.lower() else s_val + ".0"
|
|
elif dtype == "STRING":
|
|
# Escapar comillas simples dentro del string si es necesario
|
|
str_val = str(value).replace("'", "''")
|
|
return f"'{str_val}'"
|
|
elif dtype == "TYPEDCONSTANT":
|
|
# Podría necesitar formateo específico basado en el tipo real
|
|
return str(value)
|
|
else:
|
|
# Otros tipos (TIME, DATE, etc.) - devolver como string por ahora
|
|
str_val = str(value).replace("'", "''")
|
|
return f"'{str_val}'"
|
|
except Exception as e:
|
|
print(f"Advertencia: Error formateando constante {source_info}: {e}")
|
|
return f"_ERR_CONST_FORMAT_{source_info.get('uid')}_"
|
|
elif source_type == "connection":
|
|
map_key = (
|
|
network_id,
|
|
source_info.get("source_instruction_uid"),
|
|
source_info.get("source_pin"),
|
|
)
|
|
return scl_map.get(map_key)
|
|
elif source_type == "unknown_source":
|
|
print(
|
|
f"Advertencia: Refiriendo a fuente desconocida UID: {source_info.get('uid')}"
|
|
)
|
|
return f"_ERR_UNKNOWN_SRC_{source_info.get('uid')}_"
|
|
else:
|
|
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:
|
|
return "_INVALID_NAME_"
|
|
|
|
# Si ya está entre comillas dobles, asumimos que es un nombre complejo (ej. "DB"."Variable")
|
|
# y lo devolvemos tal cual para SCL.
|
|
if name.startswith('"') and name.endswith('"'):
|
|
# Podríamos añadir validación extra aquí si fuera necesario
|
|
return name
|
|
|
|
# Si no tiene comillas, es un nombre simple (ej. Tag_1, #tempVar)
|
|
# Reemplazar caracteres no válidos (excepto '_') por '_'
|
|
# Permitir '#' al inicio para variables temporales
|
|
prefix = ""
|
|
if name.startswith("#"):
|
|
prefix = "#"
|
|
name = name[1:]
|
|
|
|
# Permitir letras, números y guiones bajos. Reemplazar el resto.
|
|
# Asegurarse de que no empiece con número (después del # si existe)
|
|
if name and name[0].isdigit():
|
|
name = "_" + name
|
|
# Reemplazar caracteres no válidos
|
|
name = re.sub(r"[^a-zA-Z0-9_]", "_", 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("-", "_")
|
|
pin_name_clean = str(pin_name).replace("-", "_").lower()
|
|
# 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, output_pin_name, network_id, default_to_temp=True):
|
|
instr_uid = instruction["instruction_uid"]
|
|
output_pin_data = instruction["outputs"].get(output_pin_name)
|
|
target_scl = None
|
|
if (
|
|
output_pin_data
|
|
and isinstance(output_pin_data, list)
|
|
and len(output_pin_data) == 1
|
|
):
|
|
dest_access = output_pin_data[0]
|
|
if dest_access.get("type") == "variable":
|
|
target_scl = dest_access.get("name")
|
|
if target_scl:
|
|
target_scl = format_variable_name(target_scl) # Formatear nombre
|
|
else:
|
|
print(
|
|
f"Error: Var destino {instr_uid}.{output_pin_name} sin nombre (UID: {dest_access.get('uid')}). {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
|
)
|
|
target_scl = (
|
|
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
|
if default_to_temp
|
|
else None
|
|
)
|
|
elif dest_access.get("type") == "constant":
|
|
print(
|
|
f"Advertencia: Instr {instr_uid} escribe en const UID {dest_access.get('uid')}. {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
|
)
|
|
target_scl = (
|
|
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
|
if default_to_temp
|
|
else None
|
|
)
|
|
else:
|
|
print(
|
|
f"Advertencia: Destino {instr_uid}.{output_pin_name} no es var/const: {dest_access.get('type')}. {'Usando temp.' if default_to_temp else 'Ignorando.'}"
|
|
)
|
|
target_scl = (
|
|
generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
|
if default_to_temp
|
|
else None
|
|
)
|
|
elif default_to_temp:
|
|
target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
|
|
|
# Si target_scl sigue siendo None y no se debe usar temp, devolver None
|
|
if target_scl is None and not default_to_temp:
|
|
return None
|
|
|
|
# Si target_scl es None pero sí se permite temp, generar uno ahora
|
|
if target_scl is None and default_to_temp:
|
|
target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name)
|
|
|
|
return target_scl
|