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:
parent
2cec16af0e
commit
4a1b16117e
|
@ -2,220 +2,132 @@
|
||||||
|
|
||||||
## Introducción
|
## 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
|
```python
|
||||||
import os
|
import os
|
||||||
import sys
|
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(
|
script_root = os.path.dirname(
|
||||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||||
)
|
)
|
||||||
sys.path.append(script_root)
|
sys.path.append(script_root)
|
||||||
|
|
||||||
# Importar la función de configuración
|
# Importar la función
|
||||||
from backend.script_utils import load_configuration
|
from backend.script_utils import load_configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
**Nota:** El número de `os.path.dirname()` anidados depende de la profundidad del script:
|
## 2. Cargar la Configuración
|
||||||
- Scripts en `/backend/script_groups/grupo/`: 4 niveles
|
|
||||||
- Scripts en `/backend/`: 2 niveles
|
|
||||||
|
|
||||||
### 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:**
|
### Ejemplo de Uso
|
||||||
```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
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def main():
|
def main():
|
||||||
# Cargar configuraciones
|
# Cargar configuraciones del archivo script_config.json
|
||||||
configs = load_configuration()
|
configs = load_configuration()
|
||||||
|
|
||||||
# Obtener el directorio de trabajo
|
# Es buena práctica verificar si la configuración se cargó
|
||||||
working_directory = configs.get("working_directory", "")
|
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", {})
|
level1_config = configs.get("level1", {})
|
||||||
level2_config = configs.get("level2", {})
|
level2_config = configs.get("level2", {})
|
||||||
level3_config = configs.get("level3", {})
|
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")
|
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
|
||||||
xref_output_dir = level2_config.get("xref_output_dir", "xref_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__":
|
if __name__ == "__main__":
|
||||||
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
|
```json
|
||||||
{
|
{
|
||||||
"working_directory": "/ruta/al/directorio/de/trabajo",
|
"working_directory": "/ruta/al/directorio/de/trabajo",
|
||||||
"level1": {
|
"level1": {
|
||||||
"parametro_global_1": "valor1",
|
"parametro_global_1": "valor1"
|
||||||
"parametro_global_2": "valor2"
|
|
||||||
},
|
},
|
||||||
"level2": {
|
"level2": {
|
||||||
"scl_output_dir": "scl_output",
|
"scl_output_dir": "scl_output",
|
||||||
"xref_output_dir": "xref_output",
|
"xref_output_dir": "xref_output"
|
||||||
"xref_source_subdir": "source",
|
|
||||||
"aggregated_filename": "full_project_representation.md"
|
|
||||||
},
|
},
|
||||||
"level3": {
|
"level3": {
|
||||||
"parametro_especifico_1": true,
|
"parametro_especifico_1": true
|
||||||
"parametro_especifico_2": 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ejemplo Completo de Implementación
|
## 4. Manejo de Errores
|
||||||
|
|
||||||
```python
|
`load_configuration()` está diseñada para ser robusta:
|
||||||
"""
|
- Si `script_config.json` no se encuentra, retorna un diccionario vacío `{}`.
|
||||||
Script de ejemplo que demuestra el uso completo de load_configuration()
|
- Si el JSON es inválido, imprime un error y también retorna `{}`.
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
**Siempre** comprueba si el diccionario devuelto está vacío para manejar estos casos de forma segura en tu script.
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Configuración del path
|
## 5. Jerarquía de Archivos de Configuración
|
||||||
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
|
|
||||||
|
|
||||||
|
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():
|
A continuación se describe la finalidad y ubicación de cada archivo clave.
|
||||||
print("=== Cargando Configuración ===")
|
|
||||||
|
|
||||||
# Cargar configuraciones
|
|
||||||
configs = load_configuration()
|
|
||||||
|
|
||||||
# Verificar que se cargó correctamente
|
|
||||||
if not configs:
|
|
||||||
print("Error: No se pudo cargar la configuración")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Obtener configuraciones
|
|
||||||
working_directory = configs.get("working_directory", "")
|
|
||||||
level1_config = configs.get("level1", {})
|
|
||||||
level2_config = configs.get("level2", {})
|
|
||||||
level3_config = configs.get("level3", {})
|
|
||||||
|
|
||||||
# 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))
|
|
||||||
|
|
||||||
# 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")
|
|
||||||
|
|
||||||
print(f"Directorio de salida SCL: {scl_output_dir}")
|
|
||||||
print(f"Directorio de salida XREF: {xref_output_dir}")
|
|
||||||
|
|
||||||
|
### Archivos de Valores (Parámetros)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
Contienen los datos y variables que utilizará el script. La configuración se superpone en el siguiente orden: `Nivel 1 < Nivel 2 < Nivel 3`.
|
||||||
main()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manejo de Errores
|
- **`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`.
|
||||||
|
|
||||||
La función `load_configuration()` maneja automáticamente los siguientes casos:
|
- **`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"`.
|
||||||
|
|
||||||
1. **Archivo no encontrado**: Retorna un diccionario vacío `{}`
|
- **`work_dir.json` (Nivel 3 - Directorio de Trabajo)**
|
||||||
2. **JSON inválido**: Retorna un diccionario vacío y muestra un mensaje de error
|
- **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
|
||||||
3. **Errores de lectura**: Retorna un diccionario vacío y muestra un mensaje de error
|
- **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"`.
|
||||||
|
|
||||||
### Verificación de Configuración Válida
|
### Archivos de Esquema (Definiciones de Estructura)
|
||||||
|
|
||||||
```python
|
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.
|
||||||
configs = load_configuration()
|
|
||||||
|
|
||||||
# Verificar que se cargó correctamente
|
- **`esquema_group.json`**
|
||||||
if not configs:
|
- **Ubicación:** Raíz del directorio del grupo.
|
||||||
print("Advertencia: No se pudo cargar la configuración, usando valores por defecto")
|
- **Utilidad:** Define la estructura del `script_config.json` del grupo.
|
||||||
working_directory = "."
|
|
||||||
else:
|
|
||||||
working_directory = configs.get("working_directory", ".")
|
|
||||||
|
|
||||||
# Verificar directorio de trabajo
|
- **`esquema_work.json`**
|
||||||
if not os.path.exists(working_directory):
|
- **Ubicación:** Raíz del directorio del grupo.
|
||||||
print(f"Error: El directorio de trabajo no existe: {working_directory}")
|
- **Utilidad:** Define la estructura del `work_dir.json`.
|
||||||
return
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mejores Prácticas
|
## 6. Documentación de Scripts para el Launcher
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.
|
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
|
@ -2,220 +2,132 @@
|
||||||
|
|
||||||
## Introducción
|
## 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
|
```python
|
||||||
import os
|
import os
|
||||||
import sys
|
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(
|
script_root = os.path.dirname(
|
||||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||||
)
|
)
|
||||||
sys.path.append(script_root)
|
sys.path.append(script_root)
|
||||||
|
|
||||||
# Importar la función de configuración
|
# Importar la función
|
||||||
from backend.script_utils import load_configuration
|
from backend.script_utils import load_configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
**Nota:** El número de `os.path.dirname()` anidados depende de la profundidad del script:
|
## 2. Cargar la Configuración
|
||||||
- Scripts en `/backend/script_groups/grupo/`: 4 niveles
|
|
||||||
- Scripts en `/backend/`: 2 niveles
|
|
||||||
|
|
||||||
### 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:**
|
### Ejemplo de Uso
|
||||||
```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
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def main():
|
def main():
|
||||||
# Cargar configuraciones
|
# Cargar configuraciones del archivo script_config.json
|
||||||
configs = load_configuration()
|
configs = load_configuration()
|
||||||
|
|
||||||
# Obtener el directorio de trabajo
|
# Es buena práctica verificar si la configuración se cargó
|
||||||
working_directory = configs.get("working_directory", "")
|
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", {})
|
level1_config = configs.get("level1", {})
|
||||||
level2_config = configs.get("level2", {})
|
level2_config = configs.get("level2", {})
|
||||||
level3_config = configs.get("level3", {})
|
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")
|
scl_output_dir = level2_config.get("scl_output_dir", "scl_output")
|
||||||
xref_output_dir = level2_config.get("xref_output_dir", "xref_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__":
|
if __name__ == "__main__":
|
||||||
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
|
```json
|
||||||
{
|
{
|
||||||
"working_directory": "/ruta/al/directorio/de/trabajo",
|
"working_directory": "/ruta/al/directorio/de/trabajo",
|
||||||
"level1": {
|
"level1": {
|
||||||
"parametro_global_1": "valor1",
|
"parametro_global_1": "valor1"
|
||||||
"parametro_global_2": "valor2"
|
|
||||||
},
|
},
|
||||||
"level2": {
|
"level2": {
|
||||||
"scl_output_dir": "scl_output",
|
"scl_output_dir": "scl_output",
|
||||||
"xref_output_dir": "xref_output",
|
"xref_output_dir": "xref_output"
|
||||||
"xref_source_subdir": "source",
|
|
||||||
"aggregated_filename": "full_project_representation.md"
|
|
||||||
},
|
},
|
||||||
"level3": {
|
"level3": {
|
||||||
"parametro_especifico_1": true,
|
"parametro_especifico_1": true
|
||||||
"parametro_especifico_2": 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ejemplo Completo de Implementación
|
## 4. Manejo de Errores
|
||||||
|
|
||||||
```python
|
`load_configuration()` está diseñada para ser robusta:
|
||||||
"""
|
- Si `script_config.json` no se encuentra, retorna un diccionario vacío `{}`.
|
||||||
Script de ejemplo que demuestra el uso completo de load_configuration()
|
- Si el JSON es inválido, imprime un error y también retorna `{}`.
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
**Siempre** comprueba si el diccionario devuelto está vacío para manejar estos casos de forma segura en tu script.
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Configuración del path
|
## 5. Jerarquía de Archivos de Configuración
|
||||||
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
|
|
||||||
|
|
||||||
|
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():
|
A continuación se describe la finalidad y ubicación de cada archivo clave.
|
||||||
print("=== Cargando Configuración ===")
|
|
||||||
|
|
||||||
# Cargar configuraciones
|
|
||||||
configs = load_configuration()
|
|
||||||
|
|
||||||
# Verificar que se cargó correctamente
|
|
||||||
if not configs:
|
|
||||||
print("Error: No se pudo cargar la configuración")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Obtener configuraciones
|
|
||||||
working_directory = configs.get("working_directory", "")
|
|
||||||
level1_config = configs.get("level1", {})
|
|
||||||
level2_config = configs.get("level2", {})
|
|
||||||
level3_config = configs.get("level3", {})
|
|
||||||
|
|
||||||
# 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))
|
|
||||||
|
|
||||||
# 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")
|
|
||||||
|
|
||||||
print(f"Directorio de salida SCL: {scl_output_dir}")
|
|
||||||
print(f"Directorio de salida XREF: {xref_output_dir}")
|
|
||||||
|
|
||||||
|
### Archivos de Valores (Parámetros)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
Contienen los datos y variables que utilizará el script. La configuración se superpone en el siguiente orden: `Nivel 1 < Nivel 2 < Nivel 3`.
|
||||||
main()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manejo de Errores
|
- **`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`.
|
||||||
|
|
||||||
La función `load_configuration()` maneja automáticamente los siguientes casos:
|
- **`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"`.
|
||||||
|
|
||||||
1. **Archivo no encontrado**: Retorna un diccionario vacío `{}`
|
- **`work_dir.json` (Nivel 3 - Directorio de Trabajo)**
|
||||||
2. **JSON inválido**: Retorna un diccionario vacío y muestra un mensaje de error
|
- **Ubicación:** Dentro del directorio de trabajo que el script va a procesar.
|
||||||
3. **Errores de lectura**: Retorna un diccionario vacío y muestra un mensaje de error
|
- **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"`.
|
||||||
|
|
||||||
### Verificación de Configuración Válida
|
### Archivos de Esquema (Definiciones de Estructura)
|
||||||
|
|
||||||
```python
|
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.
|
||||||
configs = load_configuration()
|
|
||||||
|
|
||||||
# Verificar que se cargó correctamente
|
- **`esquema_group.json`**
|
||||||
if not configs:
|
- **Ubicación:** Raíz del directorio del grupo.
|
||||||
print("Advertencia: No se pudo cargar la configuración, usando valores por defecto")
|
- **Utilidad:** Define la estructura del `script_config.json` del grupo.
|
||||||
working_directory = "."
|
|
||||||
else:
|
|
||||||
working_directory = configs.get("working_directory", ".")
|
|
||||||
|
|
||||||
# Verificar directorio de trabajo
|
- **`esquema_work.json`**
|
||||||
if not os.path.exists(working_directory):
|
- **Ubicación:** Raíz del directorio del grupo.
|
||||||
print(f"Error: El directorio de trabajo no existe: {working_directory}")
|
- **Utilidad:** Define la estructura del `work_dir.json`.
|
||||||
return
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mejores Prácticas
|
## 6. Documentación de Scripts para el Launcher
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.
|
El sistema de launcher utiliza archivos JSON para mostrar información sobre los grupos de scripts y scripts individuales en la interfaz web.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
|
@ -1407,7 +1407,7 @@ def main():
|
||||||
if not configs:
|
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 = "./"
|
working_directory = "./"
|
||||||
scl_output_dir = "scl"
|
scl_output_dir = "TwinCat/scl"
|
||||||
debug_mode = True
|
debug_mode = True
|
||||||
show_optimizations = True
|
show_optimizations = True
|
||||||
show_generated_code = False
|
show_generated_code = False
|
||||||
|
|
|
@ -28,436 +28,493 @@ sys.path.append(script_root)
|
||||||
from backend.script_utils import load_configuration
|
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"""
|
"""Carga las adaptaciones de TIA Portal desde el archivo markdown"""
|
||||||
full_file_path = os.path.join(working_directory, file_path)
|
full_file_path = os.path.join(working_directory, file_path)
|
||||||
print(f"Cargando adaptaciones de TIA Portal desde: {full_file_path}")
|
print(f"Cargando adaptaciones de TIA Portal desde: {full_file_path}")
|
||||||
|
|
||||||
adaptations = {}
|
adaptations = {}
|
||||||
|
|
||||||
if not os.path.exists(full_file_path):
|
if not os.path.exists(full_file_path):
|
||||||
print(f"⚠️ Archivo {full_file_path} no encontrado")
|
print(f"⚠️ Archivo {full_file_path} no encontrado")
|
||||||
return adaptations
|
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()
|
content = f.read()
|
||||||
|
|
||||||
# Patrones mejorados para diferentes tipos de IOs
|
# Patrones mejorados para diferentes tipos de IOs
|
||||||
patterns = [
|
patterns = [
|
||||||
# Digitales: E0.0, A0.0
|
# 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
|
# 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
|
# 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:
|
for pattern in patterns:
|
||||||
matches = re.findall(pattern, content, re.MULTILINE)
|
matches = re.findall(pattern, content, re.MULTILINE)
|
||||||
for io_addr, master_tag in matches:
|
for io_addr, master_tag in matches:
|
||||||
io_addr = io_addr.strip()
|
io_addr = io_addr.strip()
|
||||||
master_tag = master_tag.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
|
adaptations[io_addr] = master_tag
|
||||||
print(f" 📍 {io_addr} → {master_tag}")
|
print(f" 📍 {io_addr} → {master_tag}")
|
||||||
|
|
||||||
print(f"✅ Cargadas {len(adaptations)} adaptaciones de TIA Portal")
|
print(f"✅ Cargadas {len(adaptations)} adaptaciones de TIA Portal")
|
||||||
return adaptations
|
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 %"""
|
"""Escanea archivos TwinCAT para encontrar definiciones de variables AT %"""
|
||||||
full_directory = os.path.join(working_directory, directory)
|
full_directory = os.path.join(working_directory, directory)
|
||||||
print(f"\n🔍 Escaneando definiciones TwinCAT en: {full_directory}")
|
print(f"\n🔍 Escaneando definiciones TwinCAT en: {full_directory}")
|
||||||
|
|
||||||
definitions = {}
|
definitions = {}
|
||||||
|
|
||||||
if not os.path.exists(full_directory):
|
if not os.path.exists(full_directory):
|
||||||
print(f"⚠️ Directorio {full_directory} no encontrado")
|
print(f"⚠️ Directorio {full_directory} no encontrado")
|
||||||
return definitions
|
return definitions
|
||||||
|
|
||||||
# Patrones para definiciones AT %
|
# Patrones para definiciones AT %
|
||||||
definition_patterns = [
|
definition_patterns = [
|
||||||
r'(\w+)\s+AT\s+%([IQ][XWB]\d+(?:\.\d+)?)\s*:\s*(\w+);', # Activas
|
# Solo se buscan definiciones activas. Se ignoran las comentadas.
|
||||||
r'(\w+)\s+\(\*\s*AT\s+%([IQ][XWB]\d+(?:\.\d+)?)\s*\*\)\s*:\s*(\w+);', # 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}")
|
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()
|
content = f.read()
|
||||||
|
|
||||||
for pattern in definition_patterns:
|
for pattern in definition_patterns:
|
||||||
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
|
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
|
||||||
for var_name, io_addr, data_type in matches:
|
for var_name, io_addr, data_type in matches:
|
||||||
var_name = var_name.strip()
|
var_name = var_name.strip()
|
||||||
io_addr = io_addr.strip()
|
io_addr = io_addr.strip()
|
||||||
data_type = data_type.strip()
|
data_type = data_type.strip()
|
||||||
|
|
||||||
definitions[var_name] = {
|
definitions[var_name] = {
|
||||||
'address': io_addr,
|
"address": io_addr,
|
||||||
'type': data_type,
|
"type": data_type,
|
||||||
'file': file_path.name,
|
"file": file_path.name,
|
||||||
'definition_line': content[:content.find(var_name)].count('\n') + 1
|
"definition_line": content[: content.find(var_name)].count("\n")
|
||||||
|
+ 1,
|
||||||
}
|
}
|
||||||
print(f" 🔗 {var_name} AT %{io_addr} : {data_type}")
|
print(f" 🔗 {var_name} AT %{io_addr} : {data_type}")
|
||||||
|
|
||||||
print(f"✅ Encontradas {len(definitions)} definiciones TwinCAT")
|
print(f"✅ Encontradas {len(definitions)} definiciones TwinCAT")
|
||||||
return definitions
|
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"""
|
"""Escanea archivos TwinCAT para encontrar uso de variables"""
|
||||||
full_directory = os.path.join(working_directory, directory)
|
full_directory = os.path.join(working_directory, directory)
|
||||||
print(f"\n🔍 Escaneando uso de variables TwinCAT en: {full_directory}")
|
print(f"\n🔍 Escaneando uso de variables TwinCAT en: {full_directory}")
|
||||||
|
|
||||||
usage_data = defaultdict(list)
|
usage_data = defaultdict(list)
|
||||||
|
|
||||||
if not os.path.exists(full_directory):
|
if not os.path.exists(full_directory):
|
||||||
print(f"⚠️ Directorio {full_directory} no encontrado")
|
print(f"⚠️ Directorio {full_directory} no encontrado")
|
||||||
return usage_data
|
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}")
|
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()
|
lines = f.readlines()
|
||||||
|
|
||||||
for line_num, line in enumerate(lines, 1):
|
for line_num, line in enumerate(lines, 1):
|
||||||
# Buscar variables que empiecen con DI_, DO_, AI_, AO_
|
# 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:
|
for var_name in var_matches:
|
||||||
usage_data[var_name].append({
|
usage_data[var_name].append(
|
||||||
'file': file_path.name,
|
{
|
||||||
'line': line_num,
|
"file": file_path.name,
|
||||||
'context': line.strip()[:100] + ('...' if len(line.strip()) > 100 else '')
|
"line": line_num,
|
||||||
})
|
"context": line.strip()[:100]
|
||||||
|
+ ("..." if len(line.strip()) > 100 else ""),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
print(f"✅ Encontrado uso de {len(usage_data)} variables diferentes")
|
print(f"✅ Encontrado uso de {len(usage_data)} variables diferentes")
|
||||||
return usage_data
|
return usage_data
|
||||||
|
|
||||||
|
|
||||||
def convert_tia_to_twincat(tia_addr):
|
def convert_tia_to_twincat(tia_addr):
|
||||||
"""Convierte direcciones TIA Portal a formato TwinCAT"""
|
"""Convierte direcciones TIA Portal a formato TwinCAT"""
|
||||||
conversions = []
|
conversions = []
|
||||||
|
|
||||||
# Digitales
|
# Digitales
|
||||||
if re.match(r'^E\d+\.\d+$', tia_addr): # E0.0 → IX0.0
|
if re.match(r"^E\d+\.\d+$", tia_addr): # E0.0 → IX0.0
|
||||||
twincat_addr = tia_addr.replace('E', 'IX')
|
twincat_addr = tia_addr.replace("E", "IX")
|
||||||
conversions.append(twincat_addr)
|
conversions.append(twincat_addr)
|
||||||
elif re.match(r'^A\d+\.\d+$', tia_addr): # A0.0 → QX0.0
|
elif re.match(r"^A\d+\.\d+$", tia_addr): # A0.0 → QX0.0
|
||||||
twincat_addr = tia_addr.replace('A', 'QX')
|
twincat_addr = tia_addr.replace("A", "QX")
|
||||||
conversions.append(twincat_addr)
|
conversions.append(twincat_addr)
|
||||||
|
|
||||||
# Analógicos
|
# Analógicos
|
||||||
elif re.match(r'^PEW\d+$', tia_addr): # PEW100 → IW100
|
elif re.match(r"^PEW\d+$", tia_addr): # PEW100 → IW100
|
||||||
twincat_addr = tia_addr.replace('PEW', 'IW')
|
twincat_addr = tia_addr.replace("PEW", "IW")
|
||||||
conversions.append(twincat_addr)
|
conversions.append(twincat_addr)
|
||||||
elif re.match(r'^PAW\d+$', tia_addr): # PAW100 → QW100
|
elif re.match(r"^PAW\d+$", tia_addr): # PAW100 → QW100
|
||||||
twincat_addr = tia_addr.replace('PAW', 'QW')
|
twincat_addr = tia_addr.replace("PAW", "QW")
|
||||||
conversions.append(twincat_addr)
|
conversions.append(twincat_addr)
|
||||||
|
|
||||||
# Profibus
|
# Profibus
|
||||||
elif re.match(r'^EW\s+\d+$', tia_addr): # EW 1234 → IB1234
|
elif re.match(r"^EW\s+\d+$", tia_addr): # EW 1234 → IB1234
|
||||||
addr_num = re.search(r'\d+', tia_addr).group()
|
addr_num = re.search(r"\d+", tia_addr).group()
|
||||||
conversions.append(f'IB{addr_num}')
|
conversions.append(f"IB{addr_num}")
|
||||||
elif re.match(r'^AW\s+\d+$', tia_addr): # AW 1234 → QB1234
|
elif re.match(r"^AW\s+\d+$", tia_addr): # AW 1234 → QB1234
|
||||||
addr_num = re.search(r'\d+', tia_addr).group()
|
addr_num = re.search(r"\d+", tia_addr).group()
|
||||||
conversions.append(f'QB{addr_num}')
|
conversions.append(f"QB{addr_num}")
|
||||||
|
|
||||||
return conversions
|
return conversions
|
||||||
|
|
||||||
|
|
||||||
def find_variable_by_address(definitions, target_address):
|
def find_variable_by_address(definitions, target_address):
|
||||||
"""Busca variable por dirección exacta"""
|
"""Busca variable por dirección exacta"""
|
||||||
for var_name, info in definitions.items():
|
for var_name, info in definitions.items():
|
||||||
if info['address'] == target_address:
|
if info["address"] == target_address:
|
||||||
return var_name, info
|
return var_name, info
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def find_variable_by_name_similarity(definitions, usage_data, master_tag):
|
def find_variable_by_name_similarity(definitions, usage_data, master_tag):
|
||||||
"""Busca variables por similitud de nombre"""
|
"""Busca variables por similitud de nombre"""
|
||||||
candidates = []
|
candidates = []
|
||||||
|
|
||||||
# Limpiar el master tag para comparación
|
# 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
|
# Buscar en definiciones
|
||||||
for var_name, info in definitions.items():
|
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:
|
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
|
# Buscar en uso
|
||||||
for var_name in usage_data.keys():
|
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:
|
if clean_master in clean_var or clean_var in clean_master:
|
||||||
# Intentar encontrar la definición de esta variable
|
# Intentar encontrar la definición de esta variable
|
||||||
var_info = definitions.get(var_name)
|
var_info = definitions.get(var_name)
|
||||||
if not var_info:
|
if not var_info:
|
||||||
var_info = {'address': 'Unknown', 'type': 'Unknown', 'file': 'Not found'}
|
var_info = {
|
||||||
candidates.append((var_name, var_info, 'usage'))
|
"address": "Unknown",
|
||||||
|
"type": "Unknown",
|
||||||
|
"file": "Not found",
|
||||||
|
}
|
||||||
|
candidates.append((var_name, var_info, "usage"))
|
||||||
|
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
def analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage):
|
def analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage):
|
||||||
"""Analiza las correlaciones entre TIA Portal y TwinCAT"""
|
"""Analiza las correlaciones entre TIA Portal y TwinCAT"""
|
||||||
print(f"\n📊 Analizando correlaciones...")
|
print(f"\n📊 Analizando correlaciones...")
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
matches_found = 0
|
matches_found = 0
|
||||||
|
|
||||||
for tia_addr, master_tag in tia_adaptations.items():
|
for tia_addr, master_tag in tia_adaptations.items():
|
||||||
result = {
|
result = {
|
||||||
'tia_address': tia_addr,
|
"tia_address": tia_addr,
|
||||||
'master_tag': master_tag,
|
"master_tag": master_tag,
|
||||||
'twincat_variable': None,
|
"twincat_variable": None,
|
||||||
'twincat_address': None,
|
"twincat_address": None,
|
||||||
'twincat_type': None,
|
"twincat_type": None,
|
||||||
'match_type': None,
|
"match_type": None,
|
||||||
'definition_file': None,
|
"definition_file": None,
|
||||||
'usage_files': [],
|
"usage_files": [],
|
||||||
'usage_count': 0,
|
"usage_count": 0,
|
||||||
'confidence': 'Low'
|
"confidence": "Low",
|
||||||
}
|
}
|
||||||
|
|
||||||
# 1. Buscar por conversión directa de dirección
|
# 1. Buscar por conversión directa de dirección
|
||||||
twincat_addresses = convert_tia_to_twincat(tia_addr)
|
twincat_addresses = convert_tia_to_twincat(tia_addr)
|
||||||
var_found = False
|
var_found = False
|
||||||
|
|
||||||
for twincat_addr in twincat_addresses:
|
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:
|
if var_name:
|
||||||
result.update({
|
result.update(
|
||||||
'twincat_variable': var_name,
|
{
|
||||||
'twincat_address': var_info['address'],
|
"twincat_variable": var_name,
|
||||||
'twincat_type': var_info['type'],
|
"twincat_address": var_info["address"],
|
||||||
'match_type': 'Address Match',
|
"twincat_type": var_info["type"],
|
||||||
'definition_file': var_info['file'],
|
"match_type": "Address Match",
|
||||||
'confidence': 'High'
|
"definition_file": var_info["file"],
|
||||||
})
|
"confidence": "High",
|
||||||
|
}
|
||||||
|
)
|
||||||
var_found = True
|
var_found = True
|
||||||
matches_found += 1
|
matches_found += 1
|
||||||
break
|
break
|
||||||
|
|
||||||
# 2. Si no se encontró por dirección, buscar por nombre
|
# 2. Si no se encontró por dirección, buscar por nombre
|
||||||
if not var_found:
|
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:
|
if candidates:
|
||||||
# Tomar el mejor candidato
|
# Tomar el mejor candidato
|
||||||
best_candidate = candidates[0]
|
best_candidate = candidates[0]
|
||||||
var_name, var_info, source = best_candidate
|
var_name, var_info, source = best_candidate
|
||||||
|
|
||||||
result.update({
|
result.update(
|
||||||
'twincat_variable': var_name,
|
{
|
||||||
'twincat_address': var_info.get('address', 'Unknown'),
|
"twincat_variable": var_name,
|
||||||
'twincat_type': var_info.get('type', 'Unknown'),
|
"twincat_address": var_info.get("address", "Unknown"),
|
||||||
'match_type': f'Name Similarity ({source})',
|
"twincat_type": var_info.get("type", "Unknown"),
|
||||||
'definition_file': var_info.get('file', 'Unknown'),
|
"match_type": f"Name Similarity ({source})",
|
||||||
'confidence': 'Medium'
|
"definition_file": var_info.get("file", "Unknown"),
|
||||||
})
|
"confidence": "Medium",
|
||||||
|
}
|
||||||
|
)
|
||||||
matches_found += 1
|
matches_found += 1
|
||||||
|
|
||||||
# 3. Buscar información de uso
|
# 3. Buscar información de uso
|
||||||
if result['twincat_variable']:
|
if result["twincat_variable"]:
|
||||||
var_name = result['twincat_variable']
|
var_name = result["twincat_variable"]
|
||||||
if var_name in twincat_usage:
|
if var_name in twincat_usage:
|
||||||
usage_info = twincat_usage[var_name]
|
usage_info = twincat_usage[var_name]
|
||||||
result['usage_files'] = list(set([u['file'] for u in usage_info]))
|
result["usage_files"] = list(set([u["file"] for u in usage_info]))
|
||||||
result['usage_count'] = len(usage_info)
|
result["usage_count"] = len(usage_info)
|
||||||
|
|
||||||
results.append(result)
|
results.append(result)
|
||||||
|
|
||||||
# Log del progreso
|
# Log del progreso
|
||||||
status = "✅" if result['twincat_variable'] else "❌"
|
status = "✅" if result["twincat_variable"] else "❌"
|
||||||
print(f" {status} {tia_addr} → {master_tag}")
|
print(f" {status} {tia_addr} → {master_tag}")
|
||||||
if result['twincat_variable']:
|
if result["twincat_variable"]:
|
||||||
print(f" 🔗 {result['twincat_variable']} AT %{result['twincat_address']}")
|
print(
|
||||||
if result['usage_count'] > 0:
|
f" 🔗 {result['twincat_variable']} AT %{result['twincat_address']}"
|
||||||
print(f" 📝 Usado en {result['usage_count']} lugares: {', '.join(result['usage_files'])}")
|
)
|
||||||
|
if result["usage_count"] > 0:
|
||||||
print(f"\n🎯 Resumen: {matches_found}/{len(tia_adaptations)} variables correlacionadas ({matches_found/len(tia_adaptations)*100:.1f}%)")
|
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
|
return results
|
||||||
|
|
||||||
|
|
||||||
def create_results_directory(working_directory):
|
def create_results_directory(working_directory):
|
||||||
"""Crea el directorio de resultados si no existe"""
|
"""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)
|
results_dir.mkdir(exist_ok=True)
|
||||||
print(f"📁 Directorio de resultados: {results_dir.absolute()}")
|
print(f"📁 Directorio de resultados: {results_dir.absolute()}")
|
||||||
return results_dir
|
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"""
|
"""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}")
|
print(f"\n📄 Generando archivo JSON: {full_output_file}")
|
||||||
|
|
||||||
json_data = {
|
json_data = {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"generated_at": pd.Timestamp.now().isoformat(),
|
"generated_at": pd.Timestamp.now().isoformat(),
|
||||||
"project": "E5.007560 - Modifica O&U - SAE235",
|
"project": "E5.007560 - Modifica O&U - SAE235",
|
||||||
"total_adaptations": len(results),
|
"total_adaptations": len(results),
|
||||||
"matched_variables": len([r for r in results if r['twincat_variable']]),
|
"matched_variables": len([r for r in results if r["twincat_variable"]]),
|
||||||
"high_confidence": len([r for r in results if r['confidence'] == 'High']),
|
"high_confidence": len([r for r in results if r["confidence"] == "High"]),
|
||||||
"medium_confidence": len([r for r in results if r['confidence'] == 'Medium'])
|
"medium_confidence": len(
|
||||||
|
[r for r in results if r["confidence"] == "Medium"]
|
||||||
|
),
|
||||||
},
|
},
|
||||||
"adaptations": []
|
"adaptations": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
adaptation = {
|
adaptation = {
|
||||||
"tia_portal": {
|
"tia_portal": {
|
||||||
"address": result['tia_address'],
|
"address": result["tia_address"],
|
||||||
"tag": result['master_tag']
|
"tag": result["master_tag"],
|
||||||
},
|
},
|
||||||
"twincat": {
|
"twincat": {
|
||||||
"variable": result['twincat_variable'],
|
"variable": result["twincat_variable"],
|
||||||
"address": result['twincat_address'],
|
"address": result["twincat_address"],
|
||||||
"data_type": result['twincat_type'],
|
"data_type": result["twincat_type"],
|
||||||
"definition_file": result['definition_file']
|
"definition_file": result["definition_file"],
|
||||||
},
|
},
|
||||||
"correlation": {
|
"correlation": {
|
||||||
"match_type": result['match_type'],
|
"match_type": result["match_type"],
|
||||||
"confidence": result['confidence'],
|
"confidence": result["confidence"],
|
||||||
"found": result['twincat_variable'] is not None
|
"found": result["twincat_variable"] is not None,
|
||||||
},
|
},
|
||||||
"usage": {
|
"usage": {
|
||||||
"usage_count": result['usage_count'],
|
"usage_count": result["usage_count"],
|
||||||
"usage_files": result['usage_files']
|
"usage_files": result["usage_files"],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
json_data["adaptations"].append(adaptation)
|
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)
|
json.dump(json_data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
print(f"✅ Archivo JSON generado: {full_output_file}")
|
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"""
|
"""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}")
|
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("# 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
|
# Estadísticas
|
||||||
total = len(results)
|
total = len(results)
|
||||||
matched = len([r for r in results if r['twincat_variable']])
|
matched = len([r for r in results if r["twincat_variable"]])
|
||||||
high_conf = len([r for r in results if r['confidence'] == 'High'])
|
high_conf = len([r for r in results if r["confidence"] == "High"])
|
||||||
medium_conf = len([r for r in results if r['confidence'] == 'Medium'])
|
medium_conf = len([r for r in results if r["confidence"] == "Medium"])
|
||||||
|
|
||||||
f.write("## 📊 Estadísticas Generales\n\n")
|
f.write("## 📊 Estadísticas Generales\n\n")
|
||||||
f.write(f"- **Total adaptaciones procesadas:** {total}\n")
|
f.write(f"- **Total adaptaciones procesadas:** {total}\n")
|
||||||
f.write(f"- **Variables encontradas:** {matched} ({matched/total*100:.1f}%)\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 alta confianza:** {high_conf}\n")
|
||||||
f.write(f"- **Coincidencias de media confianza:** {medium_conf}\n\n")
|
f.write(f"- **Coincidencias de media confianza:** {medium_conf}\n\n")
|
||||||
|
|
||||||
# Tabla de variables correlacionadas exitosamente
|
# Tabla de variables correlacionadas exitosamente
|
||||||
f.write("## ✅ Variables Correlacionadas Exitosamente\n\n")
|
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:
|
if matched_results:
|
||||||
# Encabezado de la tabla
|
# 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(
|
||||||
f.write("|-------------|---------|------------------|-----------------|------|--------|-----------|--------------|-----|---------------|\n")
|
"| TIA Address | TIA Tag | TwinCAT Variable | TwinCAT Address | Tipo | Método | Confianza | Archivo Def. | Uso | Archivos Uso |\n"
|
||||||
|
)
|
||||||
|
f.write(
|
||||||
|
"|-------------|---------|------------------|-----------------|------|--------|-----------|--------------|-----|---------------|\n"
|
||||||
|
)
|
||||||
|
|
||||||
# Filas de datos
|
# Filas de datos
|
||||||
for result in matched_results:
|
for result in matched_results:
|
||||||
usage_files_str = ', '.join(result['usage_files'][:3]) # Limitar a 3 archivos
|
usage_files_str = ", ".join(
|
||||||
if len(result['usage_files']) > 3:
|
result["usage_files"][:3]
|
||||||
|
) # Limitar a 3 archivos
|
||||||
|
if len(result["usage_files"]) > 3:
|
||||||
usage_files_str += "..."
|
usage_files_str += "..."
|
||||||
|
|
||||||
f.write(f"| {result['tia_address']} | "
|
f.write(
|
||||||
f"`{result['master_tag']}` | "
|
f"| {result['tia_address']} | "
|
||||||
f"`{result['twincat_variable']}` | "
|
f"`{result['master_tag']}` | "
|
||||||
f"`%{result['twincat_address']}` | "
|
f"`{result['twincat_variable']}` | "
|
||||||
f"`{result['twincat_type']}` | "
|
f"`%{result['twincat_address']}` | "
|
||||||
f"{result['match_type']} | "
|
f"`{result['twincat_type']}` | "
|
||||||
f"{result['confidence']} | "
|
f"{result['match_type']} | "
|
||||||
f"{result['definition_file']} | "
|
f"{result['confidence']} | "
|
||||||
f"{result['usage_count']} | "
|
f"{result['definition_file']} | "
|
||||||
f"{usage_files_str} |\n")
|
f"{result['usage_count']} | "
|
||||||
|
f"{usage_files_str} |\n"
|
||||||
|
)
|
||||||
|
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
# Tabla de variables no encontradas
|
# Tabla de variables no encontradas
|
||||||
f.write("## ❌ Variables No Encontradas\n\n")
|
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:
|
if unmatched_results:
|
||||||
f.write("| TIA Address | TIA Tag |\n")
|
f.write("| TIA Address | TIA Tag |\n")
|
||||||
f.write("|-------------|----------|\n")
|
f.write("|-------------|----------|\n")
|
||||||
|
|
||||||
for result in unmatched_results:
|
for result in unmatched_results:
|
||||||
f.write(f"| {result['tia_address']} | `{result['master_tag']}` |\n")
|
f.write(f"| {result['tia_address']} | `{result['master_tag']}` |\n")
|
||||||
|
|
||||||
f.write(f"\n**Total no encontradas:** {len(unmatched_results)}\n\n")
|
f.write(f"\n**Total no encontradas:** {len(unmatched_results)}\n\n")
|
||||||
|
|
||||||
# Recomendaciones
|
# Recomendaciones
|
||||||
f.write("## 💡 Recomendaciones\n\n")
|
f.write("## 💡 Recomendaciones\n\n")
|
||||||
f.write("1. **Variables de alta confianza** pueden migrarse directamente\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("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")
|
f.write("4. Variables con uso extensivo son prioritarias para la migración\n\n")
|
||||||
|
|
||||||
# Resumen por confianza
|
# Resumen por confianza
|
||||||
f.write("## 📈 Distribución por Confianza\n\n")
|
f.write("## 📈 Distribución por Confianza\n\n")
|
||||||
f.write("| Nivel de Confianza | Cantidad | Porcentaje |\n")
|
f.write("| Nivel de Confianza | Cantidad | Porcentaje |\n")
|
||||||
f.write("|--------------------|----------|------------|\n")
|
f.write("|--------------------|----------|------------|\n")
|
||||||
f.write(f"| Alta | {high_conf} | {high_conf/total*100:.1f}% |\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"| 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}")
|
print(f"✅ Reporte detallado generado: {full_output_file}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("🚀 Iniciando análisis detallado de adaptación de IOs TwinCAT ↔ TIA Portal")
|
print("🚀 Iniciando análisis detallado de adaptación de IOs TwinCAT ↔ TIA Portal")
|
||||||
print("=" * 80)
|
print("=" * 80)
|
||||||
|
|
||||||
# Cargar configuración
|
# Cargar configuración
|
||||||
configs = load_configuration()
|
configs = load_configuration()
|
||||||
|
|
||||||
# Verificar que se cargó correctamente
|
# Verificar que se cargó correctamente
|
||||||
if not configs:
|
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 = "./"
|
working_directory = "./"
|
||||||
else:
|
else:
|
||||||
working_directory = configs.get("working_directory", "./")
|
working_directory = configs.get("working_directory", "./")
|
||||||
|
|
||||||
# Verificar directorio de trabajo
|
# Verificar directorio de trabajo
|
||||||
if not os.path.exists(working_directory):
|
if not os.path.exists(working_directory):
|
||||||
print(f"Error: El directorio de trabajo no existe: {working_directory}")
|
print(f"Error: El directorio de trabajo no existe: {working_directory}")
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"📁 Directorio de trabajo: {working_directory}")
|
print(f"📁 Directorio de trabajo: {working_directory}")
|
||||||
|
|
||||||
# Crear directorio de resultados
|
# Crear directorio de resultados
|
||||||
results_dir = create_results_directory(working_directory)
|
results_dir = create_results_directory(working_directory)
|
||||||
|
|
||||||
# Cargar datos
|
# Cargar datos
|
||||||
tia_adaptations = load_tiaportal_adaptations(working_directory)
|
tia_adaptations = load_tiaportal_adaptations(working_directory)
|
||||||
twincat_definitions = scan_twincat_definitions(working_directory)
|
twincat_definitions = scan_twincat_definitions(working_directory)
|
||||||
twincat_usage = scan_twincat_usage(working_directory)
|
twincat_usage = scan_twincat_usage(working_directory)
|
||||||
|
|
||||||
# Analizar correlaciones
|
# Analizar correlaciones
|
||||||
results = analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage)
|
results = analyze_adaptations(tia_adaptations, twincat_definitions, twincat_usage)
|
||||||
|
|
||||||
# Generar reportes en el directorio de resultados
|
# Generar reportes en el directorio de resultados
|
||||||
generate_detailed_report(results, working_directory)
|
generate_detailed_report(results, working_directory)
|
||||||
generate_json_output(results, working_directory)
|
generate_json_output(results, working_directory)
|
||||||
|
|
||||||
# Generar CSV para análisis adicional
|
# Generar CSV para análisis adicional
|
||||||
df = pd.DataFrame(results)
|
df = pd.DataFrame(results)
|
||||||
csv_file = results_dir / 'io_detailed_analysis.csv'
|
csv_file = results_dir / "io_detailed_analysis.csv"
|
||||||
df.to_csv(csv_file, index=False, encoding='utf-8')
|
df.to_csv(csv_file, index=False, encoding="utf-8")
|
||||||
print(f"✅ Datos exportados a CSV: {csv_file}")
|
print(f"✅ Datos exportados a CSV: {csv_file}")
|
||||||
|
|
||||||
print(f"\n🎉 Análisis completado exitosamente!")
|
print(f"\n🎉 Análisis completado exitosamente!")
|
||||||
print(f"📁 Archivos generados en: {results_dir.absolute()}")
|
print(f"📁 Archivos generados en: {results_dir.absolute()}")
|
||||||
print(f" 📄 {results_dir / 'IO_Detailed_Analysis_Report.md'}")
|
print(f" 📄 {results_dir / 'IO_Detailed_Analysis_Report.md'}")
|
||||||
print(f" 📄 {results_dir / 'io_adaptation_data.json'}")
|
print(f" 📄 {results_dir / 'io_adaptation_data.json'}")
|
||||||
print(f" 📄 {results_dir / 'io_detailed_analysis.csv'}")
|
print(f" 📄 {results_dir / 'io_detailed_analysis.csv'}")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
results = main()
|
results = main()
|
||||||
|
|
|
@ -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
|
@ -15,5 +15,5 @@
|
||||||
"xref_source_subdir": "source"
|
"xref_source_subdir": "source"
|
||||||
},
|
},
|
||||||
"level3": {},
|
"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"
|
||||||
}
|
}
|
|
@ -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": [
|
"history": [
|
||||||
"C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",
|
|
||||||
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
|
"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",
|
"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\\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"
|
"C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport"
|
||||||
|
|
26095
data/log.txt
26095
data/log.txt
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue