219 lines
8.6 KiB
Python
219 lines
8.6 KiB
Python
# -*- 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)
|