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:
Miguel 2025-06-19 22:47:32 +02:00
parent b74db36cf9
commit def0f0b2d7
6 changed files with 1958 additions and 17621 deletions

View File

@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/ExportTwinCat"
}
],
"settings": {}
}

View File

@ -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()

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff