Se corrigio un error en la edicion de los Scripts del tab Python Scripts

This commit is contained in:
Miguel 2025-07-16 17:40:44 +02:00
parent 3b3cfd7062
commit f0f45df1b8
11 changed files with 28815 additions and 177 deletions

View File

@ -14,6 +14,23 @@
"description": "Subdirectorio de los proyectos actuales en el Vault de Obsidean", "description": "Subdirectorio de los proyectos actuales en el Vault de Obsidean",
"default": "\\04-SIDEL" "default": "\\04-SIDEL"
}, },
"conversion_desde": {
"type": "string",
"title": "Conversion desde TwinCat / S7-300",
"description": "Conversion desde TwinCat / S7-300",
"enum": [
"TwinCat",
"S7-300"
],
"default": "TwinCat"
},
"io_excel_file_from_ediagram": {
"type": "string",
"title": "IO scanned from ED",
"description": "IO scanned from Electrica Diagram",
"format": "file",
"default": "IO.xlsx"
},
"siemens_exp_directory": { "siemens_exp_directory": {
"type": "string", "type": "string",
"title": "Directorio base donde esta la exportación de el proyecto de Tia", "title": "Directorio base donde esta la exportación de el proyecto de Tia",
@ -32,12 +49,11 @@
"description": "", "description": "",
"format": "directory" "format": "directory"
}, },
"io_excel_file_from_ediagram": { "results_twincat_project": {
"type": "string", "type": "string",
"title": "IO scanned from ED", "title": "Resultados del Proyecto TwinCat",
"description": "IO scanned from Electrica Diagram", "description": "Directorio con los archivos md del Proyecto TwinCat de referencia si se esta conviertiendo los Tags desde TwinCat",
"format": "file", "format": "directory"
"default": "IO.xlsx"
} }
} }
} }

View File

@ -10,6 +10,7 @@
"level3": { "level3": {
"ObsideanDir": "C:\\Users\\migue\\OneDrive\\Miguel\\Obsidean\\Trabajo\\VM", "ObsideanDir": "C:\\Users\\migue\\OneDrive\\Miguel\\Obsidean\\Trabajo\\VM",
"ObsideanProjectsBase": "\\04-SIDEL", "ObsideanProjectsBase": "\\04-SIDEL",
"conversion_desde": "TwinCat",
"io_excel_file_from_ediagram": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/IO.xlsx", "io_excel_file_from_ediagram": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/IO.xlsx",
"siemens_exp_directory": "C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia", "siemens_exp_directory": "C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",
"siemens_tia_project": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/InLavoro/PLC/SSAE0235/_NEW/SAE235_v0.4", "siemens_tia_project": "C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/InLavoro/PLC/SSAE0235/_NEW/SAE235_v0.4",

View File

@ -7,9 +7,10 @@ adaptación de IO entre hardware de PLC Siemens TIA Portal y software master.
import os import os
import sys import sys
import tkinter as tk import tkinter as tk
from tkinter import filedialog, messagebox from tkinter import filedialog
import pyperclip # Para copiar al portapapeles import pyperclip # Para copiar al portapapeles
import shutil # Para copiar archivos import shutil # Para copiar archivos
import subprocess # Para abrir Cursor
# Determine script_root and add to sys.path for custom module import # Determine script_root and add to sys.path for custom module import
try: try:
@ -54,6 +55,100 @@ def select_obsidian_folder():
return folder_path if folder_path else None return folder_path if folder_path else None
def copy_obsidian_files_to_working_dir(obsidian_mixer_path, data_directory):
"""
Copia los archivos de Obsidian al directorio de trabajo.
Retorna un diccionario con las rutas de los archivos copiados.
"""
copied_files = {}
# Archivos a copiar desde Obsidian
obsidian_files = ["SIDEL - Mixer - Equivalences.md", "Default IO for Analog.md"]
for filename in obsidian_files:
source_path = os.path.join(obsidian_mixer_path, filename)
dest_path = os.path.join(data_directory, filename)
if os.path.isfile(source_path):
try:
shutil.copy2(source_path, dest_path)
print(f"Archivo copiado: {filename}")
copied_files[filename] = dest_path
except Exception as e:
print(f"Error al copiar {filename}: {e}")
else:
print(f"Archivo no encontrado en Obsidian: {filename}")
return copied_files
def copy_twincat_files_to_working_dir(twincat_project_path, data_directory):
"""
Copia todos los archivos *.md desde el directorio de proyecto TwinCat al directorio de trabajo.
Retorna un diccionario con las rutas de los archivos copiados.
"""
copied_files = {}
if not os.path.isdir(twincat_project_path):
print(f"Directorio de proyecto TwinCat no encontrado: {twincat_project_path}")
return copied_files
# Buscar todos los archivos *.md en el directorio
import glob
md_files = glob.glob(os.path.join(twincat_project_path, "*.md"))
for md_file in md_files:
filename = os.path.basename(md_file)
dest_path = os.path.join(data_directory, filename)
try:
shutil.copy2(md_file, dest_path)
print(f"Archivo TwinCat copiado: {filename}")
copied_files[filename] = dest_path
except Exception as e:
print(f"Error al copiar {filename}: {e}")
return copied_files
def open_cursor_in_directory(directory_path):
"""Abre Cursor en el directorio especificado."""
try:
# Rutas comunes donde se instala Cursor
possible_cursor_paths = [
r"C:\Users\migue\AppData\Local\Programs\cursor\Cursor.exe",
r"C:\Program Files\Cursor\Cursor.exe",
r"C:\Program Files (x86)\Cursor\Cursor.exe",
]
editor_path = None
for path in possible_cursor_paths:
if os.path.isfile(path):
editor_path = path
break
if not editor_path:
# Intentar buscar en PATH
editor_path = shutil.which("cursor")
if not editor_path:
print(
f"Cursor no encontrado. Intenté en: {', '.join(possible_cursor_paths)}"
)
return False
print(f"Launching Cursor from: {editor_path}")
print(f"Opening directory: {directory_path}")
# Ejecutar Cursor
process = subprocess.Popen(f'"{editor_path}" "{directory_path}"', shell=True)
print(f"Cursor process started with PID: {process.pid}")
return True
except Exception as e:
print(f"Error al abrir Cursor: {e}")
return False
def generate_prompt(): def generate_prompt():
""" """
Genera el prompt para la adaptación de IO y lo copia al portapapeles. Genera el prompt para la adaptación de IO y lo copia al portapapeles.
@ -86,59 +181,82 @@ def generate_prompt():
working_directory_abs = os.path.abspath(working_directory) working_directory_abs = os.path.abspath(working_directory)
print(f"Usando directorio de trabajo: {working_directory_abs}") print(f"Usando directorio de trabajo: {working_directory_abs}")
# Definir el directorio de datos
data_directory = os.path.join(working_directory_abs, resultados_exp_directory)
# Variables para las rutas de Obsidian # Variables para las rutas de Obsidian
obsidian_dir = None obsidian_mixer_path = None
obsidian_projects_base = None
obsidian_io_path = None
# Intentar obtener la carpeta base de Obsidian desde la configuración # Intentar obtener la carpeta base de Obsidian desde la configuración
copied_files = {}
obsidian_mixer_path = None
twincat_project_path = None
try: try:
# Obtener configuración del nivel 3 # Obtener configuración del nivel 3
group_config = configs.get("level3", {}) group_config = configs.get("level3", {})
obsidian_dir = group_config.get("ObsideanDir") conversion_desde = group_config.get("conversion_desde")
obsidian_projects_base = group_config.get("ObsideanProjectsBase")
if obsidian_dir and obsidian_projects_base: # Verificar si es TwinCat
# Path para la carpeta de equivalencias (00 - MASTER/MIXER/IO) if conversion_desde == "TwinCat":
obsidian_mixer_path = os.path.join( print("Detectado proyecto TwinCat")
obsidian_dir, results_twincat_project = group_config.get("results_twincat_project")
obsidian_projects_base.lstrip("\\"), if results_twincat_project:
"00 - MASTER", twincat_project_path = os.path.abspath(results_twincat_project)
"MIXER", if os.path.isdir(twincat_project_path):
"IO", print(
) f"Usando directorio de proyecto TwinCat: {twincat_project_path}"
)
# Path para la carpeta IO donde se copiará el archivo de salida # Copiar archivos TwinCat
obsidian_io_path = os.path.join( print("Copiando archivos TwinCat al directorio de trabajo...")
obsidian_dir, obsidian_projects_base.lstrip("\\"), "IO" copied_files = copy_twincat_files_to_working_dir(
) twincat_project_path, data_directory
)
# Verificar que la ruta de equivalencias exista else:
if os.path.isdir(obsidian_mixer_path): print(
print( f"Directorio de proyecto TwinCat no válido: {twincat_project_path}"
f"Usando ruta de Obsidian desde configuración: {obsidian_mixer_path}" )
)
else: else:
print( print(
f"Ruta de Obsidian para equivalencias no válida: {obsidian_mixer_path}" "Configuración incompleta para TwinCat: falta 'results_twincat_project'"
) )
obsidian_mixer_path = None
# Crear la carpeta IO si no existe
if not os.path.isdir(obsidian_io_path):
try:
os.makedirs(obsidian_io_path)
print(f"Creada carpeta para salida en Obsidian: {obsidian_io_path}")
except Exception as e:
print(f"No se pudo crear carpeta IO en Obsidian: {e}")
obsidian_io_path = None
else: else:
print("Configuración incompleta para las rutas de Obsidian") # Configuración para Obsidian (comportamiento original)
except Exception as e: obsidian_dir = group_config.get("ObsideanDir")
print(f"Error al leer configuración de Obsidian: {e}") obsidian_projects_base = group_config.get("ObsideanProjectsBase")
# Si no se pudo obtener la carpeta desde la configuración, pedirla al usuario if obsidian_dir and obsidian_projects_base:
if not obsidian_mixer_path: # Path para la carpeta de equivalencias (00 - MASTER/MIXER/IO)
obsidian_mixer_path = os.path.join(
obsidian_dir,
obsidian_projects_base.lstrip("\\"),
"00 - MASTER",
"MIXER",
"IO",
)
# Verificar que la ruta de equivalencias exista
if os.path.isdir(obsidian_mixer_path):
print(
f"Usando ruta de Obsidian desde configuración: {obsidian_mixer_path}"
)
else:
print(
f"Ruta de Obsidian para equivalencias no válida: {obsidian_mixer_path}"
)
obsidian_mixer_path = None
else:
print("Configuración incompleta para las rutas de Obsidian")
except Exception as e:
print(f"Error al leer configuración: {e}")
# Si es TwinCat y no se pudo obtener la configuración, salir
if conversion_desde == "TwinCat" and not twincat_project_path:
print("No se pudo obtener la configuración de TwinCat. Saliendo...")
return False
# Si no es TwinCat y no se pudo obtener la carpeta de Obsidian, pedirla al usuario
if conversion_desde != "TwinCat" and not obsidian_mixer_path:
print( print(
"Carpeta de equivalencias en Obsidian no encontrada en configuración o no válida." "Carpeta de equivalencias en Obsidian no encontrada en configuración o no válida."
) )
@ -147,40 +265,45 @@ def generate_prompt():
) )
obsidian_mixer_path = select_obsidian_folder() obsidian_mixer_path = select_obsidian_folder()
if not obsidian_mixer_path: if not obsidian_mixer_path:
print("No se seleccionó ninguna carpeta. Saliendo...") print("No se seleccionó ninguna carpeta. Saliendo...")
return False return False
print(f"Usando carpeta de equivalencias en Obsidian: {obsidian_mixer_path}") print(f"Usando carpeta de equivalencias en Obsidian: {obsidian_mixer_path}")
# Definir las rutas a los archivos # Copiar archivos de Obsidian al directorio de trabajo
data_directory = os.path.join(working_directory_abs, resultados_exp_directory) print("Copiando archivos de Obsidian al directorio de trabajo...")
copied_files = copy_obsidian_files_to_working_dir(
obsidian_mixer_path, data_directory
)
# Definir las rutas a los archivos (todos en el mismo directorio)
master_table_path = os.path.join(data_directory, "Master IO Tags.md") master_table_path = os.path.join(data_directory, "Master IO Tags.md")
hardware_table_path = os.path.join(data_directory, "Hardware.md") hardware_table_path = os.path.join(data_directory, "Hardware.md")
adaptation_table_path = os.path.join(working_directory_abs, "IO Adapted.md") adaptation_table_path = os.path.join(data_directory, "IO Adapted.md")
# Rutas a los archivos de datos semánticos
equivalences_data_path = os.path.join(
obsidian_mixer_path, "SIDEL - Mixer - Equivalences.md"
)
default_io_data_path = os.path.join(obsidian_mixer_path, "Default IO for Analog.md")
# Verificar que los archivos existan # Verificar que los archivos existan
files_to_check = [ files_to_check = [
{"path": master_table_path, "name": "Master IO Tags.md", "required": True}, {"path": master_table_path, "name": "Master IO Tags.md", "required": True},
{"path": hardware_table_path, "name": "Hardware.md", "required": True}, {"path": hardware_table_path, "name": "Hardware.md", "required": True},
{
"path": equivalences_data_path,
"name": "SIDEL - Mixer - Equivalences.md",
"required": False,
},
{
"path": default_io_data_path,
"name": "Default IO for Analog.md",
"required": False,
},
] ]
# Agregar archivos copiados según el tipo de conversión
if conversion_desde == "TwinCat":
# Para TwinCat, agregar todos los archivos *.md copiados
for filename in copied_files.keys():
file_path = os.path.join(data_directory, filename)
files_to_check.append(
{"path": file_path, "name": filename, "required": False}
)
else:
# Para Obsidian, agregar archivos específicos
for filename in ["SIDEL - Mixer - Equivalences.md", "Default IO for Analog.md"]:
file_path = os.path.join(data_directory, filename)
files_to_check.append(
{"path": file_path, "name": filename, "required": False}
)
missing_files = [] missing_files = []
for file_info in files_to_check: for file_info in files_to_check:
if not verify_path(file_info["path"]): if not verify_path(file_info["path"]):
@ -194,45 +317,105 @@ def generate_prompt():
for missing_file in missing_files: for missing_file in missing_files:
print(f" - {missing_file}") print(f" - {missing_file}")
# Si faltan archivos requeridos, preguntar si se quiere continuar # Si faltan archivos requeridos, mostrar advertencia pero continuar
if any(f.startswith("[REQUERIDO]") for f in missing_files): if any(f.startswith("[REQUERIDO]") for f in missing_files):
if not messagebox.askyesno( print(
"Archivos faltantes", "ADVERTENCIA: Faltan archivos requeridos. Continuando de todos modos..."
"Faltan archivos requeridos. ¿Desea continuar de todos modos?", )
):
print("Operación cancelada.")
return False
# Generar el texto del prompt # Generar el texto del prompt usando el sistema de Cursor con @archivo.md
prompt_text = f""" if conversion_desde == "TwinCat":
Estoy adaptando las entradas y salidas entre un hardware de PLC Siemens Tia Portal y un software master. Para lograr identificar que tags del software master se deben asignar a cada IO del hardware del PLC. Se debe asignar a cada IO del harware un Tag del software master. # Prompt para TwinCat
prompt_text = f"""
Estoy adaptando las entradas y salidas entre un hardware de PLC TwinCat y un software master. Para lograr identificar que tags del software master se deben asignar a cada IO del hardware del PLC. Se debe asignar a cada IO del harware un Tag del software master.
Para que me ayudes con este proceso de busqueda he creado las tablas: Para que me ayudes con este proceso de busqueda he creado las tablas:
$Master_table: que contiene los tags del software master con las descripciones. Esta tabla se divide en 4 subtablas. La "Inputs PLCTags" son los inputs mas utilizados, luego estan los "InputsMaster PLCTags" que son inputs menos utilizados. Lo mismo sucede con "Outputs PLCTags" que son los mas utilizados y "OutputsMaster PLCTags" son los outputs menos utilizados. @Master IO Tags.md: que contiene los tags del software master con las descripciones. Esta tabla se divide en 4 subtablas. La "Inputs PLCTags" son los inputs mas utilizados, luego estan los "InputsMaster PLCTags" que son inputs menos utilizados. Lo mismo sucede con "Outputs PLCTags" que son los mas utilizados y "OutputsMaster PLCTags" son los outputs menos utilizados.
$Hardware_table: que contiene todo el IO del hardware del PLC. Esta dibidido en diferentes dispositivos ya que algunos componentes se acceden mediante interfaces de comunicaciones como profibus. Pero desde el punto de vista del PLC son inputs o outputs definidos como PEW que significa: P:periferia, W:word , se sigue la nomenclatura alemana de siemens, A:output, E:input. @Hardware.md: que contiene todo el IO del hardware del PLC. Esta dibidido en diferentes dispositivos ya que algunos componentes se acceden mediante interfaces de comunicaciones como profibus. Pero desde el punto de vista del PLC son inputs o outputs definidos como PEW que significa: P:periferia, W:word , se sigue la nomenclatura alemana de siemens, A:output, E:input.
$Adaptation_table: es la tabla que deseo que crees con los IO adaptados. Seria la $Hardware_table con los tags de la $Master_table. Quisiera que se agrege una columna con el nivel de certeza y en caso que el nivel no sea el maximo quisiera que agregues 3 posibles opciones como tag1,tag2,tag3 en una columna nueva. @IO Adapted.md: es la tabla que deseo que crees con los IO adaptados. Seria la @Hardware.md con los tags de la @Master IO Tags.md. Quisiera que se agrege una columna con el nivel de certeza y en caso que el nivel no sea el maximo quisiera que agregues 3 posibles opciones como tag1,tag2,tag3 en una columna nueva.
$Equivalences_data tiene información de ciertas equivalencias que pueden ser útiles para las búsquedas.
$DefaultIO_data tiene conexiones estandard en los casos mas comunes.
Para acceder a los archivos para leer o escribir puedes usar el MCP filesystem. Para acceder a los archivos para leer o escribir puedes usar el MCP filesystem.
# Definiciones de rutas # CONTEXTO DEL PROYECTO
$Working_Directory = "{working_directory_abs}" Estoy realizando un upgrade de software para equipos que migran de un sistema TwinCat a uno nuevo. Estos equipos tienen hardware de PLC TwinCat que debe ser adaptado al software master moderno. La correcta asignación de entradas/salidas es fundamental para que el sistema funcione adecuadamente.
$Obsidean_Base_Folder = "{obsidian_mixer_path}"
# Archivos de entrada # OBJETIVO
$Master_table = $Working_Directory + "/{resultados_exp_directory}/Master IO Tags.md" Crear una tabla de adaptación que asigne correctamente cada entrada/salida (I/O) del hardware de PLC TwinCat a los tags correspondientes del software master moderno. Este mapeo permitirá la comunicación efectiva entre el hardware existente y el nuevo software de control.
$Hardware_table = $Working_Directory + "/{resultados_exp_directory}/Hardware.md"
# Archivo de salida # TECNOLOGÍA Y NOMENCLATURA
$Adaptation_table = $Working_Directory + "/IO Adapted.md" - Sistema de control: TwinCat PLC
- Nomenclatura TwinCat:
* E: Entrada (Input) - Ejemplo: I0.1, EW100
* A: Salida (Output) - Ejemplo: Q0.1, AW100
* P: Periferia
* W: Word (16 bits)
* D: Double Word (32 bits)
# Datos semánticos de apoyo # DESCRIPCIÓN DE LOS ARCHIVOS
$Equivalences_data = "{equivalences_data_path}"
$DefaultIO_data = "{default_io_data_path}" ## Hardware_table (@Hardware.md)
- FUNCIÓN: Contiene la configuración completa del hardware del PLC y el detalle de todas las señales de I/O físicas.
- ESTRUCTURA:
* Primera sección: Tabla de configuración del PLC con red, direcciones y dispositivos
* Segunda sección: Tabla de I/O con dirección (ej. I0.1, PEW100), descripción y sensor asociado
- INFORMACIÓN CLAVE: Direcciones de hardware, descripciones físicas y conexiones de sensores/actuadores
## Master_table (@Master IO Tags.md)
- FUNCIÓN: Define los tags estandarizados en el software master moderno que deben mapearse a las I/O físicas.
- ESTRUCTURA: Dividido en 4 secciones:
* "Inputs PLCTags": Entradas más utilizadas
* "InputsMaster PLCTags": Entradas secundarias
* "Outputs PLCTags": Salidas más utilizadas
* "OutputsMaster PLCTags": Salidas secundarias
- INFORMACIÓN CLAVE: Tags estandarizados, tipos de datos y descripciones en inglés
## Adaptation_table (a crear en @IO Adapted.md)
- FUNCIÓN: Tabla final que mapea cada I/O del hardware a su correspondiente tag del master.
- ESTRUCTURA: Formato requerido:
| IO | Master Tag | PLC Description | Master Description | Certeza | Alternative |
- INFORMACIÓN CLAVE: Resultado del proceso de adaptación con nivel de confianza y alternativas
# PROCESO DE ADAPTACIÓN
Lee ambos archivos (@Hardware.md y @Master IO Tags.md) y crea una tabla de adaptación que asigne a cada IO del hardware un Tag del software master.
El proceso de asignación debe:
1. Comparar semánticamente descripciones entre ambas tablas
2. Verificar compatibilidad de tipo (input/output) y tamaño de datos
3. Priorizar los tags de las tablas principales ("Inputs PLCTags"/"Outputs PLCTags") sobre las secundarias
# FORMATO DE SALIDA
Crea la tabla con esta estructura exacta:
| IO | Master Tag | PLC Description | Master Description | Certeza | Alternative |
# NIVELES DE CERTEZA
Asigna niveles de certeza según estos criterios:
- Alto (90%+): Coincidencia evidente en nombre y descripción
- Medio (70-90%): Similitud semántica notable pero no exacta
- Bajo (<70%): Similitud limitada, requiere revisión manual
Para entradas con nivel de certeza medio o bajo, añade hasta 3 tags alternativos en la columna "Alternative" separados por comas.
# EXCEPCIONES
Al final del documento, crea una sección titulada "## Excepciones y Problemas" con una tabla que liste las IO sin asignación clara y el problema detectado.
"""
else:
# Prompt para Obsidian (comportamiento original)
prompt_text = f"""
Estoy adaptando las entradas y salidas entre un hardware de PLC Siemens Tia Portal y un software master. Para lograr identificar que tags del software master se deben asignar a cada IO del hardware del PLC. Se debe asignar a cada IO del harware un Tag del software master.
Para que me ayudes con este proceso de busqueda he creado las tablas:
@Master IO Tags.md: que contiene los tags del software master con las descripciones. Esta tabla se divide en 4 subtablas. La "Inputs PLCTags" son los inputs mas utilizados, luego estan los "InputsMaster PLCTags" que son inputs menos utilizados. Lo mismo sucede con "Outputs PLCTags" que son los mas utilizados y "OutputsMaster PLCTags" son los outputs menos utilizados.
@Hardware.md: que contiene todo el IO del hardware del PLC. Esta dibidido en diferentes dispositivos ya que algunos componentes se acceden mediante interfaces de comunicaciones como profibus. Pero desde el punto de vista del PLC son inputs o outputs definidos como PEW que significa: P:periferia, W:word , se sigue la nomenclatura alemana de siemens, A:output, E:input.
@IO Adapted.md: es la tabla que deseo que crees con los IO adaptados. Seria la @Hardware.md con los tags de la @Master IO Tags.md. Quisiera que se agrege una columna con el nivel de certeza y en caso que el nivel no sea el maximo quisiera que agregues 3 posibles opciones como tag1,tag2,tag3 en una columna nueva.
@SIDEL - Mixer - Equivalences.md tiene información de ciertas equivalencias que pueden ser útiles para las búsquedas.
@Default IO for Analog.md tiene conexiones estandard en los casos mas comunes.
Para acceder a los archivos para leer o escribir puedes usar el MCP filesystem.
# CONTEXTO DEL PROYECTO # CONTEXTO DEL PROYECTO
Estoy realizando un upgrade de software para equipos SIDEL que migran de un sistema antiguo a uno nuevo. Estos equipos de mezclado de bebidas (mixers) tienen hardware de PLC Siemens que debe ser adaptado al software master moderno. La correcta asignación de entradas/salidas es fundamental para que el sistema funcione adecuadamente. Estoy realizando un upgrade de software para equipos SIDEL que migran de un sistema antiguo a uno nuevo. Estos equipos de mezclado de bebidas (mixers) tienen hardware de PLC Siemens que debe ser adaptado al software master moderno. La correcta asignación de entradas/salidas es fundamental para que el sistema funcione adecuadamente.
@ -253,14 +436,14 @@ Crear una tabla de adaptación que asigne correctamente cada entrada/salida (I/O
# DESCRIPCIÓN DE LOS ARCHIVOS # DESCRIPCIÓN DE LOS ARCHIVOS
## Hardware_table ($Hardware_table) ## Hardware_table (@Hardware.md)
- FUNCIÓN: Contiene la configuración completa del hardware del PLC y el detalle de todas las señales de I/O físicas. - FUNCIÓN: Contiene la configuración completa del hardware del PLC y el detalle de todas las señales de I/O físicas.
- ESTRUCTURA: - ESTRUCTURA:
* Primera sección: Tabla de configuración del PLC con red, direcciones y dispositivos * Primera sección: Tabla de configuración del PLC con red, direcciones y dispositivos
* Segunda sección: Tabla de I/O con dirección (ej. I0.1, PEW100), descripción en italiano/inglés y sensor asociado * Segunda sección: Tabla de I/O con dirección (ej. I0.1, PEW100), descripción en italiano/inglés y sensor asociado
- INFORMACIÓN CLAVE: Direcciones de hardware, descripciones físicas y conexiones de sensores/actuadores - INFORMACIÓN CLAVE: Direcciones de hardware, descripciones físicas y conexiones de sensores/actuadores
## Master_table ($Master_table) ## Master_table (@Master IO Tags.md)
- FUNCIÓN: Define los tags estandarizados en el software master moderno que deben mapearse a las I/O físicas. - FUNCIÓN: Define los tags estandarizados en el software master moderno que deben mapearse a las I/O físicas.
- ESTRUCTURA: Dividido en 4 secciones: - ESTRUCTURA: Dividido en 4 secciones:
* "Inputs PLCTags": Entradas más utilizadas * "Inputs PLCTags": Entradas más utilizadas
@ -269,30 +452,30 @@ Crear una tabla de adaptación que asigne correctamente cada entrada/salida (I/O
* "OutputsMaster PLCTags": Salidas secundarias * "OutputsMaster PLCTags": Salidas secundarias
- INFORMACIÓN CLAVE: Tags estandarizados, tipos de datos y descripciones en inglés - INFORMACIÓN CLAVE: Tags estandarizados, tipos de datos y descripciones en inglés
## Equivalences_data ($Equivalences_data) ## Equivalences_data (@SIDEL - Mixer - Equivalences.md)
- FUNCIÓN: Proporciona equivalencias semánticas entre terminologías antiguas/nuevas y abreviaturas. - FUNCIÓN: Proporciona equivalencias semánticas entre terminologías antiguas/nuevas y abreviaturas.
- ESTRUCTURA: Listado de equivalencias como "301 : WATER PUMP = P1" - ESTRUCTURA: Listado de equivalencias como "301 : WATER PUMP = P1"
- INFORMACIÓN CLAVE: Traducciones entre nomenclaturas, equivalencias entre códigos y nombres descriptivos - INFORMACIÓN CLAVE: Traducciones entre nomenclaturas, equivalencias entre códigos y nombres descriptivos
## DefaultIO_data ($DefaultIO_data) ## DefaultIO_data (@Default IO for Analog.md)
- FUNCIÓN: Contiene configuraciones predeterminadas para señales analógicas específicas. - FUNCIÓN: Contiene configuraciones predeterminadas para señales analógicas específicas.
- ESTRUCTURA: Tabla con tags, tipos de datos y direcciones de memoria para señales analógicas estándar - ESTRUCTURA: Tabla con tags, tipos de datos y direcciones de memoria para señales analógicas estándar
- INFORMACIÓN CLAVE: Mapeos predefinidos para señales analógicas comunes, especialmente para comunicación Profibus - INFORMACIÓN CLAVE: Mapeos predefinidos para señales analógicas comunes, especialmente para comunicación Profibus
## Adaptation_table (a crear en $Adaptation_table) ## Adaptation_table (a crear en @IO Adapted.md)
- FUNCIÓN: Tabla final que mapea cada I/O del hardware a su correspondiente tag del master. - FUNCIÓN: Tabla final que mapea cada I/O del hardware a su correspondiente tag del master.
- ESTRUCTURA: Formato requerido: - ESTRUCTURA: Formato requerido:
| IO | Master Tag | PLC Description | Master Description | Certeza | Alternative | | IO | Master Tag | PLC Description | Master Description | Certeza | Alternative |
- INFORMACIÓN CLAVE: Resultado del proceso de adaptación con nivel de confianza y alternativas - INFORMACIÓN CLAVE: Resultado del proceso de adaptación con nivel de confianza y alternativas
# PROCESO DE ADAPTACIÓN # PROCESO DE ADAPTACIÓN
Lee ambos archivos ($Hardware_table y $Master_table) y crea una tabla de adaptación que asigne a cada IO del hardware un Tag del software master. Lee ambos archivos (@Hardware.md y @Master IO Tags.md) y crea una tabla de adaptación que asigne a cada IO del hardware un Tag del software master.
El proceso de asignación debe: El proceso de asignación debe:
1. Comparar semánticamente descripciones entre ambas tablas, teniendo en cuenta el idioma (italiano/inglés) 1. Comparar semánticamente descripciones entre ambas tablas, teniendo en cuenta el idioma (italiano/inglés)
2. Verificar compatibilidad de tipo (input/output) y tamaño de datos 2. Verificar compatibilidad de tipo (input/output) y tamaño de datos
3. Utilizar las equivalencias en $Equivalences_data para mejorar la coincidencia semántica 3. Utilizar las equivalencias en @SIDEL - Mixer - Equivalences.md para mejorar la coincidencia semántica
4. Verificar configuraciones estándar en $DefaultIO_data para señales analógicas 4. Verificar configuraciones estándar en @Default IO for Analog.md para señales analógicas
5. Priorizar los tags de las tablas principales ("Inputs PLCTags"/"Outputs PLCTags") sobre las secundarias 5. Priorizar los tags de las tablas principales ("Inputs PLCTags"/"Outputs PLCTags") sobre las secundarias
# FORMATO DE SALIDA # FORMATO DE SALIDA
@ -316,50 +499,28 @@ Al final del documento, crea una sección titulada "## Excepciones y Problemas"
pyperclip.copy(prompt_text) pyperclip.copy(prompt_text)
print("¡Prompt generado y copiado al portapapeles con éxito!") print("¡Prompt generado y copiado al portapapeles con éxito!")
# Guardar el prompt en un archivo para referencia # Guardar el prompt en un archivo para referencia en el directorio de datos
prompt_file_path = os.path.join( prompt_file_path = os.path.join(data_directory, "IO_Adaptation_Prompt.txt")
working_directory_abs, "IO_Adaptation_Prompt.txt"
)
with open(prompt_file_path, "w", encoding="utf-8") as f: with open(prompt_file_path, "w", encoding="utf-8") as f:
f.write(prompt_text) f.write(prompt_text)
print(f"Prompt guardado en: {prompt_file_path}") print(f"Prompt guardado en: {prompt_file_path}")
# También copiar el archivo IO Adapted.md a la carpeta de Obsidian cuando se genere # Mostrar mensaje de éxito
if obsidian_io_path: print(f"¡Prompt generado y copiado al portapapeles con éxito!")
# Crear un mensaje para informar al usuario print(f"Prompt guardado en: {prompt_file_path}")
copy_message = "" if conversion_desde == "TwinCat":
print(f"Archivos copiados de TwinCat: {len(copied_files)} archivos")
# Explicar que la copia se realizará cuando se genere el archivo
copy_message = (
f"Se copiará automáticamente el archivo 'IO Adapted.md' a:\n"
f"{obsidian_io_path}\n\n"
f"cuando sea generado por la herramienta."
)
# Añadir la información sobre la copia al mensaje de éxito
success_message = (
f"Prompt generado y copiado al portapapeles.\n\n"
f"También se ha guardado en:\n{prompt_file_path}\n\n"
f"{copy_message}"
)
messagebox.showinfo("Éxito", success_message)
else: else:
# Mostrar mensaje de éxito sin la parte de copia print(f"Archivos copiados de Obsidian: {len(copied_files)} archivos")
messagebox.showinfo(
"Éxito", # Abrir Cursor en el directorio de datos
f"Prompt generado y copiado al portapapeles.\n\n" print("Abriendo Cursor en el directorio de datos...")
f"También se ha guardado en:\n{prompt_file_path}", open_cursor_in_directory(data_directory)
)
return True return True
except Exception as e: except Exception as e:
print(f"Error al copiar al portapapeles: {e}") print(f"Error al copiar al portapapeles: {e}")
messagebox.showerror( print("Por favor, instale pyperclip con 'pip install pyperclip'")
"Error",
f"Error al copiar al portapapeles: {e}\n\n"
f"Por favor, instale pyperclip con 'pip install pyperclip'",
)
return False return False

View File

@ -5,5 +5,5 @@
}, },
"level2": {}, "level2": {},
"level3": {}, "level3": {},
"working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source" "working_directory": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport"
} }

