# -*- coding: utf-8 -*- import json import os import re # --- Helper Functions --- # Use the CORRECT version from x2_process.py def format_variable_name(name): """Formats variable names for SCL, preserving quotes for structured names.""" if not name: return "_INVALID_NAME_" if name.startswith('"') and name.endswith('"'): return name prefix = "" if name.startswith("#"): prefix = "#" name = name[1:] if not name: return "_INVALID_NAME_" if not re.match(r"^[a-zA-Z_]", name[0]): name = "_" + name name = re.sub(r"[^a-zA-Z0-9_]", "_", name) return prefix + name def generate_scl(processed_json_filepath, output_scl_filepath): """Genera un archivo SCL a partir del JSON procesado.""" if not os.path.exists(processed_json_filepath): print( f"Error: Archivo JSON procesado no encontrado en '{processed_json_filepath}'" ) return print(f"Cargando JSON procesado desde: {processed_json_filepath}") try: with open(processed_json_filepath, "r", encoding="utf-8") as f: data = json.load(f) except Exception as e: print(f"Error al cargar JSON: {e}") return # --- Block Info --- block_name_orig = data.get("block_name", "UnknownBlock") block_number = data.get("block_number") block_lang = data.get("language", "LAD") block_comment = data.get("block_comment", "") # --- Variable Detection --- temp_vars_base = set() # Base names like _temp_... stat_vars = {} # Quoted Name -> TYPE (Bool, TON, TONR) temp_pattern = re.compile(r"#(_temp_[a-zA-Z0-9_]+)") # Capture base name after # # Regex needs to capture the QUOTED name from SCL stat_pattern_bool = re.compile( r'("stat_(?:ptrig|ntrig|sr)_mem_[a-zA-Z0-9_]+")' ) # Edge/SR mem bits stat_pattern_ton = re.compile(r'("stat_TON_[a-zA-Z0-9_]+")') # TON instances stat_pattern_tonr = re.compile(r'("stat_TONR_[a-zA-Z0-9_]+")') # TONR instances for network in data.get("networks", []): for instruction in network.get("logic", []): scl_code = instruction.get("scl", "") if scl_code: temp_vars_base.update(temp_pattern.findall(scl_code)) for name in stat_pattern_bool.findall(scl_code): stat_vars[name] = "Bool" for name in stat_pattern_ton.findall(scl_code): stat_vars[name] = "TON" for name in stat_pattern_tonr.findall(scl_code): stat_vars[name] = "TONR" has_stat = bool(stat_vars) interface_temps_list = data.get("interface", {}).get("Temp", []) has_temp = bool(temp_vars_base or interface_temps_list) block_type_keyword = "FUNCTION_BLOCK" if has_stat or has_temp else "FUNCTION" scl_block_name = format_variable_name(block_name_orig) # Format name correctly print(f"Generando SCL para bloque: {scl_block_name} como {block_type_keyword}") print(f"Variables temporales (#_temp_...) detectadas: {len(temp_vars_base)}") print(f"Variables estáticas (stat_...) detectadas: {len(stat_vars)}") # --- Build SCL Output --- scl_output = [] scl_output.append(f"// Block Name (Original): {block_name_orig}") if block_number: scl_output.append(f"// Block Number: {block_number}") scl_output.append(f"// Original Language: {block_lang}") if block_comment: scl_output.append(f"// Block Comment: {block_comment}") scl_output.append("") scl_output.append(f"{block_type_keyword} {scl_block_name}") scl_output.append("{ S7_Optimized_Access := 'TRUE' }") scl_output.append("VERSION : 0.1") scl_output.append("") # --- VAR Sections --- def add_var_section(section_name, members_list): if not members_list: return scl_output.append(f"VAR_{section_name}") for member in members_list: scl_name = format_variable_name( member["name"] ) # Format interface var names scl_output.append(f" {scl_name} : {member['datatype']};") scl_output.append("END_VAR") scl_output.append("") interface_data = data.get("interface", {}) add_var_section("INPUT", interface_data.get("Input", [])) add_var_section("OUTPUT", interface_data.get("Output", [])) add_var_section("IN_OUT", interface_data.get("InOut", [])) if stat_vars: scl_output.append("VAR_STAT") for var_name_quoted in sorted(stat_vars.keys()): var_type = stat_vars[var_name_quoted] comment = ( f"// Instance for {var_type}" if var_type in ["TON", "TONR"] else "// Memory Bit" ) scl_output.append(f" {var_name_quoted} : {var_type}; {comment}") scl_output.append("END_VAR") scl_output.append("") declared_temps_formatted = set() temp_declarations = [] if interface_temps_list: for var in interface_temps_list: scl_name = format_variable_name(var["name"]) temp_declarations.append( f" {scl_name} : {var['datatype']}; // From Interface" ) declared_temps_formatted.add(scl_name) if temp_vars_base: for base_name in sorted(list(temp_vars_base)): # Declare using the base name, quoted scl_name_declare = format_variable_name(f'"{base_name}"') if scl_name_declare not in declared_temps_formatted: # Simple inference based on common pin names in the base name inferred_type = "Bool" # Default if "ret_val" in base_name: inferred_type = "Int" elif "_et" in base_name: inferred_type = "Time" temp_declarations.append( f" {scl_name_declare} : {inferred_type}; // Auto-generated temporary" ) declared_temps_formatted.add(scl_name_declare) if temp_declarations: scl_output.append("VAR_TEMP") scl_output.extend(temp_declarations) scl_output.append("END_VAR") scl_output.append("") # --- Block Body --- scl_output.append("BEGIN") scl_output.append("") for i, network in enumerate(data.get("networks", [])): network_title = network.get("title", f'Network {network.get("id")}') network_comment = network.get("comment", "") scl_output.append(f" // Network {i+1}: {network_title}") if network_comment: for line in network_comment.splitlines(): scl_output.append(f" // {line}") scl_output.append("") network_has_code = False for instruction in network.get("logic", []): if instruction.get("grouped", False): continue scl_code = instruction.get("scl") if scl_code: lines_to_add = [] for line in scl_code.splitlines(): line_strip = line.strip() is_simple_info_comment = line_strip.startswith( ("// RLO:", "// Comparison Eq", "// Logic O") ) # Keep essential comments (Errors, Grouping, Memory Updates) and actual SCL if ( not is_simple_info_comment or line_strip.startswith("// ERROR") or line_strip.startswith(GROUPED_COMMENT) or "Edge Memory Update" in line_strip ): lines_to_add.append(line) if lines_to_add: network_has_code = True for line in lines_to_add: scl_output.append(f" {line}") if network_has_code: scl_output.append("") end_keyword = ( "END_FUNCTION_BLOCK" if block_type_keyword == "FUNCTION_BLOCK" else "END_FUNCTION" ) scl_output.append(end_keyword) # --- Write File --- print(f"Escribiendo archivo SCL en: {output_scl_filepath}") try: with open(output_scl_filepath, "w", encoding="utf-8") as f: for line in scl_output: f.write(line + "\n") print("Generación de SCL completada.") except Exception as e: print(f"Error al escribir el archivo SCL: {e}") # --- Ejecución --- if __name__ == "__main__": xml_filename_base = "BlenderCtrl__Main" input_json_file = f"{xml_filename_base}_simplified_processed.json" output_scl_file = input_json_file.replace("_simplified_processed.json", ".scl") generate_scl(input_json_file, output_scl_file)