From 5e4f351d297f1b4482cb93357b2d37b071796a8f Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 18 Apr 2025 13:37:59 +0200 Subject: [PATCH] Version mejorada cuando los EN no estan definidos --- BlenderRun_ProdTime_parsed.pas | Bin 12262 -> 0 bytes ...Run_ProdTime_simplified_scl_processed.json | 67 +-- process.py | 464 +++++++++++------- 3 files changed, 321 insertions(+), 210 deletions(-) delete mode 100644 BlenderRun_ProdTime_parsed.pas diff --git a/BlenderRun_ProdTime_parsed.pas b/BlenderRun_ProdTime_parsed.pas deleted file mode 100644 index acdf0cfd2009da38e7a57876b65bd6dc01891396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12262 zcmeHNTTc{C5Upnu|HHfqi6%0$Aeb0~;Ac!w@%loH5f=G2qC?ijON>9&zoMS%-lk{! zS~IhP#?59$*xBmpx|}*y-9LZaqWQiq(DQpnJ-5`}qDby}-X?to54q z)oQb{Ry%OK$NhDXmn}cA%q< zVD_Mn2Hq2A(gd0}qha95yNjocw}*IopS~!mam(DuSjPD8k+PVMjslw0E%&~vbWLT) z3Xq|1e)tx-sQh>U3w=cPxEZYNz*aSuwb5Ltj6H^>4OYry!OxQRu`oENSkQTpiiHvt zsBbJRu#)S8jV{K$1y7dhFqmkC--M+E=&~r{FrQ z8DJk&W4?L_=5wHG`%qO&UFTVLp0hBE$z$^7lnoy#Mq%sS4pu#YJ#*a3uk^Z-H}x&( z&6K&Y=J5)hLgf^O4!}BPF%svSc^)UVNGdY&o{Lq}G(ThhdvVqDywJkN+8!8Gh{bMr zJh4xXrxBxlj{A%#hRmVtW?p|E?EeIw{*K&aY^mugB42m2jCv2J{b-YQ_2sjgU8M4F z6)Vf~Z-LQlNA`~HfBzkfUIQbO#YhaJD@3&{?i$3L{w0uk`Hzq zJvm%n!FOf-2~V|Cv(->$l|t!xgx;fYZ;(UO?sdnAB<{J%suVHyQneIB>ZLYw+T*B;CukZmHj;HdU z;WYLHD-M7Axo$$?e_*SHZXi_N4FuO8>6;%F`MqHGe#e((t<8Nz{*;hCmS(WI z{mip}^JTT)&w6%0q1ig$b;?Js6mLV_qTNaR^RrNo7sKy(nS2=oPbDBGtk4bq+_{LhO3wLo#it9cXvAH3g(!|)4^|9fTRFETk*9a&Jhu{)kQgttE}en!9N`{ZFxwF16cd7VRU| z?E-4&-mBU7Y@`~tFGV`TGg7r=3l%M2OTx3 1 else (scl_parts[0] if scl_parts else None) else: - return None + 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" # Condición inicial + return "TRUE" elif source_type == 'variable': return source_info.get('name', f"_ERR_VAR_{source_info.get('uid')}_") @@ -47,28 +48,22 @@ def get_scl_representation(source_info, network_id, scl_map, access_map): elif source_type == 'constant': dtype = str(source_info.get('datatype', '')).upper() value = source_info.get('value') - try: # Añadir try-except para robustez en formato + 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 else s_val + ".0" + 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)}'" # Otros tipos como string + 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')) - if map_key in scl_map: - # print(f"DEBUG: Valor encontrado en scl_map para {map_key}: {scl_map[map_key]}") - return scl_map[map_key] - else: - # print(f"DEBUG: Valor NO encontrado en scl_map para {map_key}") - return None # Dependencia no resuelta + 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}") @@ -78,8 +73,10 @@ 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() # lower para consistencia - return f"_temp_{net_id_clean}_{instr_uid_clean}_{pin_name_clean}" + 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).""" @@ -91,19 +88,15 @@ def get_target_scl_name(instruction, output_pin_name, network_id, default_to_tem dest_access = output_pin_data[0] if dest_access.get('type') == 'variable': target_scl = dest_access.get('name') - # print(f"DEBUG: Target para {instr_uid}.{output_pin_name} es variable directa: {target_scl}") elif dest_access.get('type') == 'constant': - print(f"Advertencia: Instrucción {instr_uid} intenta escribir en constante UID {dest_access.get('uid')}. Usando temporal.") + 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 si aplica.") + 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: - # Si no hay salida, o va a múltiples sitios, o no es Access tipo variable, usar temporal - # print(f"DEBUG: Usando temporal para {instr_uid}.{output_pin_name}") target_scl = generate_temp_var_name(network_id, instr_uid, output_pin_name) - # print(f"DEBUG: Target final para {instr_uid}.{output_pin_name}: {target_scl}") return target_scl # --- Procesadores de Instrucciones --- @@ -112,41 +105,42 @@ 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): return False + if instr_type.endswith(SCL_SUFFIX) or "_error" in instr_type: return False - # Asume Contact normal, necesitaría info adicional para negado - # (Podría estar en 'Name' o TemplateValue si el JSON lo capturara) - is_negated = False # TODO: Determinar si es negado (ej. check instruction['Name'] == 'ContactN') + 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}") + # 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})") + # print(f"DEBUG: Dependencia no resuelta para CONTACT UID: {instr_uid} (in={in_rlo_scl}, op={operand_scl})") return False - # Construir nueva expresión RLO - term = f"(NOT {operand_scl})" if is_negated else operand_scl + 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": # Inicio de línea o RLO anterior era TRUE + if in_rlo_scl == "TRUE": new_rlo_scl = term - else: # Combinar con RLO anterior - # Quitar paréntesis externos si el RLO anterior ya los tiene - if in_rlo_scl.startswith('(') and in_rlo_scl.endswith(')'): - in_rlo_processed = in_rlo_scl + 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 = f"({in_rlo_scl})" # Asegurar paréntesis + in_rlo_processed = in_rlo_scl new_rlo_scl = f"{in_rlo_processed} AND {term}" - # Actualizar scl_map con el nuevo RLO resultante de este contacto + map_key = (network_id, instr_uid, 'out') scl_map[map_key] = new_rlo_scl - # print(f"DEBUG: CONTACT UID: {instr_uid} - Nuevo RLO en scl_map[{map_key}] = {new_rlo_scl}") - # Los contactos no generan SCL directo, solo actualizan el flujo lógico (RLO) - instruction['scl'] = f"// RLO updated by Contact {instr_uid}: {new_rlo_scl}" # Comentario opcional + instruction['scl'] = f"// RLO updated by Contact {instr_uid}: {new_rlo_scl}" instruction['type'] = instr_type + SCL_SUFFIX return True @@ -154,29 +148,37 @@ 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): return False + 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}") + # 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) - # 'pre' generalmente no se traduce directamente a SCL para comparaciones simples - # pre_scl = get_scl_representation(instruction['inputs'].get('pre'), network_id, scl_map, access_map) - if in1_scl is None or in2_scl is None: # Ignoramos 'pre' por ahora - print(f"DEBUG: Dependencia no resuelta para EQ UID: {instr_uid} (in1={in1_scl}, in2={in2_scl})") + 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 - # Generar expresión de comparación - comparison_scl = f"({in1_scl} = {in2_scl})" + # 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 - # Actualizar scl_map con el resultado booleano - map_key = (network_id, instr_uid, 'out') - scl_map[map_key] = comparison_scl - # print(f"DEBUG: EQ UID: {instr_uid} - Resultado en scl_map[{map_key}] = {comparison_scl}") + map_key_out = (network_id, instr_uid, 'out') + scl_map[map_key_out] = comparison_scl - # Eq no genera SCL directo, solo el resultado booleano - instruction['scl'] = f"// Comparison Eq {instr_uid}: {comparison_scl}" # Comentario opcional + # --- 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 @@ -184,207 +186,305 @@ 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): return False + 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}") + # 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})") + # print(f"DEBUG: Dependencia no resuelta para COIL UID: {instr_uid} (in={in_rlo_scl}, op={operand_scl})") return False - # Verificar que el operando sea una variable 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}") - # No se puede asignar a una constante o algo desconocido instruction['scl'] = f"// ERROR: Coil {instr_uid} operando no es variable" - instruction['type'] = instr_type + "_error" # Marcar como error - return True # Marcado como procesado (con error) + instruction['type'] = instr_type + "_error" + return True - # Generar la asignación SCL scl_final = f"{operand_scl} := {in_rlo_scl};" instruction['scl'] = scl_final instruction['type'] = instr_type + SCL_SUFFIX - # Coil consume el RLO, no añade nada al scl_map para la salida 'out' (que no tiene) - print(f"INFO: COIL UID: {instr_uid} procesado. SCL: {scl_final}") + # 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): return False + 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}") + # 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 --- - en_scl = get_scl_representation(instruction['inputs'].get('en'), network_id, scl_map, access_map) 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})") + # print(f"DEBUG: Dependencia no resuelta para CONVERT UID: {instr_uid} (en={en_scl}, in={in_scl})") return False - # Determinar destino (variable o temporal) - Usa pin 'out' target_scl = get_target_scl_name(instruction, 'out', network_id, default_to_temp=True) - if target_scl is None: # Should not happen with default_to_temp=True + if target_scl is None: print(f"Error Interno: No se pudo determinar destino para CONVERT UID {instr_uid}") - return False + instruction['scl'] = f"// ERROR: No se pudo determinar destino para Convert {instr_uid}" + instruction['type'] += "_error" + return True - # Determinar función de conversión (simplificado, requiere tipos exactos) - # TODO: Necesitaría info de TemplateValue para tipos Src/Dest - # Por ahora, asumimos conversión implícita o directa si no podemos determinar - conversion_expr = in_scl # Asume asignación directa por defecto - # Ejemplo (si tuviéramos tipos): - # src_type = instruction['template_values'].get('SrcType') - # dest_type = instruction['template_values'].get('DestType') - # if src_type == 'Int' and dest_type == 'DInt': conversion_expr = f"INT_TO_DINT({in_scl})" - # elif src_type == 'DInt' and dest_type == 'Real': conversion_expr = f"DINT_TO_REAL({in_scl})" - # else: conversion_expr = in_scl # Si no hay conversión específica conocida + 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 - # Generar SCL 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 - # Actualizar scl_map con el resultado (nombre de la variable destino o temporal) - map_key = (network_id, instr_uid, 'out') - scl_map[map_key] = target_scl - # print(f"DEBUG: CONVERT UID: {instr_uid} - Resultado en scl_map[{map_key}] = {target_scl}") + map_key_out = (network_id, instr_uid, 'out') + scl_map[map_key_out] = target_scl - print(f"INFO: CONVERT UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") + 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): return False + 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}") + # 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 --- - en_scl = get_scl_representation(instruction['inputs'].get('en'), network_id, scl_map, access_map) 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})") + # print(f"DEBUG: Dependencia no resuelta para MOD UID: {instr_uid} (en={en_scl}, in1={in1_scl}, in2={in2_scl})") return False - # Determinar destino (variable o temporal) - Usa pin 'out' 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}") - return False + instruction['scl'] = f"// ERROR: No se pudo determinar destino para Mod {instr_uid}" + instruction['type'] += "_error" + return True - # Generar SCL - # Asegurar paréntesis por si las entradas son expresiones complejas - scl_core = f"{target_scl} := ({in1_scl}) MOD ({in2_scl});" + 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 - # Actualizar scl_map con el resultado (nombre de la variable destino o temporal) - map_key = (network_id, instr_uid, 'out') - scl_map[map_key] = target_scl - # print(f"DEBUG: MOD UID: {instr_uid} - Resultado en scl_map[{map_key}] = {target_scl}") + map_key_out = (network_id, instr_uid, 'out') + scl_map[map_key_out] = target_scl - print(f"INFO: MOD UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") + 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 - -# --- (process_add, process_move sin cambios significativos respecto a la versión anterior) --- def process_add(instruction, network_id, scl_map, access_map): instr_uid = instruction['instruction_uid'] instr_type = instruction['type'] - # No procesar si ya tiene sufijo _scl o _error 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}") + # 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 --- - en_scl = get_scl_representation(instruction['inputs'].get('en'), network_id, scl_map, access_map) 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) - # TODO: Manejar más entradas (in3, in4...) 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 # <<<--- Sale correctamente si hay dependencias - - # Si llegamos aquí, TODAS las dependencias están resueltas - print(f"DEBUG: Dependencias RESUELTAS para ADD UID: {instr_uid}") + # 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 # Se procesó (con error) + return True - # Generar SCL Core - scl_core = f"{target_scl} := ({in1_scl}) + ({in2_scl});" # Paréntesis por seguridad - - # Añadir IF si es necesario + 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;" - # <<<--- ¡ACTUALIZAR EL JSON AQUÍ! --- >>> instruction['scl'] = scl_final - instruction['type'] = instr_type + SCL_SUFFIX # Marcar como procesado + instruction['type'] = instr_type + SCL_SUFFIX - # Actualizar el mapa SCL con el resultado de esta instrucción - map_key = (network_id, instr_uid, 'out') - scl_map[map_key] = target_scl # El valor es el nombre de la variable (temporal o destino) + map_key_out = (network_id, instr_uid, 'out') + scl_map[map_key_out] = target_scl - print(f"INFO: ADD UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") - return True # <<<--- ¡DEVOLVER TRUE PORQUE HUBO CAMBIO! --- >>> + 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'] - # No procesar si ya tiene sufijo _scl o _error 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}") + # 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 --- - en_scl = get_scl_representation(instruction['inputs'].get('en'), network_id, scl_map, access_map) 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 # <<<--- Sale correctamente si hay dependencias + # print(f"DEBUG: Dependencia no resuelta para MOVE UID: {instr_uid} (en={en_scl}, in={in_scl})") + return False - # Si llegamos aquí, TODAS las dependencias están resueltas - print(f"DEBUG: Dependencias RESUELTAS para MOVE UID: {instr_uid}") - - # MOVE asigna, así que el destino NO debe ser temporal por defecto. - target_scl = get_target_scl_name(instruction, 'out1', network_id, default_to_temp=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.") - # No marcamos como error necesariamente, simplemente no se pudo procesar. - return False # <<<--- Devuelve False si no se puede procesar + return False - # Generar SCL Core scl_core = f"{target_scl} := {in_scl};" - - # Añadir IF si es necesario scl_final = scl_core if en_scl == "TRUE" else f"IF {en_scl} THEN\n {scl_core}\nEND_IF;" - # <<<--- ¡ACTUALIZAR EL JSON AQUÍ! --- >>> instruction['scl'] = scl_final - instruction['type'] = instr_type + SCL_SUFFIX # Marcar como procesado + 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 - # No añadir a scl_map ya que MOVE asigna directamente. - print(f"INFO: MOVE UID: {instr_uid} procesado. SCL: {scl_final.splitlines()[0]}...") - return True # <<<--- ¡DEVOLVER TRUE PORQUE HUBO CAMBIO! --- >>> # --- Bucle Principal de Procesamiento --- @@ -401,45 +501,48 @@ def process_json_to_scl(json_filepath): print(f"Error al cargar o parsear JSON: {e}") return - # Reconstruir access_map dinámicamente (MEJOR SI VIENE DEL JSON) + # Reconstruir access_map dinámicamente network_access_maps = {} - print("Creando mapas de acceso por red...") + # 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', []): - # Chequear inputs 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 - # Chequear outputs 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 - # print(f"Red {net_id}: {len(current_access_map)} accesos encontrados (aprox).") scl_map = {} max_passes = 20 passes = 0 - # Lista de procesadores con nueva prioridad + # Lista de procesadores actualizada processors = [ - process_convert, # Genera valores, posiblemente temporales - process_mod, # Genera valores, posiblemente temporales - process_eq, # Genera condiciones booleanas - # Añadir otros comparadores (Ne, Gt, Lt...) aquí - process_contact, # Combina condiciones booleanas (actualiza RLO) - process_move, # Asigna valores (usa RLO de Contact/Eq) - process_add, # Calcula (usa RLO de Contact/Eq) - process_coil, # Asigna RLO final - # Añadir process_pbox, process_O, etc. + # 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 @@ -451,25 +554,25 @@ def process_json_to_scl(json_filepath): access_map = network_access_maps.get(network_id, {}) for instruction in network.get('logic', []): - # Saltar si ya está procesado - if instruction['type'].endswith(SCL_SUFFIX) or "_error" in instruction['type']: + instr_type_original = instruction['type'] + if instr_type_original.endswith(SCL_SUFFIX) or "_error" in instr_type_original: continue - # Intentar aplicar cada procesador - for processor_func in processors: - if instruction['type'] == processor_func.__name__.split('_')[1].capitalize(): # Compara tipo con nombre de función - try: - changed = processor_func(instruction, network_id, scl_map, access_map) - if changed: - made_change_in_pass = True - break # Pasar a la siguiente instrucción - 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" # Marcar como error - made_change_in_pass = True # Hubo un cambio (a estado de error) - break # No intentar otros procesadores si hubo error + 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. ---") @@ -488,5 +591,6 @@ def process_json_to_scl(json_filepath): # --- 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) \ No newline at end of file