Eliminación del archivo README.md del convertidor LAD a pseudocódigo estructurado, optimizando la estructura del proyecto y reduciendo la confusión en la documentación. Se mantiene la funcionalidad principal del convertidor sin cambios.
This commit is contained in:
parent
b74db36cf9
commit
def0f0b2d7
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/ExportTwinCat"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
|
@ -0,0 +1,463 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script para generar documentación de adaptación de IOs
|
||||
entre TwinCAT y TIA Portal - Proyecto SIDEL
|
||||
|
||||
Autor: Generado automáticamente
|
||||
Proyecto: E5.007560 - Modifica O&U - SAE235
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import pandas as pd
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
|
||||
# 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_tiaportal_adaptations(working_directory, file_path='IO Adapted.md'):
|
||||
"""Carga las adaptaciones de TIA Portal desde el archivo markdown"""
|
||||
full_file_path = os.path.join(working_directory, file_path)
|
||||
print(f"Cargando adaptaciones de TIA Portal desde: {full_file_path}")
|
||||
|
||||
adaptations = {}
|
||||
|
||||
if not os.path.exists(full_file_path):
|
||||
print(f"⚠️ Archivo {full_file_path} no encontrado")
|
||||
return adaptations
|
||||
|
||||
with open(full_file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Patrones mejorados para diferentes tipos de IOs
|
||||
patterns = [
|
||||
# Digitales: E0.0, A0.0
|
||||
r'\|\s*([EA]\d+\.\d+)\s*\|\s*([^|]+?)\s*\|',
|
||||
# Analógicos: PEW100, PAW100
|
||||
r'\|\s*(P[EA]W\d+)\s*\|\s*([^|]+?)\s*\|',
|
||||
# Profibus: EW 1640, AW 1640
|
||||
r'\|\s*([EA]W\s+\d+)\s*\|\s*([^|]+?)\s*\|'
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
matches = re.findall(pattern, content, re.MULTILINE)
|
||||
for io_addr, master_tag in matches:
|
||||
io_addr = io_addr.strip()
|
||||
master_tag = master_tag.strip()
|
||||
if io_addr and master_tag and not master_tag.startswith('-'):
|
||||
adaptations[io_addr] = master_tag
|
||||
print(f" 📍 {io_addr} → {master_tag}")
|
||||
|
||||
print(f"✅ Cargadas {len(adaptations)} adaptaciones de TIA Portal")
|
||||
return adaptations
|
||||
|
||||
def scan_twincat_definitions(working_directory, directory='TwinCat'):
|
||||
"""Escanea archivos TwinCAT para encontrar definiciones de variables AT %"""
|
||||
full_directory = os.path.join(working_directory, directory)
|
||||
print(f"\n🔍 Escaneando definiciones TwinCAT en: {full_directory}")
|
||||
|
||||
definitions = {}
|
||||
|
||||
if not os.path.exists(full_directory):
|
||||
print(f"⚠️ Directorio {full_directory} no encontrado")
|
||||
return definitions
|
||||
|
||||
# Patrones para definiciones AT %
|
||||
definition_patterns = [
|
||||
r'(\w+)\s+AT\s+%([IQ][XWB]\d+(?:\.\d+)?)\s*:\s*(\w+);', # Activas
|
||||
r'(\w+)\s+\(\*\s*AT\s+%([IQ][XWB]\d+(?:\.\d+)?)\s*\*\)\s*:\s*(\w+);', # Comentadas
|
||||
]
|
||||
|
||||
for file_path in Path(full_directory).glob('*.scl'):
|
||||
print(f" 📄 Procesando: {file_path.name}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
for pattern in definition_patterns:
|
||||
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
|
||||
for var_name, io_addr, data_type in matches:
|
||||
var_name = var_name.strip()
|
||||
io_addr = io_addr.strip()
|
||||
data_type = data_type.strip()
|
||||
|
||||
definitions[var_name] = {
|
||||
'address': io_addr,
|
||||
'type': data_type,
|
||||
'file': file_path.name,
|
||||
'definition_line': content[:content.find(var_name)].count('\n') + 1
|
||||
}
|
||||
print(f" 🔗 {var_name} AT %{io_addr} : {data_type}")
|
||||
|
||||
print(f"✅ Encontradas {len(definitions)} definiciones TwinCAT")
|
||||
return definitions
|
||||
|
||||
def scan_twincat_usage(working_directory, directory='TwinCat'):
|
||||
"""Escanea archivos TwinCAT para encontrar uso de variables"""
|
||||
full_directory = os.path.join(working_directory, directory)
|
||||
print(f"\n🔍 Escaneando uso de variables TwinCAT en: {full_directory}")
|
||||
|
||||
usage_data = defaultdict(list)
|
||||
|
||||
if not os.path.exists(full_directory):
|
||||
print(f"⚠️ Directorio {full_directory} no encontrado")
|
||||
return usage_data
|
||||
|
||||
for file_path in Path(full_directory).glob('*.scl'):
|
||||
print(f" 📄 Analizando uso en: {file_path.name}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
# Buscar variables que empiecen con DI_, DO_, AI_, AO_
|
||||
var_matches = re.findall(r'\b([DA][IO]_\w+)\b', line)
|
||||
for var_name in var_matches:
|
||||
usage_data[var_name].append({
|
||||
'file': file_path.name,
|
||||
'line': line_num,
|
||||
'context': line.strip()[:100] + ('...' if len(line.strip()) > 100 else '')
|
||||
})
|
||||
|
||||
print(f"✅ Encontrado uso de {len(usage_data)} variables diferentes")
|
||||
return usage_data
|
||||
|
||||
def convert_tia_to_twincat(tia_addr):
|
||||
"""Convierte direcciones TIA Portal a formato TwinCAT"""
|
||||
conversions = []
|
||||
|
||||
# Digitales
|
||||
if re.match(r'^E\d+\.\d+$', tia_addr): # E0.0 → IX0.0
|
||||
twincat_addr = tia_addr.replace('E', 'IX')
|
||||
conversions.append(twincat_addr)
|
||||
elif re.match(r'^A\d+\.\d+$', tia_addr): # A0.0 → QX0.0
|
||||
twincat_addr = tia_addr.replace('A', 'QX')
|
||||
conversions.append(twincat_addr)
|
||||
|
||||
# Analógicos
|
||||
elif re.match(r'^PEW\d+$', tia_addr): # PEW100 → IW100
|
||||
twincat_addr = tia_addr.replace('PEW', 'IW')
|
||||
conversions.append(twincat_addr)
|
||||
elif re.match(r'^PAW\d+$', tia_addr): # PAW100 → QW100
|
||||
twincat_addr = tia_addr.replace('PAW', 'QW')
|
||||
conversions.append(twincat_addr)
|
||||
|
||||
# Profibus
|
||||
elif re.match(r'^EW\s+\d+$', tia_addr): # EW 1234 → IB1234
|
||||
addr_num = re.search(r'\d+', tia_addr).group()
|
||||
conversions.append(f'IB{addr_num}')
|
||||
elif re.match(r'^AW\s+\d+$', tia_addr): # AW 1234 → QB1234
|
||||
addr_num = re.search(r'\d+', tia_addr).group()
|
||||
conversions.append(f'QB{addr_num}')
|
||||
|
||||
return conversions
|
||||
|
||||
def find_variable_by_address(definitions, target_address):
|
||||
"""Busca variable por dirección exacta"""
|
||||
for var_name, info in definitions.items():
|
||||
if info['address'] == target_address:
|
||||
return var_name, info
|
||||
return None, None
|
||||
|
||||
def find_variable_by_name_similarity(definitions, usage_data, master_tag):
|
||||
"""Busca variables por similitud de nombre"""
|
||||
candidates = []
|
||||
|
||||
# Limpiar el master tag para comparación
|
||||
clean_master = re.sub(r'^[DA][IO]_', '', master_tag).lower()
|
||||
|
||||
# Buscar en definiciones
|
||||
for var_name, info in definitions.items():
|
||||
clean_var = re.sub(r'^[DA][IO]_', '', var_name).lower()
|
||||
if clean_master in clean_var or clean_var in clean_master:
|
||||
candidates.append((var_name, info, 'definition'))
|
||||
|
||||
# Buscar en uso
|
||||
for var_name in usage_data.keys():
|
||||
clean_var = re.sub(r'^[DA][IO]_', '', var_name).lower()
|
||||
if clean_master in clean_var or clean_var in clean_master:
|
||||
# Intentar encontrar la definición de esta variable
|
||||
var_info = definitions.get(var_name)
|
||||
if not var_info:
|
||||
var_info = {'address': 'Unknown', 'type': 'Unknown', 'file': 'Not found'}
|
||||
candidates.append((var_name, var_info, 'usage'))
|
||||
|
||||
return candidates
|
||||
|
||||
def analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage):
|
||||
"""Analiza las correlaciones entre TIA Portal y TwinCAT"""
|
||||
print(f"\n📊 Analizando correlaciones...")
|
||||
|
||||
results = []
|
||||
matches_found = 0
|
||||
|
||||
for tia_addr, master_tag in tia_adaptations.items():
|
||||
result = {
|
||||
'tia_address': tia_addr,
|
||||
'master_tag': master_tag,
|
||||
'twincat_variable': None,
|
||||
'twincat_address': None,
|
||||
'twincat_type': None,
|
||||
'match_type': None,
|
||||
'definition_file': None,
|
||||
'usage_files': [],
|
||||
'usage_count': 0,
|
||||
'confidence': 'Low'
|
||||
}
|
||||
|
||||
# 1. Buscar por conversión directa de dirección
|
||||
twincat_addresses = convert_tia_to_twincat(tia_addr)
|
||||
var_found = False
|
||||
|
||||
for twincat_addr in twincat_addresses:
|
||||
var_name, var_info = find_variable_by_address(twincat_definitions, twincat_addr)
|
||||
if var_name:
|
||||
result.update({
|
||||
'twincat_variable': var_name,
|
||||
'twincat_address': var_info['address'],
|
||||
'twincat_type': var_info['type'],
|
||||
'match_type': 'Address Match',
|
||||
'definition_file': var_info['file'],
|
||||
'confidence': 'High'
|
||||
})
|
||||
var_found = True
|
||||
matches_found += 1
|
||||
break
|
||||
|
||||
# 2. Si no se encontró por dirección, buscar por nombre
|
||||
if not var_found:
|
||||
candidates = find_variable_by_name_similarity(twincat_definitions, twincat_usage, master_tag)
|
||||
if candidates:
|
||||
# Tomar el mejor candidato
|
||||
best_candidate = candidates[0]
|
||||
var_name, var_info, source = best_candidate
|
||||
|
||||
result.update({
|
||||
'twincat_variable': var_name,
|
||||
'twincat_address': var_info.get('address', 'Unknown'),
|
||||
'twincat_type': var_info.get('type', 'Unknown'),
|
||||
'match_type': f'Name Similarity ({source})',
|
||||
'definition_file': var_info.get('file', 'Unknown'),
|
||||
'confidence': 'Medium'
|
||||
})
|
||||
matches_found += 1
|
||||
|
||||
# 3. Buscar información de uso
|
||||
if result['twincat_variable']:
|
||||
var_name = result['twincat_variable']
|
||||
if var_name in twincat_usage:
|
||||
usage_info = twincat_usage[var_name]
|
||||
result['usage_files'] = list(set([u['file'] for u in usage_info]))
|
||||
result['usage_count'] = len(usage_info)
|
||||
|
||||
results.append(result)
|
||||
|
||||
# Log del progreso
|
||||
status = "✅" if result['twincat_variable'] else "❌"
|
||||
print(f" {status} {tia_addr} → {master_tag}")
|
||||
if result['twincat_variable']:
|
||||
print(f" 🔗 {result['twincat_variable']} AT %{result['twincat_address']}")
|
||||
if result['usage_count'] > 0:
|
||||
print(f" 📝 Usado en {result['usage_count']} lugares: {', '.join(result['usage_files'])}")
|
||||
|
||||
print(f"\n🎯 Resumen: {matches_found}/{len(tia_adaptations)} variables correlacionadas ({matches_found/len(tia_adaptations)*100:.1f}%)")
|
||||
|
||||
return results
|
||||
|
||||
def create_results_directory(working_directory):
|
||||
"""Crea el directorio de resultados si no existe"""
|
||||
results_dir = Path(working_directory) / 'resultados'
|
||||
results_dir.mkdir(exist_ok=True)
|
||||
print(f"📁 Directorio de resultados: {results_dir.absolute()}")
|
||||
return results_dir
|
||||
|
||||
def generate_json_output(results, working_directory, output_file='io_adaptation_data.json'):
|
||||
"""Genera archivo JSON con datos estructurados para análisis posterior"""
|
||||
full_output_file = os.path.join(working_directory, 'resultados', output_file)
|
||||
print(f"\n📄 Generando archivo JSON: {full_output_file}")
|
||||
|
||||
json_data = {
|
||||
"metadata": {
|
||||
"generated_at": pd.Timestamp.now().isoformat(),
|
||||
"project": "E5.007560 - Modifica O&U - SAE235",
|
||||
"total_adaptations": len(results),
|
||||
"matched_variables": len([r for r in results if r['twincat_variable']]),
|
||||
"high_confidence": len([r for r in results if r['confidence'] == 'High']),
|
||||
"medium_confidence": len([r for r in results if r['confidence'] == 'Medium'])
|
||||
},
|
||||
"adaptations": []
|
||||
}
|
||||
|
||||
for result in results:
|
||||
adaptation = {
|
||||
"tia_portal": {
|
||||
"address": result['tia_address'],
|
||||
"tag": result['master_tag']
|
||||
},
|
||||
"twincat": {
|
||||
"variable": result['twincat_variable'],
|
||||
"address": result['twincat_address'],
|
||||
"data_type": result['twincat_type'],
|
||||
"definition_file": result['definition_file']
|
||||
},
|
||||
"correlation": {
|
||||
"match_type": result['match_type'],
|
||||
"confidence": result['confidence'],
|
||||
"found": result['twincat_variable'] is not None
|
||||
},
|
||||
"usage": {
|
||||
"usage_count": result['usage_count'],
|
||||
"usage_files": result['usage_files']
|
||||
}
|
||||
}
|
||||
json_data["adaptations"].append(adaptation)
|
||||
|
||||
with open(full_output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(json_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"✅ Archivo JSON generado: {full_output_file}")
|
||||
|
||||
def generate_detailed_report(results, working_directory, output_file='IO_Detailed_Analysis_Report.md'):
|
||||
"""Genera un reporte detallado con tabla markdown"""
|
||||
full_output_file = os.path.join(working_directory, 'resultados', output_file)
|
||||
print(f"\n📄 Generando reporte detallado: {full_output_file}")
|
||||
|
||||
with open(full_output_file, 'w', encoding='utf-8') as f:
|
||||
f.write("# Reporte Detallado de Análisis de Adaptación IO\n\n")
|
||||
f.write(f"**Fecha de generación:** {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||||
|
||||
# Estadísticas
|
||||
total = len(results)
|
||||
matched = len([r for r in results if r['twincat_variable']])
|
||||
high_conf = len([r for r in results if r['confidence'] == 'High'])
|
||||
medium_conf = len([r for r in results if r['confidence'] == 'Medium'])
|
||||
|
||||
f.write("## 📊 Estadísticas Generales\n\n")
|
||||
f.write(f"- **Total adaptaciones procesadas:** {total}\n")
|
||||
f.write(f"- **Variables encontradas:** {matched} ({matched/total*100:.1f}%)\n")
|
||||
f.write(f"- **Coincidencias de alta confianza:** {high_conf}\n")
|
||||
f.write(f"- **Coincidencias de media confianza:** {medium_conf}\n\n")
|
||||
|
||||
# Tabla de variables correlacionadas exitosamente
|
||||
f.write("## ✅ Variables Correlacionadas Exitosamente\n\n")
|
||||
matched_results = [r for r in results if r['twincat_variable']]
|
||||
|
||||
if matched_results:
|
||||
# Encabezado de la tabla
|
||||
f.write("| TIA Address | TIA Tag | TwinCAT Variable | TwinCAT Address | Tipo | Método | Confianza | Archivo Def. | Uso | Archivos Uso |\n")
|
||||
f.write("|-------------|---------|------------------|-----------------|------|--------|-----------|--------------|-----|---------------|\n")
|
||||
|
||||
# Filas de datos
|
||||
for result in matched_results:
|
||||
usage_files_str = ', '.join(result['usage_files'][:3]) # Limitar a 3 archivos
|
||||
if len(result['usage_files']) > 3:
|
||||
usage_files_str += "..."
|
||||
|
||||
f.write(f"| {result['tia_address']} | "
|
||||
f"`{result['master_tag']}` | "
|
||||
f"`{result['twincat_variable']}` | "
|
||||
f"`%{result['twincat_address']}` | "
|
||||
f"`{result['twincat_type']}` | "
|
||||
f"{result['match_type']} | "
|
||||
f"{result['confidence']} | "
|
||||
f"{result['definition_file']} | "
|
||||
f"{result['usage_count']} | "
|
||||
f"{usage_files_str} |\n")
|
||||
|
||||
f.write("\n")
|
||||
|
||||
# Tabla de variables no encontradas
|
||||
f.write("## ❌ Variables No Encontradas\n\n")
|
||||
unmatched_results = [r for r in results if not r['twincat_variable']]
|
||||
|
||||
if unmatched_results:
|
||||
f.write("| TIA Address | TIA Tag |\n")
|
||||
f.write("|-------------|----------|\n")
|
||||
|
||||
for result in unmatched_results:
|
||||
f.write(f"| {result['tia_address']} | `{result['master_tag']}` |\n")
|
||||
|
||||
f.write(f"\n**Total no encontradas:** {len(unmatched_results)}\n\n")
|
||||
|
||||
# Recomendaciones
|
||||
f.write("## 💡 Recomendaciones\n\n")
|
||||
f.write("1. **Variables de alta confianza** pueden migrarse directamente\n")
|
||||
f.write("2. **Variables de media confianza** requieren verificación manual\n")
|
||||
f.write("3. **Variables no encontradas** requieren mapeo manual o pueden ser obsoletas\n")
|
||||
f.write("4. Variables con uso extensivo son prioritarias para la migración\n\n")
|
||||
|
||||
# Resumen por confianza
|
||||
f.write("## 📈 Distribución por Confianza\n\n")
|
||||
f.write("| Nivel de Confianza | Cantidad | Porcentaje |\n")
|
||||
f.write("|--------------------|----------|------------|\n")
|
||||
f.write(f"| Alta | {high_conf} | {high_conf/total*100:.1f}% |\n")
|
||||
f.write(f"| Media | {medium_conf} | {medium_conf/total*100:.1f}% |\n")
|
||||
f.write(f"| No encontradas | {total-matched} | {(total-matched)/total*100:.1f}% |\n")
|
||||
|
||||
print(f"✅ Reporte detallado generado: {full_output_file}")
|
||||
|
||||
def main():
|
||||
print("🚀 Iniciando análisis detallado de adaptación de IOs TwinCAT ↔ TIA Portal")
|
||||
print("=" * 80)
|
||||
|
||||
# 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
|
||||
results_dir = create_results_directory(working_directory)
|
||||
|
||||
# Cargar datos
|
||||
tia_adaptations = load_tiaportal_adaptations(working_directory)
|
||||
twincat_definitions = scan_twincat_definitions(working_directory)
|
||||
twincat_usage = scan_twincat_usage(working_directory)
|
||||
|
||||
# Analizar correlaciones
|
||||
results = analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage)
|
||||
|
||||
# Generar reportes en el directorio de resultados
|
||||
generate_detailed_report(results, working_directory)
|
||||
generate_json_output(results, working_directory)
|
||||
|
||||
# Generar CSV para análisis adicional
|
||||
df = pd.DataFrame(results)
|
||||
csv_file = results_dir / 'io_detailed_analysis.csv'
|
||||
df.to_csv(csv_file, index=False, encoding='utf-8')
|
||||
print(f"✅ Datos exportados a CSV: {csv_file}")
|
||||
|
||||
print(f"\n🎉 Análisis completado exitosamente!")
|
||||
print(f"📁 Archivos generados en: {results_dir.absolute()}")
|
||||
print(f" 📄 {results_dir / 'IO_Detailed_Analysis_Report.md'}")
|
||||
print(f" 📄 {results_dir / 'io_adaptation_data.json'}")
|
||||
print(f" 📄 {results_dir / 'io_detailed_analysis.csv'}")
|
||||
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
results = main()
|
|
@ -0,0 +1,115 @@
|
|||
# Análisis de Adaptación IO - TwinCAT ↔ TIA Portal
|
||||
|
||||
Scripts de análisis automático para correlacionar variables IO entre plataformas TwinCAT y TIA Portal en el proyecto SIDEL E5.007560.
|
||||
|
||||
## 📋 Descripción General
|
||||
|
||||
Este proyecto automatiza el análisis de adaptación de variables de entrada/salida (IO) entre:
|
||||
- **TIA Portal** (Siemens) - Sistema actual
|
||||
- **TwinCAT** (Beckhoff) - Sistema objetivo de migración
|
||||
|
||||
## 🔧 Scripts Incluidos
|
||||
|
||||
### 1. `x1_io_adaptation_script.py` - Análisis de Correlación IO
|
||||
|
||||
**Propósito:** Encuentra y correlaciona variables IO entre ambas plataformas generando reportes detallados.
|
||||
|
||||
**Archivos requeridos:**
|
||||
- `IO Adapted.md` - Tabla de adaptaciones TIA Portal (debe estar en directorio raíz)
|
||||
- `TwinCat/` - Directorio con archivos `.scl` de TwinCAT
|
||||
- `TiaPortal/` - Directorio con archivos `.md` de TIA Portal
|
||||
|
||||
**Archivos generados:**
|
||||
- `resultados/IO_Detailed_Analysis_Report.md` - Reporte con tablas markdown
|
||||
- `resultados/io_adaptation_data.json` - Datos estructurados para análisis
|
||||
- `resultados/io_detailed_analysis.csv` - Datos tabulares
|
||||
|
||||
### 2. `x2_code_snippets_generator.py` - Generador de Snippets de Código
|
||||
|
||||
**Propósito:** Genera snippets de código mostrando el uso real de cada variable en ambas plataformas.
|
||||
|
||||
**Archivos requeridos:**
|
||||
- `resultados/io_adaptation_data.json` - Generado por el script 1
|
||||
- `TwinCat/` - Directorio con archivos `.scl`
|
||||
- `TiaPortal/` - Directorio con archivos `.md`
|
||||
|
||||
**Archivos generados:**
|
||||
- `resultados/IO_Code_Snippets_Report.md` - Snippets de código con contexto
|
||||
- `resultados/IO_Usage_Statistics.md` - Estadísticas de uso
|
||||
|
||||
## 🚀 Uso
|
||||
|
||||
### Paso 1: Ejecutar análisis de correlación
|
||||
```bash
|
||||
python x1_io_adaptation_script.py
|
||||
```
|
||||
|
||||
### Paso 2: Generar snippets de código
|
||||
```bash
|
||||
python x2_code_snippets_generator.py
|
||||
```
|
||||
|
||||
## 📁 Estructura de Directorios Requerida
|
||||
|
||||
```
|
||||
proyecto/
|
||||
├── x1_io_adaptation_script.py
|
||||
├── x2_code_snippets_generator.py
|
||||
├── IO Adapted.md # Tabla de adaptaciones TIA
|
||||
├── TwinCat/ # Archivos .scl TwinCAT
|
||||
│ ├── GLOBAL_VARIABLES_IN_OUT.scl
|
||||
│ ├── INPUT.scl
|
||||
│ └── ... (otros archivos .scl)
|
||||
├── TiaPortal/ # Archivos .md TIA Portal
|
||||
│ ├── Input.md
|
||||
│ ├── Output.md
|
||||
│ └── ... (otros archivos .md)
|
||||
└── resultados/ # Directorio creado automáticamente
|
||||
├── IO_Detailed_Analysis_Report.md
|
||||
├── io_adaptation_data.json
|
||||
├── io_detailed_analysis.csv
|
||||
├── IO_Code_Snippets_Report.md
|
||||
└── IO_Usage_Statistics.md
|
||||
```
|
||||
|
||||
## 🔍 Funcionalidades Principales
|
||||
|
||||
### Script 1 - Análisis de Correlación
|
||||
- ✅ Convierte direcciones TIA Portal a formato TwinCAT
|
||||
- ✅ Busca variables por dirección exacta y similitud de nombres
|
||||
- ✅ Calcula nivel de confianza de correlaciones
|
||||
- ✅ Genera reportes en múltiples formatos (MD, JSON, CSV)
|
||||
|
||||
### Script 2 - Snippets de Código
|
||||
- ✅ Muestra hasta 3 usos por variable por plataforma
|
||||
- ✅ Contexto de 3 líneas (anterior, actual, siguiente)
|
||||
- ✅ Links markdown a archivos fuente
|
||||
- ✅ Estadísticas de uso y archivos más referenciados
|
||||
|
||||
## 📊 Resultados Típicos
|
||||
|
||||
- **Variables procesadas:** ~90-100 adaptaciones IO
|
||||
- **Tasa de correlación:** ~70-80% de variables encontradas
|
||||
- **Confianza alta:** Correlaciones por dirección exacta
|
||||
- **Variable más usada:** Típicamente botones de reset/start/stop
|
||||
|
||||
## 🛠 Dependencias
|
||||
|
||||
```python
|
||||
pandas
|
||||
pathlib (incluida en Python 3.4+)
|
||||
json (incluida en Python estándar)
|
||||
re (incluida en Python estándar)
|
||||
```
|
||||
|
||||
## 📝 Notas Importantes
|
||||
|
||||
1. **Orden de ejecución:** Ejecutar siempre el Script 1 antes que el Script 2
|
||||
2. **Archivos fuente:** Verificar que existan los directorios TwinCat/ y TiaPortal/
|
||||
3. **Codificación:** Los scripts manejan archivos con encoding UTF-8
|
||||
4. **Rendimiento:** El Script 2 puede tardar algunos minutos procesando archivos grandes
|
||||
|
||||
## 👥 Proyecto
|
||||
|
||||
**Proyecto SIDEL:** E5.007560 - Modifica O&U - SAE235
|
||||
**Automatización:** Migración TIA Portal → TwinCAT
|
|
@ -0,0 +1,315 @@
|
|||
#!/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()
|
18675
data/log.txt
18675
data/log.txt
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue