ParamManagerScripts/backend/script_groups/TwinCat/x3_code_snippets_generator.py

315 lines
13 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script para generar snippets de código de uso de variables IO
entre TwinCAT y TIA Portal - Proyecto SIDEL
Autor: Generado automáticamente
Proyecto: E5.007560 - Modifica O&U - SAE235
"""
import json
import os
import sys
import re
from pathlib import Path
from typing import Dict, List, Tuple, Optional
import pandas as pd
# Configurar el path al directorio raíz del proyecto
script_root = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
sys.path.append(script_root)
# Importar la función de configuración
from backend.script_utils import load_configuration
def load_adaptation_data(working_directory, json_file='io_adaptation_data.json'):
"""Carga los datos de adaptación desde el archivo JSON"""
full_json_file = os.path.join(working_directory, 'resultados', json_file)
print(f"📖 Cargando datos de adaptación desde: {full_json_file}")
if not os.path.exists(full_json_file):
print(f"⚠️ Archivo {full_json_file} no encontrado")
return None
with open(full_json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
print(f"✅ Cargados datos de {data['metadata']['total_adaptations']} adaptaciones")
return data
def find_variable_usage_in_file(file_path, variable_name, max_occurrences=3):
"""Encuentra el uso de una variable en un archivo específico y retorna el contexto"""
if not os.path.exists(file_path):
return []
usages = []
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
# Buscar todas las líneas que contienen la variable
found_lines = []
for line_num, line in enumerate(lines):
# Buscar la variable como palabra completa (no como parte de otra palabra)
if re.search(rf'\b{re.escape(variable_name)}\b', line):
found_lines.append((line_num, line.strip()))
if len(found_lines) >= max_occurrences:
break
# Para cada ocurrencia, obtener contexto (línea anterior, actual, siguiente)
for line_num, line_content in found_lines:
context = {
'line_number': line_num + 1, # Convertir a 1-indexado
'before': lines[line_num - 1].strip() if line_num > 0 else "",
'current': line_content,
'after': lines[line_num + 1].strip() if line_num < len(lines) - 1 else ""
}
usages.append(context)
except Exception as e:
print(f"⚠️ Error leyendo archivo {file_path}: {e}")
return usages
def find_tia_portal_usage(adaptation, working_directory):
"""Busca el uso de variables TIA Portal en archivos markdown"""
tia_address = adaptation['tia_portal']['address']
tia_tag = adaptation['tia_portal']['tag']
# Buscar en archivos TIA Portal (principalmente en archivos .md)
tia_usages = []
# Buscar en TiaPortal/ directory
tia_portal_dir = Path(working_directory) / 'TiaPortal'
if tia_portal_dir.exists():
for md_file in tia_portal_dir.glob('*.md'):
# Buscar por dirección TIA
address_usages = find_variable_usage_in_file(md_file, tia_address, 2)
for usage in address_usages:
usage['file'] = f"TiaPortal/{md_file.name}"
usage['search_term'] = tia_address
tia_usages.append(usage)
# Buscar por tag TIA si es diferente
if tia_tag != tia_address:
tag_usages = find_variable_usage_in_file(md_file, tia_tag, 1)
for usage in tag_usages:
usage['file'] = f"TiaPortal/{md_file.name}"
usage['search_term'] = tia_tag
tia_usages.append(usage)
# Limitar total de usos TIA
if len(tia_usages) >= 3:
break
return tia_usages[:3] # Máximo 3 usos TIA
def find_twincat_usage(adaptation, working_directory):
"""Busca el uso de variables TwinCAT en archivos .scl"""
if not adaptation['correlation']['found']:
return []
variable_name = adaptation['twincat']['variable']
usage_files = adaptation['usage']['usage_files']
twincat_usages = []
# Buscar en archivos TwinCAT
twincat_dir = Path(working_directory) / 'TwinCat'
if twincat_dir.exists():
for file_name in usage_files:
file_path = twincat_dir / file_name
if file_path.exists():
usages = find_variable_usage_in_file(file_path, variable_name, 2)
for usage in usages:
usage['file'] = f"TwinCat/{file_name}"
usage['search_term'] = variable_name
twincat_usages.append(usage)
# Limitar por archivo
if len(twincat_usages) >= 3:
break
return twincat_usages[:3] # Máximo 3 usos TwinCAT
def generate_code_snippets_report(data, working_directory, output_file='IO_Code_Snippets_Report.md'):
"""Genera el reporte con snippets de código"""
full_output_file = os.path.join(working_directory, 'resultados', output_file)
print(f"\n📄 Generando reporte de snippets: {full_output_file}")
matched_adaptations = [a for a in data['adaptations'] if a['correlation']['found']]
with open(full_output_file, 'w', encoding='utf-8') as f:
f.write("# Reporte de Snippets de Código - Adaptación IO\n\n")
f.write(f"**Fecha de generación:** {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"**Proyecto:** {data['metadata']['project']}\n\n")
f.write("## 📋 Resumen\n\n")
f.write(f"- **Variables analizadas:** {len(matched_adaptations)}\n")
f.write(f"- **Snippets generados:** Se muestran hasta 3 usos por plataforma\n")
f.write(f"- **Formato:** Contexto de 3 líneas (anterior, actual, siguiente)\n\n")
f.write("---\n\n")
# Procesar cada adaptación
for i, adaptation in enumerate(matched_adaptations, 1):
tia_address = adaptation['tia_portal']['address']
tia_tag = adaptation['tia_portal']['tag']
twincat_var = adaptation['twincat']['variable']
twincat_addr = adaptation['twincat']['address']
print(f" 📝 Procesando {i}/{len(matched_adaptations)}: {tia_address}{twincat_var}")
f.write(f"## {i}. {tia_address}{twincat_var}\n\n")
f.write(f"**TIA Portal:** `{tia_tag}` (`{tia_address}`)\n")
f.write(f"**TwinCAT:** `{twincat_var}` (`%{twincat_addr}`)\n")
f.write(f"**Tipo:** `{adaptation['twincat']['data_type']}`\n\n")
# Buscar usos en TIA Portal
f.write("### 🔵 Uso en TIA Portal\n\n")
tia_usages = find_tia_portal_usage(adaptation, working_directory)
if tia_usages:
for j, usage in enumerate(tia_usages):
f.write(f"**Uso {j+1}:** [{usage['file']}]({usage['file']}) - Línea {usage['line_number']}\n\n")
f.write("```scl\n")
if usage['before']:
f.write(f"{usage['before']}\n")
f.write(f">>> {usage['current']} // ← {usage['search_term']}\n")
if usage['after']:
f.write(f"{usage['after']}\n")
f.write("```\n\n")
else:
f.write("*No se encontraron usos específicos en archivos TIA Portal.*\n\n")
# Buscar usos en TwinCAT
f.write("### 🟢 Uso en TwinCAT\n\n")
twincat_usages = find_twincat_usage(adaptation, working_directory)
if twincat_usages:
for j, usage in enumerate(twincat_usages):
f.write(f"**Uso {j+1}:** [{usage['file']}]({usage['file']}) - Línea {usage['line_number']}\n\n")
f.write("```scl\n")
if usage['before']:
f.write(f"{usage['before']}\n")
f.write(f">>> {usage['current']} // ← {usage['search_term']}\n")
if usage['after']:
f.write(f"{usage['after']}\n")
f.write("```\n\n")
else:
f.write("*Variable definida pero no se encontraron usos específicos.*\n\n")
f.write("---\n\n")
print(f"✅ Reporte de snippets generado: {full_output_file}")
def generate_summary_statistics(data, working_directory, output_file='IO_Usage_Statistics.md'):
"""Genera estadísticas de uso de las variables"""
full_output_file = os.path.join(working_directory, 'resultados', output_file)
print(f"\n📊 Generando estadísticas de uso: {full_output_file}")
matched_adaptations = [a for a in data['adaptations'] if a['correlation']['found']]
# Calcular estadísticas
total_usage = sum(a['usage']['usage_count'] for a in matched_adaptations)
variables_with_usage = len([a for a in matched_adaptations if a['usage']['usage_count'] > 0])
# Variables más usadas
most_used = sorted(matched_adaptations, key=lambda x: x['usage']['usage_count'], reverse=True)[:10]
# Archivos más referenciados
file_usage = {}
for adaptation in matched_adaptations:
for file_name in adaptation['usage']['usage_files']:
file_usage[file_name] = file_usage.get(file_name, 0) + 1
top_files = sorted(file_usage.items(), key=lambda x: x[1], reverse=True)[:10]
with open(full_output_file, 'w', encoding='utf-8') as f:
f.write("# Estadísticas de Uso de Variables IO\n\n")
f.write(f"**Fecha de generación:** {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
f.write("## 📊 Resumen General\n\n")
f.write(f"- **Variables correlacionadas:** {len(matched_adaptations)}\n")
f.write(f"- **Variables con uso documentado:** {variables_with_usage}\n")
f.write(f"- **Total de usos encontrados:** {total_usage}\n")
f.write(f"- **Promedio de usos por variable:** {total_usage/len(matched_adaptations):.1f}\n\n")
f.write("## 🔥 Top 10 Variables Más Usadas\n\n")
f.write("| Ranking | TIA Address | TwinCAT Variable | Usos | Archivos |\n")
f.write("|---------|-------------|------------------|------|----------|\n")
for i, adaptation in enumerate(most_used, 1):
files_str = ', '.join(adaptation['usage']['usage_files'][:3])
if len(adaptation['usage']['usage_files']) > 3:
files_str += '...'
f.write(f"| {i} | {adaptation['tia_portal']['address']} | "
f"`{adaptation['twincat']['variable']}` | "
f"{adaptation['usage']['usage_count']} | {files_str} |\n")
f.write("\n## 📁 Top 10 Archivos Más Referenciados\n\n")
f.write("| Ranking | Archivo | Variables Usadas |\n")
f.write("|---------|---------|------------------|\n")
for i, (file_name, count) in enumerate(top_files, 1):
f.write(f"| {i} | `{file_name}` | {count} |\n")
print(f"✅ Estadísticas de uso generadas: {full_output_file}")
def main():
print("🚀 Iniciando generación de snippets de código para adaptación IO")
print("=" * 70)
# Cargar configuración
configs = load_configuration()
# Verificar que se cargó correctamente
if not configs:
print("Advertencia: No se pudo cargar la configuración, usando valores por defecto")
working_directory = "./"
else:
working_directory = configs.get("working_directory", "./")
# Verificar directorio de trabajo
if not os.path.exists(working_directory):
print(f"Error: El directorio de trabajo no existe: {working_directory}")
return
print(f"📁 Directorio de trabajo: {working_directory}")
# Crear directorio de resultados si no existe
results_dir = Path(working_directory) / 'resultados'
results_dir.mkdir(exist_ok=True)
# Cargar datos de adaptación
data = load_adaptation_data(working_directory)
if not data:
print("❌ No se pudieron cargar los datos de adaptación")
return
# Generar reporte de snippets
generate_code_snippets_report(data, working_directory)
# Generar estadísticas de uso
generate_summary_statistics(data, working_directory)
print(f"\n🎉 Generación completada exitosamente!")
print(f"📁 Archivos generados en: {results_dir.absolute()}")
print(f" 📄 {results_dir / 'IO_Code_Snippets_Report.md'}")
print(f" 📄 {results_dir / 'IO_Usage_Statistics.md'}")
if __name__ == "__main__":
main()