Actualización de directorios de trabajo y logs en el script de análisis XML

- Se modificaron los directorios de trabajo en `script_config.json` y `work_dir.json` para apuntar a la nueva ubicación de los archivos del proyecto 98050.
- Se actualizaron los logs de ejecución en `log_x0_main.txt` y `log_98050_PLC.txt` para reflejar las nuevas fechas, duraciones y resultados de los procesos de exportación.
- Se corrigieron rutas en varios archivos de configuración para asegurar la correcta ejecución de los scripts.
This commit is contained in:
Miguel 2025-07-09 14:12:43 +02:00
parent 2cec16af0e
commit 4a1b16117e
12 changed files with 20912 additions and 55154 deletions

View File

@ -2,220 +2,132 @@
## Introducción
Esta guía explica cómo configurar y usar correctamente la función `load_configuration()` en scripts ubicados bajo el directorio `/backend`. La función carga configuraciones desde un archivo `script_config.json` ubicado en el mismo directorio que el script que la llama.
Esta guía explica cómo usar la función `load_configuration()` para cargar parámetros desde un archivo `script_config.json` en los scripts del backend.
## Configuración del Path e Importación
## 1. Configuración del Script
### 1. Configuración estándar del Path
Para que tus scripts puedan encontrar los módulos del proyecto, necesitas añadir el directorio raíz al path de Python.
Para scripts ubicados en subdirectorios bajo `/backend`, usa este patrón estándar:
### Path Setup e Importación
Coloca este código al inicio de tu script:
```python
import os
import sys
# Configurar el path al directorio raíz del proyecto
# Añadir el directorio raíz al sys.path
# El número de `os.path.dirname()` depende de la profundidad del script.
# Para /backend/script_groups/grupo/script.py, son 4.
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
# Importar la función
from backend.script_utils import load_configuration
```
**Nota:** El número de `os.path.dirname()` anidados depende de la profundidad del script:
- Scripts en `/backend/script_groups/grupo/`: 4 niveles
- Scripts en `/backend/`: 2 niveles
## 2. Cargar la Configuración
### 2. Importación Correcta
La función `load_configuration()` busca y carga un archivo llamado `script_config.json` que debe estar en el **mismo directorio** que el script que la ejecuta. Devuelve un diccionario con todo el contenido del JSON.
**✅ Correcto:**
```python
from backend.script_utils import load_configuration
```
**❌ Incorrecto:**
```python
from script_utils import load_configuration # No funciona desde subdirectorios
```
## Uso de la Función load_configuration()
### Implementación Básica
### Ejemplo de Uso
```python
def main():
# Cargar configuraciones
# Cargar configuraciones del archivo script_config.json
configs = load_configuration()
# Obtener el directorio de trabajo
working_directory = configs.get("working_directory", "")
# Es buena práctica verificar si la configuración se cargó
if not configs:
print("Error: No se pudo cargar la configuración. Saliendo.")
return
# Acceder a configuraciones por nivel
# Acceder a los parámetros usando .get() para evitar errores
working_directory = configs.get("working_directory", "")
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
# Ejemplo de uso de parámetros específicos con valores por defecto
# Ejemplo de uso de un parámetro específico con valor por defecto
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
xref_output_dir = level2_config.get("xref_output_dir", "xref_output")
print(f"Directorio de trabajo: {working_directory}")
print(f"Directorio de salida SCL: {scl_output_dir}")
if __name__ == "__main__":
main()
```
### Estructura del Archivo script_config.json
## 3. Archivo `script_config.json`
El archivo `script_config.json` debe estar ubicado en el mismo directorio que el script que llama a `load_configuration()`. Estructura recomendada:
Este archivo contiene los parámetros de tu script. La estructura interna del JSON, como el uso de `"level1"`, `"level2"`, etc., es una convención para organizar los parámetros. `load_configuration()` simplemente lee el archivo y devuelve su contenido.
**Ejemplo de `script_config.json`:**
```json
{
"working_directory": "/ruta/al/directorio/de/trabajo",
"level1": {
"parametro_global_1": "valor1",
"parametro_global_2": "valor2"
"parametro_global_1": "valor1"
},
"level2": {
"scl_output_dir": "scl_output",
"xref_output_dir": "xref_output",
"xref_source_subdir": "source",
"aggregated_filename": "full_project_representation.md"
"xref_output_dir": "xref_output"
},
"level3": {
"parametro_especifico_1": true,
"parametro_especifico_2": 100
"parametro_especifico_1": true
}
}
```
## Ejemplo Completo de Implementación
## 4. Manejo de Errores
```python
"""
Script de ejemplo que demuestra el uso completo de load_configuration()
"""
`load_configuration()` está diseñada para ser robusta:
- Si `script_config.json` no se encuentra, retorna un diccionario vacío `{}`.
- Si el JSON es inválido, imprime un error y también retorna `{}`.
import os
import sys
import json
**Siempre** comprueba si el diccionario devuelto está vacío para manejar estos casos de forma segura en tu script.
# Configuración del path
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
## 5. Jerarquía de Archivos de Configuración
El sistema utiliza un modelo de configuración en cascada para gestionar los parámetros de los scripts. Esta jerarquía permite establecer configuraciones a nivel global, de grupo y de trabajo. Antes de la ejecución, el launcher lee estos archivos, los combina y genera un único `script_config.json` en la carpeta del script. La función `load_configuration()` es la que finalmente lee este archivo consolidado.
def main():
print("=== Cargando Configuración ===")
A continuación se describe la finalidad y ubicación de cada archivo clave.
# Cargar configuraciones
configs = load_configuration()
### Archivos de Valores (Parámetros)
# Verificar que se cargó correctamente
if not configs:
print("Error: No se pudo cargar la configuración")
return
Contienen los datos y variables que utilizará el script. La configuración se superpone en el siguiente orden: `Nivel 1 < Nivel 2 < Nivel 3`.
# Obtener configuraciones
working_directory = configs.get("working_directory", "")
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
- **`data.json` (Nivel 1 - Global)**
- **Ubicación:** `data/data.json`
- **Utilidad:** Almacena variables globales disponibles para todos los scripts. Ideal para parámetros generales.
- **Acceso:** Sus datos se cargan en la clave `"level1"` del diccionario `configs`.
# Mostrar configuraciones cargadas
print(f"Directorio de trabajo: {working_directory}")
print("Configuración Nivel 1:", json.dumps(level1_config, indent=2))
print("Configuración Nivel 2:", json.dumps(level2_config, indent=2))
print("Configuración Nivel 3:", json.dumps(level3_config, indent=2))
- **`script_config.json` (Nivel 2 - Grupo)**
- **Ubicación:** En la raíz de cada directorio de grupo (ej: `backend/script_groups/MiGrupo/script_config.json`).
- **Utilidad:** Define parámetros compartidos por todos los scripts de un grupo.
- **Acceso:** Sus datos se cargan en la clave `"level2"`.
# Ejemplo de uso de parámetros con valores por defecto
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
xref_output_dir = level2_config.get("xref_output_dir", "xref_output")
- **`work_dir.json` (Nivel 3 - Directorio de Trabajo)**
- **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
- **Utilidad:** Contiene parámetros para una ejecución específica. Es el nivel más específico y sobrescribe los anteriores.
- **Acceso:** Sus datos se cargan en la clave `"level3"`.
print(f"Directorio de salida SCL: {scl_output_dir}")
print(f"Directorio de salida XREF: {xref_output_dir}")
### Archivos de Esquema (Definiciones de Estructura)
No contienen valores, sino que describen la estructura y tipos de datos que los archivos de valores deben tener. Son usados por el launcher para validación y para generar interfaces de configuración.
if __name__ == "__main__":
main()
```
- **`esquema_group.json`**
- **Ubicación:** Raíz del directorio del grupo.
- **Utilidad:** Define la estructura del `script_config.json` del grupo.
## Manejo de Errores
- **`esquema_work.json`**
- **Ubicación:** Raíz del directorio del grupo.
- **Utilidad:** Define la estructura del `work_dir.json`.
La función `load_configuration()` maneja automáticamente los siguientes casos:
1. **Archivo no encontrado**: Retorna un diccionario vacío `{}`
2. **JSON inválido**: Retorna un diccionario vacío y muestra un mensaje de error
3. **Errores de lectura**: Retorna un diccionario vacío y muestra un mensaje de error
### Verificación de Configuración Válida
```python
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
```
## Mejores Prácticas
1. **Siempre proporciona valores por defecto** al usar `.get()`:
```python
valor = config.get("clave", "valor_por_defecto")
```
2. **Verifica la existencia de directorios críticos**:
```python
if not os.path.exists(working_directory):
print(f"Error: Directorio no encontrado: {working_directory}")
return
```
3. **Documenta los parámetros esperados** en tu script:
```python
# Parámetros esperados en level2:
# - scl_output_dir: Directorio de salida para archivos SCL
# - xref_output_dir: Directorio de salida para referencias cruzadas
```
4. **Usa nombres de parámetros consistentes** en todos los scripts del mismo grupo.
## Definición Técnica de load_configuration()
```python
def load_configuration() -> Dict[str, Any]:
"""
Load configuration from script_config.json in the current script directory.
Returns:
Dict containing configurations with levels 1, 2, 3 and working_directory
Example usage in scripts:
from backend.script_utils import load_configuration
configs = load_configuration()
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
working_dir = configs.get("working_directory", "")
"""
```
La función utiliza `inspect.stack()` para determinar automáticamente el directorio del script que la llama, asegurando que siempre busque el archivo `script_config.json` en la ubicación correcta.
## Documentación de Scripts para el Launcher
## 6. Documentación de Scripts para el Launcher
El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.

File diff suppressed because it is too large Load Diff

View File

@ -2,220 +2,132 @@
## Introducción
Esta guía explica cómo configurar y usar correctamente la función `load_configuration()` en scripts ubicados bajo el directorio `/backend`. La función carga configuraciones desde un archivo `script_config.json` ubicado en el mismo directorio que el script que la llama.
Esta guía explica cómo usar la función `load_configuration()` para cargar parámetros desde un archivo `script_config.json` en los scripts del backend.
## Configuración del Path e Importación
## 1. Configuración del Script
### 1. Configuración estándar del Path
Para que tus scripts puedan encontrar los módulos del proyecto, necesitas añadir el directorio raíz al path de Python.
Para scripts ubicados en subdirectorios bajo `/backend`, usa este patrón estándar:
### Path Setup e Importación
Coloca este código al inicio de tu script:
```python
import os
import sys
# Configurar el path al directorio raíz del proyecto
# Añadir el directorio raíz al sys.path
# El número de `os.path.dirname()` depende de la profundidad del script.
# Para /backend/script_groups/grupo/script.py, son 4.
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
# Importar la función
from backend.script_utils import load_configuration
```
**Nota:** El número de `os.path.dirname()` anidados depende de la profundidad del script:
- Scripts en `/backend/script_groups/grupo/`: 4 niveles
- Scripts en `/backend/`: 2 niveles
## 2. Cargar la Configuración
### 2. Importación Correcta
La función `load_configuration()` busca y carga un archivo llamado `script_config.json` que debe estar en el **mismo directorio** que el script que la ejecuta. Devuelve un diccionario con todo el contenido del JSON.
**✅ Correcto:**
```python
from backend.script_utils import load_configuration
```
**❌ Incorrecto:**
```python
from script_utils import load_configuration # No funciona desde subdirectorios
```
## Uso de la Función load_configuration()
### Implementación Básica
### Ejemplo de Uso
```python
def main():
# Cargar configuraciones
# Cargar configuraciones del archivo script_config.json
configs = load_configuration()
# Obtener el directorio de trabajo
working_directory = configs.get("working_directory", "")
# Es buena práctica verificar si la configuración se cargó
if not configs:
print("Error: No se pudo cargar la configuración. Saliendo.")
return
# Acceder a configuraciones por nivel
# Acceder a los parámetros usando .get() para evitar errores
working_directory = configs.get("working_directory", "")
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
# Ejemplo de uso de parámetros específicos con valores por defecto
# Ejemplo de uso de un parámetro específico con valor por defecto
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
xref_output_dir = level2_config.get("xref_output_dir", "xref_output")
print(f"Directorio de trabajo: {working_directory}")
print(f"Directorio de salida SCL: {scl_output_dir}")
if __name__ == "__main__":
main()
```
### Estructura del Archivo script_config.json
## 3. Archivo `script_config.json`
El archivo `script_config.json` debe estar ubicado en el mismo directorio que el script que llama a `load_configuration()`. Estructura recomendada:
Este archivo contiene los parámetros de tu script. La estructura interna del JSON, como el uso de `"level1"`, `"level2"`, etc., es una convención para organizar los parámetros. `load_configuration()` simplemente lee el archivo y devuelve su contenido.
**Ejemplo de `script_config.json`:**
```json
{
"working_directory": "/ruta/al/directorio/de/trabajo",
"level1": {
"parametro_global_1": "valor1",
"parametro_global_2": "valor2"
"parametro_global_1": "valor1"
},
"level2": {
"scl_output_dir": "scl_output",
"xref_output_dir": "xref_output",
"xref_source_subdir": "source",
"aggregated_filename": "full_project_representation.md"
"xref_output_dir": "xref_output"
},
"level3": {
"parametro_especifico_1": true,
"parametro_especifico_2": 100
"parametro_especifico_1": true
}
}
```
## Ejemplo Completo de Implementación
## 4. Manejo de Errores
```python
"""
Script de ejemplo que demuestra el uso completo de load_configuration()
"""
`load_configuration()` está diseñada para ser robusta:
- Si `script_config.json` no se encuentra, retorna un diccionario vacío `{}`.
- Si el JSON es inválido, imprime un error y también retorna `{}`.
import os
import sys
import json
**Siempre** comprueba si el diccionario devuelto está vacío para manejar estos casos de forma segura en tu script.
# Configuración del path
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
## 5. Jerarquía de Archivos de Configuración
El sistema utiliza un modelo de configuración en cascada para gestionar los parámetros de los scripts. Esta jerarquía permite establecer configuraciones a nivel global, de grupo y de trabajo. Antes de la ejecución, el launcher lee estos archivos, los combina y genera un único `script_config.json` en la carpeta del script. La función `load_configuration()` es la que finalmente lee este archivo consolidado.
def main():
print("=== Cargando Configuración ===")
A continuación se describe la finalidad y ubicación de cada archivo clave.
# Cargar configuraciones
configs = load_configuration()
### Archivos de Valores (Parámetros)
# Verificar que se cargó correctamente
if not configs:
print("Error: No se pudo cargar la configuración")
return
Contienen los datos y variables que utilizará el script. La configuración se superpone en el siguiente orden: `Nivel 1 < Nivel 2 < Nivel 3`.
# Obtener configuraciones
working_directory = configs.get("working_directory", "")
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
- **`data.json` (Nivel 1 - Global)**
- **Ubicación:** `data/data.json`
- **Utilidad:** Almacena variables globales disponibles para todos los scripts. Ideal para parámetros generales.
- **Acceso:** Sus datos se cargan en la clave `"level1"` del diccionario `configs`.
# Mostrar configuraciones cargadas
print(f"Directorio de trabajo: {working_directory}")
print("Configuración Nivel 1:", json.dumps(level1_config, indent=2))
print("Configuración Nivel 2:", json.dumps(level2_config, indent=2))
print("Configuración Nivel 3:", json.dumps(level3_config, indent=2))
- **`script_config.json` (Nivel 2 - Grupo)**
- **Ubicación:** En la raíz de cada directorio de grupo (ej: `backend/script_groups/MiGrupo/script_config.json`).
- **Utilidad:** Define parámetros compartidos por todos los scripts de un grupo.
- **Acceso:** Sus datos se cargan en la clave `"level2"`.
# Ejemplo de uso de parámetros con valores por defecto
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
xref_output_dir = level2_config.get("xref_output_dir", "xref_output")
- **`work_dir.json` (Nivel 3 - Directorio de Trabajo)**
- **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
- **Utilidad:** Contiene parámetros para una ejecución específica. Es el nivel más específico y sobrescribe los anteriores.
- **Acceso:** Sus datos se cargan en la clave `"level3"`.
print(f"Directorio de salida SCL: {scl_output_dir}")
print(f"Directorio de salida XREF: {xref_output_dir}")
### Archivos de Esquema (Definiciones de Estructura)
No contienen valores, sino que describen la estructura y tipos de datos que los archivos de valores deben tener. Son usados por el launcher para validación y para generar interfaces de configuración.
if __name__ == "__main__":
main()
```
- **`esquema_group.json`**
- **Ubicación:** Raíz del directorio del grupo.
- **Utilidad:** Define la estructura del `script_config.json` del grupo.
## Manejo de Errores
- **`esquema_work.json`**
- **Ubicación:** Raíz del directorio del grupo.
- **Utilidad:** Define la estructura del `work_dir.json`.
La función `load_configuration()` maneja automáticamente los siguientes casos:
1. **Archivo no encontrado**: Retorna un diccionario vacío `{}`
2. **JSON inválido**: Retorna un diccionario vacío y muestra un mensaje de error
3. **Errores de lectura**: Retorna un diccionario vacío y muestra un mensaje de error
### Verificación de Configuración Válida
```python
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
```
## Mejores Prácticas
1. **Siempre proporciona valores por defecto** al usar `.get()`:
```python
valor = config.get("clave", "valor_por_defecto")
```
2. **Verifica la existencia de directorios críticos**:
```python
if not os.path.exists(working_directory):
print(f"Error: Directorio no encontrado: {working_directory}")
return
```
3. **Documenta los parámetros esperados** en tu script:
```python
# Parámetros esperados en level2:
# - scl_output_dir: Directorio de salida para archivos SCL
# - xref_output_dir: Directorio de salida para referencias cruzadas
```
4. **Usa nombres de parámetros consistentes** en todos los scripts del mismo grupo.
## Definición Técnica de load_configuration()
```python
def load_configuration() -> Dict[str, Any]:
"""
Load configuration from script_config.json in the current script directory.
Returns:
Dict containing configurations with levels 1, 2, 3 and working_directory
Example usage in scripts:
from backend.script_utils import load_configuration
configs = load_configuration()
level1_config = configs.get("level1", {})
level2_config = configs.get("level2", {})
level3_config = configs.get("level3", {})
working_dir = configs.get("working_directory", "")
"""
```
La función utiliza `inspect.stack()` para determinar automáticamente el directorio del script que la llama, asegurando que siempre busque el archivo `script_config.json` en la ubicación correcta.
## Documentación de Scripts para el Launcher
## 6. Documentación de Scripts para el Launcher
El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.

View File

@ -1,172 +0,0 @@
# Convertidor LAD a Pseudocódigo Estructurado
## Descripción
Este proyecto proporciona herramientas para convertir código LAD (Ladder Diagram) de TwinCAT a pseudocódigo estructurado compatible con IEC61131-3. El convertidor mantiene la semántica original del código LAD mientras lo transforma a un formato más legible y estructurado.
## Características
- ✅ **Parsing completo de LAD**: Analiza la estructura completa de archivos `.EXP` de TwinCAT
- ✅ **Conversión semántica**: Mantiene la lógica original del diagrama ladder
- ✅ **Formato estructurado**: Genera código pseudo estructurado con IF-THEN-END_IF
- ✅ **Manejo de contactos**: Convierte contactos normales y negados correctamente
- ✅ **Function blocks**: Identifica y convierte llamadas a bloques de función
- ✅ **Operadores matemáticos**: Maneja operaciones aritméticas y lógicas
- ✅ **Comentarios**: Preserva comentarios de las redes originales
## Archivos del Proyecto
### Convertidores Principales
1. **`simple_lad_converter.py`** - Convertidor simplificado y robusto (recomendado)
2. **`lad_to_pseudocode_converter.py`** - Convertidor básico inicial
3. **`lad_to_pseudocode_converter_enhanced.py`** - Versión avanzada con más características
### Archivos de Prueba
- **`test_simple.py`** - Script de prueba para el convertidor simple
- **`ejemplo_conversion.py`** - Ejemplo de uso del convertidor básico
- **`test_enhanced_converter.py`** - Prueba para el convertidor avanzado
## Uso Rápido
### Método Simple (Recomendado)
```bash
python test_simple.py
```
Este comando procesará el archivo `.example/INPUT.EXP` y generará `output_simple.txt` con el código convertido.
### Uso Directo del Convertidor
```python
from simple_lad_converter import SimpleLadConverter
converter = SimpleLadConverter()
converter.parse_file("mi_archivo.EXP")
structured_code = converter.save_to_file("salida.txt")
```
### Línea de Comandos
```bash
python lad_to_pseudocode_converter.py archivo_entrada.EXP archivo_salida.txt
```
## Estructura del Código LAD Soportada
El convertidor puede procesar las siguientes estructuras de TwinCAT:
### Elementos LAD Reconocidos
- `_NETWORK` - Inicio de red
- `_LD_ASSIGN` - Asignaciones
- `_LD_CONTACT` - Contactos (entradas)
- `_LD_AND` / `_LD_OR` - Operaciones lógicas
- `_FUNCTIONBLOCK` - Bloques de función
- `_OPERATOR` - Operadores matemáticos
- `_COMMENT` / `_END_COMMENT` - Comentarios
- `_OUTPUT` - Variables de salida
### Operadores Soportados
- **Aritméticos**: ADD, SUB, MUL, DIV
- **Lógicos**: AND, OR
- **Comparación**: LT, GT, EQ
- **Especiales**: SEL, MOVE
## Ejemplo de Conversión
### Código LAD Original
```
_NETWORK
_COMMENT
Verificación de presión CO2
_END_COMMENT
_LD_ASSIGN
_LD_CONTACT
DI_Air_InletPress_OK
_NEGATIV
_FUNCTIONBLOCK
mAirPressOk
_OUTPUT
gInLinePressAirOk
```
### Código Estructurado Generado
```
// Red 5
// Verificación de presión CO2
IF NOT DI_Air_InletPress_OK THEN
gInLinePressAirOk := mAirPressOk();
END_IF;
```
## Estructura del Archivo de Salida
El código generado sigue esta estructura:
```
// Código pseudo estructurado generado desde LAD TwinCAT
// Compatible con IEC61131-3
PROGRAM Input_Converted
// Red 1
IF condicion1 AND condicion2 THEN
variable_salida := funcion_bloque(parametros);
END_IF;
// Red 2
variable := operando1 ADD operando2;
// Red N...
END_PROGRAM
```
## Resultados de la Conversión
El convertidor ha procesado exitosamente el archivo `INPUT.EXP` que contiene:
- **86 redes LAD** en total
- **Múltiples tipos de elementos**: contactos, function blocks, operadores
- **Preservación de comentarios** originales
- **Conversión correcta de lógica condicional** con IF-THEN-END_IF
### Estadísticas del Ejemplo
- Archivo de entrada: `.example/INPUT.EXP` (4,611 líneas)
- Redes procesadas: 86
- Archivo de salida: ~235 líneas de código estructurado
- Reducción de complejidad: ~95%
## Ventajas del Código Convertido
1. **Legibilidad**: Más fácil de leer que el formato LAD textual
2. **Mantenibilidad**: Estructura clara con comentarios preservados
3. **Debugging**: Lógica condicional explícita
4. **Documentación**: Comentarios de red integrados
5. **Portabilidad**: Formato pseudo-código universal
## Limitaciones Conocidas
- Algunos parámetros internos pueden aparecer como `_POSITIV`, `_NEGATIV`
- Estructuras complejas de LAD pueden requerir revisión manual
- El convertidor es específico para el formato de TwinCAT
## Desarrollo Futuro
- [ ] Mejorar el parsing de parámetros de function blocks
- [ ] Añadir soporte para más tipos de operadores
- [ ] Implementar validación de sintaxis
- [ ] Crear interfaz gráfica para conversión
- [ ] Soporte para otros formatos de PLC
## Contribuciones
Este convertidor fue desarrollado para facilitar el análisis y mantenimiento de código LAD en proyectos de automatización industrial. Las contribuciones y mejoras son bienvenidas.
---
**Nota**: Este es un proyecto de código abierto para ayudar en la migración y análisis de código LAD de TwinCAT a formatos más estructurados.

