# -*- coding: utf-8 -*- import json import os import copy import traceback import re # --- Constantes y Configuración --- SCL_SUFFIX = "_scl" GROUPED_COMMENT = "// Logic included in grouped IF" # Global data variable data = {} # --- 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): # ... (código sin cambios) 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") return ( format_variable_name(name) # Asegura formato correcto aquí también 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": str_val = str(value).replace("'", "''"); return f"'{str_val}'" elif dtype == "TYPEDCONSTANT": return str(value) # Ej: T#5s else: 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): # ... (código sin cambios) 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 def generate_temp_var_name(network_id, instr_uid, pin_name): # ... (código sin cambios) net_id_clean = str(network_id).replace("-", "_") instr_uid_clean = str(instr_uid).replace("-", "_") pin_name_clean = str(pin_name).replace("-", "_").lower() 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): # ... (código sin cambios) 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) 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) if target_scl is None and not default_to_temp: return None 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 # --- Procesadores de Instrucciones --- # (process_contact, process_eq, process_coil, process_convert, process_mod, # process_add, process_move, process_o, process_call - sin cambios significativos) # ... (resto de procesadores base aquí) ... def process_contact(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False is_negated = instruction.get("negated_pins", {}).get("operand", False) in_input = instruction["inputs"].get("in") in_rlo_scl = "TRUE" if in_input is None else get_scl_representation(in_input, network_id, scl_map, access_map) operand_scl = get_scl_representation(instruction["inputs"].get("operand"), network_id, scl_map, access_map) if in_rlo_scl is None or operand_scl is None: return False term = f"NOT {operand_scl}" if is_negated else operand_scl if not (term.startswith('"') and term.endswith('"')): if is_negated or (" " in term and not (term.startswith("(") and term.endswith(")"))): term = f"({term})" new_rlo_scl = term if in_rlo_scl == "TRUE" else (f"({in_rlo_scl}) AND {term}" if ("AND" in in_rlo_scl or "OR" in in_rlo_scl) and not (in_rlo_scl.startswith("(") and in_rlo_scl.endswith(")")) else f"{in_rlo_scl} AND {term}") map_key = (network_id, instr_uid, "out"); scl_map[map_key] = new_rlo_scl instruction["scl"] = f"// RLO: {new_rlo_scl}"; instruction["type"] = instr_type + SCL_SUFFIX return True def process_eq(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False in1_info = instruction["inputs"].get("in1"); in2_info = instruction["inputs"].get("in2") in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map); in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map) if in1_scl is None or in2_scl is None: return False op1 = format_variable_name(in1_scl) if in1_info and in1_info.get("type") == "variable" else in1_scl op2 = format_variable_name(in2_scl) if in2_info and in2_info.get("type") == "variable" else in2_scl op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1; op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2 comparison_scl = f"{op1} = {op2}"; map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = comparison_scl pre_input = instruction["inputs"].get("pre"); pre_scl = "TRUE" if pre_input is None else get_scl_representation(pre_input, network_id, scl_map, access_map) if pre_scl is None: return False map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = pre_scl instruction["scl"] = f"// Comparison Eq {instr_uid}: {comparison_scl}"; instruction["type"] = instr_type + SCL_SUFFIX return True def process_coil(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False in_rlo_scl = get_scl_representation(instruction["inputs"].get("in"), network_id, scl_map, access_map) operand_info = instruction["inputs"].get("operand"); operand_scl = get_scl_representation(operand_info, network_id, scl_map, access_map) if in_rlo_scl is None or operand_scl is None: return False if not (operand_info and operand_info.get("type") == "variable"): print(f"Error: Operando COIL {instr_uid} no es variable o falta información."); instruction["scl"] = f"// ERROR: Coil {instr_uid} operando no es variable o falta info"; instruction["type"] = instr_type + "_error"; return True operand_scl_formatted = format_variable_name(operand_scl) if in_rlo_scl == "(TRUE)": in_rlo_scl = "TRUE"; elif in_rlo_scl == "(FALSE)": in_rlo_scl = "FALSE" scl_final = f"{operand_scl_formatted} := {in_rlo_scl};"; instruction["scl"] = scl_final; instruction["type"] = instr_type + SCL_SUFFIX return True def process_convert(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"]; if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False en_input = instruction["inputs"].get("en"); en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE" in_info = instruction["inputs"].get("in"); in_scl = get_scl_representation(in_info, network_id, scl_map, access_map) if en_scl is None or in_scl is None: return False target_scl = get_target_scl_name(instruction, "out", network_id, default_to_temp=True) if target_scl is None: print(f"Error: Sin destino claro para CONVERT {instr_uid}"); instruction["scl"] = f"// ERROR: Convert {instr_uid} sin destino"; instruction["type"] += "_error"; return True in_scl_formatted = format_variable_name(in_scl) if in_info and in_info.get("type") == "variable" else in_scl conversion_expr = in_scl_formatted # Simplificación, asume asignación directa scl_core = f"{target_scl} := {conversion_expr};" scl_final = f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core instruction["scl"] = scl_final; instruction["type"] = instr_type + SCL_SUFFIX map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = target_scl map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = en_scl return True def process_mod(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"]; if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False en_input = instruction["inputs"].get("en"); en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE" in1_info = instruction["inputs"].get("in1"); in2_info = instruction["inputs"].get("in2") in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map); in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map) if en_scl is None or in1_scl is None or in2_scl is None: return False target_scl = get_target_scl_name(instruction, "out", network_id, default_to_temp=True) if target_scl is None: print(f"Error: Sin destino MOD {instr_uid}"); instruction["scl"] = f"// ERROR: Mod {instr_uid} sin destino"; instruction["type"] += "_error"; return True op1 = format_variable_name(in1_scl) if in1_info and in1_info.get("type") == "variable" else in1_scl op2 = format_variable_name(in2_scl) if in2_info and in2_info.get("type") == "variable" else in2_scl op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1; op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2 scl_core = f"{target_scl} := {op1} MOD {op2};"; scl_final = f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core instruction["scl"] = scl_final; instruction["type"] = instr_type + SCL_SUFFIX map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = target_scl map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = en_scl return True def process_add(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False en_input = instruction["inputs"].get("en"); en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE" in1_info = instruction["inputs"].get("in1"); in2_info = instruction["inputs"].get("in2") in1_scl = get_scl_representation(in1_info, network_id, scl_map, access_map); in2_scl = get_scl_representation(in2_info, network_id, scl_map, access_map) if en_scl is None or in1_scl is None or in2_scl is None: return False target_scl = get_target_scl_name(instruction, "out", network_id, default_to_temp=True) if target_scl is None: print(f"Error: Sin destino ADD {instr_uid}"); instruction["scl"] = f"// ERROR: Add {instr_uid} sin destino"; instruction["type"] += "_error"; return True op1 = format_variable_name(in1_scl) if in1_info and in1_info.get("type") == "variable" else in1_scl op2 = format_variable_name(in2_scl) if in2_info and in2_info.get("type") == "variable" else in2_scl op1 = f"({op1})" if " " in op1 and not op1.startswith("(") else op1; op2 = f"({op2})" if " " in op2 and not op2.startswith("(") else op2 scl_core = f"{target_scl} := {op1} + {op2};"; scl_final = f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core instruction["scl"] = scl_final; instruction["type"] = instr_type + SCL_SUFFIX map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = target_scl map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = en_scl return True def process_move(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False en_input = instruction["inputs"].get("en"); en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE" in_info = instruction["inputs"].get("in"); in_scl = get_scl_representation(in_info, network_id, scl_map, access_map) if en_scl is None or in_scl is None: return False target_scl = get_target_scl_name(instruction, "out1", network_id, default_to_temp=False) if target_scl is None: target_scl = get_target_scl_name(instruction, "out", network_id, default_to_temp=False) if target_scl is None: print(f"Advertencia/Error: MOVE {instr_uid} sin destino claro en 'out' o 'out1'."); return False in_scl_formatted = format_variable_name(in_scl) if in_info and in_info.get("type") == "variable" else in_scl scl_core = f"{target_scl} := {in_scl_formatted};"; scl_final = f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" if en_scl != "TRUE" else scl_core instruction["scl"] = scl_final; instruction["type"] = instr_type + SCL_SUFFIX map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = target_scl map_key_out1 = (network_id, instr_uid, "out1"); scl_map[map_key_out1] = target_scl map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = en_scl return True # --- NUEVA FUNCIÓN UNIFICADA para PBox y NBox --- def process_edge_detector(instruction, network_id, scl_map, access_map): """Genera SCL para PBox (P_TRIG) o NBox (N_TRIG).""" instr_uid = instruction["instruction_uid"] instr_type_original = instruction["type"] # PBox o NBox if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original: return False # Obtener CLK (señal de entrada) y MemBit (bit de memoria) clk_input = instruction["inputs"].get("in") mem_bit_input = instruction["inputs"].get("bit") clk_scl = get_scl_representation(clk_input, network_id, scl_map, access_map) mem_bit_scl_original = get_scl_representation(mem_bit_input, network_id, scl_map, access_map) if clk_scl is None or mem_bit_scl_original is None: # print(f"DEBUG Edge: Esperando dependencias para {instr_type_original} UID {instr_uid}") return False # Dependencias no listas # Validar que el bit de memoria sea una variable if not (mem_bit_input and mem_bit_input.get("type") == "variable"): print(f"Error: {instr_type_original} {instr_uid} 'bit' no es variable o falta información.") instruction["scl"] = f"// ERROR: {instr_type_original} {instr_uid} 'bit' no es variable." instruction["type"] = instr_type_original + "_error" return True # Procesado con error # --- Renombrar bit de memoria para VAR_STAT --- # Quitar comillas existentes, añadir prefijo "stat_" y volver a añadir comillas mem_bit_name_clean = mem_bit_scl_original.strip('"') stat_mem_bit_scl = f'"stat_{mem_bit_name_clean}"' # Nombre SCL para la variable estática # Asegurar paréntesis alrededor de CLK si es complejo clk_scl_formatted = clk_scl if (' ' in clk_scl or 'AND' in clk_scl or 'OR' in clk_scl) and not (clk_scl.startswith('(') and clk_scl.endswith(')')): clk_scl_formatted = f"({clk_scl})" # --- Generar Lógica SCL --- result_scl = "FALSE" # SCL para la salida del flanco (pin 'out') scl_comment = "" # Comentario informativo if instr_type_original == "PBox": # Flanco Positivo (P_TRIG) result_scl = f"{clk_scl_formatted} AND NOT {stat_mem_bit_scl}" scl_comment = f"// P_TRIG: {result_scl}" elif instr_type_original == "NBox": # Flanco Negativo (N_TRIG) result_scl = f"NOT {clk_scl_formatted} AND {stat_mem_bit_scl}" scl_comment = f"// N_TRIG: {result_scl}" else: # No debería ocurrir si el mapeo de procesadores es correcto print(f"Error interno: process_edge_detector llamado para tipo inesperado {instr_type_original}") instruction["scl"] = f"// ERROR: Tipo de flanco inesperado {instr_type_original}" instruction["type"] = instr_type_original + "_error" return True # La actualización del bit de memoria es igual para P_TRIG y N_TRIG estándar scl_mem_update = f"{stat_mem_bit_scl} := {clk_scl_formatted};" # --- Almacenar Resultados --- # El pulso resultante va al mapa SCL para que lo usen las instrucciones siguientes map_key_out = (network_id, instr_uid, "out") scl_map[map_key_out] = result_scl # La actualización de memoria es la acción principal de esta instrucción en SCL instruction["scl"] = f"{scl_mem_update} {scl_comment}" instruction["type"] = instr_type_original + SCL_SUFFIX # El pin ENO normalmente sigue al CLK en los bloques de flanco estándar map_key_eno = (network_id, instr_uid, "eno") scl_map[map_key_eno] = clk_scl # Usar clk_scl original sin formateo extra return True # --- FIN NUEVA FUNCIÓN UNIFICADA --- def process_o(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False input_pins = sorted([pin for pin in instruction["inputs"] if pin.startswith("in")]) if not input_pins: print(f"Error: O {instr_uid} sin pines de entrada (inX)."); instruction["scl"] = f"// ERROR: O {instr_uid} sin pines inX"; instruction["type"] += "_error"; return True scl_parts = []; all_resolved = True for pin in input_pins: in_scl = get_scl_representation(instruction["inputs"][pin], network_id, scl_map, access_map) if in_scl is None: all_resolved = False; break term = in_scl; if (" " in term or "AND" in term) and not (term.startswith("(") and term.endswith(")")): term = f"({term})" scl_parts.append(term) if not all_resolved: return False result_scl = "FALSE"; if scl_parts: result_scl = " OR ".join(scl_parts) if len(scl_parts) == 1: result_scl = scl_parts[0] map_key_out = (network_id, instr_uid, "out"); scl_map[map_key_out] = result_scl instruction["scl"] = f"// Logic O {instr_uid}: {result_scl}"; instruction["type"] = instr_type + SCL_SUFFIX return True def process_call(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction.get("type", "") if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False block_name = instruction.get("block_name", f"UnknownCall_{instr_uid}"); block_type = instruction.get("block_type") instance_db = instruction.get("instance_db"); instance_db_scl = format_variable_name(instance_db) if instance_db else None block_name_scl = format_variable_name(block_name) en_input = instruction["inputs"].get("en"); en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) if en_input else "TRUE" if en_scl is None: return False scl_call_params = []; processed_inputs = {"en"} for pin_name, source_info in instruction.get("inputs", {}).items(): if pin_name not in processed_inputs: param_scl = get_scl_representation(source_info, network_id, scl_map, access_map) if param_scl is None: return False param_scl_formatted = format_variable_name(param_scl) if source_info.get("type") == "variable" else param_scl scl_call_params.append(f"{format_variable_name(pin_name)} := {param_scl_formatted}") processed_inputs.add(pin_name) scl_call_body = ""; param_string = ", ".join(scl_call_params) if block_type == "FB": if not instance_db_scl: print(f"Error: Llamada a FB '{block_name_scl}' (UID {instr_uid}) sin DB de instancia especificado."); instruction["scl"] = f"// ERROR: FB Call {block_name_scl} sin instancia"; instruction["type"] = "Call_FB_error"; return True scl_call_body = f"{instance_db_scl}({param_string});" elif block_type == "FC": scl_call_body = f"{block_name_scl}({param_string});" else: print(f"Advertencia: Tipo de bloque no soportado para Call UID {instr_uid}: {block_type}"); scl_call_body = f"// ERROR: Call a bloque tipo '{block_type}' no soportado: {block_name_scl}"; instruction["type"] = f"Call_{block_type}_error" scl_final = ""; if en_scl != "TRUE": indented_call = "\\n".join([f" {line}" for line in scl_call_body.splitlines()]); scl_final = f"IF {en_scl} THEN\\n{indented_call}\\nEND_IF;" else: scl_final = scl_call_body instruction["scl"] = scl_final; instruction["type"] = f"Call_{block_type}_scl" if "_error" not in instruction["type"] else instruction["type"] map_key_eno = (network_id, instr_uid, "eno"); scl_map[map_key_eno] = en_scl return True # --- Procesador de Agrupación (Sin cambios) --- def process_group_ifs(instruction, network_id, scl_map, access_map): # ... (código sin cambios) ... instr_uid = instruction["instruction_uid"]; instr_type = instruction["type"]; instr_type_original = instr_type.replace("_scl", "").replace("_error", "") made_change = False if (not instr_type.endswith("_scl") or "_error" in instr_type or instruction.get("grouped", False) or instr_type_original not in ["Contact", "O", "Eq", "Ne", "Gt", "Lt", "Ge", "Le", "PBox", "NBox", "And", "Xor"]): return False # Añadido NBox aquí current_scl = instruction.get("scl", "") if (current_scl.strip().startswith("IF") and "END_IF;" in current_scl) or (current_scl.strip().startswith("//") and "IF" in current_scl): return False map_key_out = (network_id, instr_uid, "out"); condition_scl = scl_map.get(map_key_out) if condition_scl is None or condition_scl in ["TRUE", "FALSE"]: return False grouped_instructions_cores = []; consumer_instr_list = [] network_logic = next((net["logic"] for net in data["networks"] if net["id"] == network_id), []); if not network_logic: return False groupable_types = ["Move", "Add", "Sub", "Mul", "Div", "Mod", "Convert", "Call_FC", "Call_FB"] for consumer_instr in network_logic: consumer_uid = consumer_instr["instruction_uid"] if consumer_instr.get("grouped", False) or consumer_uid == instr_uid: continue consumer_en = consumer_instr.get("inputs", {}).get("en"); consumer_type = consumer_instr.get("type", ""); consumer_type_original = consumer_type.replace("_scl", "").replace("_error", "") is_enabled_by_us = False if (isinstance(consumer_en, dict) and consumer_en.get("type") == "connection" and consumer_en.get("source_instruction_uid") == instr_uid and consumer_en.get("source_pin") == "out"): is_enabled_by_us = True if (is_enabled_by_us and consumer_type.endswith("_scl") and consumer_type_original in groupable_types): consumer_scl = consumer_instr.get("scl", ""); core_scl = None if consumer_scl.strip().startswith("IF"): match = re.search(r"IF\\s+.*\\s+THEN\\s*(.*?)\\s*END_IF;", consumer_scl, re.DOTALL | re.IGNORECASE) if match: core_scl = match.group(1).strip() elif consumer_scl and not consumer_scl.strip().startswith("//"): core_scl = consumer_scl.strip() if core_scl: grouped_instructions_cores.append(core_scl); consumer_instr_list.append(consumer_instr) if len(grouped_instructions_cores) > 1: print(f"INFO: Agrupando {len(grouped_instructions_cores)} instrucciones bajo condición de {instr_type_original} UID {instr_uid} (Cond: {condition_scl})") scl_grouped = [f"IF {condition_scl} THEN"] for core_line in grouped_instructions_cores: indented_core = "\\n".join([f" {line.strip()}" for line in core_line.splitlines()]); scl_grouped.append(indented_core) scl_grouped.append("END_IF;"); final_grouped_scl = "\\n".join(scl_grouped) instruction["scl"] = final_grouped_scl for consumer_instr in consumer_instr_list: consumer_instr["scl"] = f"{GROUPED_COMMENT} (by UID {instr_uid})"; consumer_instr["grouped"] = True made_change = True return made_change # --- Bucle Principal de Procesamiento --- def process_json_to_scl(json_filepath): # ... (Inicio sin cambios) ... if not os.path.exists(json_filepath): print(f"Error: JSON no encontrado: {json_filepath}"); return print(f"Cargando JSON desde: {json_filepath}") try: global data; data = json.load(f) except Exception as e: print(f"Error al cargar JSON: {e}"); traceback.print_exc(); return network_access_maps = {} for network in data.get("networks", []): net_id = network["id"]; current_access_map = {} for instr in network.get("logic", []): for _, source in instr.get("inputs", {}).items(): sources_to_check = source if isinstance(source, list) else ([source] if isinstance(source, dict) else []) for src in sources_to_check: if isinstance(src, dict) and src.get("uid") and src.get("type") in ["variable", "constant"]: current_access_map[src["uid"]] = src for _, dest_list in instr.get("outputs", {}).items(): if isinstance(dest_list, list): for dest in dest_list: if isinstance(dest, dict) and dest.get("uid") and dest.get("type") in ["variable", "constant"]: current_access_map[dest["uid"]] = dest network_access_maps[net_id] = current_access_map scl_map = {} max_passes = 30; passes = 0; processing_complete = False # --- MODIFICACIÓN: Añadir process_edge_detector al mapa --- base_processors = [ process_convert, process_mod, process_eq, process_contact, process_o, process_edge_detector, # Usar la nueva función unificada process_add, process_move, process_call, process_coil, # ... otros procesadores base ... ] processor_map = {} for func in base_processors: match = re.match(r"process_(\w+)", func.__name__) if match: type_name = match.group(1).lower() if type_name == "call": processor_map["call_fc"] = func; processor_map["call_fb"] = func; processor_map["call"] = func elif type_name == "edge_detector": # Mapear PBox y NBox a la nueva función processor_map["pbox"] = func processor_map["nbox"] = func else: processor_map[type_name] = func # --- FIN MODIFICACIÓN --- print("\\n--- Iniciando Bucle de Procesamiento Iterativo ---") while passes < max_passes and not processing_complete: # ... (Lógica del bucle iterativo sin cambios, # ahora usará process_edge_detector para PBox y NBox # a través del processor_map actualizado) ... passes += 1; made_change_in_base_pass = False; made_change_in_group_pass = False print(f"\\n--- Pase {passes} ---"); num_processed_this_pass = 0; num_grouped_this_pass = 0 for network in data.get("networks", []): network_id = network["id"]; access_map = network_access_maps.get(network_id, {}) network_logic = network.get("logic", []) for instruction in network_logic: instr_uid = instruction.get("instruction_uid"); instr_type_original = instruction.get("type", "Unknown") if (instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original or instruction.get("grouped", False)): continue lookup_key = instr_type_original.lower() if instr_type_original == "Call": block_type = instruction.get("block_type", "").upper() if block_type == "FC": lookup_key = "call_fc" elif block_type == "FB": lookup_key = "call_fb" func_to_call = processor_map.get(lookup_key) if func_to_call: try: # No necesita pasar network_logic excepto para la versión anterior de PBox changed = func_to_call(instruction, network_id, scl_map, access_map) if changed: made_change_in_base_pass = True; num_processed_this_pass += 1 except Exception as e: print(f"ERROR(Base) al procesar {instr_type_original} UID {instr_uid} con {func_to_call.__name__}: {e}"); traceback.print_exc(); instruction["scl"] = f"// ERROR en procesador base: {e}"; instruction["type"] = instr_type_original + "_error"; made_change_in_base_pass = True if made_change_in_base_pass or passes == 1: for network in data.get("networks", []): network_id = network["id"]; access_map = network_access_maps.get(network_id, {}); network_logic = network.get("logic", []) for instruction in network_logic: if instruction["type"].endswith("_scl") and not instruction.get("grouped", False): try: group_changed = process_group_ifs(instruction, network_id, scl_map, access_map) if group_changed: made_change_in_group_pass = True; num_grouped_this_pass += 1 except Exception as e: print(f"ERROR(Group) al intentar agrupar desde UID {instruction.get('instruction_uid')}: {e}"); traceback.print_exc() if not made_change_in_base_pass and not made_change_in_group_pass: print(f"\\n--- No se hicieron más cambios en el pase {passes}. Proceso iterativo completado. ---"); processing_complete = True else: print(f"--- Fin Pase {passes}: {num_processed_this_pass} procesados, {num_grouped_this_pass} agrupados. Continuando...") if passes == max_passes and not processing_complete: print(f"\\n--- ADVERTENCIA: Límite de {max_passes} pases alcanzado. Puede haber dependencias no resueltas. ---") # --- Verificación Final y Guardado (Sin cambios) --- print("\\n--- Verificación Final de Instrucciones No Procesadas ---"); unprocessed_count = 0; unprocessed_details = [] for network in data.get("networks", []): network_id = network.get("id", "Unknown ID"); network_title = network.get("title", f"Network {network_id}") for instruction in network.get("logic", []): instr_uid = instruction.get("instruction_uid", "Unknown UID"); instr_type = instruction.get("type", "Unknown Type"); is_grouped = instruction.get("grouped", False) if (not instr_type.endswith(SCL_SUFFIX) and "_error" not in instr_type and not is_grouped): unprocessed_count += 1; unprocessed_details.append(f" - Red '{network_title}' (ID: {network_id}), Instrucción UID: {instr_uid}, Tipo Original: '{instr_type}'") if unprocessed_count > 0: print(f"ADVERTENCIA: Se encontraron {unprocessed_count} instrucciones que no pudieron ser procesadas a SCL:"); for detail in unprocessed_details: print(detail) print(">>> Estos tipos de instrucción podrían necesitar un procesador específico en 'x2_process.py'.") else: print("INFO: Todas las instrucciones fueron procesadas a SCL, marcadas como error o agrupadas exitosamente.") output_filename = json_filepath.replace("_simplified.json", "_simplified_processed.json") print(f"\\nGuardando JSON procesado en: {output_filename}") try: with open(output_filename, "w", encoding="utf-8") as f: json.dump(data, f, indent=4, ensure_ascii=False) print("Guardado completado.") except Exception as e: print(f"Error Crítico al guardar JSON procesado: {e}"); traceback.print_exc() # --- Ejecución --- if __name__ == "__main__": # Asegurarse de usar el nombre base del XML de prueba xml_filename_base = "TestLAD" # <--- CAMBIADO PARA USAR TestLAD input_json_file = f"{xml_filename_base}_simplified.json" if not os.path.exists(input_json_file): print(f"Error Fatal: El archivo de entrada JSON simplificado no existe: '{input_json_file}'"); print("Asegúrate de haber ejecutado 'x1_to_json.py' primero sobre el archivo XML correcto.") else: process_json_to_scl(input_json_file)