View File

@ -1,6 +1,7 @@
{ {
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "path": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport",
"history": [ "history": [
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport",
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source",
"D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia", "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia",
"C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia", "C:\\Trabajo\\SIDEL\\13 - E5.007560 - Modifica O&U - SAE235\\Reporte\\ExportTia",

View File

@ -15,5 +15,5 @@
"xref_source_subdir": "source" "xref_source_subdir": "source"
}, },
"level3": {}, "level3": {},
"working_directory": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source" "working_directory": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport"
} }

View File

@ -1,6 +1,7 @@
{ {
"path": "D:\\Trabajo\\VM\\44 - 98050 - Fiera\\Reporte\\ExportsTia\\Source", "path": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport",
"history": [ "history": [
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\TiaExport",
"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", "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",

View File

@ -1,5 +1,31 @@
{ {
"history": [ "history": [
{
"id": "e7060cbc",
"group_id": "2",
"script_name": "main.py",
"executed_date": "2025-07-15T12:44:13.087028Z",
"arguments": [],
"working_directory": "D:/Proyectos/Scripts/RS485/MaselliSimulatorApp",
"python_env": "tia_scripting",
"executable_type": "pythonw.exe",
"status": "running",
"pid": 30804,
"execution_time": null
},
{
"id": "72f091cc",
"group_id": "2",
"script_name": "main.py",
"executed_date": "2025-07-15T09:43:04.199490Z",
"arguments": [],
"working_directory": "D:/Proyectos/Scripts/RS485/MaselliSimulatorApp",
"python_env": "tia_scripting",
"executable_type": "pythonw.exe",
"status": "running",
"pid": 14544,
"execution_time": null
},
{ {
"id": "5115260f", "id": "5115260f",
"group_id": "2", "group_id": "2",

28470
data/log.txt

File diff suppressed because it is too large Load Diff

View File

@ -336,7 +336,7 @@ class LauncherManager {
<div class="flex justify-between items-start mb-2"> <div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-gray-900">${script.display_name}</h4> <h4 class="font-medium text-gray-900">${script.display_name}</h4>
<button class="favorite-star ${isFavorite ? 'active' : ''}" <button class="favorite-star ${isFavorite ? 'active' : ''}"
onclick="launcherManager.toggleFavorite('${this.currentGroup.id}', '${script.name}')"> onclick="launcherManager.toggleFavorite('${this.currentGroup.id}', '${script.name.replace(/'/g, "\\'")}')">
</button> </button>
</div> </div>
@ -347,22 +347,22 @@ class LauncherManager {
<div class="flex gap-1"> <div class="flex gap-1">
${script.long_description && script.long_description.trim() ? ` ${script.long_description && script.long_description.trim() ? `
<button class="text-green-600 hover:underline" <button class="text-green-600 hover:underline"
onclick="launcherManager.showDescriptionModal('${script.name}', '${this.escapeHtml(script.display_name)}')"> onclick="launcherManager.showDescriptionModal('${script.name.replace(/'/g, "\\'")}', '${this.escapeHtml(script.display_name)}')">
Descripción Descripción
</button>` : ''} </button>` : ''}
<button class="text-blue-500 hover:underline" <button class="text-blue-500 hover:underline"
onclick="launcherManager.showArgsModal('${script.name}', '${this.escapeHtml(script.display_name)}')"> onclick="launcherManager.showArgsModal('${script.name.replace(/'/g, "\\'")}', '${this.escapeHtml(script.display_name)}')">
Con Argumentos Con Argumentos
</button> </button>
</div> </div>
<div class="flex gap-1"> <div class="flex gap-1">
<button class="bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600 text-xs" <button class="bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600 text-xs"
onclick="launcherManager.executeScript('${script.name}', [], null, false)" onclick="launcherManager.executeScript('${script.name.replace(/'/g, "\\'")}', [], null, false)"
title="Ejecutar con python.exe (muestra logs)"> title="Ejecutar con python.exe (muestra logs)">
🖥 Con Log 🖥 Con Log
</button> </button>
<button class="bg-green-500 text-white px-2 py-1 rounded hover:bg-green-600 text-xs" <button class="bg-green-500 text-white px-2 py-1 rounded hover:bg-green-600 text-xs"
onclick="launcherManager.executeScript('${script.name}', [], null, true)" onclick="launcherManager.executeScript('${script.name.replace(/'/g, "\\'")}', [], null, true)"
title="Ejecutar con pythonw.exe (sin ventana de consola)"> title="Ejecutar con pythonw.exe (sin ventana de consola)">
🚀 Sin Log 🚀 Sin Log
</button> </button>
@ -544,7 +544,7 @@ class LauncherManager {
<p class="text-xs text-gray-500">Archivo: ${script.name}</p> <p class="text-xs text-gray-500">Archivo: ${script.name}</p>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button onclick="launcherManager.editScriptMetadata('${script.name}')" <button onclick="launcherManager.editScriptMetadata('${script.name.replace(/'/g, "\\'")}')"
class="text-blue-500 hover:underline text-sm"> class="text-blue-500 hover:underline text-sm">
Editar Editar
</button> </button>

View File

@ -269,7 +269,7 @@ function createPythonScriptCard(script) {
<h3 class="font-semibold text-lg">${script.display_name}</h3> <h3 class="font-semibold text-lg">${script.display_name}</h3>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
${script.tags.map(tag => `<span class="bg-gray-100 text-xs px-2 py-1 rounded">${tag}</span>`).join('')} ${script.tags.map(tag => `<span class="bg-gray-100 text-xs px-2 py-1 rounded">${tag}</span>`).join('')}
<button onclick="togglePythonFavorite('${selectedPythonProject.id}', '${script.filename}')" <button onclick="togglePythonFavorite('${selectedPythonProject.id}', '${script.filename.replace(/'/g, "\\'")}')"
class="text-xl hover:scale-110 transition-transform ml-2" class="text-xl hover:scale-110 transition-transform ml-2"
title="${isFavorite ? 'Quitar de favoritos' : 'Agregar a favoritos'}"> title="${isFavorite ? 'Quitar de favoritos' : 'Agregar a favoritos'}">
${isFavorite ? '⭐' : '☆'} ${isFavorite ? '⭐' : '☆'}
@ -291,29 +291,29 @@ function createPythonScriptCard(script) {
📄 ${script.filename} 📄 ${script.filename}
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button onclick="showPythonScriptDescription('${script.filename}', '${script.display_name}')" <button onclick="showPythonScriptDescription('${script.filename.replace(/'/g, "\\'")}', '${script.display_name}')"
class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600" class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600"
title="Ver descripción detallada"> title="Ver descripción detallada">
</button> </button>
<button onclick="showPythonScriptOptions('${script.filename}')" <button onclick="showPythonScriptOptions('${script.filename.replace(/'/g, "\\'")}')"
class="bg-purple-500 text-white px-3 py-1 rounded text-sm hover:bg-purple-600" class="bg-purple-500 text-white px-3 py-1 rounded text-sm hover:bg-purple-600"
title="Ejecutar con opciones"> title="Ejecutar con opciones">
</button> </button>
${isServerScript ? ` ${isServerScript ? `
<button onclick="executePythonScript('${script.filename}', [], true)" <button onclick="executePythonScript('${script.filename.replace(/'/g, "\\'")}', [], true)"
class="bg-green-500 text-white px-3 py-1 rounded text-sm hover:bg-green-600" class="bg-green-500 text-white px-3 py-1 rounded text-sm hover:bg-green-600"
title="Ejecutar como servidor (background)"> title="Ejecutar como servidor (background)">
🔄 Servidor 🔄 Servidor
</button> </button>
` : ''} ` : ''}
<button onclick="executePythonScript('${script.filename}', [], ${isServerScript})" <button onclick="executePythonScript('${script.filename.replace(/'/g, "\\'")}', [], ${isServerScript})"
class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600" class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600"
title="Ejecutar script"> title="Ejecutar script">
Ejecutar Ejecutar
</button> </button>
<button onclick="showPythonScriptOptions('${script.filename}')" <button onclick="showPythonScriptOptions('${script.filename.replace(/'/g, "\\'")}')"
class="bg-gray-500 text-white px-3 py-1 rounded text-sm hover:bg-gray-600" class="bg-gray-500 text-white px-3 py-1 rounded text-sm hover:bg-gray-600"
title="Opciones avanzadas"> title="Opciones avanzadas">
@ -1124,10 +1124,10 @@ function renderPythonScriptManagerList(scripts) {
${script.hidden ? '<span class="text-xs bg-gray-200 text-gray-600 px-2 py-1 rounded">Oculto</span>' : ''} ${script.hidden ? '<span class="text-xs bg-gray-200 text-gray-600 px-2 py-1 rounded">Oculto</span>' : ''}
</div> </div>
<p class="text-sm text-gray-600 mb-2">${script.description || 'Sin descripción'}</p> <p class="text-sm text-gray-600 mb-2">${script.description || 'Sin descripción'}</p>
<p class="text-xs text-gray-500">Archivo: ${script.name}</p> <p class="text-xs text-gray-500">Archivo: ${script.filename}</p>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button onclick="editPythonScriptMetadata('${script.name}')" <button onclick="editPythonScriptMetadata('${script.filename.replace(/'/g, "\\'")}')"
class="text-blue-500 hover:underline text-sm"> class="text-blue-500 hover:underline text-sm">
Editar Editar
</button> </button>