View File

@ -1407,7 +1407,7 @@ def main():
if not configs:
print("Advertencia: No se pudo cargar la configuración, usando valores por defecto")
working_directory = "./"
scl_output_dir = "scl"
scl_output_dir = "TwinCat/scl"
debug_mode = True
show_optimizations = True
show_generated_code = False

View File

@ -28,7 +28,7 @@ sys.path.append(script_root)
from backend.script_utils import load_configuration
def load_tiaportal_adaptations(working_directory, file_path='IO Adapted.md'):
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}")
@ -39,17 +39,17 @@ def load_tiaportal_adaptations(working_directory, file_path='IO Adapted.md'):
print(f"⚠️ Archivo {full_file_path} no encontrado")
return adaptations
with open(full_file_path, 'r', encoding='utf-8') as f:
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*\|',
r"\|\s*([EA]\d+\.\d+)\s*\|\s*([^|]+?)\s*\|",
# Analógicos: PEW100, PAW100
r'\|\s*(P[EA]W\d+)\s*\|\s*([^|]+?)\s*\|',
r"\|\s*(P[EA]W\d+)\s*\|\s*([^|]+?)\s*\|",
# Profibus: EW 1640, AW 1640
r'\|\s*([EA]W\s+\d+)\s*\|\s*([^|]+?)\s*\|'
r"\|\s*([EA]W\s+\d+)\s*\|\s*([^|]+?)\s*\|",
]
for pattern in patterns:
@ -57,14 +57,15 @@ def load_tiaportal_adaptations(working_directory, file_path='IO Adapted.md'):
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('-'):
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'):
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}")
@ -77,14 +78,16 @@ def scan_twincat_definitions(working_directory, directory='TwinCat'):
# 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
# Solo se buscan definiciones activas. Se ignoran las comentadas.
# Ejemplo Válido: DO_CIP_DrainCompleted AT %QX2.1 : BOOL ;
# Ejemplo a Ignorar: DO_FillerNextRecipe_1 (* AT %QX2.1 *) : BOOL;
r"(\w+)\s+AT\s+%([IQ][XWB]\d+(?:\.\d+)?)\s*:\s*(\w+);"
]
for file_path in Path(full_directory).glob('*.scl'):
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:
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
for pattern in definition_patterns:
@ -95,17 +98,19 @@ def scan_twincat_definitions(working_directory, directory='TwinCat'):
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
"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'):
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}")
@ -116,87 +121,98 @@ def scan_twincat_usage(working_directory, directory='TwinCat'):
print(f"⚠️ Directorio {full_directory} no encontrado")
return usage_data
for file_path in Path(full_directory).glob('*.scl'):
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:
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)
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 '')
})
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')
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')
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')
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')
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}')
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:
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()
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()
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'))
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()
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'))
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...")
@ -206,16 +222,16 @@ def analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage):
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'
"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
@ -223,70 +239,88 @@ def analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage):
var_found = False
for twincat_addr in twincat_addresses:
var_name, var_info = find_variable_by_address(twincat_definitions, twincat_addr)
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'
})
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)
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'
})
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 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)
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 ""
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'])}")
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}%)")
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 = 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'):
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)
full_output_file = os.path.join(working_directory, "resultados", output_file)
print(f"\n📄 Generando archivo JSON: {full_output_file}")
json_data = {
@ -294,56 +328,63 @@ def generate_json_output(results, working_directory, output_file='io_adaptation_
"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'])
"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": []
"adaptations": [],
}
for result in results:
adaptation = {
"tia_portal": {
"address": result['tia_address'],
"tag": result['master_tag']
"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']
"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
"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']
}
"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:
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'):
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)
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:
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")
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'])
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")
@ -353,20 +394,27 @@ def generate_detailed_report(results, working_directory, output_file='IO_Detaile
# Tabla de variables correlacionadas exitosamente
f.write("## ✅ Variables Correlacionadas Exitosamente\n\n")
matched_results = [r for r in results if r['twincat_variable']]
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")
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 = ", ".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.write(
f"| {result['tia_address']} | "
f"`{result['master_tag']}` | "
f"`{result['twincat_variable']}` | "
f"`%{result['twincat_address']}` | "
@ -375,13 +423,14 @@ def generate_detailed_report(results, working_directory, output_file='IO_Detaile
f"{result['confidence']} | "
f"{result['definition_file']} | "
f"{result['usage_count']} | "
f"{usage_files_str} |\n")
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']]
unmatched_results = [r for r in results if not r["twincat_variable"]]
if unmatched_results:
f.write("| TIA Address | TIA Tag |\n")
@ -396,7 +445,9 @@ def generate_detailed_report(results, working_directory, output_file='IO_Detaile
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(
"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
@ -405,10 +456,13 @@ def generate_detailed_report(results, working_directory, output_file='IO_Detaile
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")
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)
@ -418,7 +472,9 @@ def main():
# Verificar que se cargó correctamente
if not configs:
print("Advertencia: No se pudo cargar la configuración, usando valores por defecto")
print(
"Advertencia: No se pudo cargar la configuración, usando valores por defecto"
)
working_directory = "./"
else:
working_directory = configs.get("working_directory", "./")
@ -447,8 +503,8 @@ def main():
# 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')
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!")
@ -459,5 +515,6 @@ def main():
return results
if __name__ == "__main__":
results = main()

View File

@ -1,115 +0,0 @@
# 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,5 +15,5 @@
"xref_source_subdir": "source"
},
"level3": {},
"working_directory": "C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia"
"working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source"
}

View File

@ -1,8 +1,8 @@
{
"path": "C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"history": [
"C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",
"D:\\Trabajo\\VM\\22 - 93841 - Sidel - Tilting\\Reporte\\TiaExports",
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML",
"C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport"

26095
data/log.txt

File diff suppressed because it is too large Load Diff