# --- 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 ---")