primera etapa de adaptacion a carpeta process
This commit is contained in:
parent
27442c88fd
commit
e3b4d413c9
|
@ -0,0 +1,150 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Directorio donde se crearán los archivos de procesador
|
||||||
|
PROCESSORS_DIR = "processors"
|
||||||
|
|
||||||
|
# Cabecera estándar para añadir a cada nuevo archivo
|
||||||
|
FILE_HEADER = """# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# TODO: Import necessary functions from processor_utils
|
||||||
|
# from .processor_utils import get_scl_representation, format_variable_name, ...
|
||||||
|
# Or: import processors.processor_utils as utils
|
||||||
|
|
||||||
|
# TODO: Define constants if needed (e.g., SCL_SUFFIX) or import them
|
||||||
|
SCL_SUFFIX = "_scl"
|
||||||
|
|
||||||
|
# --- Function code starts ---
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Pie de página estándar con la función get_processor_info de plantilla
|
||||||
|
def get_file_footer(func_name):
|
||||||
|
# Intenta adivinar el type_name quitando 'process_'
|
||||||
|
# Necesitará ajustes manuales para casos como 'call', 'edge_detector', 'comparison', 'math'
|
||||||
|
type_name_guess = func_name.replace('process_', '')
|
||||||
|
return f"""
|
||||||
|
# --- Function code ends ---
|
||||||
|
|
||||||
|
# --- Processor Information Function ---
|
||||||
|
def get_processor_info():
|
||||||
|
\"\"\"Returns the type name and processing function for this module.\"\"\"
|
||||||
|
# TODO: Adjust the type_name if needed (e.g., for call, edge_detector, comparison, math)
|
||||||
|
# TODO: Return a list if this module handles multiple types (e.g., PBox/NBox, FC/FB)
|
||||||
|
type_name = "{type_name_guess}" # Basic guess
|
||||||
|
return {{'type_name': type_name, 'processor_func': {func_name}}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def extract_and_create_processors(source_py_file):
|
||||||
|
"""
|
||||||
|
Extracts top-level functions starting with 'process_' from the source file
|
||||||
|
and creates individual processor files in the PROCESSORS_DIR.
|
||||||
|
"""
|
||||||
|
if not os.path.exists(source_py_file):
|
||||||
|
print(f"Error: Source file not found: '{source_py_file}'")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Reading source file: '{source_py_file}'")
|
||||||
|
try:
|
||||||
|
with open(source_py_file, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading source file: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Crear directorio de procesadores si no existe
|
||||||
|
os.makedirs(PROCESSORS_DIR, exist_ok=True)
|
||||||
|
print(f"Ensuring '{PROCESSORS_DIR}' directory exists.")
|
||||||
|
|
||||||
|
current_func_name = None
|
||||||
|
current_func_lines = []
|
||||||
|
processor_count = 0
|
||||||
|
|
||||||
|
print("Searching for processor functions (def process_...):")
|
||||||
|
|
||||||
|
# Usamos una expresión regular para encontrar definiciones de función de nivel superior
|
||||||
|
# que empiecen por 'process_'
|
||||||
|
func_def_pattern = re.compile(r"^def\s+(process_\w+)\s*\(")
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
match = func_def_pattern.match(line)
|
||||||
|
|
||||||
|
if match: # Encontrada una nueva definición de función 'process_'
|
||||||
|
new_func_name = match.group(1)
|
||||||
|
|
||||||
|
# Si estábamos procesando una función anterior, guardarla ahora
|
||||||
|
if current_func_name:
|
||||||
|
create_processor_file(current_func_name, current_func_lines)
|
||||||
|
processor_count += 1
|
||||||
|
|
||||||
|
# Empezar a recolectar para la nueva función
|
||||||
|
print(f" - Found: {new_func_name}")
|
||||||
|
current_func_name = new_func_name
|
||||||
|
current_func_lines = [line] # Empezar con la línea 'def'
|
||||||
|
|
||||||
|
elif line.strip() == "" and not current_func_lines:
|
||||||
|
# Ignorar líneas en blanco antes de la primera función
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif current_func_name and not line.startswith(' '):
|
||||||
|
# Si estamos dentro de una función y encontramos una línea
|
||||||
|
# que NO empieza con espacio (y no es un 'def process_'),
|
||||||
|
# podría ser el fin de la función (o una definición de otra cosa).
|
||||||
|
# Por simplicidad, asumimos que marca el fin. Guardamos la actual.
|
||||||
|
# (Esto funciona si las 'def process_' están una tras otra o separadas
|
||||||
|
# por comentarios o definiciones de funciones NO 'process_')
|
||||||
|
create_processor_file(current_func_name, current_func_lines)
|
||||||
|
processor_count += 1
|
||||||
|
current_func_name = None
|
||||||
|
current_func_lines = []
|
||||||
|
|
||||||
|
|
||||||
|
elif current_func_name:
|
||||||
|
# Si estamos recolectando líneas para una función, añadir la línea actual
|
||||||
|
current_func_lines.append(line)
|
||||||
|
|
||||||
|
# Guardar la última función encontrada después de salir del bucle
|
||||||
|
if current_func_name:
|
||||||
|
create_processor_file(current_func_name, current_func_lines)
|
||||||
|
processor_count += 1
|
||||||
|
|
||||||
|
if processor_count == 0:
|
||||||
|
print("\nWarning: No functions starting with 'process_' found at the top level.")
|
||||||
|
else:
|
||||||
|
print(f"\nFinished processing. Attempted to create/check {processor_count} processor files in '{PROCESSORS_DIR}'.")
|
||||||
|
|
||||||
|
def create_processor_file(func_name, func_lines):
|
||||||
|
"""Creates the individual processor file if it doesn't exist."""
|
||||||
|
target_filename = f"{func_name}.py"
|
||||||
|
target_filepath = os.path.join(PROCESSORS_DIR, target_filename)
|
||||||
|
|
||||||
|
if os.path.exists(target_filepath):
|
||||||
|
print(f" * Skipping: '{target_filename}' already exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" * Creating: '{target_filename}'...")
|
||||||
|
try:
|
||||||
|
with open(target_filepath, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(FILE_HEADER)
|
||||||
|
f.writelines(func_lines) # Escribir las líneas de la función
|
||||||
|
f.write(get_file_footer(func_name))
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Error writing file '{target_filename}': {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Extracts 'process_*' functions from a source Python file "
|
||||||
|
"and creates individual processor files."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"source_file",
|
||||||
|
default="x2_process.py", # Valor por defecto
|
||||||
|
nargs='?', # Hacerlo opcional para que use el default
|
||||||
|
help="Path to the source Python file (default: x2_process.py)"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
extract_and_create_processors(args.source_file)
|
|
@ -0,0 +1,209 @@
|
||||||
|
# -*- 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
|
Loading…
Reference in New Issue