ParamManagerScripts/backend/script_groups/S7_DB_Utils/x5.py

206 lines
9.0 KiB
Python

# --- x5_refactored.py ---
import json
from typing import List, Dict, Any, Optional
import sys
import os
import glob
from datetime import datetime
script_root = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
sys.path.append(script_root)
from backend.script_utils import load_configuration
# Importar funciones comunes desde x3
from x3 import flatten_db_structure, format_address_for_display
from x4 import format_data_type_for_source
def find_working_directory():
configs = load_configuration()
working_directory = configs.get("working_directory")
if not working_directory:
print("No working directory specified in the configuration file.")
sys.exit(1)
return working_directory
def generate_members_table_md(
db_info: Dict[str, Any],
is_udt_definition: bool = False,
include_current_value: bool = True
) -> List[str]:
"""
Genera tabla markdown para todos los miembros usando las funciones de aplanamiento de x3.
"""
lines = []
# Definir columnas de la tabla
if include_current_value:
header = "| Nombre Miembro (Ruta) | Tipo de Dato | Offset (Byte.Bit) | Tamaño (Bytes) | Tamaño (Bits) | Valor Inicial (Decl.) | Valor Actual (Efectivo) | Comentario |"
separator = "|---|---|---|---|---|---|---|---|"
else:
header = "| Nombre Miembro | Tipo de Dato | Offset (Byte.Bit) | Tamaño (Bytes) | Tamaño (Bits) | Valor Inicial | Comentario |"
separator = "|---|---|---|---|---|---|---|"
lines.append(header)
lines.append(separator)
# Usar la función de aplanamiento importada
flat_vars = flatten_db_structure(db_info)
# Generar filas para cada variable
for var in flat_vars:
# Usar la dirección formateada desde flatten_db_structure
address = var.get("address_display", format_address_for_display(var["byte_offset"], var.get("bit_size", 0)))
name_display = f"`{var['full_path']}`"
data_type_display = f"`{format_data_type_for_source(var)}`"
size_bytes_display = str(var.get('size_in_bytes', '0'))
bit_size_display = str(var.get('bit_size', '0')) if var.get('bit_size', 0) > 0 else ""
initial_value = str(var.get('initial_value', '')).replace("|", "\\|").replace("\n", " ")
initial_value_display = f"`{initial_value}`" if initial_value else ""
comment_display = str(var.get('comment', '')).replace("|", "\\|").replace("\n", " ")
if include_current_value:
current_value = str(var.get('current_value', '')).replace("|", "\\|").replace("\n", " ")
current_value_display = f"`{current_value}`" if current_value else ""
row = f"| {name_display} | {data_type_display} | {address} | {size_bytes_display} | {bit_size_display} | {initial_value_display} | {current_value_display} | {comment_display} |"
else:
row = f"| {name_display} | {data_type_display} | {address} | {size_bytes_display} | {bit_size_display} | {initial_value_display} | {comment_display} |"
lines.append(row)
return lines
def generate_begin_block_documentation(db_info: Dict[str, Any]) -> List[str]:
"""
Genera documentación para el bloque BEGIN utilizando flatten_db_structure.
"""
lines = []
lines.append("#### Contenido del Bloque `BEGIN` (Valores Actuales Asignados):")
lines.append("El bloque `BEGIN` define los valores actuales de las variables en el DB. Las siguientes asignaciones son ordenadas por offset:")
lines.append("")
# Usar la función de aplanamiento importada de x3
flat_vars = flatten_db_structure(db_info)
# Filtrar solo variables con valores actuales
vars_with_values = [var for var in flat_vars if var.get("current_value") is not None]
if vars_with_values:
lines.append("```scl")
for var in vars_with_values:
value_str = str(var["current_value"])
if value_str.lower() == "true": value_str = "TRUE"
elif value_str.lower() == "false": value_str = "FALSE"
lines.append(f" {var['full_path']} := {value_str};")
lines.append("```")
lines.append("")
else:
lines.append("No se encontraron asignaciones de valores actuales.")
lines.append("")
return lines
def generate_json_documentation(data: Dict[str, Any], output_filename: str):
"""Genera la documentación Markdown completa para el archivo JSON parseado."""
lines = []
lines.append(f"# Documentación del Archivo de Datos S7 Parseado")
current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines.append(f"_Generado el: {current_date}_")
lines.append("")
lines.append("Este documento describe la estructura y el contenido del archivo JSON generado por el parser de fuentes S7 (`x3.py`).")
lines.append("")
# --- Sección UDTs ---
lines.append("## 1. Tipos de Datos de Usuario (UDTs)")
lines.append("")
if data.get("udts"):
for udt in data["udts"]:
lines.append(f'### 1.{data["udts"].index(udt) + 1}. UDT: `{udt["name"]}`')
if udt.get("family"): lines.append(f"- **Familia**: {udt['family']}")
if udt.get("version"): lines.append(f"- **Versión**: {udt['version']}")
lines.append(f"- **Tamaño Total**: {udt['total_size_in_bytes']} bytes")
lines.append("")
lines.append("#### Miembros del UDT:")
# Usar la función optimizada para generar tabla
udt_member_lines = generate_members_table_md(udt, is_udt_definition=True, include_current_value=False)
lines.extend(udt_member_lines)
lines.append("")
else:
lines.append("No se encontraron UDTs en el archivo JSON.")
lines.append("")
# --- Sección DBs ---
lines.append("## 2. Bloques de Datos (DBs)")
lines.append("")
if data.get("dbs"):
for db in data["dbs"]:
lines.append(f'### 2.{data["dbs"].index(db) + 1}. DB: `{db["name"]}`')
if db.get("title"): lines.append(f"- **TITLE**: `{db['title']}`")
if db.get("family"): lines.append(f"- **Familia**: {db['family']}")
if db.get("version"): lines.append(f"- **Versión**: {db['version']}")
lines.append(f"- **Tamaño Declaraciones**: {db['total_size_in_bytes']} bytes")
lines.append("")
lines.append("#### Miembros del DB (Sección de Declaración):")
db_member_lines = generate_members_table_md(db, include_current_value=True)
lines.extend(db_member_lines)
lines.append("")
# Generar sección BEGIN usando la función optimizada
begin_lines = generate_begin_block_documentation(db)
lines.extend(begin_lines)
else:
lines.append("No se encontraron DBs en el archivo JSON.")
# Guardar el archivo Markdown
try:
with open(output_filename, 'w', encoding='utf-8') as f:
for line in lines:
f.write(line + "\n")
print(f"Documentación Markdown completa generada: {output_filename}")
except Exception as e:
print(f"Error al escribir el archivo Markdown de documentación {output_filename}: {e}")
if __name__ == "__main__":
working_dir = find_working_directory()
print(f"Using working directory: {working_dir}")
input_json_dir = os.path.join(working_dir, "json")
documentation_dir = os.path.join(working_dir, "documentation")
os.makedirs(documentation_dir, exist_ok=True)
print(f"Los archivos Markdown de descripción se guardarán en: {documentation_dir}")
json_files_to_process = glob.glob(os.path.join(input_json_dir, "*.json"))
if not json_files_to_process:
print(f"No se encontraron archivos .json en {input_json_dir}")
else:
print(f"Archivos JSON encontrados para procesar: {len(json_files_to_process)}")
for json_input_filepath in json_files_to_process:
json_filename_base = os.path.splitext(os.path.basename(json_input_filepath))[0]
current_json_filename = os.path.basename(json_input_filepath)
print(f"\n--- Procesando archivo JSON para descripción: {current_json_filename} ---")
markdown_output_filename = os.path.join(documentation_dir, f"{json_filename_base}_description.md")
try:
with open(json_input_filepath, 'r', encoding='utf-8') as f:
data_from_json = json.load(f)
print(f"Archivo JSON '{current_json_filename}' cargado correctamente.")
except Exception as e:
print(f"Error al leer el archivo JSON {json_input_filepath}: {e}")
continue
try:
generate_json_documentation(data_from_json, markdown_output_filename)
except Exception as e:
print(f"Error al generar la documentación para {current_json_filename}: {e}")
print("\n--- Proceso de generación de descripciones Markdown completado ---")