# -*- coding: utf-8 -*- import json import os import copy import traceback # --- Constantes y Configuración --- SCL_SUFFIX = "_scl" # --- Helper Functions --- def get_scl_representation(source_info, network_id, scl_map, access_map): """ Busca la representación SCL de una entrada. source_info: Puede ser {'type': 'powerrail'}, un Access dict, o un Connection dict, o una lista (OR). """ if not source_info: return None # Entrada no conectada o dependencia no lista # Si es una lista (rama OR), procesarla recursivamente 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 # Evitar paréntesis innecesarios si ya es una expresión simple o contenida 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})") # Añadir paréntesis por precaución de precedencia if all_resolved: return " OR ".join(scl_parts) if len(scl_parts) > 1 else (scl_parts[0] if scl_parts else None) else: return None # Dependencia en la rama no resuelta # Si no es lista, procesar como fuente única source_type = source_info.get('type') if source_type == 'powerrail': return "TRUE" elif source_type == 'variable': return source_info.get('name', f"_ERR_VAR_{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': return f"'{str(value)}'" elif dtype == 'TYPEDCONSTANT': return str(value) # Ej: DINT#60 else: return f"'{str(value)}'" 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) # Devuelve valor o None si no existe else: print(f"Advertencia: Tipo de fuente desconocido o inválido: {source_info}") return f"_ERR_UNKNOWN_SOURCE_" def generate_temp_var_name(network_id, instr_uid, pin_name): """Genera un nombre único para una variable temporal SCL.""" net_id_clean = str(network_id).replace('-', '_') instr_uid_clean = str(instr_uid).replace('-', '_') pin_name_clean = str(pin_name).replace('-', '_').lower() # Evitar nombres que empiecen con número si network_id es numérico prefix = "_" if str(net_id_clean)[0].isdigit() else "" return f"{prefix}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): """Determina el nombre SCL del destino (variable o temporal).""" 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') elif dest_access.get('type') == 'constant': print(f"Advertencia: Instrucción {instr_uid} intenta escribir en constante UID {dest_access.get('uid')}. {'Usando temporal.' if default_to_temp else 'Ignorando.'}") if default_to_temp: target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name) else: print(f"Advertencia: Destino de {instr_uid}.{output_pin_name} no es variable: {dest_access.get('type')}. {'Usando temporal.' if default_to_temp else 'Ignorando.'}") if default_to_temp: target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name) elif default_to_temp: target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name) return target_scl # --- Procesadores de Instrucciones --- def process_contact(instruction, network_id, scl_map, access_map): """Traduce Contact a una expresión booleana SCL y actualiza scl_map.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False is_negated = False # TODO: Determinar si es negado # print(f"DEBUG: Intentando procesar CONTACT{' (N)' if is_negated else ''} - UID: {instr_uid} en Red: {network_id}") in_rlo_scl = get_scl_representation(instruction['inputs'].get('in'), 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: # print(f"DEBUG: Dependencia no resuelta para CONTACT UID: {instr_uid} (in={in_rlo_scl}, op={operand_scl})") return False term = f"NOT {operand_scl}" if is_negated else operand_scl # Asegurarse de que el operando esté entre paréntesis si no es una variable simple if not (term.startswith('"') and term.endswith('"')): if not (term.startswith('(') and term.endswith(')')): term = f"({term})" new_rlo_scl = "" if in_rlo_scl == "TRUE": new_rlo_scl = term else: # Poner el RLO anterior entre paréntesis si es necesario if not (in_rlo_scl.startswith('(') and in_rlo_scl.endswith(')')) and 'AND' in in_rlo_scl or 'OR' in in_rlo_scl: in_rlo_processed = f"({in_rlo_scl})" else: in_rlo_processed = in_rlo_scl new_rlo_scl = f"{in_rlo_processed} AND {term}" map_key = (network_id, instr_uid, 'out') scl_map[map_key] = new_rlo_scl instruction['scl'] = f"// RLO updated by Contact {instr_uid}: {new_rlo_scl}" instruction['type'] = instr_type + SCL_SUFFIX return True def process_eq(instruction, network_id, scl_map, access_map): """Traduce Eq (comparación) a una expresión booleana SCL y actualiza scl_map.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar EQ - UID: {instr_uid} en Red: {network_id}") in1_scl = get_scl_representation(instruction['inputs'].get('in1'), network_id, scl_map, access_map) in2_scl = get_scl_representation(instruction['inputs'].get('in2'), network_id, scl_map, access_map) if in1_scl is None or in2_scl is None: # print(f"DEBUG: Dependencia no resuelta para EQ UID: {instr_uid} (in1={in1_scl}, in2={in2_scl})") return False # Poner operandos entre paréntesis si son complejos op1 = f"({in1_scl})" if ' ' in in1_scl else in1_scl op2 = f"({in2_scl})" if ' ' in in2_scl else in2_scl comparison_scl = f"{op1} = {op2}" # SCL usa '=' para comparación map_key_out = (network_id, instr_uid, 'out') scl_map[map_key_out] = comparison_scl # --- Manejo de ENO --- # El 'pre' en LAD para comparación a menudo actúa como el EN. pre_scl = get_scl_representation(instruction['inputs'].get('pre'), network_id, scl_map, access_map) # Si 'pre' no está conectado, asumimos TRUE. effective_en_scl = "TRUE" if pre_scl is None else pre_scl # El estado ENO de una comparación es TRUE si se ejecuta (EN=TRUE) y FALSE si no. map_key_eno = (network_id, instr_uid, 'eno') scl_map[map_key_eno] = effective_en_scl # print(f"DEBUG: EQ UID: {instr_uid} - Estado ENO en scl_map[{map_key_eno}] = {effective_en_scl}") # --- Fin Manejo de ENO --- 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): """Traduce Coil a una asignación SCL.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar COIL - UID: {instr_uid} en Red: {network_id}") in_rlo_scl = get_scl_representation(instruction['inputs'].get('in'), 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: # print(f"DEBUG: Dependencia no resuelta para COIL UID: {instr_uid} (in={in_rlo_scl}, op={operand_scl})") return False operand_info = instruction['inputs'].get('operand') if not (operand_info and operand_info.get('type') == 'variable'): print(f"Error: Operando de COIL UID {instr_uid} no es una variable: {operand_info}") instruction['scl'] = f"// ERROR: Coil {instr_uid} operando no es variable" instruction['type'] = instr_type + "_error" return True scl_final = f"{operand_scl} := {in_rlo_scl};" instruction['scl'] = scl_final instruction['type'] = instr_type + SCL_SUFFIX # print(f"INFO: COIL UID: {instr_uid} procesado. SCL: {scl_final}") return True def process_convert(instruction, network_id, scl_map, access_map): """Traduce Convert a SCL, usando temporal si es necesario.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar CONVERT - UID: {instr_uid} en Red: {network_id}") # --- Manejo de EN --- en_input = instruction['inputs'].get('en') en_scl = None if en_input is None: # print(f"DEBUG: Asumiendo EN=TRUE para CONVERT UID {instr_uid} (pin 'en' no conectado)") en_scl = "TRUE" else: en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) # --- Fin Manejo de EN --- in_scl = get_scl_representation(instruction['inputs'].get('in'), network_id, scl_map, access_map) if en_scl is None or in_scl is None: # print(f"DEBUG: Dependencia no resuelta para CONVERT UID: {instr_uid} (en={en_scl}, in={in_scl})") return False target_scl = get_target_scl_name(instruction, 'out', network_id, default_to_temp=True) if target_scl is None: print(f"Error Interno: No se pudo determinar destino para CONVERT UID {instr_uid}") instruction['scl'] = f"// ERROR: No se pudo determinar destino para Convert {instr_uid}" instruction['type'] += "_error" return True conversion_expr = in_scl # Asume conversión implícita por ahora # TODO: Añadir lógica de conversión explícita si se extraen tipos de TemplateValue scl_core = f"{target_scl} := {conversion_expr};" scl_final = scl_core if en_scl == "TRUE" else f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" 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 # ENO sigue a EN # print(f"DEBUG: CONVERT UID: {instr_uid} - Estado ENO en scl_map[{map_key_eno}] = {en_scl}") # print(f"INFO: CONVERT UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") return True def process_mod(instruction, network_id, scl_map, access_map): """Traduce Mod (módulo) a SCL, usando temporal si es necesario.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar MOD - UID: {instr_uid} en Red: {network_id}") # --- Manejo de EN --- en_input = instruction['inputs'].get('en') en_scl = None if en_input is None: # print(f"DEBUG: Asumiendo EN=TRUE para MOD UID {instr_uid} (pin 'en' no conectado)") en_scl = "TRUE" else: en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) # --- Fin Manejo de EN --- in1_scl = get_scl_representation(instruction['inputs'].get('in1'), network_id, scl_map, access_map) in2_scl = get_scl_representation(instruction['inputs'].get('in2'), network_id, scl_map, access_map) if en_scl is None or in1_scl is None or in2_scl is None: # print(f"DEBUG: Dependencia no resuelta para MOD UID: {instr_uid} (en={en_scl}, in1={in1_scl}, in2={in2_scl})") return False target_scl = get_target_scl_name(instruction, 'out', network_id, default_to_temp=True) if target_scl is None: print(f"Error Interno: No se pudo determinar destino para MOD UID {instr_uid}") instruction['scl'] = f"// ERROR: No se pudo determinar destino para Mod {instr_uid}" instruction['type'] += "_error" return True op1 = f"({in1_scl})" if ' ' in in1_scl else in1_scl op2 = f"({in2_scl})" if ' ' in in2_scl else in2_scl scl_core = f"{target_scl} := {op1} MOD {op2};" scl_final = scl_core if en_scl == "TRUE" else f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" 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 # ENO sigue a EN # print(f"DEBUG: MOD UID: {instr_uid} - Estado ENO en scl_map[{map_key_eno}] = {en_scl}") # print(f"INFO: MOD UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") return True def process_add(instruction, network_id, scl_map, access_map): instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar ADD - UID: {instr_uid} en Red: {network_id}") # --- Manejo de EN --- en_input = instruction['inputs'].get('en') en_scl = None if en_input is None: # print(f"DEBUG: Asumiendo EN=TRUE para ADD UID {instr_uid} (pin 'en' no conectado)") en_scl = "TRUE" else: en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) # --- Fin Manejo de EN --- in1_scl = get_scl_representation(instruction['inputs'].get('in1'), network_id, scl_map, access_map) in2_scl = get_scl_representation(instruction['inputs'].get('in2'), network_id, scl_map, access_map) if en_scl is None or in1_scl is None or in2_scl is None: # print(f"DEBUG: Dependencia no resuelta para ADD UID: {instr_uid} (en={en_scl}, in1={in1_scl}, in2={in2_scl})") return False target_scl = get_target_scl_name(instruction, 'out', network_id, default_to_temp=True) if target_scl is None: print(f"Error Interno: No se pudo determinar destino para ADD UID {instr_uid}") instruction['scl'] = f"// ERROR: No se pudo determinar destino para Add {instr_uid}" instruction['type'] += "_error" return True op1 = f"({in1_scl})" if ' ' in in1_scl else in1_scl op2 = f"({in2_scl})" if ' ' in in2_scl else in2_scl scl_core = f"{target_scl} := {op1} + {op2};" scl_final = scl_core if en_scl == "TRUE" else f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" 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 # ENO sigue a EN # print(f"DEBUG: ADD UID: {instr_uid} - Estado ENO en scl_map[{map_key_eno}] = {en_scl}") # print(f"INFO: ADD UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") return True def process_move(instruction, network_id, scl_map, access_map): instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar MOVE - UID: {instr_uid} en Red: {network_id}") # --- Manejo de EN --- en_input = instruction['inputs'].get('en') en_scl = None if en_input is None: # print(f"DEBUG: Asumiendo EN=TRUE para MOVE UID {instr_uid} (pin 'en' no conectado)") en_scl = "TRUE" else: en_scl = get_scl_representation(en_input, network_id, scl_map, access_map) # --- Fin Manejo de EN --- in_scl = get_scl_representation(instruction['inputs'].get('in'), network_id, scl_map, access_map) if en_scl is None or in_scl is None: # print(f"DEBUG: Dependencia no resuelta para MOVE UID: {instr_uid} (en={en_scl}, in={in_scl})") return False target_scl = get_target_scl_name(instruction, 'out1', network_id, default_to_temp=False) # No usar temp por defecto if target_scl is None: print(f"Advertencia: MOVE UID: {instr_uid} no tiene un destino variable único claro en out1. No se procesa.") return False scl_core = f"{target_scl} := {in_scl};" scl_final = scl_core if en_scl == "TRUE" else f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" instruction['scl'] = scl_final instruction['type'] = instr_type + SCL_SUFFIX # print(f"INFO: MOVE UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") return True def process_o(instruction, network_id, scl_map, access_map): """Traduce O (OR lógico) a una expresión booleana SCL.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar O - UID: {instr_uid} en Red: {network_id}") # Obtener todas las entradas (in1, in2, in3...) input_pins = [pin for pin in instruction['inputs'] if pin.startswith('in')] if not input_pins: print(f"Error: Instrucción O UID {instr_uid} no tiene pines de entrada 'inX'.") instruction['scl'] = f"// ERROR: O {instr_uid} sin pines de entrada" instruction['type'] += "_error" return True scl_parts = [] all_resolved = True for pin in sorted(input_pins): # Ordenar para consistencia in_scl = get_scl_representation(instruction['inputs'][pin], network_id, scl_map, access_map) if in_scl is None: all_resolved = False # print(f"DEBUG: Dependencia no resuelta para O UID: {instr_uid} (pin {pin})") break # Poner entre paréntesis si es complejo term = f"({in_scl})" if ' ' in in_scl else in_scl scl_parts.append(term) if not all_resolved: return False result_scl = " OR ".join(scl_parts) if len(scl_parts) > 1 else (scl_parts[0] if scl_parts else "FALSE") 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_pbox(instruction, network_id, scl_map, access_map): """Traduce PBox (lectura de bit, posible flanco) a SCL.""" instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False # print(f"DEBUG: Intentando procesar PBOX - UID: {instr_uid} en Red: {network_id}") # La entrada relevante es 'bit' bit_scl = get_scl_representation(instruction['inputs'].get('bit'), network_id, scl_map, access_map) if bit_scl is None: # print(f"DEBUG: Dependencia no resuelta para PBOX UID: {instr_uid} (bit={bit_scl})") return False # Lógica específica de PBox: # - Si solo lee un bit, la salida es ese bit. # - Si detecta flanco (P, N), necesita lógica adicional (variable estática) # TODO: Detectar si es detección de flanco (requiere más info del XML o nombre 'FP'/'FN') is_edge_detection = False # Asumir que no por ahora edge_type = '' # 'P' o 'N' result_scl = bit_scl # Por defecto, la salida es el bit de entrada if is_edge_detection: # Necesita una variable estática (en TEMP o STAT) para guardar el estado anterior static_var_name = f"stat_{network_id}_{instr_uid}_Flank" if edge_type == 'P': result_scl = f"({bit_scl} AND NOT {static_var_name})" # La actualización de la variable estática ocurriría al final del ciclo o red # scl_update = f"{static_var_name} := {bit_scl};" print(f"Advertencia: Detección de Flanco P (PBox {instr_uid}) requiere manejo de variable estática '{static_var_name}' (no implementado completamente).") elif edge_type == 'N': result_scl = f"(NOT {bit_scl} AND {static_var_name})" # scl_update = f"{static_var_name} := {bit_scl};" print(f"Advertencia: Detección de Flanco N (PBox {instr_uid}) requiere manejo de variable estática '{static_var_name}' (no implementado completamente).") else: result_scl = bit_scl # Volver al caso simple si no se reconoce el tipo map_key_out = (network_id, instr_uid, 'out') scl_map[map_key_out] = result_scl instruction['scl'] = f"// PBox {instr_uid} Output: {result_scl}" + (" (Edge detection logic simplified)" if is_edge_detection else "") instruction['type'] = instr_type + SCL_SUFFIX return True # --- Bucle Principal de Procesamiento --- def process_json_to_scl(json_filepath): if not os.path.exists(json_filepath): print(f"Error: Archivo JSON no encontrado en {json_filepath}") return print(f"Cargando JSON desde: {json_filepath}") try: with open(json_filepath, 'r', encoding='utf-8') as f: data = json.load(f) except Exception as e: print(f"Error al cargar o parsear JSON: {e}") return # Reconstruir access_map dinámicamente network_access_maps = {} # print("Creando mapas de acceso por red...") for network in data.get('networks', []): net_id = network['id'] current_access_map = {} for instr in network.get('logic', []): for pin, 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('scope') and src.get('type') in ['variable', 'constant']: current_access_map[src['uid']] = src for pin, 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('scope') 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 = 20 passes = 0 # Lista de procesadores actualizada processors = [ # Instrucciones que generan valores base o condiciones process_convert, process_mod, process_eq, process_pbox, # Procesa lectura de bit # Instrucciones que combinan lógica booleana process_contact, process_o, # Procesa OR # Instrucciones que usan resultados y condiciones process_add, process_move, process_coil, # Añadir más procesadores aquí (Sub, Mul, Div, GT, LT, temporizadores, contadores...) ] processor_map = {func.__name__.split('_')[1].capitalize(): func for func in processors} print("\n--- Iniciando Bucle de Procesamiento Iterativo ---") while passes < max_passes: passes += 1 made_change_in_pass = False print(f"\n--- Pase {passes} ---") for network in data.get('networks', []): network_id = network['id'] access_map = network_access_maps.get(network_id, {}) for instruction in network.get('logic', []): instr_type_original = instruction['type'] if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original: continue processor_func = processor_map.get(instr_type_original) if processor_func: try: changed = processor_func(instruction, network_id, scl_map, access_map) if changed: made_change_in_pass = True except Exception as e: print(f"ERROR al ejecutar {processor_func.__name__} en UID {instruction.get('instruction_uid')} Red {network_id}: {e}") traceback.print_exc() instruction['scl'] = f"// ERROR during processing: {e}" instruction['type'] += "_error" made_change_in_pass = True # else: # Comentado para reducir ruido # print(f"DEBUG: No hay procesador para el tipo: {instr_type_original}") if not made_change_in_pass: print(f"\n--- No se hicieron cambios en el pase {passes}. Proceso completado. ---") break elif passes == max_passes: print(f"\n--- Límite de {max_passes} pases alcanzado. Puede haber dependencias circulares o lógica no procesada. ---") output_filename = json_filepath.replace('.json', '_scl_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 al guardar el JSON procesado: {e}") # --- Ejecución --- if __name__ == "__main__": # Asegúrate de usar el JSON generado por el script anterior (el que tiene comentarios y eno_logic si aplica) input_json_file = 'BlenderRun_ProdTime_simplified.json' process_json_to_scl(input_json_file)