344 lines
24 KiB
Python
344 lines
24 KiB
Python
# x3_generate_scl.py
|
|
# -*- coding: utf-8 -*-
|
|
import json
|
|
import os
|
|
import re
|
|
import argparse
|
|
import sys
|
|
import traceback
|
|
|
|
# --- Importar Utilidades y Constantes ---
|
|
try:
|
|
from processors.processor_utils import format_variable_name
|
|
SCL_SUFFIX = "_sympy_processed"
|
|
GROUPED_COMMENT = "// Logic included in grouped IF"
|
|
except ImportError:
|
|
print("Advertencia: No se pudo importar 'format_variable_name'. Usando fallback.")
|
|
def format_variable_name(name): # Fallback
|
|
if not name: return "_INVALID_NAME_"
|
|
if name.startswith('"') and name.endswith('"'): return name
|
|
prefix = "#" if name.startswith("#") else ""
|
|
if prefix: name = name[1:]
|
|
if name and name[0].isdigit(): name = "_" + name
|
|
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
|
return prefix + name
|
|
SCL_SUFFIX = "_sympy_processed"
|
|
GROUPED_COMMENT = "// Logic included in grouped IF"
|
|
|
|
# --- Función format_scl_start_value (SIN CAMBIOS) ---
|
|
def format_scl_start_value(value, datatype):
|
|
if value is None: return None
|
|
datatype_lower = datatype.lower() if datatype else ""
|
|
value_str = str(value); value_str_unquoted = value_str
|
|
if value_str.startswith('"') and value_str.endswith('"') and len(value_str) > 1: value_str_unquoted = value_str[1:-1]
|
|
elif value_str.startswith("'") and value_str.endswith("'") and len(value_str) > 1: value_str_unquoted = value_str[1:-1]
|
|
if any(t in datatype_lower for t in ["int","byte","word","dint","dword","lint","lword","sint","usint","uint","udint","ulint"]):
|
|
try: return str(int(value_str_unquoted))
|
|
except ValueError:
|
|
if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", value_str_unquoted): return value_str_unquoted
|
|
escaped_for_scl = value_str_unquoted.replace("\\", "\\\\").replace("'", "''").replace("\n", "").replace("\r", ""); return f"'{escaped_for_scl}'"
|
|
elif "bool" in datatype_lower: return "TRUE" if value_str_unquoted.lower() == "true" else "FALSE"
|
|
elif "string" in datatype_lower: escaped_value = value_str_unquoted.replace("'", "''"); return f"'{escaped_value}'"
|
|
elif "char" in datatype_lower: escaped_value = value_str_unquoted.replace("'", "''"); return f"'{escaped_value}'"
|
|
elif "real" in datatype_lower or "lreal" in datatype_lower:
|
|
try:
|
|
f_val = float(value_str_unquoted); s_val = str(f_val)
|
|
if "." not in s_val and "e" not in s_val.lower(): s_val += ".0"
|
|
return s_val
|
|
except ValueError:
|
|
if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", value_str_unquoted): return value_str_unquoted
|
|
escaped_for_scl = value_str_unquoted.replace("\\", "\\\\").replace("'", "''").replace("\n", "").replace("\r", ""); return f"'{escaped_for_scl}'"
|
|
elif "time" in datatype_lower:
|
|
prefix, val_to_use = "", value_str_unquoted
|
|
if val_to_use.upper().startswith("T#"): prefix, val_to_use = "T#", val_to_use[2:]
|
|
elif val_to_use.upper().startswith("LT#"): prefix, val_to_use = "LT#", val_to_use[3:]
|
|
elif val_to_use.upper().startswith("S5T#"): prefix, val_to_use = "S5T#", val_to_use[4:]
|
|
if "s5time" in datatype_lower: return f"S5T#{val_to_use}"
|
|
elif "ltime" in datatype_lower: return f"LT#{val_to_use}"
|
|
else: return f"T#{val_to_use}"
|
|
elif "date" in datatype_lower:
|
|
val_to_use = value_str_unquoted
|
|
if "dtl" in datatype_lower or "date_and_time" in datatype_lower:
|
|
prefix = "DTL#" if val_to_use.upper().startswith("DTL#") else "DTL#"
|
|
val_to_use = val_to_use[4:] if val_to_use.upper().startswith("DTL#") else val_to_use; return f"{prefix}{val_to_use}"
|
|
elif "dt" in datatype_lower:
|
|
prefix = "DT#" if val_to_use.upper().startswith("DT#") else "DT#"
|
|
val_to_use = val_to_use[3:] if val_to_use.upper().startswith("DT#") else val_to_use; return f"{prefix}{val_to_use}"
|
|
elif "tod" in datatype_lower or "time_of_day" in datatype_lower:
|
|
prefix = "TOD#" if val_to_use.upper().startswith("TOD#") else "TOD#"
|
|
val_to_use = val_to_use[4:] if val_to_use.upper().startswith("TOD#") else val_to_use; return f"{prefix}{val_to_use}"
|
|
else: # Default a Date D#
|
|
prefix = "D#" if val_to_use.upper().startswith("D#") else "D#"
|
|
val_to_use = val_to_use[2:] if val_to_use.upper().startswith("D#") else val_to_use; return f"{prefix}{val_to_use}"
|
|
else: # Fallback
|
|
if re.match(r'^[a-zA-Z_#"][a-zA-Z0-9_."#\[\]%]+$', value_str):
|
|
if value_str.startswith('"') and value_str.endswith('"') and len(value_str) > 1: return value_str[1:-1]
|
|
if '"' in value_str and "." in value_str and value_str.count('"') == 2: return value_str
|
|
if not value_str.startswith('"') and not value_str.startswith("'"):
|
|
if value_str.startswith("#") or value_str.startswith("%"): return value_str
|
|
else: return value_str
|
|
return value_str
|
|
else:
|
|
escaped_for_scl = value_str_unquoted.replace("\\", "\\\\").replace("'", "''").replace("\n", "").replace("\r", ""); return f"'{escaped_for_scl}'"
|
|
|
|
# --- Función generate_scl_declarations (SIN CAMBIOS) ---
|
|
def generate_scl_declarations(variables, indent_level=1):
|
|
scl_lines = []
|
|
indent = " " * indent_level
|
|
for var in variables:
|
|
var_name_scl = format_variable_name(var.get("name"))
|
|
var_dtype_raw = var.get("datatype", "VARIANT")
|
|
var_comment = var.get("comment")
|
|
start_value = var.get("start_value"); children = var.get("children"); array_elements = var.get("array_elements")
|
|
var_dtype_cleaned = var_dtype_raw
|
|
if isinstance(var_dtype_raw, str):
|
|
if var_dtype_raw.startswith('"') and var_dtype_raw.endswith('"'): var_dtype_cleaned = var_dtype_raw[1:-1]
|
|
array_match = re.match(r'(Array\[.*\]\s+of\s+)"(.*)"', var_dtype_raw, re.IGNORECASE)
|
|
if array_match: var_dtype_cleaned = f"{array_match.group(1)}{array_match.group(2)}"
|
|
base_type_for_init = var_dtype_cleaned; array_prefix_for_decl = ""
|
|
if var_dtype_cleaned.lower().startswith("array["):
|
|
match = re.match(r"(Array\[.*\]\s+of\s+)(.*)", var_dtype_cleaned, re.IGNORECASE)
|
|
if match: array_prefix_for_decl, base_type_for_init = match.group(1), match.group(2).strip()
|
|
declaration_dtype = var_dtype_raw
|
|
if base_type_for_init != var_dtype_cleaned and not array_prefix_for_decl:
|
|
if not base_type_for_init.startswith('"'): declaration_dtype = f'"{base_type_for_init}"'
|
|
else: declaration_dtype = base_type_for_init
|
|
elif array_prefix_for_decl and base_type_for_init != var_dtype_cleaned:
|
|
if not base_type_for_init.startswith('"'): declaration_dtype = f'{array_prefix_for_decl}"{base_type_for_init}"'
|
|
else: declaration_dtype = f"{array_prefix_for_decl}{base_type_for_init}"
|
|
declaration_line = f"{indent}{var_name_scl} : {declaration_dtype}"; init_value_scl = None
|
|
if array_elements:
|
|
try:
|
|
indices_numeric = {int(k): v for k, v in array_elements.items()}
|
|
sorted_indices_str = [str(k) for k in sorted(indices_numeric.keys())]
|
|
except ValueError: print(f"Advertencia: Índices de array no numéricos para '{var_name_scl}'. Usando orden alfabético."); sorted_indices_str = sorted(array_elements.keys())
|
|
init_values = []
|
|
for idx_str in sorted_indices_str:
|
|
try: formatted_val = format_scl_start_value(array_elements[idx_str], base_type_for_init); init_values.append(formatted_val)
|
|
except Exception as e_fmt: print(f"ERROR: Falló formateo para índice {idx_str} de array '{var_name_scl}'. Valor: {array_elements[idx_str]}. Error: {e_fmt}"); init_values.append(f"/*ERR_FMT_{idx_str}*/")
|
|
valid_inits = [v for v in init_values if v is not None]
|
|
if valid_inits: init_value_scl = f"[{', '.join(valid_inits)}]"
|
|
elif array_elements: print(f"Advertencia: Todos los valores iniciales para array '{var_name_scl}' son None o inválidos.")
|
|
elif children:
|
|
scl_lines.append(declaration_line); scl_lines.append(f"{indent}STRUCT")
|
|
scl_lines.extend(generate_scl_declarations(children, indent_level + 1))
|
|
scl_lines.append(f"{indent}END_STRUCT;");
|
|
if var_comment: scl_lines.append(f"{indent}// {var_comment}")
|
|
scl_lines.append(""); continue
|
|
else:
|
|
if start_value is not None:
|
|
try: init_value_scl = format_scl_start_value(start_value, base_type_for_init)
|
|
except Exception as e_fmt_simple: print(f"ERROR: Falló formateo para valor simple de '{var_name_scl}'. Valor: {start_value}. Error: {e_fmt_simple}"); init_value_scl = f"/*ERR_FMT_SIMPLE*/"
|
|
if init_value_scl is not None: declaration_line += f" := {init_value_scl}"
|
|
declaration_line += ";"
|
|
if var_comment: declaration_line += f" // {var_comment}"
|
|
scl_lines.append(declaration_line)
|
|
return scl_lines
|
|
|
|
# --- NUEVAS FUNCIONES para generar Markdown ---
|
|
def generate_udt_markdown(data):
|
|
"""Genera contenido Markdown para un UDT."""
|
|
md_lines = []; udt_name = data.get("block_name", "UnknownUDT"); udt_comment = data.get("block_comment", "")
|
|
md_lines.append(f"# UDT: {udt_name}"); md_lines.append("")
|
|
if udt_comment:
|
|
md_lines.append(f"**Comment:**")
|
|
for line in udt_comment.splitlines(): md_lines.append(f"> {line}")
|
|
md_lines.append("")
|
|
members = data.get("interface", {}).get("None", [])
|
|
if members:
|
|
md_lines.append("## Members"); md_lines.append("")
|
|
md_lines.append("| Name | Datatype | Start Value | Comment |"); md_lines.append("|---|---|---|---|")
|
|
md_lines.extend(generate_markdown_member_rows(members)) # Llamada a la función auxiliar
|
|
md_lines.append("")
|
|
else: md_lines.append("No members found in the UDT interface."); md_lines.append("")
|
|
return md_lines
|
|
|
|
# --- generate_markdown_member_rows (MODIFICADA) ---
|
|
def generate_markdown_member_rows(members, level=0):
|
|
"""Función auxiliar recursiva para generar filas Markdown para miembros de UDT."""
|
|
md_rows = []; prefix = " " * level
|
|
for member in members:
|
|
name = member.get("name", "N/A"); datatype = member.get("datatype", "N/A")
|
|
start_value_raw = member.get("start_value")
|
|
start_value_fmt = format_scl_start_value(start_value_raw, datatype) if start_value_raw is not None else ""
|
|
# CORRECCIÓN: Manejar el caso en que comment sea None
|
|
comment_raw = member.get("comment")
|
|
comment = comment_raw.replace('|', '\|').replace('\n', ' ') if comment_raw else "" # Usar "" si es None
|
|
|
|
md_rows.append(f"| {prefix}`{name}` | `{datatype}` | `{start_value_fmt}` | {comment} |")
|
|
children = member.get("children")
|
|
if children: md_rows.extend(generate_markdown_member_rows(children, level + 1))
|
|
array_elements = member.get("array_elements")
|
|
if array_elements:
|
|
base_type_for_init = datatype
|
|
if isinstance(datatype, str) and datatype.lower().startswith("array["):
|
|
match = re.match(r"(Array\[.*\]\s+of\s+)(.*)", datatype, re.IGNORECASE)
|
|
if match: base_type_for_init = match.group(2).strip()
|
|
md_rows.append(f"| {prefix} *(Initial Values)* | | | |")
|
|
try:
|
|
indices_numeric = {int(k): v for k, v in array_elements.items()}
|
|
sorted_indices_str = [str(k) for k in sorted(indices_numeric.keys())]
|
|
except ValueError: sorted_indices_str = sorted(array_elements.keys())
|
|
for idx_str in sorted_indices_str:
|
|
val_raw = array_elements[idx_str]
|
|
val_fmt = format_scl_start_value(val_raw, base_type_for_init) if val_raw is not None else ""
|
|
md_rows.append(f"| {prefix} `[{idx_str}]` | | `{val_fmt}` | |")
|
|
return md_rows
|
|
|
|
def generate_tag_table_markdown(data):
|
|
"""Genera contenido Markdown para una tabla de tags."""
|
|
md_lines = []; table_name = data.get("block_name", "UnknownTagTable"); tags = data.get("tags", [])
|
|
md_lines.append(f"# Tag Table: {table_name}"); md_lines.append("")
|
|
if tags:
|
|
md_lines.append("| Name | Datatype | Address | Comment |"); md_lines.append("|---|---|---|---|")
|
|
for tag in tags:
|
|
name = tag.get("name", "N/A"); datatype = tag.get("datatype", "N/A")
|
|
address = tag.get("address", "N/A") or " ";
|
|
# CORRECCIÓN: Manejar el caso en que comment sea None
|
|
comment_raw = tag.get("comment")
|
|
comment = comment_raw.replace('|', '\|').replace('\n', ' ') if comment_raw else ""
|
|
md_lines.append(f"| `{name}` | `{datatype}` | `{address}` | {comment} |")
|
|
md_lines.append("")
|
|
else: md_lines.append("No tags found in this table."); md_lines.append("")
|
|
return md_lines
|
|
|
|
# --- Función Principal de Generación (SIN CAMBIOS EN LA LÓGICA PRINCIPAL) ---
|
|
def generate_scl_or_markdown(processed_json_filepath, output_directory):
|
|
"""
|
|
Genera un archivo SCL o Markdown a partir del JSON procesado,
|
|
eligiendo el formato y la extensión según el tipo de bloque.
|
|
"""
|
|
if not os.path.exists(processed_json_filepath): print(f"Error: Archivo JSON no encontrado: '{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 o parsear JSON: {e}"); traceback.print_exc(); return
|
|
|
|
block_name = data.get("block_name", "UnknownBlock"); block_number = data.get("block_number")
|
|
block_type = data.get("block_type", "Unknown"); block_comment = data.get("block_comment", "")
|
|
scl_block_name = format_variable_name(block_name); output_content = []; output_extension = ".scl"
|
|
print(f"Generando salida para: {block_type} '{scl_block_name}' (Original: {block_name})")
|
|
|
|
if block_type == "PlcUDT":
|
|
print(" -> Modo de generación: UDT Markdown"); output_content = generate_udt_markdown(data); output_extension = ".md"
|
|
elif block_type == "PlcTagTable":
|
|
print(" -> Modo de generación: Tag Table Markdown"); output_content = generate_tag_table_markdown(data); output_extension = ".md"
|
|
elif block_type == "GlobalDB":
|
|
print(" -> Modo de generación: DATA_BLOCK SCL"); output_extension = ".scl"
|
|
# (Lógica SCL DB...)
|
|
output_content.append(f"// Block Type: {block_type}")
|
|
if block_name != scl_block_name : output_content.append(f"// Block Name (Original): {block_name}")
|
|
if block_number: output_content.append(f"// Block Number: {block_number}")
|
|
if block_comment: output_content.append(f"// Block Comment:"); [output_content.append(f"// {line}") for line in block_comment.splitlines()]
|
|
output_content.append(""); output_content.append(f'DATA_BLOCK "{scl_block_name}"')
|
|
output_content.append("{ S7_Optimized_Access := 'TRUE' }"); output_content.append("VERSION : 0.1"); output_content.append("")
|
|
interface_data = data.get("interface", {}); static_vars = interface_data.get("Static", [])
|
|
if static_vars: output_content.append("VAR"); output_content.extend(generate_scl_declarations(static_vars, indent_level=1)); output_content.append("END_VAR")
|
|
else: print("Advertencia: No se encontró sección 'Static' o está vacía en la interfaz del DB."); output_content.append("VAR\nEND_VAR")
|
|
output_content.append(""); output_content.append("BEGIN"); output_content.append(" // Data Blocks have no executable code"); output_content.append("END_DATA_BLOCK")
|
|
elif block_type in ["FC", "FB", "OB"]:
|
|
print(f" -> Modo de generación: {block_type} SCL"); output_extension = ".scl"
|
|
# (Lógica SCL FC/FB/OB...)
|
|
scl_block_keyword = "FUNCTION_BLOCK";
|
|
if block_type == "FC": scl_block_keyword = "FUNCTION"
|
|
elif block_type == "OB": scl_block_keyword = "ORGANIZATION_BLOCK"
|
|
output_content.append(f"// Block Type: {block_type}")
|
|
if block_name != scl_block_name : output_content.append(f"// Block Name (Original): {block_name}")
|
|
if block_number: output_content.append(f"// Block Number: {block_number}")
|
|
original_net_langs = set(n.get("language", "Unknown") for n in data.get("networks", []))
|
|
output_content.append(f"// Original Network Languages: {', '.join(l for l in original_net_langs if l != 'Unknown')}")
|
|
if block_comment: output_content.append(f"// Block Comment:"); [output_content.append(f"// {line}") for line in block_comment.splitlines()]
|
|
output_content.append("")
|
|
return_type = "Void"; interface_data = data.get("interface", {})
|
|
if scl_block_keyword == "FUNCTION" and interface_data.get("Return"):
|
|
return_member = interface_data["Return"][0]; return_type_raw = return_member.get("datatype", "Void")
|
|
return_type = (return_type_raw[1:-1] if isinstance(return_type_raw, str) and return_type_raw.startswith('"') and return_type_raw.endswith('"') else return_type_raw)
|
|
if return_type != return_type_raw and not return_type_raw.lower().startswith("array"): return_type = f'"{return_type}"'
|
|
else: return_type = return_type_raw
|
|
if scl_block_keyword == "FUNCTION": output_content.append(f'{scl_block_keyword} "{scl_block_name}" : {return_type}')
|
|
else: output_content.append(f'{scl_block_keyword} "{scl_block_name}"')
|
|
output_content.append("{ S7_Optimized_Access := 'TRUE' }"); output_content.append("VERSION : 0.1"); output_content.append("")
|
|
section_order = ["Input", "Output", "InOut", "Static", "Temp", "Constant"]
|
|
declared_temps = set(); has_declarations = False
|
|
for section_name in section_order:
|
|
vars_in_section = interface_data.get(section_name, [])
|
|
if vars_in_section:
|
|
has_declarations = True; scl_section_keyword = f"VAR_{section_name.upper()}"
|
|
if section_name == "Static": scl_section_keyword = "VAR_STAT"
|
|
if section_name == "Temp": scl_section_keyword = "VAR_TEMP"
|
|
if section_name == "Constant": scl_section_keyword = "CONSTANT"
|
|
output_content.append(scl_section_keyword)
|
|
output_content.extend(generate_scl_declarations(vars_in_section, indent_level=1))
|
|
output_content.append("END_VAR" if section_name != "Constant" else "END_CONSTANT"); output_content.append("")
|
|
if section_name == "Temp": declared_temps.update(format_variable_name(v.get("name")) for v in vars_in_section if v.get("name"))
|
|
temp_vars_detected = set(); temp_pattern = re.compile(r'"?(#\w+)"?')
|
|
for network in data.get("networks", []):
|
|
for instruction in network.get("logic", []):
|
|
scl_code = instruction.get("scl", ""); edge_update_code = instruction.get("_edge_mem_update_scl", "")
|
|
code_to_scan = (scl_code if scl_code else "") + "\n" + (edge_update_code if edge_update_code else "")
|
|
if code_to_scan:
|
|
found_temps = temp_pattern.findall(code_to_scan)
|
|
for temp_name in found_temps:
|
|
if temp_name: temp_vars_detected.add(temp_name)
|
|
additional_temps = sorted(list(temp_vars_detected - declared_temps))
|
|
if additional_temps:
|
|
print(f"INFO: Detectadas {len(additional_temps)} VAR_TEMP adicionales.")
|
|
if "Temp" not in interface_data or not interface_data["Temp"]: output_content.append("VAR_TEMP")
|
|
for temp_name in additional_temps: scl_name = format_variable_name(temp_name); inferred_type = "Bool"; output_content.append(f" {scl_name} : {inferred_type}; // Auto-generated temporary")
|
|
if "Temp" not in interface_data or not interface_data["Temp"]: output_content.append("END_VAR"); output_content.append("")
|
|
output_content.append("BEGIN"); output_content.append("")
|
|
for i, network in enumerate(data.get("networks", [])):
|
|
network_title = network.get("title", f'Network {network.get("id", i+1)}')
|
|
network_comment = network.get("comment", ""); network_lang = network.get("language", "LAD")
|
|
output_content.append(f" // Network {i+1}: {network_title} (Original Language: {network_lang})")
|
|
if network_comment: [output_content.append(f" // {line}") for line in network_comment.splitlines()]
|
|
output_content.append(""); network_has_code = False; logic_in_network = network.get("logic", [])
|
|
if not logic_in_network: output_content.append(f" // Network {i+1} has no logic elements."); output_content.append(""); continue
|
|
if network_lang == "STL":
|
|
if logic_in_network[0].get("type") == "RAW_STL_CHUNK":
|
|
network_has_code = True; raw_stl_code = logic_in_network[0].get("stl", "// ERROR: STL code missing")
|
|
output_content.append(f" // --- BEGIN STL Network {i+1} ---"); [output_content.append(f" // {stl_line}") for stl_line in raw_stl_code.splitlines()]; output_content.append(f" // --- END STL Network {i+1} ---"); output_content.append("")
|
|
else: output_content.append(f" // ERROR: Contenido STL inesperado en Network {i+1}."); output_content.append("")
|
|
else: # SCL/LAD/FBD
|
|
for instruction in logic_in_network:
|
|
instruction_type = instruction.get("type", ""); scl_code = instruction.get("scl", ""); is_grouped = instruction.get("grouped", False)
|
|
if is_grouped: continue
|
|
if (instruction_type.endswith(SCL_SUFFIX) or instruction_type in ["RAW_SCL_CHUNK","UNSUPPORTED_LANG","UNSUPPORTED_CONTENT","PARSING_ERROR"] or "_error" in instruction_type) and scl_code:
|
|
is_only_comment = all(line.strip().startswith("//") for line in scl_code.splitlines() if line.strip())
|
|
is_if_block = scl_code.strip().startswith("IF")
|
|
if (not is_only_comment or is_if_block or "_error" in instruction_type or instruction_type in ["UNSUPPORTED_LANG","UNSUPPORTED_CONTENT","PARSING_ERROR"]):
|
|
network_has_code = True; [output_content.append(f" {line}") for line in scl_code.splitlines()]; output_content.append("")
|
|
if not network_has_code and network_lang != "STL": output_content.append(f" // Network {i+1} did not produce printable SCL/MD code."); output_content.append("")
|
|
output_content.append(f"END_{scl_block_keyword}")
|
|
|
|
else: print(f"Error: Tipo de bloque desconocido '{block_type}' en JSON."); return
|
|
|
|
# --- Escritura del Archivo ---
|
|
output_filename_base = f"{scl_block_name}{output_extension}"
|
|
output_filepath = os.path.join(output_directory, output_filename_base)
|
|
print(f" -> Escribiendo archivo de salida en: {output_filepath}")
|
|
try:
|
|
os.makedirs(output_directory, exist_ok=True)
|
|
with open(output_filepath, "w", encoding="utf-8") as f:
|
|
for line in output_content: f.write(line + "\n")
|
|
print(f"Generación de {output_extension.upper()} completada.")
|
|
except Exception as e: print(f"Error al escribir el archivo {output_extension.upper()}: {e}"); traceback.print_exc()
|
|
|
|
# --- Ejecución (SIN CAMBIOS) ---
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Generate final SCL or Markdown file from processed JSON.")
|
|
parser.add_argument("source_xml_filepath",help="Path to the original source XML file.")
|
|
args = parser.parse_args(); source_xml_file = args.source_xml_filepath
|
|
if not os.path.exists(source_xml_file): print(f"Advertencia (x3): Archivo XML original no encontrado: '{source_xml_file}'.")
|
|
xml_filename_base = os.path.splitext(os.path.basename(source_xml_file))[0]
|
|
base_dir = os.path.dirname(source_xml_file)
|
|
input_json_file = os.path.join(base_dir, f"{xml_filename_base}_simplified_processed.json")
|
|
output_dir = base_dir
|
|
print(f"(x3) Generando SCL/MD desde: '{os.path.relpath(input_json_file)}' en directorio: '{os.path.relpath(output_dir)}'")
|
|
if not os.path.exists(input_json_file):
|
|
print(f"Error Fatal (x3): Archivo JSON procesado no encontrado: '{input_json_file}'")
|
|
print(f"Asegúrate de que 'x2_process.py' se ejecutó correctamente para '{os.path.relpath(source_xml_file)}'.")
|
|
sys.exit(1)
|
|
else:
|
|
try: generate_scl_or_markdown(input_json_file, output_dir); sys.exit(0)
|
|
except Exception as e: print(f"Error Crítico (x3) durante la generación de SCL/MD desde '{input_json_file}': {e}"); traceback.print_exc(); sys.exit(1) |