Compare commits
3 Commits
ffabf6b2b0
...
ea35ae1211
Author | SHA1 | Date |
---|---|---|
|
ea35ae1211 | |
|
a3618246b7 | |
|
c0a0a5e088 |
File diff suppressed because it is too large
Load Diff
149
app.py
149
app.py
|
@ -1,6 +1,7 @@
|
||||||
from flask import Flask, render_template, request, jsonify, url_for
|
from flask import Flask, render_template, request, jsonify, url_for
|
||||||
from flask_sock import Sock
|
from flask_sock import Sock
|
||||||
from lib.config_manager import ConfigurationManager
|
from lib.config_manager import ConfigurationManager
|
||||||
|
from lib.launcher_manager import LauncherManager
|
||||||
import os
|
import os
|
||||||
import json # Added import
|
import json # Added import
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -22,6 +23,9 @@ app = Flask(
|
||||||
sock = Sock(app)
|
sock = Sock(app)
|
||||||
config_manager = ConfigurationManager()
|
config_manager = ConfigurationManager()
|
||||||
|
|
||||||
|
# Inicializar launcher manager
|
||||||
|
launcher_manager = LauncherManager(config_manager.data_path)
|
||||||
|
|
||||||
# Lista global para mantener las conexiones WebSocket activas
|
# Lista global para mantener las conexiones WebSocket activas
|
||||||
websocket_connections = set()
|
websocket_connections = set()
|
||||||
|
|
||||||
|
@ -509,6 +513,151 @@ def exit_application(icon, item):
|
||||||
tray_icon.stop()
|
tray_icon.stop()
|
||||||
|
|
||||||
|
|
||||||
|
# === LAUNCHER GUI APIs ===
|
||||||
|
|
||||||
|
@app.route("/api/launcher-groups", methods=["GET", "POST"])
|
||||||
|
def handle_launcher_groups():
|
||||||
|
"""Gestionar grupos de launcher (GET: obtener, POST: crear)"""
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
groups = launcher_manager.get_launcher_groups()
|
||||||
|
return jsonify(groups)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
else: # POST
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
result = launcher_manager.add_launcher_group(data)
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/launcher-groups/<group_id>", methods=["GET", "PUT", "DELETE"])
|
||||||
|
def handle_launcher_group(group_id):
|
||||||
|
"""Gestionar grupo específico (GET: obtener, PUT: actualizar, DELETE: eliminar)"""
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
group = launcher_manager.get_launcher_group(group_id)
|
||||||
|
if not group:
|
||||||
|
return jsonify({"error": "Group not found"}), 404
|
||||||
|
return jsonify(group)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
elif request.method == "PUT":
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
result = launcher_manager.update_launcher_group(group_id, data)
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
else: # DELETE
|
||||||
|
try:
|
||||||
|
result = launcher_manager.delete_launcher_group(group_id)
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/launcher-scripts/<group_id>")
|
||||||
|
def get_launcher_scripts(group_id):
|
||||||
|
"""Obtener scripts de un grupo del launcher"""
|
||||||
|
try:
|
||||||
|
scripts = launcher_manager.get_group_scripts(group_id)
|
||||||
|
return jsonify(scripts)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/execute-gui-script", methods=["POST"])
|
||||||
|
def execute_gui_script():
|
||||||
|
"""Ejecutar script GUI con argumentos opcionales"""
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
group_id = data["group_id"]
|
||||||
|
script_name = data["script_name"]
|
||||||
|
script_args = data.get("args", [])
|
||||||
|
|
||||||
|
result = launcher_manager.execute_gui_script(
|
||||||
|
group_id, script_name, script_args, broadcast_message
|
||||||
|
)
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error ejecutando script GUI: {str(e)}"
|
||||||
|
broadcast_message(error_msg)
|
||||||
|
return jsonify({"error": error_msg}), 500
|
||||||
|
|
||||||
|
@app.route("/api/launcher-favorites", methods=["GET", "POST"])
|
||||||
|
def handle_launcher_favorites():
|
||||||
|
"""Gestionar favoritos del launcher"""
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
favorites = launcher_manager.get_favorites()
|
||||||
|
return jsonify({"favorites": favorites})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
else: # POST
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
group_id = data["group_id"]
|
||||||
|
script_name = data["script_name"]
|
||||||
|
result = launcher_manager.toggle_favorite(group_id, script_name)
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/launcher-history", methods=["GET", "DELETE"])
|
||||||
|
def handle_launcher_history():
|
||||||
|
"""Gestionar historial del launcher"""
|
||||||
|
if request.method == "GET":
|
||||||
|
try:
|
||||||
|
history = launcher_manager.get_history()
|
||||||
|
return jsonify({"history": history})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
else: # DELETE
|
||||||
|
try:
|
||||||
|
result = launcher_manager.clear_history()
|
||||||
|
return jsonify(result)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/launcher-categories")
|
||||||
|
def get_launcher_categories():
|
||||||
|
"""Obtener categorías disponibles del launcher"""
|
||||||
|
try:
|
||||||
|
categories = launcher_manager.get_categories()
|
||||||
|
return jsonify(categories)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/group-icon/<launcher_type>/<group_id>")
|
||||||
|
def get_group_icon(launcher_type, group_id):
|
||||||
|
"""Obtener icono de un grupo (config o launcher)"""
|
||||||
|
try:
|
||||||
|
if launcher_type == "launcher":
|
||||||
|
group = launcher_manager.get_launcher_group(group_id)
|
||||||
|
if not group:
|
||||||
|
return jsonify({"error": "Group not found"}), 404
|
||||||
|
|
||||||
|
icon_path = os.path.join(group["directory"], "icon.ico")
|
||||||
|
if os.path.exists(icon_path):
|
||||||
|
from flask import send_file
|
||||||
|
return send_file(icon_path, mimetype='image/x-icon')
|
||||||
|
|
||||||
|
elif launcher_type == "config":
|
||||||
|
group_path = os.path.join(config_manager.script_groups_path, group_id)
|
||||||
|
icon_path = os.path.join(group_path, "icon.ico")
|
||||||
|
if os.path.exists(icon_path):
|
||||||
|
from flask import send_file
|
||||||
|
return send_file(icon_path, mimetype='image/x-icon')
|
||||||
|
|
||||||
|
# Icono por defecto - devolver datos para que el frontend genere el icono
|
||||||
|
return jsonify({"type": "default", "icon": "📁"})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
# === FIN LAUNCHER GUI APIs ===
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# --- Start Flask in a background thread ---
|
# --- Start Flask in a background thread ---
|
||||||
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,39 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Use TIA Scripting via file import (Only if TIA Scripting is not installed as package)
|
||||||
|
# need to set a global environment variable “TIA_SCRIPTING” with path containing TIA Scripting binaries
|
||||||
|
if os.getenv('TIA_SCRIPTING') == None:
|
||||||
|
# if TIA_SCRIPTING global environment variable is not set
|
||||||
|
# set local variable with the path to TIA Scripting binaries
|
||||||
|
tia_scripting_directory = "C:\\your\\path\\to\\tia-scripting-python"
|
||||||
|
sys.path.append(tia_scripting_directory)
|
||||||
|
else:
|
||||||
|
# TIA_SCRIPTING global environment variable is set and will be used for import
|
||||||
|
sys.path.append(os.getenv('TIA_SCRIPTING'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# import TIA Scripting binaries
|
||||||
|
# if TIA Scripting is installed as package, global environment variable will be ignored
|
||||||
|
import siemens_tia_scripting as ts
|
||||||
|
except ImportError:
|
||||||
|
# you will run into ImportError also if you are using Python version which is not 3.12.X
|
||||||
|
print("siemens_tia_scripting could not be found")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
portal_mode_ui = ts.Enums.PortalMode.AnyUserInterface
|
||||||
|
# need to specify target version e.g. "17.0", requires that TIA portal V17 is installed
|
||||||
|
version = "19.0"
|
||||||
|
|
||||||
|
portal = ts.attach_portal(portal_mode = portal_mode_ui, version = version)
|
||||||
|
|
||||||
|
project = portal.get_project()
|
||||||
|
|
||||||
|
plcs = project.get_plcs()
|
||||||
|
for plc in plcs:
|
||||||
|
print(plc.get_name())
|
||||||
|
|
||||||
|
plcs[0].open_device_editor()
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -0,0 +1,114 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Use TIA Scripting via file import (Only if TIA Scripting is not installed as package)
|
||||||
|
# need to set a global environment variable “TIA_SCRIPTING” with path containing TIA Scripting binaries
|
||||||
|
if os.getenv('TIA_SCRIPTING') == None:
|
||||||
|
# if TIA_SCRIPTING global environment variable is not set
|
||||||
|
# set local variable with the path to TIA Scripting binaries
|
||||||
|
tia_scripting_directory = "C:\\your\\path\\to\\tia-scripting-python"
|
||||||
|
sys.path.append(tia_scripting_directory)
|
||||||
|
else:
|
||||||
|
# TIA_SCRIPTING global environment variable is set and will be used for import
|
||||||
|
sys.path.append(os.getenv('TIA_SCRIPTING'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# import TIA Scripting binaries
|
||||||
|
# if TIA Scripting is installed as package, global environment variable will be ignored
|
||||||
|
import siemens_tia_scripting as ts
|
||||||
|
except ImportError:
|
||||||
|
# you will run into ImportError also if you are using Python version which is not 3.12.X
|
||||||
|
print("siemens_tia_scripting could not be found")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
portal_mode_ui = True
|
||||||
|
keep_tia_portal = False
|
||||||
|
# example values for directories
|
||||||
|
project_file_path = "D:\\TiaScripting\\PLC_ExportAll\\PLC_ExportAll.ap19"
|
||||||
|
export_target_dir = "D:\\TiaScripting\\Export"
|
||||||
|
keep_folder_structure = True
|
||||||
|
# need to specify target version e.g. "17.0", requires that TIA portal V17 is installed
|
||||||
|
version = "19.0"
|
||||||
|
export_options = ts.Enums.ExportOptions.WithDefaults
|
||||||
|
export_format = ts.Enums.ExportFormats.SimaticML
|
||||||
|
|
||||||
|
if portal_mode_ui == True:
|
||||||
|
portal = ts.open_portal(ts.Enums.PortalMode.WithGraphicalUserInterface, version = version)
|
||||||
|
print("- open/attach tia portal with ui")
|
||||||
|
else:
|
||||||
|
portal = ts.open_portal(ts.Enums.PortalMode.WithoutGraphicalUserInterface, version = version)
|
||||||
|
print("- open/attach tia portal without ui")
|
||||||
|
|
||||||
|
project = portal.open_project(project_file_path = project_file_path, server_project_view = False)
|
||||||
|
print("- open project")
|
||||||
|
|
||||||
|
plcs = project.get_plcs()
|
||||||
|
for plc in plcs:
|
||||||
|
print(plc.get_name())
|
||||||
|
for tag_table in plc.get_plc_tag_tables():
|
||||||
|
tag_table.export(target_directory_path = export_target_dir + "\\PlcTags", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
for program_block in plc.get_program_blocks():
|
||||||
|
if program_block.is_consistent() == False:
|
||||||
|
program_block.compile()
|
||||||
|
if program_block.is_consistent() == True:
|
||||||
|
program_block.export(target_directory_path = export_target_dir + "\\ProgramBlocks", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
if program_block.get_property(name="ProgrammingLanguage") == "SCL":
|
||||||
|
program_block.export(target_directory_path = export_target_dir + "\\ProgramBlocks", export_options = export_options, export_format = ts.Enums.ExportFormats.ExternalSource, keep_folder_structure = keep_folder_structure)
|
||||||
|
|
||||||
|
for system_block in plc.get_system_blocks():
|
||||||
|
if system_block.is_consistent() == False:
|
||||||
|
system_block.compile()
|
||||||
|
if system_block.is_consistent() == True:
|
||||||
|
system_block.export(target_directory_path = export_target_dir + "\\ProgramBlocks", export_options = export_options)
|
||||||
|
|
||||||
|
for udt in plc.get_user_data_types():
|
||||||
|
if udt.is_consistent() == False:
|
||||||
|
udt.compile()
|
||||||
|
if udt.is_consistent() == True:
|
||||||
|
udt.export(target_directory_path = export_target_dir + "\\PlcDataTypes", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
udt.export(target_directory_path = export_target_dir + "\\PlcDataTypes", export_options = export_options, keep_folder_structure = True)
|
||||||
|
|
||||||
|
for force_table in plc.get_force_tables():
|
||||||
|
if force_table.is_consistent() == True:
|
||||||
|
force_table.export(target_directory_path = export_target_dir + "\\WatchAndForceTables", export_options = export_options)
|
||||||
|
|
||||||
|
for watch_table in plc.get_watch_tables():
|
||||||
|
if watch_table.is_consistent() == False:
|
||||||
|
watch_table.compile()
|
||||||
|
if watch_table.is_consistent() == True:
|
||||||
|
watch_table.export(target_directory_path = export_target_dir + "\\WatchAndForceTables", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
|
||||||
|
for to in plc.get_technology_objects():
|
||||||
|
if to.is_consistent() == False:
|
||||||
|
to.compile()
|
||||||
|
if to.is_consistent() == True:
|
||||||
|
to.export(target_directory_path = export_target_dir + "\\TechnologyObjects", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
|
||||||
|
for software_unit in plc.get_software_units():
|
||||||
|
target_dir_su= export_target_dir + "\\SoftwareUnits\\" + software_unit.get_name()
|
||||||
|
software_unit.export_configuration(target_directory_path = target_dir_su)
|
||||||
|
for program_block in software_unit.get_program_blocks():
|
||||||
|
if program_block.is_consistent() == False:
|
||||||
|
program_block.compile()
|
||||||
|
if program_block.is_consistent() == True:
|
||||||
|
program_block.export(target_directory_path = target_dir_su + "\\ProgramBlocks", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
if program_block.get_property(name="ProgrammingLanguage") == "SCL":
|
||||||
|
program_block.export(target_directory_path = export_target_dir + "\\ProgramBlocks", export_options = export_options, export_format = ts.Enums.ExportFormats.ExternalSource, keep_folder_structure = keep_folder_structure)
|
||||||
|
|
||||||
|
|
||||||
|
for udt in software_unit.get_user_data_types():
|
||||||
|
if udt.is_consistent() == False:
|
||||||
|
udt.compile()
|
||||||
|
if udt.is_consistent() == True:
|
||||||
|
udt.export(target_directory_path = target_dir_su+ "\\PlcDataTypes", export_options = export_options, keep_folder_structure = keep_folder_structure)
|
||||||
|
udt.export(target_directory_path = target_dir_su + "\\PlcDataTypes", export_options = export_options, keep_folder_structure = True)
|
||||||
|
|
||||||
|
|
||||||
|
if keep_tia_portal == "False":
|
||||||
|
portal.close_portal()
|
||||||
|
print("- close tia portal")
|
||||||
|
|
||||||
|
if keep_tia_portal == "True":
|
||||||
|
print("- keep tia portal")
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -0,0 +1,45 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Use TIA Scripting via file import (Only if TIA Scripting is not installed as package)
|
||||||
|
# need to set a global environment variable “TIA_SCRIPTING” with path containing TIA Scripting binaries
|
||||||
|
if os.getenv('TIA_SCRIPTING') == None:
|
||||||
|
# if TIA_SCRIPTING global environment variable is not set
|
||||||
|
# set local variable with the path to TIA Scripting binaries
|
||||||
|
tia_scripting_directory = "C:\\your\\path\\to\\tia-scripting-python"
|
||||||
|
sys.path.append(tia_scripting_directory)
|
||||||
|
else:
|
||||||
|
# TIA_SCRIPTING global environment variable is set and will be used for import
|
||||||
|
sys.path.append(os.getenv('TIA_SCRIPTING'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# import TIA Scripting binaries
|
||||||
|
# if TIA Scripting is installed as package, global environment variable will be ignored
|
||||||
|
import siemens_tia_scripting as ts
|
||||||
|
except ImportError:
|
||||||
|
# you will run into ImportError also if you are using Python version which is not 3.12.X
|
||||||
|
print("siemens_tia_scripting could not be found")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
bundles = ts.get_installed_bundles()
|
||||||
|
for bundle in bundles:
|
||||||
|
print("---------------------------------------------------------------------------------------")
|
||||||
|
print("Bundle ",bundle.get_title(), bundle.get_release())
|
||||||
|
for product in bundle.get_products():
|
||||||
|
print("product: ",product.get_name(), "release: ", product.get_release(), "version: ",product.get_version())
|
||||||
|
print("---------------------------------------------------------------------------------------")
|
||||||
|
|
||||||
|
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
print("------------------------------------------GET INSTALLED PRODUCTS--------------------------------------------------")
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
print("------------------------------------------------------------------------------------------------------------------")
|
||||||
|
products = ts.get_installed_products()
|
||||||
|
for product in products:
|
||||||
|
print("product: ",product.get_name(), "version: ",product.get_version())
|
||||||
|
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -0,0 +1,59 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Use TIA Scripting via file import (Only if TIA Scripting is not installed as package)
|
||||||
|
# need to set a global environment variable “TIA_SCRIPTING” with path containing TIA Scripting binaries
|
||||||
|
if os.getenv('TIA_SCRIPTING') == None:
|
||||||
|
# if TIA_SCRIPTING global environment variable is not set
|
||||||
|
# set local variable with the path to TIA Scripting binaries
|
||||||
|
tia_scripting_directory = "C:\\your\\path\\to\\tia-scripting-python"
|
||||||
|
sys.path.append(tia_scripting_directory)
|
||||||
|
else:
|
||||||
|
# TIA_SCRIPTING global environment variable is set and will be used for import
|
||||||
|
sys.path.append(os.getenv('TIA_SCRIPTING'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# import TIA Scripting binaries
|
||||||
|
# if TIA Scripting is installed as package, global environment variable will be ignored
|
||||||
|
import siemens_tia_scripting as ts
|
||||||
|
except ImportError:
|
||||||
|
# you will run into ImportError also if you are using Python version which is not 3.12.X
|
||||||
|
print("siemens_tia_scripting could not be found")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
portal_mode_ui = True
|
||||||
|
keep_tia_portal = False
|
||||||
|
project_file_path = "D:\\TiaScripting\\PLC_ImportAll\\PLC_ImportAll.ap17"
|
||||||
|
import_root_dir = "D:\\TiaScripting\\Export"
|
||||||
|
version = "17.0"
|
||||||
|
|
||||||
|
if portal_mode_ui == True:
|
||||||
|
portal = ts.open_portal(ts.Enums.PortalMode.WithGraphicalUserInterface, version = version)
|
||||||
|
print("- open/attach tia portal with ui")
|
||||||
|
else:
|
||||||
|
portal = ts.open_portal(ts.Enums.PortalMode.WithoutGraphicalUserInterface, version = version)
|
||||||
|
print("- open/attach tia portal without ui")
|
||||||
|
|
||||||
|
project = portal.open_project(project_file_path = project_file_path)
|
||||||
|
print("- open project")
|
||||||
|
|
||||||
|
plcs = project.get_plcs()
|
||||||
|
for plc in plcs:
|
||||||
|
try: plc.import_software_units(import_root_directory= import_root_dir + "\\SoftwareUnits")
|
||||||
|
except: pass
|
||||||
|
plc.import_data_types(import_root_directory= import_root_dir + "\\PlcDataTypes")
|
||||||
|
plc.import_technology_objects(import_root_directory= import_root_dir + "\\TechnologyObjects")
|
||||||
|
plc.import_blocks(import_root_directory= import_root_dir + "\\ProgramBlocks")
|
||||||
|
plc.import_plc_tags(import_root_directory= import_root_dir + "\\PlcTags")
|
||||||
|
plc.import_watch_tables(import_root_directory= import_root_dir + "\\WatchAndForceTables")
|
||||||
|
|
||||||
|
|
||||||
|
if keep_tia_portal == "False":
|
||||||
|
portal.close_portal()
|
||||||
|
print("- close tia portal")
|
||||||
|
|
||||||
|
if keep_tia_portal == "True":
|
||||||
|
print("- keep tia portal")
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -0,0 +1,190 @@
|
||||||
|
"""
|
||||||
|
Explorador de la API real del paquete siemens_tiaportal_scripting instalado
|
||||||
|
Descubre qué está realmente disponible
|
||||||
|
"""
|
||||||
|
|
||||||
|
def explore_actual_api():
|
||||||
|
"""Explore what's actually available in the installed package."""
|
||||||
|
|
||||||
|
print("="*70)
|
||||||
|
print("EXPLORADOR DE LA API REAL INSTALADA")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import siemens_tiaportal_scripting as ts
|
||||||
|
print("✅ Paquete importado correctamente")
|
||||||
|
|
||||||
|
# Get all available attributes
|
||||||
|
all_attrs = dir(ts)
|
||||||
|
public_attrs = [attr for attr in all_attrs if not attr.startswith('_')]
|
||||||
|
|
||||||
|
print(f"\n📋 TODOS LOS ATRIBUTOS DISPONIBLES ({len(public_attrs)}):")
|
||||||
|
for i, attr in enumerate(sorted(public_attrs), 1):
|
||||||
|
try:
|
||||||
|
obj = getattr(ts, attr)
|
||||||
|
obj_type = type(obj).__name__
|
||||||
|
print(f" {i:2d}. {attr:<25} : {obj_type}")
|
||||||
|
|
||||||
|
# If it's a callable, try to get more info
|
||||||
|
if callable(obj):
|
||||||
|
try:
|
||||||
|
doc = obj.__doc__
|
||||||
|
if doc:
|
||||||
|
# Show first line of docstring
|
||||||
|
first_line = doc.split('\n')[0].strip()
|
||||||
|
if first_line:
|
||||||
|
print(f" └─ {first_line}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Exception as attr_ex:
|
||||||
|
print(f" {i:2d}. {attr:<25} : Error - {attr_ex}")
|
||||||
|
|
||||||
|
# Look for functions that might be entry points
|
||||||
|
print(f"\n🔍 FUNCIONES DISPONIBLES:")
|
||||||
|
functions = [attr for attr in public_attrs if callable(getattr(ts, attr, None))]
|
||||||
|
|
||||||
|
for func_name in functions:
|
||||||
|
try:
|
||||||
|
func = getattr(ts, func_name)
|
||||||
|
print(f" 📞 {func_name}")
|
||||||
|
|
||||||
|
# Try to get function signature
|
||||||
|
import inspect
|
||||||
|
try:
|
||||||
|
sig = inspect.signature(func)
|
||||||
|
print(f" Signature: {func_name}{sig}")
|
||||||
|
except:
|
||||||
|
print(f" Signature: No disponible")
|
||||||
|
|
||||||
|
# Get docstring
|
||||||
|
doc = func.__doc__
|
||||||
|
if doc:
|
||||||
|
lines = doc.strip().split('\n')
|
||||||
|
for line in lines[:3]: # Show first 3 lines
|
||||||
|
if line.strip():
|
||||||
|
print(f" Doc: {line.strip()}")
|
||||||
|
|
||||||
|
except Exception as func_ex:
|
||||||
|
print(f" 📞 {func_name}: Error - {func_ex}")
|
||||||
|
|
||||||
|
# Look for classes or types
|
||||||
|
print(f"\n🏗️ CLASES/TIPOS DISPONIBLES:")
|
||||||
|
types_found = []
|
||||||
|
for attr in public_attrs:
|
||||||
|
try:
|
||||||
|
obj = getattr(ts, attr)
|
||||||
|
obj_type = type(obj)
|
||||||
|
|
||||||
|
# Check if it's a type/class
|
||||||
|
if str(obj_type) in ['<class \'type\'>', '<class \'ABCMeta\'>'] or hasattr(obj, '__bases__'):
|
||||||
|
types_found.append(attr)
|
||||||
|
print(f" 🏷️ {attr}: {obj_type}")
|
||||||
|
|
||||||
|
# Show methods of the class
|
||||||
|
methods = [m for m in dir(obj) if not m.startswith('_') and callable(getattr(obj, m, None))]
|
||||||
|
if methods:
|
||||||
|
print(f" Methods: {', '.join(methods[:5])}")
|
||||||
|
if len(methods) > 5:
|
||||||
|
print(f" ... and {len(methods)-5} more")
|
||||||
|
|
||||||
|
except Exception as type_ex:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not types_found:
|
||||||
|
print(" ❌ No se encontraron clases obvias")
|
||||||
|
|
||||||
|
# Try to find TIA Portal related functionality
|
||||||
|
print(f"\n🎯 BÚSQUEDA DE FUNCIONALIDAD TIA PORTAL:")
|
||||||
|
tia_keywords = ['tia', 'portal', 'open', 'connect', 'project', 'export']
|
||||||
|
|
||||||
|
tia_related = []
|
||||||
|
for attr in public_attrs:
|
||||||
|
attr_lower = attr.lower()
|
||||||
|
if any(keyword in attr_lower for keyword in tia_keywords):
|
||||||
|
tia_related.append(attr)
|
||||||
|
|
||||||
|
if tia_related:
|
||||||
|
print(" Elementos relacionados con TIA Portal:")
|
||||||
|
for attr in tia_related:
|
||||||
|
try:
|
||||||
|
obj = getattr(ts, attr)
|
||||||
|
print(f" ✅ {attr}: {type(obj).__name__}")
|
||||||
|
except:
|
||||||
|
print(f" ❌ {attr}: Error")
|
||||||
|
else:
|
||||||
|
print(" ❌ No se encontraron elementos obvios relacionados con TIA Portal")
|
||||||
|
|
||||||
|
# Try some common function names that might exist
|
||||||
|
print(f"\n🔍 PROBANDO FUNCIONES COMUNES:")
|
||||||
|
common_functions = [
|
||||||
|
'open_portal',
|
||||||
|
'OpenPortal',
|
||||||
|
'open_project',
|
||||||
|
'OpenProject',
|
||||||
|
'connect',
|
||||||
|
'Connect',
|
||||||
|
'TiaPortal',
|
||||||
|
'create_portal',
|
||||||
|
'get_portal'
|
||||||
|
]
|
||||||
|
|
||||||
|
for func_name in common_functions:
|
||||||
|
if hasattr(ts, func_name):
|
||||||
|
print(f" ✅ {func_name}: Disponible")
|
||||||
|
try:
|
||||||
|
func = getattr(ts, func_name)
|
||||||
|
print(f" Tipo: {type(func).__name__}")
|
||||||
|
if callable(func):
|
||||||
|
try:
|
||||||
|
sig = inspect.signature(func)
|
||||||
|
print(f" Signature: {func_name}{sig}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print(f" ❌ {func_name}: No disponible")
|
||||||
|
|
||||||
|
# Check the module file for more info
|
||||||
|
print(f"\n📁 INFORMACIÓN DEL ARCHIVO:")
|
||||||
|
if hasattr(ts, '__file__'):
|
||||||
|
print(f" Archivo: {ts.__file__}")
|
||||||
|
|
||||||
|
# Try to read some info from the file
|
||||||
|
import os
|
||||||
|
file_path = ts.__file__
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
file_size = os.path.getsize(file_path)
|
||||||
|
print(f" Tamaño: {file_size:,} bytes")
|
||||||
|
|
||||||
|
# Check if there are any associated files
|
||||||
|
file_dir = os.path.dirname(file_path)
|
||||||
|
files_in_dir = os.listdir(file_dir)
|
||||||
|
related_files = [f for f in files_in_dir if 'siemens' in f.lower() or 'tia' in f.lower()]
|
||||||
|
if related_files:
|
||||||
|
print(f" Archivos relacionados:")
|
||||||
|
for f in related_files[:10]:
|
||||||
|
print(f" - {f}")
|
||||||
|
|
||||||
|
print(f"\n" + "="*70)
|
||||||
|
print("🎯 ANÁLISIS COMPLETADO")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error durante la exploración: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
success = explore_actual_api()
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print(f"\n📋 Basándome en esta información, crearé el exportador correcto")
|
||||||
|
else:
|
||||||
|
print(f"\n❌ No se pudo explorar la API")
|
||||||
|
|
||||||
|
input("\nPresiona Enter para salir...")
|
|
@ -0,0 +1,229 @@
|
||||||
|
"""
|
||||||
|
Verificar si las DLLs de TIA Portal V20 están disponibles para pythonnet
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import clr
|
||||||
|
|
||||||
|
def check_v20_dlls():
|
||||||
|
"""Check if TIA Portal V20 DLLs are available."""
|
||||||
|
|
||||||
|
print("="*70)
|
||||||
|
print("VERIFICADOR DE DLL TIA PORTAL V20 PARA SIMATIC SD")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
# Possible paths for V20 DLLs
|
||||||
|
v20_paths = [
|
||||||
|
r"C:\Program Files\Siemens\Automation\Portal V20\PublicAPI\V20",
|
||||||
|
r"C:\Program Files (x86)\Siemens\Automation\Portal V20\PublicAPI\V20",
|
||||||
|
r"C:\Program Files\Siemens\Automation\Portal V20\bin\PublicAPI\V20"
|
||||||
|
]
|
||||||
|
|
||||||
|
print("🔍 Buscando instalación de TIA Portal V20...")
|
||||||
|
|
||||||
|
v20_path = None
|
||||||
|
for path in v20_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
dll_path = os.path.join(path, "Siemens.Engineering.dll")
|
||||||
|
if os.path.exists(dll_path):
|
||||||
|
v20_path = path
|
||||||
|
print(f"✅ Encontrado TIA Portal V20 en: {path}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"❌ Directorio existe pero falta Siemens.Engineering.dll: {path}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Directorio no encontrado: {path}")
|
||||||
|
|
||||||
|
if not v20_path:
|
||||||
|
print("\n❌ TIA Portal V20 no encontrado")
|
||||||
|
print("\n📋 OPCIONES DISPONIBLES:")
|
||||||
|
print("1. 🔧 Instalar TIA Portal V20 completo con Openness")
|
||||||
|
print("2. 🔧 Usar C# en lugar de Python")
|
||||||
|
print("3. 🔧 Esperar a una versión más nueva del paquete Python")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"\n📁 Explorando DLLs en: {v20_path}")
|
||||||
|
|
||||||
|
# List all DLLs
|
||||||
|
try:
|
||||||
|
dlls = [f for f in os.listdir(v20_path) if f.endswith('.dll')]
|
||||||
|
print(f"DLLs encontradas: {len(dlls)}")
|
||||||
|
|
||||||
|
for dll in sorted(dlls):
|
||||||
|
print(f" - {dll}")
|
||||||
|
|
||||||
|
# Check for V20 specific DLLs
|
||||||
|
v20_dll = "Siemens.TiaPortal.OpennessApi20.dll"
|
||||||
|
if v20_dll in dlls:
|
||||||
|
print(f"\n✅ ¡{v20_dll} encontrada!")
|
||||||
|
print(" Esto significa que tienes API V20 disponible")
|
||||||
|
else:
|
||||||
|
print(f"\n❌ {v20_dll} NO encontrada")
|
||||||
|
print(" Solo tienes APIs hasta V19 - sin SIMATIC SD")
|
||||||
|
|
||||||
|
# List available API versions
|
||||||
|
api_dlls = [dll for dll in dlls if "OpennessApi" in dll]
|
||||||
|
if api_dlls:
|
||||||
|
print(f" APIs disponibles: {api_dlls}")
|
||||||
|
|
||||||
|
except Exception as list_ex:
|
||||||
|
print(f"❌ Error listando DLLs: {list_ex}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Try to load V20 DLLs with pythonnet
|
||||||
|
print(f"\n🔧 Probando carga con pythonnet...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
sys.path.append(v20_path)
|
||||||
|
|
||||||
|
# Try to load main assembly
|
||||||
|
clr.AddReference("Siemens.Engineering")
|
||||||
|
print("✅ Siemens.Engineering cargada")
|
||||||
|
|
||||||
|
# Try to import V20-specific classes
|
||||||
|
try:
|
||||||
|
from Siemens.Engineering import TiaPortal, TiaPortalMode
|
||||||
|
print("✅ TiaPortal y TiaPortalMode importados")
|
||||||
|
|
||||||
|
# Try to connect
|
||||||
|
try:
|
||||||
|
portal = TiaPortal.Open(TiaPortalMode.WithoutUserInterface)
|
||||||
|
if portal:
|
||||||
|
print("✅ Portal V20 abierto exitosamente")
|
||||||
|
|
||||||
|
# This would be where you test ExportAsDocuments
|
||||||
|
print("🎯 Portal V20 funcional - SIMATIC SD potencialmente disponible")
|
||||||
|
|
||||||
|
portal.Close()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("❌ No se pudo abrir portal")
|
||||||
|
|
||||||
|
except Exception as portal_ex:
|
||||||
|
print(f"❌ Error abriendo portal: {portal_ex}")
|
||||||
|
|
||||||
|
except Exception as import_ex:
|
||||||
|
print(f"❌ Error importando clases V20: {import_ex}")
|
||||||
|
|
||||||
|
except Exception as load_ex:
|
||||||
|
print(f"❌ Error cargando DLLs con pythonnet: {load_ex}")
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_c_sharp_alternative():
|
||||||
|
"""Create a C# script as alternative."""
|
||||||
|
|
||||||
|
print(f"\n🔧 CREANDO ALTERNATIVA EN C#...")
|
||||||
|
|
||||||
|
csharp_code = '''
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Siemens.Engineering;
|
||||||
|
using Siemens.Engineering.HW.Features;
|
||||||
|
|
||||||
|
class SimaticSDExporter
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Uso: SimaticSDExporter.exe <proyecto.ap20> <directorio_export>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string projectPath = args[0];
|
||||||
|
string exportDir = args[1];
|
||||||
|
|
||||||
|
TiaPortal portal = null;
|
||||||
|
Project project = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Abrir TIA Portal V20
|
||||||
|
portal = TiaPortal.Open(TiaPortalMode.WithUserInterface);
|
||||||
|
Console.WriteLine("✅ TIA Portal V20 abierto");
|
||||||
|
|
||||||
|
// Abrir proyecto
|
||||||
|
project = portal.Projects.Open(projectPath);
|
||||||
|
Console.WriteLine($"✅ Proyecto abierto: {project.Name}");
|
||||||
|
|
||||||
|
// Buscar PLCs
|
||||||
|
foreach (Device device in project.Devices)
|
||||||
|
{
|
||||||
|
foreach (DeviceItem deviceItem in device.DeviceItems)
|
||||||
|
{
|
||||||
|
if (deviceItem.GetService<SoftwareContainer>() != null)
|
||||||
|
{
|
||||||
|
var sw = deviceItem.GetService<SoftwareContainer>().Software;
|
||||||
|
Console.WriteLine($"Procesando PLC: {deviceItem.Name}");
|
||||||
|
|
||||||
|
// Exportar bloques en SIMATIC SD
|
||||||
|
foreach (var block in sw.BlockGroup.Blocks)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string blockExportDir = Path.Combine(exportDir, deviceItem.Name, "SIMATIC_SD");
|
||||||
|
Directory.CreateDirectory(blockExportDir);
|
||||||
|
|
||||||
|
// ¡AQUÍ ESTÁ EL MÉTODO SIMATIC SD!
|
||||||
|
block.ExportAsDocuments(
|
||||||
|
new DirectoryInfo(blockExportDir),
|
||||||
|
block.Name
|
||||||
|
);
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ {block.Name} exportado en SIMATIC SD");
|
||||||
|
}
|
||||||
|
catch (Exception blockEx)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Error con {block.Name}: {blockEx.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("🎉 Exportación SIMATIC SD completada");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
project?.Close();
|
||||||
|
portal?.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Save C# code to file
|
||||||
|
csharp_file = "SimaticSDExporter.cs"
|
||||||
|
with open(csharp_file, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(csharp_code)
|
||||||
|
|
||||||
|
print(f"✅ Código C# guardado en: {csharp_file}")
|
||||||
|
print(f"\n📋 PARA COMPILAR Y USAR:")
|
||||||
|
print(f"1. Instalar Visual Studio o .NET SDK")
|
||||||
|
print(f"2. Agregar referencias a TIA Portal V20 DLLs")
|
||||||
|
print(f"3. Compilar: csc SimaticSDExporter.cs /r:Siemens.Engineering.dll")
|
||||||
|
print(f"4. Ejecutar: SimaticSDExporter.exe proyecto.ap20 directorio_export")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
has_v20 = check_v20_dlls()
|
||||||
|
|
||||||
|
if not has_v20:
|
||||||
|
create_c_sharp_alternative()
|
||||||
|
|
||||||
|
print(f"\n" + "="*70)
|
||||||
|
print("📋 RESUMEN DE OPCIONES PARA SIMATIC SD:")
|
||||||
|
print("="*70)
|
||||||
|
print("❌ Python: API V19 no tiene SIMATIC SD")
|
||||||
|
print("✅ C#: Acceso directo a ExportAsDocuments")
|
||||||
|
print("🔧 Requiere: TIA Portal V20 + Visual Studio")
|
||||||
|
else:
|
||||||
|
print(f"\n🎉 ¡TIA Portal V20 disponible para Python!")
|
||||||
|
print("Puedo crear un exportador Python con pythonnet + V20 DLLs")
|
||||||
|
|
||||||
|
input("\nPresiona Enter para salir...")
|
|
@ -0,0 +1,80 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Siemens.Engineering;
|
||||||
|
using Siemens.Engineering.HW.Features;
|
||||||
|
|
||||||
|
class SimaticSDExporter
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Uso: SimaticSDExporter.exe <proyecto.ap20> <directorio_export>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string projectPath = args[0];
|
||||||
|
string exportDir = args[1];
|
||||||
|
|
||||||
|
TiaPortal portal = null;
|
||||||
|
Project project = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Abrir TIA Portal V20
|
||||||
|
portal = TiaPortal.Open(TiaPortalMode.WithUserInterface);
|
||||||
|
Console.WriteLine("✅ TIA Portal V20 abierto");
|
||||||
|
|
||||||
|
// Abrir proyecto
|
||||||
|
project = portal.Projects.Open(projectPath);
|
||||||
|
Console.WriteLine($"✅ Proyecto abierto: {project.Name}");
|
||||||
|
|
||||||
|
// Buscar PLCs
|
||||||
|
foreach (Device device in project.Devices)
|
||||||
|
{
|
||||||
|
foreach (DeviceItem deviceItem in device.DeviceItems)
|
||||||
|
{
|
||||||
|
if (deviceItem.GetService<SoftwareContainer>() != null)
|
||||||
|
{
|
||||||
|
var sw = deviceItem.GetService<SoftwareContainer>().Software;
|
||||||
|
Console.WriteLine($"Procesando PLC: {deviceItem.Name}");
|
||||||
|
|
||||||
|
// Exportar bloques en SIMATIC SD
|
||||||
|
foreach (var block in sw.BlockGroup.Blocks)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string blockExportDir = Path.Combine(exportDir, deviceItem.Name, "SIMATIC_SD");
|
||||||
|
Directory.CreateDirectory(blockExportDir);
|
||||||
|
|
||||||
|
// ¡AQUÍ ESTÁ EL MÉTODO SIMATIC SD!
|
||||||
|
block.ExportAsDocuments(
|
||||||
|
new DirectoryInfo(blockExportDir),
|
||||||
|
block.Name
|
||||||
|
);
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ {block.Name} exportado en SIMATIC SD");
|
||||||
|
}
|
||||||
|
catch (Exception blockEx)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Error con {block.Name}: {blockEx.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("🎉 Exportación SIMATIC SD completada");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
project?.Close();
|
||||||
|
portal?.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,424 +1,242 @@
|
||||||
--- Log de Ejecución: x4.py ---
|
--- Log de Ejecución: x4.py ---
|
||||||
Grupo: ObtainIOFromProjectTia
|
Grupo: ObtainIOFromProjectTia
|
||||||
Directorio de Trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport
|
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML
|
||||||
Inicio: 2025-05-05 13:26:23
|
Inicio: 2025-05-20 12:12:51
|
||||||
Fin: 2025-05-05 13:35:16
|
Fin: 2025-05-20 12:20:01
|
||||||
Duración: 0:08:53.119788
|
Duración: 0:07:09.648304
|
||||||
Estado: SUCCESS (Código de Salida: 0)
|
Estado: SUCCESS (Código de Salida: 0)
|
||||||
|
|
||||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||||
--- TIA Portal Cross-Reference Exporter ---
|
--- TIA Portal Cross-Reference Exporter ---
|
||||||
|
|
||||||
Selected Project: C:/Trabajo/SIDEL/06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)/InLavoro/PLC/SAE196_c0.2/SAE196_c0.2.ap18
|
Selected Project: C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452/SAE452.ap18
|
||||||
Using Base Export Directory: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport
|
Using Base Export Directory: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML
|
||||||
|
|
||||||
Connecting to TIA Portal V18.0...
|
Connecting to TIA Portal V18.0...
|
||||||
2025-05-05 13:26:29,175 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
2025-05-20 12:12:56,780 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||||
2025-05-05 13:26:29,200 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - With user interface
|
2025-05-20 12:12:56,804 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Global OpenPortal - With user interface
|
||||||
Connected to TIA Portal.
|
Connected to TIA Portal.
|
||||||
2025-05-05 13:27:07,831 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal GetProcessId - Process id: 5272
|
2025-05-20 12:13:30,582 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal GetProcessId - Process id: 21952
|
||||||
Portal Process ID: 5272
|
Portal Process ID: 21952
|
||||||
Opening project: SAE196_c0.2.ap18...
|
Opening project: SAE452.ap18...
|
||||||
2025-05-05 13:27:08,303 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal OpenProject - Open project... C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\InLavoro\PLC\SAE196_c0.2\SAE196_c0.2.ap18
|
2025-05-20 12:13:31,077 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal OpenProject - Open project... C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\Migration\SAE452\SAE452.ap18
|
||||||
Project opened successfully.
|
Project opened successfully.
|
||||||
2025-05-05 13:27:39,932 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Project GetPlcs - Found plc PLC with parent name S71500/ET200MP station_1
|
2025-05-20 12:14:15,234 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Project GetPlcs - Found plc CPU 315F-2 PN/DP with parent name _SSAE0452
|
||||||
Found 1 PLC(s). Starting cross-reference export process...
|
Found 1 PLC(s). Starting cross-reference export process...
|
||||||
|
|
||||||
--- Processing PLC: PLC ---
|
--- Processing PLC: CPU 315F-2 PN/DP ---
|
||||||
|
|
||||||
[PLC: PLC] Exporting Program Block Cross-References...
|
[PLC: CPU 315F-2 PN/DP] Exporting Program Block Cross-References...
|
||||||
Target: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\ProgramBlocks_CR
|
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\ProgramBlocks_CR
|
||||||
Found 380 program blocks.
|
Found 410 program blocks.
|
||||||
Processing block: _CYCL_EXC...
|
Processing block: ISOonTCP_or_TCP_Protocol...
|
||||||
Exporting cross-references for _CYCL_EXC...
|
Exporting cross-references for ISOonTCP_or_TCP_Protocol...
|
||||||
Processing block: COMPLETE RESTART...
|
Processing block: PIDControl...
|
||||||
Exporting cross-references for COMPLETE RESTART...
|
Exporting cross-references for PIDControl...
|
||||||
Processing block: I/O_FLT1...
|
Processing block: DETAIL_DP_DIAG...
|
||||||
Exporting cross-references for I/O_FLT1...
|
Exporting cross-references for DETAIL_DP_DIAG...
|
||||||
Processing block: MOD_ERR...
|
Processing block: Net Dosing Sys Prof...
|
||||||
Exporting cross-references for MOD_ERR...
|
Exporting cross-references for Net Dosing Sys Prof...
|
||||||
Processing block: ProDiagOB...
|
Processing block: ICS Profibus Comm...
|
||||||
Exporting cross-references for ProDiagOB...
|
Exporting cross-references for ICS Profibus Comm...
|
||||||
Processing block: Programming error...
|
Processing block: GNS DriveDiag...
|
||||||
Exporting cross-references for Programming error...
|
Exporting cross-references for GNS DriveDiag...
|
||||||
Processing block: RACK_FLT...
|
Processing block: HMI_Blender_Parameters...
|
||||||
Exporting cross-references for RACK_FLT...
|
Exporting cross-references for HMI_Blender_Parameters...
|
||||||
Processing block: Time error interrupt...
|
Processing block: HMI Drive...
|
||||||
Exporting cross-references for Time error interrupt...
|
Exporting cross-references for HMI Drive...
|
||||||
Processing block: Baialage...
|
Processing block: GNS DriveDiagMain...
|
||||||
Exporting cross-references for Baialage...
|
Exporting cross-references for GNS DriveDiagMain...
|
||||||
Processing block: BlenderCtrl__Main...
|
Processing block: Integral...
|
||||||
Exporting cross-references for BlenderCtrl__Main...
|
Exporting cross-references for Integral...
|
||||||
Processing block: BlenderCtrl_CIPModeInit...
|
Processing block: LowPassFilter...
|
||||||
Exporting cross-references for BlenderCtrl_CIPModeInit...
|
Exporting cross-references for LowPassFilter...
|
||||||
Processing block: BlenderCtrl_ProdModeInit...
|
Processing block: SlewLimit...
|
||||||
Exporting cross-references for BlenderCtrl_ProdModeInit...
|
Exporting cross-references for SlewLimit...
|
||||||
Processing block: BlenderCtrl_ResetSPWord...
|
Processing block: MSE Slope...
|
||||||
Exporting cross-references for BlenderCtrl_ResetSPWord...
|
Exporting cross-references for MSE Slope...
|
||||||
Processing block: BlenderCtrl_UpdatePWord...
|
Processing block: Statistical_Analisys...
|
||||||
Exporting cross-references for BlenderCtrl_UpdatePWord...
|
Exporting cross-references for Statistical_Analisys...
|
||||||
Processing block: BlenderPID_NextRecipe...
|
Processing block: Blender_Variables...
|
||||||
Exporting cross-references for BlenderPID_NextRecipe...
|
Exporting cross-references for Blender_Variables...
|
||||||
Processing block: BlenderRinse...
|
Processing block: BrixTracking_ProdSamples...
|
||||||
Exporting cross-references for BlenderRinse...
|
Exporting cross-references for BrixTracking_ProdSamples...
|
||||||
Processing block: BlenderRinse_Done...
|
Processing block: Procedure_Variables...
|
||||||
Exporting cross-references for BlenderRinse_Done...
|
Exporting cross-references for Procedure_Variables...
|
||||||
Processing block: BlenderRun_ProdTime...
|
Processing block: Blender_Constants...
|
||||||
Exporting cross-references for BlenderRun_ProdTime...
|
Exporting cross-references for Blender_Constants...
|
||||||
Processing block: BlenderRun_Stopping...
|
Processing block: BrixTracking_SampleTime...
|
||||||
Exporting cross-references for BlenderRun_Stopping...
|
Exporting cross-references for BrixTracking_SampleTime...
|
||||||
Processing block: Blocco_1...
|
Processing block: Delay...
|
||||||
Exporting cross-references for Blocco_1...
|
Exporting cross-references for Delay...
|
||||||
Processing block: Block_compare...
|
Processing block: CO2Tracking_ProdSamples...
|
||||||
Exporting cross-references for Block_compare...
|
Exporting cross-references for CO2Tracking_ProdSamples...
|
||||||
Processing block: Block_move...
|
Processing block: CO2Tracking_SampleTime...
|
||||||
Exporting cross-references for Block_move...
|
Exporting cross-references for CO2Tracking_SampleTime...
|
||||||
Processing block: CarboWaterLine_Seq...
|
Processing block: Interlocking_Variables...
|
||||||
Exporting cross-references for CarboWaterLine_Seq...
|
Exporting cross-references for Interlocking_Variables...
|
||||||
Processing block: Cetrifugal_Head...
|
Processing block: System_RunOut_Variables...
|
||||||
Exporting cross-references for Cetrifugal_Head...
|
Exporting cross-references for System_RunOut_Variables...
|
||||||
Processing block: CIP CVQ...
|
Processing block: CIP_Program_Variables...
|
||||||
Exporting cross-references for CIP CVQ...
|
Exporting cross-references for CIP_Program_Variables...
|
||||||
Processing block: CIP FlipFlop...
|
Processing block: Filler_Head_Variables...
|
||||||
Exporting cross-references for CIP FlipFlop...
|
Exporting cross-references for Filler_Head_Variables...
|
||||||
Processing block: CIPLocal_ProgInizialize...
|
Processing block: Filling_Time_Tranfer_DB...
|
||||||
Exporting cross-references for CIPLocal_ProgInizialize...
|
Exporting cross-references for Filling_Time_Tranfer_DB...
|
||||||
Processing block: CIPLocal_WaitEvent_Ctrl...
|
Processing block: Blender_Variables_Pers...
|
||||||
Exporting cross-references for CIPLocal_WaitEvent_Ctrl...
|
Exporting cross-references for Blender_Variables_Pers...
|
||||||
Processing block: CIPMain...
|
Processing block: HMI_Alarms...
|
||||||
Exporting cross-references for CIPMain...
|
Exporting cross-references for HMI_Alarms...
|
||||||
Processing block: CIPMain_Flood...
|
Processing block: HMI_Local_CIP_Variables...
|
||||||
Exporting cross-references for CIPMain_Flood...
|
Exporting cross-references for HMI_Local_CIP_Variables...
|
||||||
Processing block: CIPMain_Total Drain...
|
Processing block: HMI_Service...
|
||||||
Exporting cross-references for CIPMain_Total Drain...
|
Exporting cross-references for HMI_Service...
|
||||||
Processing block: Clock Signal...
|
Processing block: HMI_Variables_Cmd...
|
||||||
Exporting cross-references for Clock Signal...
|
Exporting cross-references for HMI_Variables_Cmd...
|
||||||
Processing block: CO2 Solubility...
|
Processing block: HMI_Variables_Status...
|
||||||
Exporting cross-references for CO2 Solubility...
|
Exporting cross-references for HMI_Variables_Status...
|
||||||
Processing block: CO2EqPress...
|
Processing block: HMI_Device...
|
||||||
Exporting cross-references for CO2EqPress...
|
Exporting cross-references for HMI_Device...
|
||||||
Processing block: CO2InjPressure...
|
Processing block: HMI_Instrument...
|
||||||
Exporting cross-references for CO2InjPressure...
|
Exporting cross-references for HMI_Instrument...
|
||||||
Processing block: CTRLCoolingSystem...
|
Processing block: HMI_Digital...
|
||||||
Exporting cross-references for CTRLCoolingSystem...
|
Exporting cross-references for HMI_Digital...
|
||||||
Processing block: DeairCO2TempComp...
|
Processing block: HMI_PID...
|
||||||
Exporting cross-references for DeairCO2TempComp...
|
Exporting cross-references for HMI_PID...
|
||||||
Processing block: DeaireationValve...
|
Processing block: HMI_ICS...
|
||||||
Exporting cross-references for DeaireationValve...
|
Exporting cross-references for HMI_ICS...
|
||||||
Processing block: Deaireator StartUp_Seq...
|
Processing block: HMI_Device_AVS...
|
||||||
Exporting cross-references for Deaireator StartUp_Seq...
|
Exporting cross-references for HMI_Device_AVS...
|
||||||
Processing block: DeltaP...
|
Processing block: Profibus_Variables...
|
||||||
Exporting cross-references for DeltaP...
|
Exporting cross-references for Profibus_Variables...
|
||||||
Processing block: FeedForward...
|
Processing block: Input_CheckFlowMetersSta...
|
||||||
Exporting cross-references for FeedForward...
|
Exporting cross-references for Input_CheckFlowMetersSta...
|
||||||
Processing block: Flow_To_Press_Loss...
|
Processing block: Input_DigitalScanner...
|
||||||
Exporting cross-references for Flow_To_Press_Loss...
|
Exporting cross-references for Input_DigitalScanner...
|
||||||
Processing block: Freq_To_mmH2O...
|
|
||||||
Exporting cross-references for Freq_To_mmH2O...
|
|
||||||
Processing block: FrictionLoss...
|
|
||||||
Exporting cross-references for FrictionLoss...
|
|
||||||
Processing block: FW_DRand...
|
|
||||||
Exporting cross-references for FW_DRand...
|
|
||||||
Processing block: GetProdBrixCO2_Anal_Inpt...
|
|
||||||
Exporting cross-references for GetProdBrixCO2_Anal_Inpt...
|
|
||||||
Processing block: Interlocking_Panel_1...
|
|
||||||
Exporting cross-references for Interlocking_Panel_1...
|
|
||||||
Processing block: ITC Communic CIPRoom...
|
|
||||||
Exporting cross-references for ITC Communic CIPRoom...
|
|
||||||
Processing block: ITC Communic Filler...
|
|
||||||
Exporting cross-references for ITC Communic Filler...
|
|
||||||
Processing block: ITC Communic MainRoutine...
|
|
||||||
Exporting cross-references for ITC Communic MainRoutine...
|
|
||||||
Processing block: ITC Communic ProdRoom...
|
|
||||||
Exporting cross-references for ITC Communic ProdRoom...
|
|
||||||
Processing block: ITC DataIn...
|
|
||||||
Exporting cross-references for ITC DataIn...
|
|
||||||
Processing block: ITC DataOut...
|
|
||||||
Exporting cross-references for ITC DataOut...
|
|
||||||
Processing block: ITC Exchange MainRoutine...
|
|
||||||
Exporting cross-references for ITC Exchange MainRoutine...
|
|
||||||
Processing block: ITC MainRoutine...
|
|
||||||
Exporting cross-references for ITC MainRoutine...
|
|
||||||
Processing block: LIMIT_I...
|
|
||||||
Exporting cross-references for LIMIT_I...
|
|
||||||
Processing block: LIMIT_R...
|
|
||||||
Exporting cross-references for LIMIT_R...
|
|
||||||
Processing block: Maselli_PA_Control...
|
|
||||||
Exporting cross-references for Maselli_PA_Control...
|
|
||||||
Processing block: Maselli_PA_Ctrl_Transfer...
|
|
||||||
Exporting cross-references for Maselli_PA_Ctrl_Transfer...
|
|
||||||
Processing block: Maselli_PA_Ctrl_Write...
|
|
||||||
Exporting cross-references for Maselli_PA_Ctrl_Write...
|
|
||||||
Processing block: MFMAnalogValues_Totalize...
|
|
||||||
Exporting cross-references for MFMAnalogValues_Totalize...
|
|
||||||
Processing block: mmH2O_TO_Freq...
|
|
||||||
Exporting cross-references for mmH2O_TO_Freq...
|
|
||||||
Processing block: ModValveFault...
|
|
||||||
Exporting cross-references for ModValveFault...
|
|
||||||
Processing block: mPDS_SYR_PA_Control...
|
|
||||||
Exporting cross-references for mPDS_SYR_PA_Control...
|
|
||||||
Processing block: ONS_R...
|
|
||||||
Exporting cross-references for ONS_R...
|
|
||||||
Processing block: Prod Tank RunOut_Seq...
|
|
||||||
Exporting cross-references for Prod Tank RunOut_Seq...
|
|
||||||
Processing block: ProductLiterInTank...
|
Processing block: ProductLiterInTank...
|
||||||
Exporting cross-references for ProductLiterInTank...
|
Exporting cross-references for ProductLiterInTank...
|
||||||
Processing block: ProductPipeDrain_Seq...
|
Processing block: ProductAvailable...
|
||||||
Exporting cross-references for ProductPipeDrain_Seq...
|
Exporting cross-references for ProductAvailable...
|
||||||
Processing block: ProductPipeRunOut_Seq...
|
Processing block: T_Timer...
|
||||||
Exporting cross-references for ProductPipeRunOut_Seq...
|
Exporting cross-references for T_Timer...
|
||||||
Processing block: ProductQuality...
|
|
||||||
Exporting cross-references for ProductQuality...
|
|
||||||
Processing block: SEL_I...
|
Processing block: SEL_I...
|
||||||
Exporting cross-references for SEL_I...
|
Exporting cross-references for SEL_I...
|
||||||
Processing block: SEL_R...
|
|
||||||
Exporting cross-references for SEL_R...
|
|
||||||
Processing block: SelCheckBrixSource...
|
|
||||||
Exporting cross-references for SelCheckBrixSource...
|
|
||||||
Processing block: SLIM_Block...
|
|
||||||
Exporting cross-references for SLIM_Block...
|
|
||||||
Processing block: SpeedAdjust...
|
|
||||||
Exporting cross-references for SpeedAdjust...
|
|
||||||
Processing block: Syrup Line MFM Prep_Seq...
|
|
||||||
Exporting cross-references for Syrup Line MFM Prep_Seq...
|
|
||||||
Processing block: Syrup MFM StartUp_Seq...
|
|
||||||
Exporting cross-references for Syrup MFM StartUp_Seq...
|
|
||||||
Processing block: SyrupDensity...
|
|
||||||
Exporting cross-references for SyrupDensity...
|
|
||||||
Processing block: SyrupRoomCtrl...
|
|
||||||
Exporting cross-references for SyrupRoomCtrl...
|
|
||||||
Processing block: WaterDensity...
|
|
||||||
Exporting cross-references for WaterDensity...
|
|
||||||
Processing block: WritePeripheral...
|
|
||||||
Exporting cross-references for WritePeripheral...
|
|
||||||
Processing block: CIPRecipeManagement_Data...
|
|
||||||
Exporting cross-references for CIPRecipeManagement_Data...
|
|
||||||
Processing block: Co2_Counters_DB...
|
|
||||||
Exporting cross-references for Co2_Counters_DB...
|
|
||||||
Processing block: Default_SupervisionDB...
|
|
||||||
Exporting cross-references for Default_SupervisionDB...
|
|
||||||
Processing block: ITC Communic CIP DI...
|
|
||||||
Exporting cross-references for ITC Communic CIP DI...
|
|
||||||
Processing block: ITC Communic Filler DI...
|
|
||||||
Exporting cross-references for ITC Communic Filler DI...
|
|
||||||
Processing block: ITC Communic Mixer DI...
|
|
||||||
Exporting cross-references for ITC Communic Mixer DI...
|
|
||||||
Processing block: ITC Communic Product Room DI...
|
|
||||||
Exporting cross-references for ITC Communic Product Room DI...
|
|
||||||
Processing block: Key Read & Write Data...
|
|
||||||
Exporting cross-references for Key Read & Write Data...
|
|
||||||
Processing block: mPPM303StartUpRamp...
|
|
||||||
Exporting cross-references for mPPM303StartUpRamp...
|
|
||||||
Processing block: PID_RMM304_Data...
|
|
||||||
Exporting cross-references for PID_RMM304_Data...
|
|
||||||
Processing block: PID_RVN302_Data...
|
|
||||||
Exporting cross-references for PID_RVN302_Data...
|
|
||||||
Processing block: PID_RVS318_Data...
|
|
||||||
Exporting cross-references for PID_RVS318_Data...
|
|
||||||
Processing block: ProdBrixRecovery_DB...
|
|
||||||
Exporting cross-references for ProdBrixRecovery_DB...
|
|
||||||
Processing block: Prod Tank Drain_Seq...
|
|
||||||
Exporting cross-references for Prod Tank Drain_Seq...
|
|
||||||
Processing block: _StepMove...
|
Processing block: _StepMove...
|
||||||
Exporting cross-references for _StepMove...
|
Exporting cross-references for _StepMove...
|
||||||
Processing block: _StepMove_Test...
|
Processing block: ProductPipeDrain_Seq...
|
||||||
Exporting cross-references for _StepMove_Test...
|
Exporting cross-references for ProductPipeDrain_Seq...
|
||||||
Processing block: RecipeManagement_Data...
|
Processing block: ProductPipeDrain...
|
||||||
Exporting cross-references for RecipeManagement_Data...
|
Exporting cross-references for ProductPipeDrain...
|
||||||
Processing block: Blender_Procedure Data...
|
Processing block: ProductPipeRunOut_Seq...
|
||||||
Exporting cross-references for Blender_Procedure Data...
|
Exporting cross-references for ProductPipeRunOut_Seq...
|
||||||
Processing block: BlenderPID__Main_Data...
|
Processing block: SEL_R...
|
||||||
Exporting cross-references for BlenderPID__Main_Data...
|
Exporting cross-references for SEL_R...
|
||||||
Processing block: BlenderRun_MeasFil_Data...
|
Processing block: ProductPipeRunOut...
|
||||||
Exporting cross-references for BlenderRun_MeasFil_Data...
|
Exporting cross-references for ProductPipeRunOut...
|
||||||
Processing block: BrixTracking_Data...
|
Processing block: LIMIT_I...
|
||||||
Exporting cross-references for BrixTracking_Data...
|
Exporting cross-references for LIMIT_I...
|
||||||
Processing block: CO2Tracking_Data...
|
Processing block: System_Run_Out...
|
||||||
Exporting cross-references for CO2Tracking_Data...
|
Exporting cross-references for System_Run_Out...
|
||||||
Processing block: FirstProduction_Data...
|
|
||||||
Exporting cross-references for FirstProduction_Data...
|
|
||||||
Processing block: Input_Data...
|
|
||||||
Exporting cross-references for Input_Data...
|
|
||||||
Processing block: ISOonTCP_or_TCP_Protocol_DB...
|
|
||||||
Exporting cross-references for ISOonTCP_or_TCP_Protocol_DB...
|
|
||||||
Processing block: MFM_Analog_Value_Data...
|
|
||||||
Exporting cross-references for MFM_Analog_Value_Data...
|
|
||||||
Processing block: PID MAIN Data...
|
|
||||||
Exporting cross-references for PID MAIN Data...
|
|
||||||
Processing block: PID_Filling_Head_Data...
|
|
||||||
Exporting cross-references for PID_Filling_Head_Data...
|
|
||||||
Processing block: PID_RMM301_Data...
|
|
||||||
Exporting cross-references for PID_RMM301_Data...
|
|
||||||
Processing block: PID_RMM303_Data...
|
|
||||||
Exporting cross-references for PID_RMM303_Data...
|
|
||||||
Processing block: PID_RMP302_Data...
|
|
||||||
Exporting cross-references for PID_RMP302_Data...
|
|
||||||
Processing block: PID_RVM301_Data...
|
|
||||||
Exporting cross-references for PID_RVM301_Data...
|
|
||||||
Processing block: PID_RVM319_Data...
|
|
||||||
Exporting cross-references for PID_RVM319_Data...
|
|
||||||
Processing block: PID_RVP303_Data...
|
|
||||||
Exporting cross-references for PID_RVP303_Data...
|
|
||||||
Processing block: Sel_Check_Brix_Data...
|
|
||||||
Exporting cross-references for Sel_Check_Brix_Data...
|
|
||||||
Processing block: Signal_Gen_Data...
|
|
||||||
Exporting cross-references for Signal_Gen_Data...
|
|
||||||
Processing block: System_Run_Out_Data...
|
Processing block: System_Run_Out_Data...
|
||||||
Exporting cross-references for System_Run_Out_Data...
|
Exporting cross-references for System_Run_Out_Data...
|
||||||
Processing block: SubCarb_DB...
|
ERROR accessing Program Blocks for cross-reference export: RemotingException: El objeto '/bd68de4c_3307_463d_b3ce_57a1378b3bde/lnycb9uriadrpgmom_mjdspm_249.rem' se desconectó o no existe en el servidor.
|
||||||
Exporting cross-references for SubCarb_DB...
|
|
||||||
Processing block: CYC_INT5...
|
|
||||||
Exporting cross-references for CYC_INT5...
|
|
||||||
Processing block: BlenderCtrl_All Auto...
|
|
||||||
Exporting cross-references for BlenderCtrl_All Auto...
|
|
||||||
Processing block: BlenderCtrl_InitErrors...
|
|
||||||
Exporting cross-references for BlenderCtrl_InitErrors...
|
|
||||||
Processing block: BlenderCtrl_ManualActive...
|
|
||||||
Exporting cross-references for BlenderCtrl_ManualActive...
|
|
||||||
Processing block: BlenderCtrl_MFM Command...
|
|
||||||
Exporting cross-references for BlenderCtrl_MFM Command...
|
|
||||||
Processing block: BlenderPID_FlowMeterErro...
|
|
||||||
Exporting cross-references for BlenderPID_FlowMeterErro...
|
|
||||||
Processing block: BlenderPID_PIDResInteg...
|
|
||||||
Exporting cross-references for BlenderPID_PIDResInteg...
|
|
||||||
Processing block: BlenderPIDCtrl_PresRelea...
|
|
||||||
Exporting cross-references for BlenderPIDCtrl_PresRelea...
|
|
||||||
Processing block: BlenderPIDCtrl_SaveValve...
|
|
||||||
Exporting cross-references for BlenderPIDCtrl_SaveValve...
|
|
||||||
Processing block: BlenderRun__Control...
|
|
||||||
Exporting cross-references for BlenderRun__Control...
|
|
||||||
Processing block: BlenderRun_SelectConstan...
|
|
||||||
Exporting cross-references for BlenderRun_SelectConstan...
|
|
||||||
Processing block: BlendFill StartUp_Seq...
|
|
||||||
Exporting cross-references for BlendFill StartUp_Seq...
|
|
||||||
Processing block: CIP_SimpleProgr_Init...
|
|
||||||
Exporting cross-references for CIP_SimpleProgr_Init...
|
|
||||||
Processing block: CIPLocal...
|
|
||||||
Exporting cross-references for CIPLocal...
|
|
||||||
Processing block: CIPLocal_ExecSimpleCIP...
|
|
||||||
Exporting cross-references for CIPLocal_ExecSimpleCIP...
|
|
||||||
Processing block: CIPLocal_ExecStep...
|
|
||||||
Exporting cross-references for CIPLocal_ExecStep...
|
|
||||||
Processing block: CIPLocal_ProgStepDown...
|
|
||||||
Exporting cross-references for CIPLocal_ProgStepDown...
|
|
||||||
Processing block: CIPLocal_ProgStepUp...
|
|
||||||
Exporting cross-references for CIPLocal_ProgStepUp...
|
|
||||||
Processing block: CIPReportManager...
|
|
||||||
Exporting cross-references for CIPReportManager...
|
|
||||||
ERROR accessing Program Blocks for cross-reference export: RemotingException: El objeto '/460a527c_f027_40c0_bbfb_2f9184c04002/hwhq0szmkxqfz2pc1xmghz0a_310.rem' se desconectó o no existe en el servidor.
|
|
||||||
|
|
||||||
[PLC: PLC] Exporting PLC Tag Table Cross-References...
|
[PLC: CPU 315F-2 PN/DP] Exporting PLC Tag Table Cross-References...
|
||||||
Target: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\PlcTags_CR
|
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\PlcTags_CR
|
||||||
Found 10 Tag Tables.
|
Found 2 Tag Tables.
|
||||||
Processing Tag Table: Memories...
|
Processing Tag Table: Default tag table...
|
||||||
Exporting cross-references for Memories...
|
Exporting cross-references for Default tag table...
|
||||||
Processing Tag Table: Tabella delle variabili standard...
|
Processing Tag Table: STEP7 classic symbols...
|
||||||
Exporting cross-references for Tabella delle variabili standard...
|
Exporting cross-references for STEP7 classic symbols...
|
||||||
Processing Tag Table: Timers_Counters...
|
Tag Table CR Export Summary: Exported=2, Skipped/Errors=0
|
||||||
Exporting cross-references for Timers_Counters...
|
|
||||||
Processing Tag Table: Inputs...
|
|
||||||
Exporting cross-references for Inputs...
|
|
||||||
Processing Tag Table: Outputs...
|
|
||||||
Exporting cross-references for Outputs...
|
|
||||||
Processing Tag Table: Tabella delle variabili_1...
|
|
||||||
Exporting cross-references for Tabella delle variabili_1...
|
|
||||||
Processing Tag Table: Tabella delle variabili_2...
|
|
||||||
Exporting cross-references for Tabella delle variabili_2...
|
|
||||||
Processing Tag Table: OutputsFesto...
|
|
||||||
Exporting cross-references for OutputsFesto...
|
|
||||||
Processing Tag Table: InputsMaster...
|
|
||||||
Exporting cross-references for InputsMaster...
|
|
||||||
Processing Tag Table: OutputsMaster...
|
|
||||||
Exporting cross-references for OutputsMaster...
|
|
||||||
Tag Table CR Export Summary: Exported=10, Skipped/Errors=0
|
|
||||||
|
|
||||||
[PLC: PLC] Exporting PLC Data Type (UDT) Cross-References...
|
[PLC: CPU 315F-2 PN/DP] Exporting PLC Data Type (UDT) Cross-References...
|
||||||
Target: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\PlcDataTypes_CR
|
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\PlcDataTypes_CR
|
||||||
Found 24 UDTs.
|
Found 21 UDTs.
|
||||||
Processing UDT: AnalogInstrument...
|
Processing UDT: AnyPoint...
|
||||||
Exporting cross-references for AnalogInstrument...
|
Exporting cross-references for AnyPoint...
|
||||||
Processing UDT: CIP_Link_Type...
|
|
||||||
Exporting cross-references for CIP_Link_Type...
|
|
||||||
Processing UDT: CIP_Simple_Type...
|
|
||||||
Exporting cross-references for CIP_Simple_Type...
|
|
||||||
Processing UDT: CIP_Step_Type...
|
|
||||||
Exporting cross-references for CIP_Step_Type...
|
|
||||||
Processing UDT: CIP_WaitEvent_Type...
|
|
||||||
Exporting cross-references for CIP_WaitEvent_Type...
|
|
||||||
Processing UDT: Device...
|
|
||||||
Exporting cross-references for Device...
|
|
||||||
Processing UDT: DigitalInstrument...
|
|
||||||
Exporting cross-references for DigitalInstrument...
|
|
||||||
Processing UDT: FunctionButton...
|
Processing UDT: FunctionButton...
|
||||||
Exporting cross-references for FunctionButton...
|
Exporting cross-references for FunctionButton...
|
||||||
Processing UDT: PID...
|
|
||||||
Exporting cross-references for PID...
|
|
||||||
Processing UDT: QCO Phase...
|
|
||||||
Exporting cross-references for QCO Phase...
|
|
||||||
Processing UDT: QCO Spare...
|
|
||||||
Exporting cross-references for QCO Spare...
|
|
||||||
Processing UDT: QCO Timer...
|
|
||||||
Exporting cross-references for QCO Timer...
|
|
||||||
Processing UDT: QCO Timer_Array_1...
|
|
||||||
Exporting cross-references for QCO Timer_Array_1...
|
|
||||||
Processing UDT: Recipe_Prod...
|
|
||||||
Exporting cross-references for Recipe_Prod...
|
|
||||||
Processing UDT: ReportCIPSimpleData...
|
|
||||||
Exporting cross-references for ReportCIPSimpleData...
|
|
||||||
Processing UDT: TADDR_PAR...
|
Processing UDT: TADDR_PAR...
|
||||||
Exporting cross-references for TADDR_PAR...
|
Exporting cross-references for TADDR_PAR...
|
||||||
|
Processing UDT: Device...
|
||||||
|
Exporting cross-references for Device...
|
||||||
|
Processing UDT: AnalogInstrument...
|
||||||
|
Exporting cross-references for AnalogInstrument...
|
||||||
|
Processing UDT: DigitalInstrument...
|
||||||
|
Exporting cross-references for DigitalInstrument...
|
||||||
|
Processing UDT: PID...
|
||||||
|
Exporting cross-references for PID...
|
||||||
|
Processing UDT: EHS16...
|
||||||
|
Exporting cross-references for EHS16...
|
||||||
|
Processing UDT: Danfoss Diag...
|
||||||
|
Exporting cross-references for Danfoss Diag...
|
||||||
|
Processing UDT: QCO Timer...
|
||||||
|
Exporting cross-references for QCO Timer...
|
||||||
|
Processing UDT: QCO Phase...
|
||||||
|
Exporting cross-references for QCO Phase...
|
||||||
|
Processing UDT: ReportCIPSimpleData...
|
||||||
|
Exporting cross-references for ReportCIPSimpleData...
|
||||||
|
Processing UDT: CIP_WaitEvent_Type...
|
||||||
|
Exporting cross-references for CIP_WaitEvent_Type...
|
||||||
|
Processing UDT: CIP_Step_Type_New...
|
||||||
|
Exporting cross-references for CIP_Step_Type_New...
|
||||||
|
Processing UDT: CIP_Simple_Type...
|
||||||
|
Exporting cross-references for CIP_Simple_Type...
|
||||||
|
Processing UDT: CIP_Link_Type...
|
||||||
|
Exporting cross-references for CIP_Link_Type...
|
||||||
|
Processing UDT: CIP_Step_Type...
|
||||||
|
Exporting cross-references for CIP_Step_Type...
|
||||||
|
Processing UDT: Recipe_Prod...
|
||||||
|
Exporting cross-references for Recipe_Prod...
|
||||||
|
Processing UDT: ICS Hndsk receive signal...
|
||||||
|
Exporting cross-references for ICS Hndsk receive signal...
|
||||||
|
Processing UDT: ICS Hndsk send signal...
|
||||||
|
Exporting cross-references for ICS Hndsk send signal...
|
||||||
Processing UDT: TCON_PAR...
|
Processing UDT: TCON_PAR...
|
||||||
Exporting cross-references for TCON_PAR...
|
Exporting cross-references for TCON_PAR...
|
||||||
Processing UDT: TCON_PAR_LF...
|
UDT CR Export Summary: Exported=21, Skipped/Errors=0
|
||||||
Exporting cross-references for TCON_PAR_LF...
|
|
||||||
Processing UDT: Tipo di dati utente_1...
|
|
||||||
Exporting cross-references for Tipo di dati utente_1...
|
|
||||||
Processing UDT: Tipo di dati utente_2...
|
|
||||||
Exporting cross-references for Tipo di dati utente_2...
|
|
||||||
Processing UDT: ASLeds...
|
|
||||||
Exporting cross-references for ASLeds...
|
|
||||||
Processing UDT: IFLeds...
|
|
||||||
Exporting cross-references for IFLeds...
|
|
||||||
Processing UDT: SV_FB_State...
|
|
||||||
Exporting cross-references for SV_FB_State...
|
|
||||||
Processing UDT: SV_State...
|
|
||||||
Exporting cross-references for SV_State...
|
|
||||||
UDT CR Export Summary: Exported=24, Skipped/Errors=0
|
|
||||||
|
|
||||||
[PLC: PLC] Attempting to Export System Block Cross-References...
|
[PLC: CPU 315F-2 PN/DP] Attempting to Export System Block Cross-References...
|
||||||
Target: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\SystemBlocks_CR
|
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SystemBlocks_CR
|
||||||
Found 9 system blocks (using get_system_blocks).
|
Found 12 system blocks (using get_system_blocks).
|
||||||
Processing System Block: T_301...
|
Processing System Block: TIM_S5TI...
|
||||||
Exporting cross-references for T_301...
|
Exporting cross-references for TIM_S5TI...
|
||||||
Processing System Block: IEC_Timer_0_DB_9...
|
Processing System Block: REPLACE...
|
||||||
Exporting cross-references for IEC_Timer_0_DB_9...
|
Exporting cross-references for REPLACE...
|
||||||
Processing System Block: T_302...
|
Processing System Block: LIMIT...
|
||||||
Exporting cross-references for T_302...
|
Exporting cross-references for LIMIT...
|
||||||
Processing System Block: GET_Reciver...
|
Processing System Block: NE_STRNG...
|
||||||
Exporting cross-references for GET_Reciver...
|
Exporting cross-references for NE_STRNG...
|
||||||
Processing System Block: PUT_Send_Filler...
|
Processing System Block: TURCV...
|
||||||
Exporting cross-references for PUT_Send_Filler...
|
Exporting cross-references for TURCV...
|
||||||
Processing System Block: LED...
|
Processing System Block: TUSEND...
|
||||||
Exporting cross-references for LED...
|
Exporting cross-references for TUSEND...
|
||||||
Processing System Block: SCALE...
|
Processing System Block: PID_Continuos...
|
||||||
Exporting cross-references for SCALE...
|
Exporting cross-references for PID_Continuos...
|
||||||
Processing System Block: CONT_C...
|
Processing System Block: TDISCON...
|
||||||
Exporting cross-references for CONT_C...
|
Exporting cross-references for TDISCON...
|
||||||
Processing System Block: DeviceStates...
|
Processing System Block: TCON...
|
||||||
Exporting cross-references for DeviceStates...
|
Exporting cross-references for TCON...
|
||||||
System Block CR Export Summary: Exported=9, Skipped/Errors=0
|
Processing System Block: TRCV...
|
||||||
|
Exporting cross-references for TRCV...
|
||||||
|
Processing System Block: TSEND...
|
||||||
|
Exporting cross-references for TSEND...
|
||||||
|
Processing System Block: DT_DATE...
|
||||||
|
Exporting cross-references for DT_DATE...
|
||||||
|
System Block CR Export Summary: Exported=12, Skipped/Errors=0
|
||||||
|
|
||||||
[PLC: PLC] Attempting to Export Software Unit Cross-References...
|
[PLC: CPU 315F-2 PN/DP] Attempting to Export Software Unit Cross-References...
|
||||||
Target: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\IOExport\PLC\SoftwareUnits_CR
|
Target: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourceXML\CPU 315F-2 PN\DP\SoftwareUnits_CR
|
||||||
Found 0 Software Units.
|
Found 0 Software Units.
|
||||||
Software Unit CR Export Summary: Exported=0, Skipped/Errors=0
|
Software Unit CR Export Summary: Exported=0, Skipped/Errors=0
|
||||||
|
|
||||||
--- Finished processing PLC: PLC ---
|
--- Finished processing PLC: CPU 315F-2 PN/DP ---
|
||||||
|
|
||||||
Cross-reference export process completed.
|
Cross-reference export process completed.
|
||||||
|
|
||||||
Closing TIA Portal...
|
Closing TIA Portal...
|
||||||
2025-05-05 13:35:02,332 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal ClosePortal - Close TIA Portal
|
2025-05-20 12:19:54,187 [1] INFO Siemens.TiaPortal.OpennessApi18.Implementations.Portal ClosePortal - Close TIA Portal
|
||||||
TIA Portal closed.
|
TIA Portal closed.
|
||||||
|
|
||||||
Script finished.
|
Script finished.
|
||||||
|
@ -428,6 +246,6 @@ Traceback (most recent call last):
|
||||||
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 99, in export_plc_cross_references
|
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\x4.py", line 99, in export_plc_cross_references
|
||||||
block_name = block.get_name()
|
block_name = block.get_name()
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
ValueError: RemotingException: El objeto '/460a527c_f027_40c0_bbfb_2f9184c04002/hwhq0szmkxqfz2pc1xmghz0a_310.rem' se desconectó o no existe en el servidor.
|
ValueError: RemotingException: El objeto '/bd68de4c_3307_463d_b3ce_57a1378b3bde/lnycb9uriadrpgmom_mjdspm_249.rem' se desconectó o no existe en el servidor.
|
||||||
|
|
||||||
--- FIN DEL LOG ---
|
--- FIN DEL LOG ---
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
--- Log de Ejecución: xTest.py ---
|
||||||
|
Grupo: ObtainIOFromProjectTia
|
||||||
|
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\SourceDoc\SourcdSD
|
||||||
|
Inicio: 2025-05-22 11:17:27
|
||||||
|
Fin: 2025-05-22 11:18:44
|
||||||
|
Duración: 0:01:16.758340
|
||||||
|
Estado: ERROR (Código de Salida: 1)
|
||||||
|
|
||||||
|
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||||
|
============================================================
|
||||||
|
PRUEBA DE EXPORTACIÓN SIMATIC SD - TIA PORTAL V20
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Project: C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452_V20/SAE452_V20.ap20
|
||||||
|
Export Directory: C:/Users/migue/Downloads/Nueva carpeta (18)\SIMATIC_SD_Test
|
||||||
|
|
||||||
|
Connecting to TIA Portal V20...
|
||||||
|
2025-05-22 11:17:49,266 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - Start TIA Portal, please acknowledge the security dialog.
|
||||||
|
2025-05-22 11:17:49,283 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Global OpenPortal - With user interface
|
||||||
|
Connected successfully.
|
||||||
|
Opening project...
|
||||||
|
2025-05-22 11:18:05,562 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal OpenProject - Open project... C:/Trabajo/SIDEL/09 - SAE452 - Diet as Regular - San Giovanni in Bosco/Reporte/SourceDoc/Migration/SAE452_V20/SAE452_V20.ap20
|
||||||
|
Project opened successfully.
|
||||||
|
2025-05-22 11:18:20,088 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Project GetPlcs - Found plc CPU 315F-2 PN/DP with parent name _SSAE0452
|
||||||
|
Found 1 PLC(s)
|
||||||
|
|
||||||
|
Testing with PLC: CPU 315F-2 PN/DP
|
||||||
|
Found 410 program blocks
|
||||||
|
|
||||||
|
--- Testing Block 1/3: ISOonTCP_or_TCP_Protocol ---
|
||||||
|
Programming Language: STL
|
||||||
|
Available methods on block:
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
✗ ExportAsDocuments method NOT found
|
||||||
|
Available methods containing 'export':
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
|
||||||
|
--- Testing Block 2/3: PIDControl ---
|
||||||
|
Compiling block...
|
||||||
|
2025-05-22 11:18:24,970 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Compile the PLC program block PIDControl. Result:
|
||||||
|
2025-05-22 11:18:31,184 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Warning: CPU 315F-2 PN/DP > General warnings > Inputs or outputs are used that do not exist in the configured hardware.
|
||||||
|
2025-05-22 11:18:31,185 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.ProgramBlock Compile - Warning: CPU 315F-2 PN/DP > Compiling finished (errors: 0; warnings: 1)
|
||||||
|
Programming Language: LAD
|
||||||
|
Available methods on block:
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
✗ ExportAsDocuments method NOT found
|
||||||
|
Available methods containing 'export':
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
|
||||||
|
--- Testing Block 3/3: DETAIL_DP_DIAG ---
|
||||||
|
Programming Language: STL
|
||||||
|
Available methods on block:
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
✗ ExportAsDocuments method NOT found
|
||||||
|
Available methods containing 'export':
|
||||||
|
- export
|
||||||
|
- export_cross_references
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
PRUEBA COMPLETADA
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
No se crearon archivos en C:/Users/migue/Downloads/Nueva carpeta (18)\SIMATIC_SD_Test
|
||||||
|
|
||||||
|
Closing TIA Portal...
|
||||||
|
2025-05-22 11:18:31,209 [1] INFO Siemens.TiaPortal.OpennessApi19.Implementations.Portal ClosePortal - Close TIA Portal
|
||||||
|
|
||||||
|
Press Enter to exit...
|
||||||
|
|
||||||
|
--- ERRORES (STDERR) ---
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\ObtainIOFromProjectTia\xTest.py", line 215, in <module>
|
||||||
|
input("\nPress Enter to exit...")
|
||||||
|
EOFError: EOF when reading a line
|
||||||
|
|
||||||
|
--- FIN DEL LOG ---
|
|
@ -5,5 +5,5 @@
|
||||||
},
|
},
|
||||||
"level2": {},
|
"level2": {},
|
||||||
"level3": {},
|
"level3": {},
|
||||||
"working_directory": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML"
|
"working_directory": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourcdSD"
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"x1.py": {
|
"x1.py": {
|
||||||
"display_name": "1: Exportar Lógica desde TIA Portal",
|
"display_name": "1: Exportar Lógica desde TIA Portal v18 en XML",
|
||||||
"short_description": "Exporta la lógica del PLC desde TIA Portal en archivos XML y SCL.",
|
"short_description": "Exporta la lógica del PLC desde TIA Portal en archivos XML y SCL.",
|
||||||
"long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal.\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.",
|
"long_description": "Este script utiliza TIA Portal Openness para exportar la lógica de un PLC en formato XML y SCL. Permite seleccionar un proyecto de TIA Portal y genera los archivos de exportación en el directorio configurado.\n***\n**Lógica Principal:**\n\n1. **Configuración:** Carga parámetros desde `ParamManagerScripts` (directorio de trabajo, versión de TIA Portal).\n2. **Selección de Proyecto:** Abre un cuadro de diálogo para seleccionar el archivo del proyecto de TIA Portal.\n3. **Conexión a TIA Portal:** Utiliza la API de TIA Openness para conectarse al portal y abrir el proyecto seleccionado.\n4. **Exportación:** Exporta la lógica del PLC en archivos XML y SCL al directorio configurado.\n5. **Cierre:** Cierra la conexión con TIA Portal al finalizar.",
|
||||||
"hidden": false
|
"hidden": false
|
||||||
|
@ -10,5 +10,17 @@
|
||||||
"short_description": "Script para exportar las referencias cruzadas",
|
"short_description": "Script para exportar las referencias cruzadas",
|
||||||
"long_description": "",
|
"long_description": "",
|
||||||
"hidden": false
|
"hidden": false
|
||||||
|
},
|
||||||
|
"x2.py": {
|
||||||
|
"display_name": "2: Exportar Lógica desde TIA Portal V20 en SIMATIC SD format",
|
||||||
|
"short_description": "export_logic_from_tia_v20_simatic_sd : Script para exportar el software de un PLC desde TIA Portal V20",
|
||||||
|
"long_description": "",
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
"xTest.py": {
|
||||||
|
"display_name": "xTest",
|
||||||
|
"short_description": "Test específico para exportación SIMATIC SD usando ExportAsDocuments()",
|
||||||
|
"long_description": "",
|
||||||
|
"hidden": false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"path": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML",
|
"path": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourcdSD",
|
||||||
"history": [
|
"history": [
|
||||||
"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\\SourcdSD",
|
||||||
"C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport"
|
"C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -0,0 +1,462 @@
|
||||||
|
"""
|
||||||
|
export_simatic_sd_tia20 : Script para exportar bloques de PLC desde TIA Portal 20 en formato SIMATIC SD.
|
||||||
|
Basado en la nueva funcionalidad de exportación SIMATIC SD disponible en TIA Portal V20.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import filedialog
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
TIA_PORTAL_VERSION = "20.0" # Target TIA Portal version for SIMATIC SD support
|
||||||
|
EXPORT_OPTIONS = None # Use default export options
|
||||||
|
KEEP_FOLDER_STRUCTURE = (
|
||||||
|
True # Replicate TIA project folder structure in export directory
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- TIA Scripting Import Handling ---
|
||||||
|
# Check if the TIA_SCRIPTING environment variable is set
|
||||||
|
if os.getenv("TIA_SCRIPTING"):
|
||||||
|
sys.path.append(os.getenv("TIA_SCRIPTING"))
|
||||||
|
else:
|
||||||
|
# Optional: Define a fallback path if the environment variable isn't set
|
||||||
|
# fallback_path = "C:\\path\\to\\your\\TIA_Scripting_binaries"
|
||||||
|
# if os.path.exists(fallback_path):
|
||||||
|
# sys.path.append(fallback_path)
|
||||||
|
pass # Allow import to fail if not found
|
||||||
|
|
||||||
|
try:
|
||||||
|
import siemens_tia_scripting as ts
|
||||||
|
|
||||||
|
EXPORT_OPTIONS = (
|
||||||
|
ts.Enums.ExportOptions.WithDefaults
|
||||||
|
) # Set default options now that 'ts' is imported
|
||||||
|
except ImportError:
|
||||||
|
print("ERROR: Failed to import 'siemens_tia_scripting'.")
|
||||||
|
print("Ensure:")
|
||||||
|
print(f"1. TIA Portal Openness for V{TIA_PORTAL_VERSION} is installed.")
|
||||||
|
print(
|
||||||
|
"2. The 'siemens_tia_scripting' Python module is installed (pip install ...) or"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
" the path to its binaries is set in the 'TIA_SCRIPTING' environment variable."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"3. You are using a compatible Python version (e.g., 3.12.X as per documentation)."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred during import: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# --- Functions ---
|
||||||
|
|
||||||
|
|
||||||
|
def select_project_file():
|
||||||
|
"""Opens a dialog to select a TIA Portal project file."""
|
||||||
|
root = tk.Tk()
|
||||||
|
root.withdraw() # Hide the main tkinter window
|
||||||
|
file_path = filedialog.askopenfilename(
|
||||||
|
title="Select TIA Portal Project File",
|
||||||
|
filetypes=[
|
||||||
|
(
|
||||||
|
f"TIA Portal V{TIA_PORTAL_VERSION} Projects",
|
||||||
|
f"*.ap{TIA_PORTAL_VERSION.split('.')[0]}",
|
||||||
|
)
|
||||||
|
], # e.g. *.ap20
|
||||||
|
)
|
||||||
|
root.destroy()
|
||||||
|
if not file_path:
|
||||||
|
print("No project file selected. Exiting.")
|
||||||
|
sys.exit(0)
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
|
||||||
|
def select_export_directory():
|
||||||
|
"""Opens a dialog to select the export directory."""
|
||||||
|
root = tk.Tk()
|
||||||
|
root.withdraw() # Hide the main tkinter window
|
||||||
|
dir_path = filedialog.askdirectory(title="Select Export Directory")
|
||||||
|
root.destroy()
|
||||||
|
if not dir_path:
|
||||||
|
print("No export directory selected. Exiting.")
|
||||||
|
sys.exit(0)
|
||||||
|
return dir_path
|
||||||
|
|
||||||
|
|
||||||
|
def check_simatic_sd_support():
|
||||||
|
"""Verifies if SIMATIC SD format is available in the current TIA Scripting version."""
|
||||||
|
try:
|
||||||
|
# Check if SimaticSD is available in ExportFormats enum
|
||||||
|
simatic_sd_format = ts.Enums.ExportFormats.SimaticSD
|
||||||
|
print(f"✓ SIMATIC SD format supported (enum value: {simatic_sd_format})")
|
||||||
|
return True
|
||||||
|
except AttributeError:
|
||||||
|
print("✗ ERROR: SIMATIC SD format not available in this TIA Scripting version.")
|
||||||
|
print("Please ensure you are using TIA Portal V20 or later with compatible TIA Scripting.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def export_plc_data_simatic_sd(plc, export_base_dir):
|
||||||
|
"""Exports Blocks, UDTs, and Tag Tables from a given PLC in SIMATIC SD format."""
|
||||||
|
plc_name = plc.get_name()
|
||||||
|
print(f"\n--- Processing PLC: {plc_name} (SIMATIC SD Export) ---")
|
||||||
|
|
||||||
|
# Define base export path for this PLC with timestamp to avoid conflicts
|
||||||
|
import datetime
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
plc_export_dir = os.path.join(export_base_dir, f"{plc_name}_SimaticSD_{timestamp}")
|
||||||
|
os.makedirs(plc_export_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# --- Export Program Blocks in SIMATIC SD Format ---
|
||||||
|
blocks_exported = 0
|
||||||
|
blocks_skipped = 0
|
||||||
|
print(f"\n[PLC: {plc_name}] Exporting Program Blocks (SIMATIC SD)...")
|
||||||
|
sd_blocks_path = os.path.join(plc_export_dir, "01_ProgramBlocks_SD")
|
||||||
|
os.makedirs(sd_blocks_path, exist_ok=True)
|
||||||
|
print(f" SIMATIC SD Target: {sd_blocks_path}")
|
||||||
|
|
||||||
|
# Also create a separate SimaticML export for comparison
|
||||||
|
xml_blocks_path = os.path.join(plc_export_dir, "02_ProgramBlocks_XML_Compare")
|
||||||
|
os.makedirs(xml_blocks_path, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
program_blocks = plc.get_program_blocks()
|
||||||
|
print(f" Found {len(program_blocks)} program blocks.")
|
||||||
|
for block in program_blocks:
|
||||||
|
block_name = block.get_name()
|
||||||
|
print(f" Processing block: {block_name}...")
|
||||||
|
try:
|
||||||
|
if not block.is_consistent():
|
||||||
|
print(f" Compiling block {block_name}...")
|
||||||
|
block.compile()
|
||||||
|
if not block.is_consistent():
|
||||||
|
print(
|
||||||
|
f" WARNING: Block {block_name} inconsistent after compile. Skipping."
|
||||||
|
)
|
||||||
|
blocks_skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f" Exporting {block_name} as SIMATIC SD...")
|
||||||
|
try:
|
||||||
|
block.export(
|
||||||
|
target_directory_path=sd_blocks_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticSD, # New SIMATIC SD format
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
blocks_exported += 1
|
||||||
|
print(f" ✓ Successfully exported {block_name} in SIMATIC SD")
|
||||||
|
|
||||||
|
# Also export same block in XML for comparison
|
||||||
|
print(f" Exporting {block_name} as XML for comparison...")
|
||||||
|
block.export(
|
||||||
|
target_directory_path=xml_blocks_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticML, # Traditional XML format
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
print(f" + Also exported {block_name} in XML for comparison")
|
||||||
|
|
||||||
|
except Exception as export_ex:
|
||||||
|
print(f" ERROR during export: {export_ex}")
|
||||||
|
# Try to export only in XML if SD fails
|
||||||
|
try:
|
||||||
|
print(f" Attempting fallback XML export for {block_name}...")
|
||||||
|
block.export(
|
||||||
|
target_directory_path=xml_blocks_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
print(f" ✓ Fallback XML export successful for {block_name}")
|
||||||
|
blocks_exported += 1
|
||||||
|
except Exception as fallback_ex:
|
||||||
|
print(f" ERROR: Both SD and XML export failed: {fallback_ex}")
|
||||||
|
blocks_skipped += 1
|
||||||
|
|
||||||
|
except Exception as block_ex:
|
||||||
|
print(f" ERROR exporting block {block_name}: {block_ex}")
|
||||||
|
blocks_skipped += 1
|
||||||
|
|
||||||
|
print(
|
||||||
|
f" Program Blocks Export Summary: Exported={blocks_exported}, Skipped/Errors={blocks_skipped}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ERROR processing Program Blocks: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# --- Export PLC Data Types (UDTs) in SIMATIC SD Format ---
|
||||||
|
udts_exported = 0
|
||||||
|
udts_skipped = 0
|
||||||
|
print(f"\n[PLC: {plc_name}] Exporting PLC Data Types - UDTs (SIMATIC SD)...")
|
||||||
|
udt_sd_export_path = os.path.join(plc_export_dir, "03_PlcDataTypes_SD")
|
||||||
|
udt_xml_export_path = os.path.join(plc_export_dir, "04_PlcDataTypes_XML_Compare")
|
||||||
|
os.makedirs(udt_sd_export_path, exist_ok=True)
|
||||||
|
os.makedirs(udt_xml_export_path, exist_ok=True)
|
||||||
|
print(f" SIMATIC SD Target: {udt_sd_export_path}")
|
||||||
|
print(f" XML Compare Target: {udt_xml_export_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
udts = plc.get_user_data_types()
|
||||||
|
print(f" Found {len(udts)} UDTs.")
|
||||||
|
for udt in udts:
|
||||||
|
udt_name = udt.get_name()
|
||||||
|
print(f" Processing UDT: {udt_name}...")
|
||||||
|
try:
|
||||||
|
if not udt.is_consistent():
|
||||||
|
print(f" Compiling UDT {udt_name}...")
|
||||||
|
udt.compile()
|
||||||
|
if not udt.is_consistent():
|
||||||
|
print(
|
||||||
|
f" WARNING: UDT {udt_name} inconsistent after compile. Skipping."
|
||||||
|
)
|
||||||
|
udts_skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f" Exporting {udt_name} as SIMATIC SD...")
|
||||||
|
try:
|
||||||
|
udt.export(
|
||||||
|
target_directory_path=udt_sd_export_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticSD, # SIMATIC SD format for UDTs
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
# Also export in XML for comparison
|
||||||
|
udt.export(
|
||||||
|
target_directory_path=udt_xml_export_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
udts_exported += 1
|
||||||
|
print(f" ✓ Successfully exported {udt_name} (SD + XML)")
|
||||||
|
except Exception as udt_export_ex:
|
||||||
|
print(f" ERROR during UDT export: {udt_export_ex}")
|
||||||
|
udts_skipped += 1
|
||||||
|
|
||||||
|
except Exception as udt_ex:
|
||||||
|
print(f" ERROR exporting UDT {udt_name}: {udt_ex}")
|
||||||
|
udts_skipped += 1
|
||||||
|
|
||||||
|
print(
|
||||||
|
f" UDT Export Summary: Exported={udts_exported}, Skipped/Errors={udts_skipped}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ERROR processing UDTs: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# --- Export PLC Tag Tables in SIMATIC SD Format ---
|
||||||
|
tags_exported = 0
|
||||||
|
tags_skipped = 0
|
||||||
|
print(f"\n[PLC: {plc_name}] Exporting PLC Tag Tables (SIMATIC SD)...")
|
||||||
|
tags_sd_export_path = os.path.join(plc_export_dir, "05_PlcTags_SD")
|
||||||
|
tags_xml_export_path = os.path.join(plc_export_dir, "06_PlcTags_XML_Compare")
|
||||||
|
os.makedirs(tags_sd_export_path, exist_ok=True)
|
||||||
|
os.makedirs(tags_xml_export_path, exist_ok=True)
|
||||||
|
print(f" SIMATIC SD Target: {tags_sd_export_path}")
|
||||||
|
print(f" XML Compare Target: {tags_xml_export_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
tag_tables = plc.get_plc_tag_tables()
|
||||||
|
print(f" Found {len(tag_tables)} Tag Tables.")
|
||||||
|
for table in tag_tables:
|
||||||
|
table_name = table.get_name()
|
||||||
|
print(f" Processing Tag Table: {table_name}...")
|
||||||
|
try:
|
||||||
|
print(f" Exporting {table_name} as SIMATIC SD...")
|
||||||
|
try:
|
||||||
|
table.export(
|
||||||
|
target_directory_path=tags_sd_export_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticSD, # SIMATIC SD format for Tag Tables
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
# Also export in XML for comparison
|
||||||
|
table.export(
|
||||||
|
target_directory_path=tags_xml_export_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
tags_exported += 1
|
||||||
|
print(f" ✓ Successfully exported {table_name} (SD + XML)")
|
||||||
|
except Exception as tag_export_ex:
|
||||||
|
print(f" ERROR during Tag Table export: {tag_export_ex}")
|
||||||
|
tags_skipped += 1
|
||||||
|
|
||||||
|
except Exception as table_ex:
|
||||||
|
print(f" ERROR exporting Tag Table {table_name}: {table_ex}")
|
||||||
|
tags_skipped += 1
|
||||||
|
|
||||||
|
print(
|
||||||
|
f" Tag Table Export Summary: Exported={tags_exported}, Skipped/Errors={tags_skipped}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ERROR processing Tag Tables: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
print(f"\n--- Finished processing PLC: {plc_name} ---")
|
||||||
|
|
||||||
|
|
||||||
|
def export_additional_formats(plc, export_base_dir):
|
||||||
|
"""Optional: Export in traditional formats alongside SIMATIC SD for comparison."""
|
||||||
|
plc_name = plc.get_name()
|
||||||
|
print(f"\n[Optional] Exporting traditional formats for comparison - PLC: {plc_name}")
|
||||||
|
|
||||||
|
# Create comparison directory
|
||||||
|
comparison_dir = os.path.join(export_base_dir, plc_name, "Comparison_Formats")
|
||||||
|
os.makedirs(comparison_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Export a few blocks in SimaticML for comparison
|
||||||
|
xml_comparison_path = os.path.join(comparison_dir, "SimaticML_Sample")
|
||||||
|
os.makedirs(xml_comparison_path, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
program_blocks = plc.get_program_blocks()
|
||||||
|
# Export first 3 blocks in SimaticML for comparison
|
||||||
|
for i, block in enumerate(program_blocks[:3]):
|
||||||
|
if block.is_consistent():
|
||||||
|
block_name = block.get_name()
|
||||||
|
print(f" Exporting {block_name} in SimaticML for comparison...")
|
||||||
|
block.export(
|
||||||
|
target_directory_path=xml_comparison_path,
|
||||||
|
export_options=EXPORT_OPTIONS,
|
||||||
|
export_format=ts.Enums.ExportFormats.SimaticML,
|
||||||
|
keep_folder_structure=KEEP_FOLDER_STRUCTURE,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warning: Could not export comparison formats: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# --- Main Script ---
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
configs = load_configuration()
|
||||||
|
working_directory = configs.get("working_directory")
|
||||||
|
|
||||||
|
print("--- TIA Portal 20 SIMATIC SD Exporter ---")
|
||||||
|
print("Exporting Blocks, UDTs, and Tags in SIMATIC SD Format")
|
||||||
|
|
||||||
|
# Check SIMATIC SD support first
|
||||||
|
if not check_simatic_sd_support():
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate working directory
|
||||||
|
if not working_directory or not os.path.isdir(working_directory):
|
||||||
|
print("ERROR: Working directory not set or invalid in configuration.")
|
||||||
|
print("Please configure the working directory using the main application.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 1. Select Project File, Export Directory comes from config
|
||||||
|
project_file = select_project_file()
|
||||||
|
export_dir = working_directory # Use working directory from config
|
||||||
|
|
||||||
|
print(f"\nSelected Project: {project_file}")
|
||||||
|
print(f"Using Export Directory (Working Directory): {export_dir}")
|
||||||
|
|
||||||
|
portal_instance = None
|
||||||
|
project_object = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 2. Connect to TIA Portal V20
|
||||||
|
print(f"\nConnecting to TIA Portal V{TIA_PORTAL_VERSION}...")
|
||||||
|
portal_instance = ts.open_portal(
|
||||||
|
version=TIA_PORTAL_VERSION,
|
||||||
|
portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
|
||||||
|
)
|
||||||
|
print("Connected to TIA Portal V20.")
|
||||||
|
print(f"Portal Process ID: {portal_instance.get_process_id()}")
|
||||||
|
|
||||||
|
# 3. Open Project
|
||||||
|
print(f"Opening project: {os.path.basename(project_file)}...")
|
||||||
|
project_object = portal_instance.open_project(project_file_path=project_file)
|
||||||
|
if project_object is None:
|
||||||
|
print("Project might already be open, attempting to get handle...")
|
||||||
|
project_object = portal_instance.get_project()
|
||||||
|
if project_object is None:
|
||||||
|
raise Exception("Failed to open or get the specified project.")
|
||||||
|
print("Project opened successfully.")
|
||||||
|
|
||||||
|
# 4. Get PLCs
|
||||||
|
plcs = project_object.get_plcs()
|
||||||
|
if not plcs:
|
||||||
|
print("No PLC devices found in the project.")
|
||||||
|
else:
|
||||||
|
print(f"Found {len(plcs)} PLC(s). Starting SIMATIC SD export process...")
|
||||||
|
|
||||||
|
# 5. Iterate and Export Data for each PLC in SIMATIC SD format
|
||||||
|
import datetime # Add this import for timestamp
|
||||||
|
for plc_device in plcs:
|
||||||
|
export_plc_data_simatic_sd(
|
||||||
|
plc=plc_device, export_base_dir=export_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
print("\n🎉 SIMATIC SD Export process completed successfully!")
|
||||||
|
print("\nExported files structure:")
|
||||||
|
print("├── [PLC_Name]_SimaticSD_[timestamp]/")
|
||||||
|
print("│ ├── 01_ProgramBlocks_SD/ # SIMATIC SD format")
|
||||||
|
print("│ ├── 02_ProgramBlocks_XML_Compare/ # Traditional XML for comparison")
|
||||||
|
print("│ ├── 03_PlcDataTypes_SD/")
|
||||||
|
print("│ ├── 04_PlcDataTypes_XML_Compare/")
|
||||||
|
print("│ ├── 05_PlcTags_SD/")
|
||||||
|
print("│ └── 06_PlcTags_XML_Compare/")
|
||||||
|
print("\nNow you can compare the differences between SIMATIC SD and traditional XML formats!")
|
||||||
|
|
||||||
|
# Add file analysis
|
||||||
|
print("\n=== FILE ANALYSIS ===")
|
||||||
|
for plc_device in plcs:
|
||||||
|
plc_name = plc_device.get_name()
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
plc_export_dir = os.path.join(export_dir, f"{plc_name}_SimaticSD_{timestamp}")
|
||||||
|
|
||||||
|
print(f"\nAnalyzing exported files for PLC: {plc_name}")
|
||||||
|
folders_to_check = [
|
||||||
|
("01_ProgramBlocks_SD", "SIMATIC SD Blocks"),
|
||||||
|
("02_ProgramBlocks_XML_Compare", "XML Blocks"),
|
||||||
|
("03_PlcDataTypes_SD", "SIMATIC SD UDTs"),
|
||||||
|
("04_PlcDataTypes_XML_Compare", "XML UDTs"),
|
||||||
|
("05_PlcTags_SD", "SIMATIC SD Tags"),
|
||||||
|
("06_PlcTags_XML_Compare", "XML Tags")
|
||||||
|
]
|
||||||
|
|
||||||
|
for folder_name, description in folders_to_check:
|
||||||
|
folder_path = os.path.join(plc_export_dir, folder_name)
|
||||||
|
if os.path.exists(folder_path):
|
||||||
|
files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
|
||||||
|
print(f" {description}: {len(files)} files")
|
||||||
|
if files:
|
||||||
|
extensions = set(os.path.splitext(f)[1].lower() for f in files)
|
||||||
|
print(f" File extensions: {', '.join(extensions) if extensions else 'No extensions'}")
|
||||||
|
else:
|
||||||
|
print(f" {description}: Folder not found")
|
||||||
|
|
||||||
|
except ts.TiaException as tia_ex:
|
||||||
|
print(f"\nTIA Portal Openness Error: {tia_ex}")
|
||||||
|
traceback.print_exc()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"\nERROR: Project file not found at {project_file}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nAn unexpected error occurred: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
# 6. Cleanup
|
||||||
|
if portal_instance:
|
||||||
|
try:
|
||||||
|
print("\nClosing TIA Portal...")
|
||||||
|
portal_instance.close_portal()
|
||||||
|
print("TIA Portal closed.")
|
||||||
|
except Exception as close_ex:
|
||||||
|
print(f"Error during TIA Portal cleanup: {close_ex}")
|
||||||
|
|
||||||
|
print("\nScript finished.")
|
|
@ -1,9 +1,9 @@
|
||||||
--- Log de Ejecución: x3.py ---
|
--- Log de Ejecución: x3.py ---
|
||||||
Grupo: S7_DB_Utils
|
Grupo: S7_DB_Utils
|
||||||
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
||||||
Inicio: 2025-05-18 02:09:01
|
Inicio: 2025-05-20 16:06:51
|
||||||
Fin: 2025-05-18 02:09:01
|
Fin: 2025-05-20 16:06:52
|
||||||
Duración: 0:00:00.154928
|
Duración: 0:00:00.220075
|
||||||
Estado: SUCCESS (Código de Salida: 0)
|
Estado: SUCCESS (Código de Salida: 0)
|
||||||
|
|
||||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||||
|
@ -11,13 +11,13 @@ Using working directory: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Gi
|
||||||
Los archivos JSON de salida se guardarán en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json
|
Los archivos JSON de salida se guardarán en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json
|
||||||
Archivos encontrados para procesar: 2
|
Archivos encontrados para procesar: 2
|
||||||
|
|
||||||
--- Procesando archivo: db1001_data.db ---
|
--- Procesando archivo: DB1001_data.AWL ---
|
||||||
Parseo completo. Intentando serializar a JSON: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\db1001_data.json
|
Parseo completo. Intentando serializar a JSON: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\DB1001_data.json
|
||||||
Resultado guardado en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\db1001_data.json
|
Resultado guardado en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\DB1001_data.json
|
||||||
|
|
||||||
--- Procesando archivo: db1001_format.db ---
|
--- Procesando archivo: DB1001_format.AWL ---
|
||||||
Parseo completo. Intentando serializar a JSON: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\db1001_format.json
|
Parseo completo. Intentando serializar a JSON: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\DB1001_format.json
|
||||||
Resultado guardado en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\db1001_format.json
|
Resultado guardado en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\json\DB1001_format.json
|
||||||
|
|
||||||
--- Proceso completado ---
|
--- Proceso completado ---
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
--- Log de Ejecución: x4.py ---
|
--- Log de Ejecución: x4.py ---
|
||||||
Grupo: S7_DB_Utils
|
Grupo: S7_DB_Utils
|
||||||
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
||||||
Inicio: 2025-05-18 13:15:28
|
Inicio: 2025-05-20 15:49:22
|
||||||
Fin: 2025-05-18 13:15:28
|
Fin: 2025-05-20 15:49:23
|
||||||
Duración: 0:00:00.188819
|
Duración: 0:00:00.797004
|
||||||
Estado: SUCCESS (Código de Salida: 0)
|
Estado: SUCCESS (Código de Salida: 0)
|
||||||
|
|
||||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||||
|
@ -11,20 +11,20 @@ Using working directory: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Gi
|
||||||
Los archivos de documentación generados se guardarán en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation
|
Los archivos de documentación generados se guardarán en: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation
|
||||||
Archivos JSON encontrados para procesar: 3
|
Archivos JSON encontrados para procesar: 3
|
||||||
|
|
||||||
--- Procesando archivo JSON: db1001_data.json ---
|
--- Procesando archivo JSON: DB1001_data.json ---
|
||||||
Archivo JSON 'db1001_data.json' cargado correctamente.
|
Archivo JSON 'DB1001_data.json' cargado correctamente.
|
||||||
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_data.txt
|
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_data.txt
|
||||||
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_data.md
|
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_data.md
|
||||||
|
|
||||||
--- Procesando archivo JSON: db1001_format.json ---
|
--- Procesando archivo JSON: DB1001_format.json ---
|
||||||
Archivo JSON 'db1001_format.json' cargado correctamente.
|
Archivo JSON 'DB1001_format.json' cargado correctamente.
|
||||||
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_format.txt
|
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_format.txt
|
||||||
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_format.md
|
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_format.md
|
||||||
|
|
||||||
--- Procesando archivo JSON: db1001_updated.json ---
|
--- Procesando archivo JSON: DB1001_updated.json ---
|
||||||
Archivo JSON 'db1001_updated.json' cargado correctamente.
|
Archivo JSON 'DB1001_updated.json' cargado correctamente.
|
||||||
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_updated.txt
|
Archivo S7 reconstruido generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_updated.txt
|
||||||
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\db1001_updated.md
|
Archivo Markdown de documentación generado: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001\documentation\DB1001_updated.md
|
||||||
|
|
||||||
--- Proceso de generación de documentación completado ---
|
--- Proceso de generación de documentación completado ---
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
--- Log de Ejecución: x7_value_updater.py ---
|
--- Log de Ejecución: x7_value_updater.py ---
|
||||||
Grupo: S7_DB_Utils
|
Grupo: S7_DB_Utils
|
||||||
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
Directorio de Trabajo: C:\Trabajo\SIDEL\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\Reporte\DB1001
|
||||||
Inicio: 2025-05-18 15:48:00
|
Inicio: 2025-05-23 12:38:21
|
||||||
Fin: 2025-05-18 15:48:01
|
Fin: 2025-05-23 12:38:23
|
||||||
Duración: 0:00:00.811974
|
Duración: 0:00:01.539943
|
||||||
Estado: SUCCESS (Código de Salida: 0)
|
Estado: SUCCESS (Código de Salida: 0)
|
||||||
|
|
||||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -135,6 +135,20 @@ def parse_access(access_element):
|
||||||
uid = access_element.get("UId")
|
uid = access_element.get("UId")
|
||||||
scope = access_element.get("Scope")
|
scope = access_element.get("Scope")
|
||||||
info = {"uid": uid, "scope": scope, "type": "unknown"}
|
info = {"uid": uid, "scope": scope, "type": "unknown"}
|
||||||
|
|
||||||
|
# Manejo específico para direcciones (Address)
|
||||||
|
if scope == "Address":
|
||||||
|
address_elem = access_element.xpath("./flg:Address", namespaces=ns)
|
||||||
|
if address_elem:
|
||||||
|
addr = address_elem[0]
|
||||||
|
# Extraer toda la información disponible sobre la dirección
|
||||||
|
info["type"] = "unknown_structure" # Mantener compatible con el código existente
|
||||||
|
info["Area"] = addr.get("Area", "DB")
|
||||||
|
info["BitOffset"] = addr.get("BitOffset", "0")
|
||||||
|
info["BlockNumber"] = addr.get("BlockNumber", "")
|
||||||
|
info["Type"] = addr.get("Type", "Word") # Tipo por defecto: Word
|
||||||
|
return info
|
||||||
|
|
||||||
symbol = access_element.xpath("./flg:Symbol", namespaces=ns)
|
symbol = access_element.xpath("./flg:Symbol", namespaces=ns)
|
||||||
constant = access_element.xpath("./flg:Constant", namespaces=ns)
|
constant = access_element.xpath("./flg:Constant", namespaces=ns)
|
||||||
|
|
||||||
|
@ -248,7 +262,6 @@ def parse_access(access_element):
|
||||||
info["type"] = "error_no_name"
|
info["type"] = "error_no_name"
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def parse_part(part_element):
|
def parse_part(part_element):
|
||||||
"""Parsea un nodo <flg:Part> de LAD/FBD."""
|
"""Parsea un nodo <flg:Part> de LAD/FBD."""
|
||||||
if part_element is None:
|
if part_element is None:
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -51,13 +51,21 @@ def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
||||||
break # Salir si una dependencia no está lista
|
break # Salir si una dependencia no está lista
|
||||||
|
|
||||||
# Convertir la expresión/constante a SCL para la llamada
|
# Convertir la expresión/constante a SCL para la llamada
|
||||||
# Simplificar ANTES de convertir? Probablemente no necesario para parámetros de entrada
|
# Detectar si es una dirección indirecta (comienza con %)
|
||||||
# a menos que queramos optimizar el valor pasado. Por ahora, convertir directo.
|
is_address_ref = isinstance(source_sympy_or_const, str) and source_sympy_or_const.startswith("%")
|
||||||
param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
|
|
||||||
|
|
||||||
# El nombre del pin SÍ necesita formateo
|
# El nombre del pin SÍ necesita formateo
|
||||||
pin_name_scl = format_variable_name(pin_name)
|
pin_name_scl = format_variable_name(pin_name)
|
||||||
scl_call_params.append(f"{pin_name_scl} := {param_scl_value}")
|
|
||||||
|
# Para direcciones indirectas, usar => en lugar de :=
|
||||||
|
if is_address_ref:
|
||||||
|
# Es un parámetro InOut con dirección indirecta
|
||||||
|
scl_call_params.append(f"{pin_name_scl} => {source_sympy_or_const}")
|
||||||
|
else:
|
||||||
|
# Es un parámetro normal (In)
|
||||||
|
param_scl_value = sympy_expr_to_scl(source_sympy_or_const, symbol_manager)
|
||||||
|
scl_call_params.append(f"{pin_name_scl} := {param_scl_value}")
|
||||||
|
|
||||||
processed_inputs.add(pin_name)
|
processed_inputs.add(pin_name)
|
||||||
|
|
||||||
if not dependencies_resolved:
|
if not dependencies_resolved:
|
||||||
|
@ -121,7 +129,6 @@ def process_call(instruction, network_id, sympy_map, symbol_manager: SymbolManag
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# --- Processor Information Function ---
|
# --- Processor Information Function ---
|
||||||
def get_processor_info():
|
def get_processor_info():
|
||||||
"""Devuelve la información para las llamadas a FC y FB."""
|
"""Devuelve la información para las llamadas a FC y FB."""
|
||||||
|
|
|
@ -47,6 +47,7 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
||||||
|
|
||||||
# Handle single source dictionary
|
# Handle single source dictionary
|
||||||
source_type = source_info.get("type")
|
source_type = source_info.get("type")
|
||||||
|
source_scope = source_info.get("scope")
|
||||||
|
|
||||||
if source_type == "powerrail":
|
if source_type == "powerrail":
|
||||||
return sympy.true
|
return sympy.true
|
||||||
|
@ -70,7 +71,6 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
||||||
# Let's return their string value for now, processors will handle it.
|
# Let's return their string value for now, processors will handle it.
|
||||||
# This might need refinement if constants need symbolic handling.
|
# This might need refinement if constants need symbolic handling.
|
||||||
return str(value) # Or maybe symbol_manager.get_symbol(str(value))?
|
return str(value) # Or maybe symbol_manager.get_symbol(str(value))?
|
||||||
|
|
||||||
elif source_type == "connection":
|
elif source_type == "connection":
|
||||||
map_key = (
|
map_key = (
|
||||||
network_id,
|
network_id,
|
||||||
|
@ -79,6 +79,37 @@ def get_sympy_representation(source_info, network_id, sympy_map, symbol_manager)
|
||||||
)
|
)
|
||||||
# Return the SymPy object from the map
|
# Return the SymPy object from the map
|
||||||
return sympy_map.get(map_key) # Returns None if not found (dependency not ready)
|
return sympy_map.get(map_key) # Returns None if not found (dependency not ready)
|
||||||
|
# Nueva lógica para manejar parámetros de tipo Address
|
||||||
|
elif source_type == "unknown_structure" and source_scope == "Address":
|
||||||
|
# Crear una representación especial para direcciones indirectas
|
||||||
|
area = source_info.get("Area", "DB") # DB por defecto
|
||||||
|
bit_offset = source_info.get("BitOffset", "0")
|
||||||
|
|
||||||
|
# Calcular byte y bit (para direcciones de bits)
|
||||||
|
try:
|
||||||
|
bit_offset_num = int(bit_offset)
|
||||||
|
byte_offset = bit_offset_num // 8
|
||||||
|
bit_in_byte = bit_offset_num % 8
|
||||||
|
|
||||||
|
# Generar referencia de dirección según el tipo
|
||||||
|
addr_type = source_info.get("Type", "Word") # Tipo por defecto: Word
|
||||||
|
if addr_type.upper() in ["WORD", "INT"]:
|
||||||
|
addr_ref = f"%{area}W{byte_offset}"
|
||||||
|
elif addr_type.upper() in ["DWORD", "DINT", "REAL"]:
|
||||||
|
addr_ref = f"%{area}D{byte_offset}"
|
||||||
|
elif addr_type.upper() in ["BYTE", "CHAR", "SINT"]:
|
||||||
|
addr_ref = f"%{area}B{byte_offset}"
|
||||||
|
elif addr_type.upper() in ["BOOL", "X"]:
|
||||||
|
addr_ref = f"%{area}X{byte_offset}.{bit_in_byte}"
|
||||||
|
else:
|
||||||
|
# Tipo no reconocido, usar Word como fallback
|
||||||
|
addr_ref = f"%{area}W{byte_offset}"
|
||||||
|
except ValueError:
|
||||||
|
# Si hay error en la conversión, usar un formato genérico
|
||||||
|
addr_ref = f"%{area}W{bit_offset}"
|
||||||
|
|
||||||
|
# Devolver la referencia como string - será manejada por process_call
|
||||||
|
return addr_ref
|
||||||
elif source_type == "unknown_source":
|
elif source_type == "unknown_source":
|
||||||
print(f"Warning: Referring to unknown source UID: {source_info.get('uid')}")
|
print(f"Warning: Referring to unknown source UID: {source_info.get('uid')}")
|
||||||
return None # Cannot resolve
|
return None # Cannot resolve
|
||||||
|
|
|
@ -15,5 +15,5 @@
|
||||||
"xref_source_subdir": "source"
|
"xref_source_subdir": "source"
|
||||||
},
|
},
|
||||||
"level3": {},
|
"level3": {},
|
||||||
"working_directory": "C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport"
|
"working_directory": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML"
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"path": "C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\IOExport",
|
"path": "C:\\Trabajo\\SIDEL\\09 - SAE452 - Diet as Regular - San Giovanni in Bosco\\Reporte\\SourceDoc\\SourceXML",
|
||||||
"history": [
|
"history": [
|
||||||
|
"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"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -174,8 +174,20 @@ if __name__ == "__main__":
|
||||||
cfg_call_xref_filename = xml_parser_config.get("call_xref_filename", "xref_calls_tree.md")
|
cfg_call_xref_filename = xml_parser_config.get("call_xref_filename", "xref_calls_tree.md")
|
||||||
cfg_db_usage_xref_filename = xml_parser_config.get("db_usage_xref_filename", "xref_db_usage_summary.md")
|
cfg_db_usage_xref_filename = xml_parser_config.get("db_usage_xref_filename", "xref_db_usage_summary.md")
|
||||||
cfg_plc_tag_xref_filename = xml_parser_config.get("plc_tag_xref_filename", "xref_plc_tags_summary.md")
|
cfg_plc_tag_xref_filename = xml_parser_config.get("plc_tag_xref_filename", "xref_plc_tags_summary.md")
|
||||||
cfg_max_call_depth = xml_parser_config.get("max_call_depth", 5)
|
|
||||||
cfg_max_users_list = xml_parser_config.get("max_users_list", 20)
|
# Ensure max_call_depth is an integer
|
||||||
|
try:
|
||||||
|
cfg_max_call_depth = int(xml_parser_config.get("max_call_depth", 5))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
print("Advertencia: Valor inválido para 'max_call_depth' en la configuración. Usando valor por defecto 5.", file=sys.stderr)
|
||||||
|
cfg_max_call_depth = 5
|
||||||
|
|
||||||
|
# Ensure max_users_list is an integer
|
||||||
|
try:
|
||||||
|
cfg_max_users_list = int(xml_parser_config.get("max_users_list", 20))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
print("Advertencia: Valor inválido para 'max_users_list' en la configuración. Usando valor por defecto 20.", file=sys.stderr)
|
||||||
|
cfg_max_users_list = 20
|
||||||
cfg_aggregated_filename = xml_parser_config.get("aggregated_filename", "full_project_representation.md")
|
cfg_aggregated_filename = xml_parser_config.get("aggregated_filename", "full_project_representation.md")
|
||||||
# <-- FIN NUEVO -->
|
# <-- FIN NUEVO -->
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"favorites": [
|
||||||
|
{
|
||||||
|
"id": "1_calc.py",
|
||||||
|
"group_id": "1",
|
||||||
|
"script_name": "calc.py",
|
||||||
|
"added_date": "2025-06-03T11:47:24.757186Z",
|
||||||
|
"execution_count": 0,
|
||||||
|
"last_executed": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"history": [],
|
||||||
|
"settings": {
|
||||||
|
"max_entries": 100,
|
||||||
|
"auto_cleanup_days": 30,
|
||||||
|
"track_execution_time": true,
|
||||||
|
"track_arguments": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "Calculadora",
|
||||||
|
"description": "",
|
||||||
|
"category": "Herramientas",
|
||||||
|
"version": "1.0",
|
||||||
|
"directory": "D:/Proyectos/Scripts/Calcv2",
|
||||||
|
"author": "",
|
||||||
|
"tags": [],
|
||||||
|
"created_date": "2025-06-03T11:46:28.443910Z",
|
||||||
|
"updated_date": "2025-06-03T11:46:28.443910Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categories": {
|
||||||
|
"Herramientas": {
|
||||||
|
"color": "#3B82F6",
|
||||||
|
"icon": "🔧",
|
||||||
|
"subcategories": [
|
||||||
|
"Generales",
|
||||||
|
"Desarrollo",
|
||||||
|
"Sistema"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Análisis": {
|
||||||
|
"color": "#10B981",
|
||||||
|
"icon": "📊",
|
||||||
|
"subcategories": [
|
||||||
|
"Datos",
|
||||||
|
"Estadísticas",
|
||||||
|
"Visualización"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Utilidades": {
|
||||||
|
"color": "#8B5CF6",
|
||||||
|
"icon": "⚙️",
|
||||||
|
"subcategories": [
|
||||||
|
"Archivos",
|
||||||
|
"Texto",
|
||||||
|
"Conversión"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Desarrollo": {
|
||||||
|
"color": "#F59E0B",
|
||||||
|
"icon": "💻",
|
||||||
|
"subcategories": [
|
||||||
|
"Code",
|
||||||
|
"Testing",
|
||||||
|
"Deploy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Visualización": {
|
||||||
|
"color": "#EF4444",
|
||||||
|
"icon": "📈",
|
||||||
|
"subcategories": [
|
||||||
|
"Gráficos",
|
||||||
|
"Reportes",
|
||||||
|
"Dashboard"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Otros": {
|
||||||
|
"color": "#6B7280",
|
||||||
|
"icon": "📁",
|
||||||
|
"subcategories": [
|
||||||
|
"Misceláneos"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"default_execution_directory": "script_directory",
|
||||||
|
"enable_argument_validation": true,
|
||||||
|
"max_history_entries": 100,
|
||||||
|
"auto_cleanup_days": 30
|
||||||
|
}
|
||||||
|
}
|
1468
data/log.txt
1468
data/log.txt
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,416 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
class LauncherManager:
|
||||||
|
def __init__(self, data_path: str):
|
||||||
|
self.data_path = data_path
|
||||||
|
self.launcher_config_path = os.path.join(data_path, "launcher_scripts.json")
|
||||||
|
self.favorites_path = os.path.join(data_path, "launcher_favorites.json")
|
||||||
|
self.history_path = os.path.join(data_path, "launcher_history.json")
|
||||||
|
|
||||||
|
# Inicializar archivos si no existen
|
||||||
|
self._initialize_files()
|
||||||
|
|
||||||
|
def _initialize_files(self):
|
||||||
|
"""Crear archivos de configuración por defecto si no existen"""
|
||||||
|
# Inicializar launcher_scripts.json
|
||||||
|
if not os.path.exists(self.launcher_config_path):
|
||||||
|
default_config = {
|
||||||
|
"version": "1.0",
|
||||||
|
"groups": [],
|
||||||
|
"categories": {
|
||||||
|
"Herramientas": {
|
||||||
|
"color": "#3B82F6",
|
||||||
|
"icon": "🔧",
|
||||||
|
"subcategories": ["Generales", "Desarrollo", "Sistema"]
|
||||||
|
},
|
||||||
|
"Análisis": {
|
||||||
|
"color": "#10B981",
|
||||||
|
"icon": "📊",
|
||||||
|
"subcategories": ["Datos", "Estadísticas", "Visualización"]
|
||||||
|
},
|
||||||
|
"Utilidades": {
|
||||||
|
"color": "#8B5CF6",
|
||||||
|
"icon": "⚙️",
|
||||||
|
"subcategories": ["Archivos", "Texto", "Conversión"]
|
||||||
|
},
|
||||||
|
"Desarrollo": {
|
||||||
|
"color": "#F59E0B",
|
||||||
|
"icon": "💻",
|
||||||
|
"subcategories": ["Code", "Testing", "Deploy"]
|
||||||
|
},
|
||||||
|
"Visualización": {
|
||||||
|
"color": "#EF4444",
|
||||||
|
"icon": "📈",
|
||||||
|
"subcategories": ["Gráficos", "Reportes", "Dashboard"]
|
||||||
|
},
|
||||||
|
"Otros": {
|
||||||
|
"color": "#6B7280",
|
||||||
|
"icon": "📁",
|
||||||
|
"subcategories": ["Misceláneos"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"default_execution_directory": "script_directory",
|
||||||
|
"enable_argument_validation": True,
|
||||||
|
"max_history_entries": 100,
|
||||||
|
"auto_cleanup_days": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(default_config, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
# Inicializar launcher_favorites.json
|
||||||
|
if not os.path.exists(self.favorites_path):
|
||||||
|
default_favorites = {"favorites": []}
|
||||||
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(default_favorites, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
# Inicializar launcher_history.json
|
||||||
|
if not os.path.exists(self.history_path):
|
||||||
|
default_history = {
|
||||||
|
"history": [],
|
||||||
|
"settings": {
|
||||||
|
"max_entries": 100,
|
||||||
|
"auto_cleanup_days": 30,
|
||||||
|
"track_execution_time": True,
|
||||||
|
"track_arguments": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(default_history, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
def get_launcher_groups(self) -> List[Dict[str, Any]]:
|
||||||
|
"""Obtener todos los grupos de scripts GUI"""
|
||||||
|
try:
|
||||||
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config.get("groups", [])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading launcher groups: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_launcher_group(self, group_id: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Obtener un grupo específico por ID"""
|
||||||
|
groups = self.get_launcher_groups()
|
||||||
|
for group in groups:
|
||||||
|
if group.get("id") == group_id:
|
||||||
|
return group
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_launcher_group(self, group_data: Dict[str, Any]) -> Dict[str, str]:
|
||||||
|
"""Agregar nuevo grupo de scripts GUI"""
|
||||||
|
try:
|
||||||
|
# Validar datos requeridos
|
||||||
|
required_fields = ["name", "directory"]
|
||||||
|
for field in required_fields:
|
||||||
|
if not group_data.get(field):
|
||||||
|
return {"status": "error", "message": f"Campo requerido: {field}"}
|
||||||
|
|
||||||
|
# Validar que el directorio existe
|
||||||
|
if not os.path.isdir(group_data["directory"]):
|
||||||
|
return {"status": "error", "message": "El directorio especificado no existe"}
|
||||||
|
|
||||||
|
# Generar ID único si no se proporciona
|
||||||
|
if not group_data.get("id"):
|
||||||
|
group_data["id"] = str(uuid.uuid4())[:8]
|
||||||
|
|
||||||
|
# Verificar que el ID no exista
|
||||||
|
if self.get_launcher_group(group_data["id"]):
|
||||||
|
return {"status": "error", "message": "Ya existe un grupo con este ID"}
|
||||||
|
|
||||||
|
# Agregar campos por defecto
|
||||||
|
current_time = datetime.now().isoformat() + "Z"
|
||||||
|
group_data.setdefault("description", "")
|
||||||
|
group_data.setdefault("category", "Otros")
|
||||||
|
group_data.setdefault("version", "1.0")
|
||||||
|
group_data.setdefault("author", "")
|
||||||
|
group_data.setdefault("tags", [])
|
||||||
|
group_data.setdefault("created_date", current_time)
|
||||||
|
group_data["updated_date"] = current_time
|
||||||
|
|
||||||
|
# Cargar configuración y agregar grupo
|
||||||
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
config["groups"].append(group_data)
|
||||||
|
|
||||||
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
return {"status": "success", "message": "Grupo agregado exitosamente"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": f"Error agregando grupo: {str(e)}"}
|
||||||
|
|
||||||
|
def update_launcher_group(self, group_id: str, group_data: Dict[str, Any]) -> Dict[str, str]:
|
||||||
|
"""Actualizar grupo existente"""
|
||||||
|
try:
|
||||||
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
# Buscar y actualizar el grupo
|
||||||
|
group_found = False
|
||||||
|
for i, group in enumerate(config["groups"]):
|
||||||
|
if group["id"] == group_id:
|
||||||
|
# Mantener ID y fechas de creación
|
||||||
|
group_data["id"] = group_id
|
||||||
|
group_data["created_date"] = group.get("created_date", datetime.now().isoformat() + "Z")
|
||||||
|
group_data["updated_date"] = datetime.now().isoformat() + "Z"
|
||||||
|
|
||||||
|
config["groups"][i] = group_data
|
||||||
|
group_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not group_found:
|
||||||
|
return {"status": "error", "message": "Grupo no encontrado"}
|
||||||
|
|
||||||
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
return {"status": "success", "message": "Grupo actualizado exitosamente"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": f"Error actualizando grupo: {str(e)}"}
|
||||||
|
|
||||||
|
def delete_launcher_group(self, group_id: str) -> Dict[str, str]:
|
||||||
|
"""Eliminar grupo de scripts GUI"""
|
||||||
|
try:
|
||||||
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
# Filtrar el grupo a eliminar
|
||||||
|
original_count = len(config["groups"])
|
||||||
|
config["groups"] = [g for g in config["groups"] if g["id"] != group_id]
|
||||||
|
|
||||||
|
if len(config["groups"]) == original_count:
|
||||||
|
return {"status": "error", "message": "Grupo no encontrado"}
|
||||||
|
|
||||||
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
# Limpiar favoritos del grupo eliminado
|
||||||
|
self._cleanup_favorites_for_group(group_id)
|
||||||
|
|
||||||
|
return {"status": "success", "message": "Grupo eliminado exitosamente"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": f"Error eliminando grupo: {str(e)}"}
|
||||||
|
|
||||||
|
def get_group_scripts(self, group_id: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Obtener scripts de un grupo específico"""
|
||||||
|
try:
|
||||||
|
group = self.get_launcher_group(group_id)
|
||||||
|
if not group:
|
||||||
|
return []
|
||||||
|
|
||||||
|
directory = group["directory"]
|
||||||
|
if not os.path.isdir(directory):
|
||||||
|
return []
|
||||||
|
|
||||||
|
scripts = []
|
||||||
|
for file in os.listdir(directory):
|
||||||
|
if file.endswith('.py') and not file.startswith('_'):
|
||||||
|
script_path = os.path.join(directory, file)
|
||||||
|
if os.path.isfile(script_path):
|
||||||
|
scripts.append({
|
||||||
|
"name": file,
|
||||||
|
"display_name": file[:-3], # Sin extensión .py
|
||||||
|
"path": script_path,
|
||||||
|
"size": os.path.getsize(script_path),
|
||||||
|
"modified": datetime.fromtimestamp(os.path.getmtime(script_path)).isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
return sorted(scripts, key=lambda x: x["name"])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting scripts for group {group_id}: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def execute_gui_script(self, group_id: str, script_name: str, script_args: List[str],
|
||||||
|
broadcast_func) -> Dict[str, Any]:
|
||||||
|
"""Ejecutar script GUI con argumentos opcionales"""
|
||||||
|
try:
|
||||||
|
group = self.get_launcher_group(group_id)
|
||||||
|
if not group:
|
||||||
|
return {"status": "error", "message": "Grupo no encontrado"}
|
||||||
|
|
||||||
|
script_path = os.path.join(group["directory"], script_name)
|
||||||
|
if not os.path.isfile(script_path):
|
||||||
|
return {"status": "error", "message": "Script no encontrado"}
|
||||||
|
|
||||||
|
# Construir comando
|
||||||
|
cmd = [sys.executable, script_path] + script_args
|
||||||
|
working_dir = group["directory"] # Por defecto directorio del script
|
||||||
|
|
||||||
|
broadcast_func(f"Ejecutando script GUI: {script_name}")
|
||||||
|
broadcast_func(f"Comando: {' '.join(cmd)}")
|
||||||
|
broadcast_func(f"Directorio: {working_dir}")
|
||||||
|
|
||||||
|
# Ejecutar script
|
||||||
|
start_time = datetime.now()
|
||||||
|
process = subprocess.Popen(
|
||||||
|
cmd,
|
||||||
|
cwd=working_dir,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
creationflags=subprocess.CREATE_NEW_CONSOLE if sys.platform == "win32" else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Registrar en historial
|
||||||
|
execution_id = str(uuid.uuid4())[:8]
|
||||||
|
self._add_to_history({
|
||||||
|
"id": execution_id,
|
||||||
|
"group_id": group_id,
|
||||||
|
"script_name": script_name,
|
||||||
|
"executed_date": start_time.isoformat() + "Z",
|
||||||
|
"arguments": script_args,
|
||||||
|
"working_directory": working_dir,
|
||||||
|
"status": "running",
|
||||||
|
"pid": process.pid
|
||||||
|
})
|
||||||
|
|
||||||
|
broadcast_func(f"Script GUI ejecutado con PID: {process.pid}")
|
||||||
|
broadcast_func(f"ID de ejecución: {execution_id}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Script GUI ejecutado exitosamente",
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"pid": process.pid
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error ejecutando script GUI: {str(e)}"
|
||||||
|
broadcast_func(error_msg)
|
||||||
|
return {"status": "error", "message": error_msg}
|
||||||
|
|
||||||
|
def get_favorites(self) -> List[Dict[str, Any]]:
|
||||||
|
"""Obtener lista de favoritos"""
|
||||||
|
try:
|
||||||
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data.get("favorites", [])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading favorites: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def toggle_favorite(self, group_id: str, script_name: str) -> Dict[str, str]:
|
||||||
|
"""Agregar o quitar script de favoritos"""
|
||||||
|
try:
|
||||||
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
favorites = data.get("favorites", [])
|
||||||
|
favorite_id = f"{group_id}_{script_name}"
|
||||||
|
|
||||||
|
# Buscar si ya existe
|
||||||
|
existing_favorite = None
|
||||||
|
for i, fav in enumerate(favorites):
|
||||||
|
if fav["group_id"] == group_id and fav["script_name"] == script_name:
|
||||||
|
existing_favorite = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if existing_favorite is not None:
|
||||||
|
# Quitar de favoritos
|
||||||
|
favorites.pop(existing_favorite)
|
||||||
|
action = "removed"
|
||||||
|
else:
|
||||||
|
# Agregar a favoritos
|
||||||
|
favorites.append({
|
||||||
|
"id": favorite_id,
|
||||||
|
"group_id": group_id,
|
||||||
|
"script_name": script_name,
|
||||||
|
"added_date": datetime.now().isoformat() + "Z",
|
||||||
|
"execution_count": 0,
|
||||||
|
"last_executed": None
|
||||||
|
})
|
||||||
|
action = "added"
|
||||||
|
|
||||||
|
data["favorites"] = favorites
|
||||||
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
return {"status": "success", "action": action}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": f"Error managing favorite: {str(e)}"}
|
||||||
|
|
||||||
|
def get_history(self) -> List[Dict[str, Any]]:
|
||||||
|
"""Obtener historial de ejecución"""
|
||||||
|
try:
|
||||||
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data.get("history", [])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading history: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def clear_history(self) -> Dict[str, str]:
|
||||||
|
"""Limpiar historial de ejecución"""
|
||||||
|
try:
|
||||||
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
data["history"] = []
|
||||||
|
|
||||||
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
return {"status": "success", "message": "Historial limpiado exitosamente"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": f"Error clearing history: {str(e)}"}
|
||||||
|
|
||||||
|
def get_categories(self) -> Dict[str, Any]:
|
||||||
|
"""Obtener categorías disponibles"""
|
||||||
|
try:
|
||||||
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config.get("categories", {})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading categories: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _add_to_history(self, entry: Dict[str, Any]):
|
||||||
|
"""Agregar entrada al historial"""
|
||||||
|
try:
|
||||||
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
history = data.get("history", [])
|
||||||
|
history.insert(0, entry) # Agregar al inicio
|
||||||
|
|
||||||
|
# Limitar tamaño del historial
|
||||||
|
max_entries = data.get("settings", {}).get("max_entries", 100)
|
||||||
|
if len(history) > max_entries:
|
||||||
|
history = history[:max_entries]
|
||||||
|
|
||||||
|
data["history"] = history
|
||||||
|
|
||||||
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error adding to history: {e}")
|
||||||
|
|
||||||
|
def _cleanup_favorites_for_group(self, group_id: str):
|
||||||
|
"""Limpiar favoritos de un grupo eliminado"""
|
||||||
|
try:
|
||||||
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
# Filtrar favoritos del grupo eliminado
|
||||||
|
data["favorites"] = [f for f in data.get("favorites", []) if f.get("group_id") != group_id]
|
||||||
|
|
||||||
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error cleaning up favorites for group {group_id}: {e}")
|
|
@ -5,7 +5,7 @@
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
||||||
transition: right 0.3s ease;
|
transition: right 0.3s ease;
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: rgba(0,0,0,0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
}
|
}
|
||||||
|
@ -106,23 +106,29 @@
|
||||||
|
|
||||||
/* Estilos para encabezados dentro de la descripción larga del script */
|
/* Estilos para encabezados dentro de la descripción larga del script */
|
||||||
.long-description-content h1 {
|
.long-description-content h1 {
|
||||||
font-size: 1.875rem; /* Equivalente a text-3xl de Tailwind */
|
font-size: 1.875rem;
|
||||||
|
/* Equivalente a text-3xl de Tailwind */
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content h2 {
|
.long-description-content h2 {
|
||||||
font-size: 1.5rem; /* Equivalente a text-2xl */
|
font-size: 1.5rem;
|
||||||
|
/* Equivalente a text-2xl */
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: 0.875rem;
|
margin-top: 0.875rem;
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content h3 {
|
.long-description-content h3 {
|
||||||
font-size: 1.25rem; /* Equivalente a text-xl */
|
font-size: 1.25rem;
|
||||||
|
/* Equivalente a text-xl */
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
margin-bottom: 0.3rem;
|
margin-bottom: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Puedes añadir estilos para h4, h5, h6 si los necesitas */
|
/* Puedes añadir estilos para h4, h5, h6 si los necesitas */
|
||||||
|
|
||||||
.long-description-content hr {
|
.long-description-content hr {
|
||||||
|
@ -139,26 +145,392 @@
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content ul {
|
.long-description-content ul {
|
||||||
list-style-type: disc;
|
list-style-type: disc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content ol {
|
.long-description-content ol {
|
||||||
list-style-type: decimal;
|
list-style-type: decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content pre {
|
.long-description-content pre {
|
||||||
background-color: #f3f4f6; /* bg-gray-100 */
|
background-color: #f3f4f6;
|
||||||
|
/* bg-gray-100 */
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.long-description-content code {
|
.long-description-content code {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
/* Estilo para código en línea si es necesario */
|
/* Estilo para código en línea si es necesario */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo específico para bloques de código dentro de <pre> */
|
/* Estilo específico para bloques de código dentro de <pre> */
|
||||||
.long-description-content pre code {
|
.long-description-content pre code {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== LAUNCHER GUI STYLES ===== */
|
||||||
|
|
||||||
|
/* Tab Styles */
|
||||||
|
.tab-button {
|
||||||
|
color: #6B7280;
|
||||||
|
border-color: transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button:hover {
|
||||||
|
color: #374151;
|
||||||
|
border-color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button.active {
|
||||||
|
color: #3B82F6;
|
||||||
|
border-color: #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon Styles */
|
||||||
|
.group-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-icon.default {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-icon-small {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 1px solid #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-icon-small.default {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category Styles */
|
||||||
|
.category-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
background: #EFF6FF;
|
||||||
|
color: #1D4ED8;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-btn {
|
||||||
|
background: #F9FAFB;
|
||||||
|
color: #6B7280;
|
||||||
|
border-color: #E5E7EB;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-btn:hover {
|
||||||
|
background: #F3F4F6;
|
||||||
|
color: #374151;
|
||||||
|
border-color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-btn.active {
|
||||||
|
background: #3B82F6;
|
||||||
|
color: white;
|
||||||
|
border-color: #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Script Card Styles */
|
||||||
|
.script-card {
|
||||||
|
border: 1px solid #E5E7EB;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
background: white;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-card:hover {
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
border-color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-card.favorited {
|
||||||
|
border-color: #FCD34D;
|
||||||
|
background: #FFFBEB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Favorite Star */
|
||||||
|
.favorite-star {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-star:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-star.active {
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* History Item */
|
||||||
|
.history-item {
|
||||||
|
border: 1px solid #E5E7EB;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: #F9FAFB;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item:hover {
|
||||||
|
background: #F3F4F6;
|
||||||
|
border-color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item.success {
|
||||||
|
border-left: 4px solid #10B981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item.error {
|
||||||
|
border-left: 4px solid #EF4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item.running {
|
||||||
|
border-left: 4px solid #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Favorites Panel */
|
||||||
|
.favorites-panel {
|
||||||
|
background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-panel.empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Group List Item */
|
||||||
|
.group-list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list-item:hover {
|
||||||
|
background: #F3F4F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list-item.selected {
|
||||||
|
background: #EBF8FF;
|
||||||
|
border: 1px solid #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list-item .group-info {
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list-item .group-name {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list-item .group-category {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #6B7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Improvements */
|
||||||
|
.modal-content {
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 90vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border-bottom: 1px solid #E5E7EB;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
border-top: 1px solid #E5E7EB;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Script Grid */
|
||||||
|
.scripts-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover::after {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: #374151;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1000;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading States */
|
||||||
|
.loading {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
border: 2px solid #E5E7EB;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #3B82F6;
|
||||||
|
animation: spin 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.scripts-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button span {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-button svg {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation Classes */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-in {
|
||||||
|
animation: slideIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status Indicators */
|
||||||
|
.status-success {
|
||||||
|
color: #10B981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-error {
|
||||||
|
color: #EF4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-running {
|
||||||
|
color: #3B82F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-warning {
|
||||||
|
color: #F59E0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
color: #6B7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state svg {
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
color: #D1D5DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button Variants */
|
||||||
|
.btn-outline {
|
||||||
|
border: 1px solid currentColor;
|
||||||
|
background: transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline:hover {
|
||||||
|
background: currentColor;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus States */
|
||||||
|
.focus-visible:focus {
|
||||||
|
outline: 2px solid #3B82F6;
|
||||||
|
outline-offset: 2px;
|
||||||
}
|
}
|
|
@ -0,0 +1,709 @@
|
||||||
|
// launcher.js - Funcionalidad del Launcher GUI
|
||||||
|
|
||||||
|
class LauncherManager {
|
||||||
|
constructor() {
|
||||||
|
this.currentGroup = null;
|
||||||
|
this.groups = [];
|
||||||
|
this.scripts = [];
|
||||||
|
this.favorites = new Set();
|
||||||
|
this.history = [];
|
||||||
|
this.categories = {};
|
||||||
|
this.currentFilter = 'all';
|
||||||
|
this.currentEditingGroup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
console.log('Inicializando Launcher GUI...');
|
||||||
|
await this.loadCategories();
|
||||||
|
await this.loadGroups();
|
||||||
|
await this.loadFavorites();
|
||||||
|
await this.loadHistory();
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.renderInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadCategories() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-categories');
|
||||||
|
this.categories = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading launcher categories:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadGroups() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-groups');
|
||||||
|
this.groups = await response.json();
|
||||||
|
this.renderGroupSelector();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading launcher groups:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadFavorites() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-favorites');
|
||||||
|
const data = await response.json();
|
||||||
|
this.favorites = new Set(data.favorites.map(f => `${f.group_id}_${f.script_name}`));
|
||||||
|
this.renderFavorites(data.favorites);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading favorites:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadHistory() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-history');
|
||||||
|
const data = await response.json();
|
||||||
|
this.history = data.history || [];
|
||||||
|
this.renderHistory();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading history:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
// Event listener para el formulario de grupos
|
||||||
|
const groupForm = document.getElementById('group-form');
|
||||||
|
if (groupForm) {
|
||||||
|
groupForm.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.saveGroup();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInterface() {
|
||||||
|
this.renderGroupSelector();
|
||||||
|
this.renderCategoryFilter();
|
||||||
|
this.updateFavoritesCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroupSelector() {
|
||||||
|
const selector = document.getElementById('launcher-group-select');
|
||||||
|
if (!selector) return;
|
||||||
|
|
||||||
|
selector.innerHTML = '<option value="">-- Seleccionar Grupo --</option>';
|
||||||
|
|
||||||
|
this.groups.forEach(group => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = group.id;
|
||||||
|
option.textContent = group.name;
|
||||||
|
option.dataset.category = group.category;
|
||||||
|
option.dataset.description = group.description;
|
||||||
|
selector.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCategoryFilter() {
|
||||||
|
const filterContainer = document.querySelector('.category-filter .flex');
|
||||||
|
if (!filterContainer) return;
|
||||||
|
|
||||||
|
// Limpiar botones existentes excepto "Todas"
|
||||||
|
const buttons = filterContainer.querySelectorAll('.category-btn:not([data-category="all"])');
|
||||||
|
buttons.forEach(btn => btn.remove());
|
||||||
|
|
||||||
|
// Agregar botones por categoría
|
||||||
|
Object.keys(this.categories).forEach(category => {
|
||||||
|
const categoryData = this.categories[category];
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.className = 'category-btn px-3 py-1 rounded-full text-sm border';
|
||||||
|
button.dataset.category = category;
|
||||||
|
button.innerHTML = `${categoryData.icon} ${category}`;
|
||||||
|
button.onclick = () => this.filterByCategory(category);
|
||||||
|
filterContainer.appendChild(button);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLauncherScripts() {
|
||||||
|
const groupId = document.getElementById('launcher-group-select').value;
|
||||||
|
if (!groupId) {
|
||||||
|
this.scripts = [];
|
||||||
|
this.renderScripts();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/launcher-scripts/${groupId}`);
|
||||||
|
this.scripts = await response.json();
|
||||||
|
this.currentGroup = this.groups.find(g => g.id === groupId);
|
||||||
|
this.updateGroupIcon();
|
||||||
|
this.renderScripts();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading launcher scripts:', error);
|
||||||
|
this.scripts = [];
|
||||||
|
this.renderScripts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGroupIcon() {
|
||||||
|
const iconElement = document.getElementById('selected-group-icon');
|
||||||
|
if (!iconElement || !this.currentGroup) return;
|
||||||
|
|
||||||
|
// Intentar cargar icono personalizado
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = `/api/group-icon/launcher/${this.currentGroup.id}`;
|
||||||
|
img.className = 'w-6 h-6 rounded';
|
||||||
|
img.onerror = () => {
|
||||||
|
// Fallback a icono por defecto
|
||||||
|
iconElement.innerHTML = this.getDefaultIconForCategory(this.currentGroup.category);
|
||||||
|
};
|
||||||
|
img.onload = () => {
|
||||||
|
iconElement.innerHTML = '';
|
||||||
|
iconElement.appendChild(img);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultIconForCategory(category) {
|
||||||
|
const icons = {
|
||||||
|
'Herramientas': '🔧',
|
||||||
|
'Análisis': '📊',
|
||||||
|
'Utilidades': '⚙️',
|
||||||
|
'Desarrollo': '💻',
|
||||||
|
'Visualización': '📈',
|
||||||
|
'Otros': '📁'
|
||||||
|
};
|
||||||
|
return icons[category] || '📁';
|
||||||
|
}
|
||||||
|
|
||||||
|
renderScripts() {
|
||||||
|
const grid = document.getElementById('launcher-scripts-grid');
|
||||||
|
if (!grid) return;
|
||||||
|
|
||||||
|
if (this.scripts.length === 0) {
|
||||||
|
grid.innerHTML = `
|
||||||
|
<div class="col-span-full empty-state">
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<p class="text-lg font-medium">No hay scripts disponibles</p>
|
||||||
|
<p class="text-sm">Selecciona un grupo o verifica que el directorio contenga archivos .py</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteredScripts = this.scripts;
|
||||||
|
if (this.currentFilter !== 'all' && this.currentGroup) {
|
||||||
|
if (this.currentGroup.category !== this.currentFilter) {
|
||||||
|
filteredScripts = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.innerHTML = '';
|
||||||
|
filteredScripts.forEach(script => {
|
||||||
|
const favoriteId = `${this.currentGroup.id}_${script.name}`;
|
||||||
|
const isFavorite = this.favorites.has(favoriteId);
|
||||||
|
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = `script-card ${isFavorite ? 'favorited' : ''}`;
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="flex justify-between items-start mb-2">
|
||||||
|
<h4 class="font-medium text-gray-900">${script.display_name}</h4>
|
||||||
|
<button class="favorite-star ${isFavorite ? 'active' : ''}"
|
||||||
|
onclick="launcherManager.toggleFavorite('${this.currentGroup.id}', '${script.name}')">
|
||||||
|
⭐
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-600 mb-3 line-clamp-2">Script: ${script.name}</p>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="category-badge">${this.currentGroup.category}</span>
|
||||||
|
<div class="space-x-2">
|
||||||
|
<button class="text-blue-500 hover:underline text-sm"
|
||||||
|
onclick="launcherManager.showArgsModal('${script.name}', '${script.display_name}')">
|
||||||
|
Con Argumentos
|
||||||
|
</button>
|
||||||
|
<button class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600"
|
||||||
|
onclick="launcherManager.executeScript('${script.name}')">
|
||||||
|
Ejecutar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterByCategory(category) {
|
||||||
|
this.currentFilter = category;
|
||||||
|
|
||||||
|
// Actualizar botones activos
|
||||||
|
document.querySelectorAll('.category-btn').forEach(btn => {
|
||||||
|
btn.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.querySelector(`[data-category="${category}"]`).classList.add('active');
|
||||||
|
|
||||||
|
// Filtrar grupos en el selector
|
||||||
|
const selector = document.getElementById('launcher-group-select');
|
||||||
|
Array.from(selector.options).forEach(option => {
|
||||||
|
if (option.value === '') return;
|
||||||
|
option.style.display = (category === 'all' || option.dataset.category === category) ? '' : 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-renderizar scripts si hay grupo seleccionado
|
||||||
|
if (this.currentGroup) {
|
||||||
|
this.renderScripts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleFavorite(groupId, scriptName) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-favorites', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
group_id: groupId,
|
||||||
|
script_name: scriptName
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
const favoriteId = `${groupId}_${scriptName}`;
|
||||||
|
if (result.action === 'added') {
|
||||||
|
this.favorites.add(favoriteId);
|
||||||
|
} else {
|
||||||
|
this.favorites.delete(favoriteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recargar datos y re-renderizar
|
||||||
|
await this.loadFavorites();
|
||||||
|
this.renderScripts();
|
||||||
|
this.updateFavoritesCount();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error toggling favorite:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeScript(scriptName, args = []) {
|
||||||
|
if (!this.currentGroup) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/execute-gui-script', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
group_id: this.currentGroup.id,
|
||||||
|
script_name: scriptName,
|
||||||
|
args: args
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
// Recargar historial
|
||||||
|
await this.loadHistory();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing script:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showArgsModal(scriptName, displayName) {
|
||||||
|
const modal = document.getElementById('script-args-modal');
|
||||||
|
const scriptDisplayElement = document.getElementById('script-display-name');
|
||||||
|
const argsInput = document.getElementById('script-args-input');
|
||||||
|
|
||||||
|
if (modal && scriptDisplayElement && argsInput) {
|
||||||
|
scriptDisplayElement.textContent = displayName;
|
||||||
|
argsInput.value = '';
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
|
||||||
|
// Guardar datos para uso posterior
|
||||||
|
modal.dataset.scriptName = scriptName;
|
||||||
|
modal.dataset.groupId = this.currentGroup.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFavorites(favorites) {
|
||||||
|
const favoritesList = document.getElementById('favorites-list');
|
||||||
|
const favoritesPanel = document.getElementById('favorites-panel');
|
||||||
|
|
||||||
|
if (!favoritesList || !favoritesPanel) return;
|
||||||
|
|
||||||
|
if (favorites.length === 0) {
|
||||||
|
favoritesPanel.classList.add('empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
favoritesPanel.classList.remove('empty');
|
||||||
|
favoritesList.innerHTML = '';
|
||||||
|
|
||||||
|
favorites.slice(0, 5).forEach(fav => {
|
||||||
|
const group = this.groups.find(g => g.id === fav.group_id);
|
||||||
|
if (!group) return;
|
||||||
|
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'flex items-center justify-between p-2 bg-white rounded border';
|
||||||
|
item.innerHTML = `
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="group-icon-small default mr-2">
|
||||||
|
${this.getDefaultIconForCategory(group.category)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-sm">${fav.script_name.replace('.py', '')}</div>
|
||||||
|
<div class="text-xs text-gray-500">${group.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="text-blue-500 hover:underline text-sm"
|
||||||
|
onclick="launcherManager.executeFavoriteScript('${fav.group_id}', '${fav.script_name}')">
|
||||||
|
Ejecutar
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
favoritesList.appendChild(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeFavoriteScript(groupId, scriptName) {
|
||||||
|
// Cambiar al grupo correcto si no está seleccionado
|
||||||
|
if (!this.currentGroup || this.currentGroup.id !== groupId) {
|
||||||
|
document.getElementById('launcher-group-select').value = groupId;
|
||||||
|
await this.loadLauncherScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.executeScript(scriptName);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHistory() {
|
||||||
|
const historyList = document.getElementById('history-list');
|
||||||
|
if (!historyList) return;
|
||||||
|
|
||||||
|
if (this.history.length === 0) {
|
||||||
|
historyList.innerHTML = `
|
||||||
|
<div class="text-center text-gray-500 py-4">
|
||||||
|
<p>No hay ejecuciones recientes</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
historyList.innerHTML = '';
|
||||||
|
this.history.slice(0, 10).forEach(entry => {
|
||||||
|
const group = this.groups.find(g => g.id === entry.group_id);
|
||||||
|
const groupName = group ? group.name : 'Grupo desconocido';
|
||||||
|
|
||||||
|
const timeAgo = this.getTimeAgo(entry.executed_date);
|
||||||
|
const statusClass = entry.status === 'success' ? 'success' :
|
||||||
|
entry.status === 'error' ? 'error' : 'running';
|
||||||
|
const statusIcon = entry.status === 'success' ? '✅' :
|
||||||
|
entry.status === 'error' ? '❌' : '🔄';
|
||||||
|
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = `history-item ${statusClass}`;
|
||||||
|
item.innerHTML = `
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">${entry.script_name.replace('.py', '')}</span>
|
||||||
|
<span class="text-sm text-gray-500 ml-2">${groupName}</span>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-gray-400">${timeAgo}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-600 mt-1">
|
||||||
|
${statusIcon} ${entry.status.charAt(0).toUpperCase() + entry.status.slice(1)}
|
||||||
|
${entry.execution_time ? ` - ${entry.execution_time}s` : ''}
|
||||||
|
${entry.arguments && entry.arguments.length > 0 ? ` - Con argumentos` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
historyList.appendChild(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeAgo(dateString) {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMs = now - date;
|
||||||
|
const diffMinutes = Math.floor(diffMs / 60000);
|
||||||
|
|
||||||
|
if (diffMinutes < 1) return 'ahora';
|
||||||
|
if (diffMinutes < 60) return `hace ${diffMinutes}m`;
|
||||||
|
if (diffMinutes < 1440) return `hace ${Math.floor(diffMinutes / 60)}h`;
|
||||||
|
return `hace ${Math.floor(diffMinutes / 1440)}d`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFavoritesCount() {
|
||||||
|
const countElement = document.getElementById('favorites-count');
|
||||||
|
if (countElement) {
|
||||||
|
countElement.textContent = `${this.favorites.size} favoritos`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearLauncherHistory() {
|
||||||
|
if (!confirm('¿Estás seguro de que quieres limpiar el historial?')) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/launcher-history', {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
this.history = [];
|
||||||
|
this.renderHistory();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error clearing history:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === GESTIÓN DE GRUPOS ===
|
||||||
|
|
||||||
|
openGroupEditor() {
|
||||||
|
const modal = document.getElementById('group-editor-modal');
|
||||||
|
if (modal) {
|
||||||
|
this.currentEditingGroup = null;
|
||||||
|
this.clearGroupForm();
|
||||||
|
this.renderExistingGroups();
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeGroupEditor() {
|
||||||
|
const modal = document.getElementById('group-editor-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
this.currentEditingGroup = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderExistingGroups() {
|
||||||
|
const container = document.getElementById('existing-groups-list');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
this.groups.forEach(group => {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'group-list-item';
|
||||||
|
item.innerHTML = `
|
||||||
|
<div class="group-icon-small default">
|
||||||
|
${this.getDefaultIconForCategory(group.category)}
|
||||||
|
</div>
|
||||||
|
<div class="group-info">
|
||||||
|
<div class="group-name">${group.name}</div>
|
||||||
|
<div class="group-category">${group.category}</div>
|
||||||
|
</div>
|
||||||
|
<button class="text-blue-500 hover:underline text-sm" onclick="launcherManager.editGroup('${group.id}')">
|
||||||
|
Editar
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
container.appendChild(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editGroup(groupId) {
|
||||||
|
const group = this.groups.find(g => g.id === groupId);
|
||||||
|
if (!group) return;
|
||||||
|
|
||||||
|
this.currentEditingGroup = group;
|
||||||
|
this.populateGroupForm(group);
|
||||||
|
document.getElementById('delete-group-btn').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
populateGroupForm(group) {
|
||||||
|
document.getElementById('group-id').value = group.id;
|
||||||
|
document.getElementById('group-name').value = group.name;
|
||||||
|
document.getElementById('group-description').value = group.description || '';
|
||||||
|
document.getElementById('group-category').value = group.category;
|
||||||
|
document.getElementById('group-version').value = group.version || '1.0';
|
||||||
|
document.getElementById('group-directory').value = group.directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearGroupForm() {
|
||||||
|
document.getElementById('group-id').value = '';
|
||||||
|
document.getElementById('group-name').value = '';
|
||||||
|
document.getElementById('group-description').value = '';
|
||||||
|
document.getElementById('group-category').value = 'Otros';
|
||||||
|
document.getElementById('group-version').value = '1.0';
|
||||||
|
document.getElementById('group-directory').value = '';
|
||||||
|
document.getElementById('delete-group-btn').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveGroup() {
|
||||||
|
const formData = {
|
||||||
|
id: document.getElementById('group-id').value,
|
||||||
|
name: document.getElementById('group-name').value,
|
||||||
|
description: document.getElementById('group-description').value,
|
||||||
|
category: document.getElementById('group-category').value,
|
||||||
|
version: document.getElementById('group-version').value,
|
||||||
|
directory: document.getElementById('group-directory').value
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response;
|
||||||
|
if (this.currentEditingGroup) {
|
||||||
|
// Actualizar grupo existente
|
||||||
|
response = await fetch(`/api/launcher-groups/${this.currentEditingGroup.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Crear nuevo grupo
|
||||||
|
response = await fetch('/api/launcher-groups', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
await this.loadGroups();
|
||||||
|
this.closeGroupEditor();
|
||||||
|
this.renderInterface();
|
||||||
|
} else {
|
||||||
|
alert(`Error: ${result.message}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving group:', error);
|
||||||
|
alert('Error al guardar el grupo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteGroup() {
|
||||||
|
if (!this.currentEditingGroup) return;
|
||||||
|
|
||||||
|
if (!confirm(`¿Estás seguro de que quieres eliminar el grupo "${this.currentEditingGroup.name}"?`)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/launcher-groups/${this.currentEditingGroup.id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
await this.loadGroups();
|
||||||
|
this.closeGroupEditor();
|
||||||
|
this.renderInterface();
|
||||||
|
|
||||||
|
// Limpiar selección si era el grupo actual
|
||||||
|
if (this.currentGroup && this.currentGroup.id === this.currentEditingGroup.id) {
|
||||||
|
document.getElementById('launcher-group-select').value = '';
|
||||||
|
this.currentGroup = null;
|
||||||
|
this.scripts = [];
|
||||||
|
this.renderScripts();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert(`Error: ${result.message}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting group:', error);
|
||||||
|
alert('Error al eliminar el grupo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
browseGroupDirectory() {
|
||||||
|
// Similar a la función existente pero para el formulario de grupos
|
||||||
|
fetch('/api/browse-directories')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'success') {
|
||||||
|
document.getElementById('group-directory').value = data.path;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error browsing directory:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === FUNCIONES GLOBALES ===
|
||||||
|
|
||||||
|
// Función para cambiar entre tabs
|
||||||
|
function switchTab(tabName) {
|
||||||
|
// Cambiar tabs activos
|
||||||
|
document.querySelectorAll('.tab-button').forEach(btn => {
|
||||||
|
btn.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||||
|
|
||||||
|
// Cambiar contenido
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||||||
|
content.classList.add('hidden');
|
||||||
|
});
|
||||||
|
document.getElementById(`${tabName}-content`).classList.remove('hidden');
|
||||||
|
|
||||||
|
// Inicializar launcher si es la primera vez
|
||||||
|
if (tabName === 'launcher' && !window.launcherManager) {
|
||||||
|
window.launcherManager = new LauncherManager();
|
||||||
|
window.launcherManager.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funciones para modales
|
||||||
|
function openGroupEditor() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.openGroupEditor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeGroupEditor() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.closeGroupEditor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteGroup() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.deleteGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function browseGroupDirectory() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.browseGroupDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterByCategory(category) {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.filterByCategory(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLauncherScripts() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.loadLauncherScripts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLauncherHistory() {
|
||||||
|
if (window.launcherManager) {
|
||||||
|
window.launcherManager.clearLauncherHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funciones para modal de argumentos
|
||||||
|
function closeArgsModal() {
|
||||||
|
const modal = document.getElementById('script-args-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeWithArgs() {
|
||||||
|
const modal = document.getElementById('script-args-modal');
|
||||||
|
const argsInput = document.getElementById('script-args-input');
|
||||||
|
|
||||||
|
if (modal && argsInput && window.launcherManager) {
|
||||||
|
const scriptName = modal.dataset.scriptName;
|
||||||
|
const args = argsInput.value.trim().split(/\s+/).filter(arg => arg.length > 0);
|
||||||
|
|
||||||
|
window.launcherManager.executeScript(scriptName, args);
|
||||||
|
closeArgsModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicialización cuando se carga la página
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
console.log('Launcher JS loaded');
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="es">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
@ -7,13 +8,17 @@
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-100">
|
<body class="bg-gray-100">
|
||||||
<!-- Settings Button -->
|
<!-- Settings Button -->
|
||||||
<div class="fixed top-4 right-4 z-50">
|
<div class="fixed top-4 right-4 z-50">
|
||||||
<button onclick="toggleSidebar()" class="bg-white p-2 rounded-full shadow-lg hover:bg-gray-100">
|
<button onclick="toggleSidebar()" class="bg-white p-2 rounded-full shadow-lg hover:bg-gray-100">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
|
||||||
|
</path>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +31,8 @@
|
||||||
<h2 class="text-xl font-bold">Configuración Global</h2>
|
<h2 class="text-xl font-bold">Configuración Global</h2>
|
||||||
<button onclick="toggleSidebar()" class="text-gray-500 hover:text-gray-700">
|
<button onclick="toggleSidebar()" class="text-gray-500 hover:text-gray-700">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
|
||||||
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,8 +41,7 @@
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-xl font-bold">Configuración Base</h2>
|
<h2 class="text-xl font-bold">Configuración Base</h2>
|
||||||
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="toggleConfig('level1-content')">
|
||||||
onclick="toggleConfig('level1-content')">
|
|
||||||
Mostrar Configuración
|
Mostrar Configuración
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,9 +58,8 @@
|
||||||
<!-- Level 2 Configuration -->
|
<!-- Level 2 Configuration -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-xl font-bold">Configuración del Scrips</h2>
|
<h2 class="text-xl font-bold">Configuración del Scripts</h2>
|
||||||
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="toggleConfig('level2-content')">
|
||||||
onclick="toggleConfig('level2-content')">
|
|
||||||
Mostrar Configuración
|
Mostrar Configuración
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,10 +75,12 @@
|
||||||
|
|
||||||
<!-- Botón para detener el servidor -->
|
<!-- Botón para detener el servidor -->
|
||||||
<div class="mt-8 pt-4 border-t border-gray-300">
|
<div class="mt-8 pt-4 border-t border-gray-300">
|
||||||
<button class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded shadow mb-2" onclick="openMinicondaConsole()">
|
<button class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded shadow mb-2"
|
||||||
|
onclick="openMinicondaConsole()">
|
||||||
Abrir Miniconda Console
|
Abrir Miniconda Console
|
||||||
</button>
|
</button>
|
||||||
<button class="w-full bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded shadow" onclick="shutdownServer()">
|
<button class="w-full bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded shadow"
|
||||||
|
onclick="shutdownServer()">
|
||||||
Detener Servidor
|
Detener Servidor
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,84 +89,214 @@
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<!-- Script Group Selection -->
|
<!-- Tab Navigation -->
|
||||||
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
<div class="mb-8">
|
||||||
<h2 class="text-xl font-bold mb-4">Funciones a utilizar:</h2>
|
<div class="border-b border-gray-200">
|
||||||
<div class="flex gap-2 items-center">
|
<nav class="-mb-px flex space-x-8">
|
||||||
<select id="script-group" class="flex-1 p-2 border rounded mb-2">
|
<button id="config-tab" onclick="switchTab('config')"
|
||||||
{% for group in script_groups %}
|
class="tab-button active py-2 px-1 border-b-2 font-medium text-sm">
|
||||||
<option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}</option>
|
<span class="flex items-center">
|
||||||
{% endfor %}
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
</select>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
<button onclick="editGroupDescription()" class="bg-blue-500 text-white p-2 rounded mb-2">
|
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</path>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
</svg>
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||||
</button>
|
</svg>
|
||||||
<button onclick="openGroupInVsCode()" class="bg-blue-500 text-white p-2 rounded mb-2" title="Abrir grupo en VS Code">
|
Scripts de Configuración
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</span>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
</button>
|
||||||
</svg>
|
<button id="launcher-tab" onclick="switchTab('launcher')"
|
||||||
</button>
|
class="tab-button py-2 px-1 border-b-2 font-medium text-sm">
|
||||||
</div>
|
<span class="flex items-center">
|
||||||
<p id="group-description" class="text-gray-600 text-sm italic"></p>
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<div class="text-xs text-gray-500 mt-2">
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
<span id="group-version"></span>
|
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z">
|
||||||
<span id="group-author"></span>
|
</path>
|
||||||
|
</svg>
|
||||||
|
Launcher GUI
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Working Directory -->
|
<!-- Tab Content: Sistema de Configuración Actual -->
|
||||||
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
<div id="config-content" class="tab-content">
|
||||||
<h2 class="text-xl font-bold mb-4">Directorio de Trabajo</h2>
|
<!-- Script Group Selection -->
|
||||||
<div class="flex gap-4">
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
||||||
<div class="flex-1 flex gap-2">
|
<h2 class="text-xl font-bold mb-4">Funciones a utilizar:</h2>
|
||||||
<input type="text" id="working-directory" class="flex-1 p-2 border rounded bg-green-50">
|
<div class="flex gap-2 items-center">
|
||||||
<button class="bg-gray-500 text-white px-4 py-2 rounded" onclick="browseDirectory()">
|
<select id="script-group" class="flex-1 p-2 border rounded mb-2">
|
||||||
Explorar
|
{% for group in script_groups %}
|
||||||
|
<option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<button onclick="editGroupDescription()" class="bg-blue-500 text-white p-2 rounded mb-2">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="open-in-explorer-btn" class="bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded" title="Abrir directorio actual en el explorador de archivos">
|
<button onclick="openGroupInVsCode()" class="bg-blue-500 text-white p-2 rounded mb-2"
|
||||||
Abrir Carpeta
|
title="Abrir grupo en VS Code">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()">
|
<p id="group-description" class="text-gray-600 text-sm italic"></p>
|
||||||
Confirmar
|
<div class="text-xs text-gray-500 mt-2">
|
||||||
</button>
|
<span id="group-version"></span>
|
||||||
|
<span id="group-author"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Add directory history dropdown -->
|
|
||||||
<div class="mt-2">
|
<!-- Working Directory -->
|
||||||
<select id="directory-history" class="w-full p-2 border rounded text-gray-600" onchange="loadHistoryDirectory(this.value)">
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
||||||
<option value="">-- Directorios recientes --</option>
|
<h2 class="text-xl font-bold mb-4">Directorio de Trabajo</h2>
|
||||||
</select>
|
<div class="flex gap-4">
|
||||||
|
<div class="flex-1 flex gap-2">
|
||||||
|
<input type="text" id="working-directory" class="flex-1 p-2 border rounded bg-green-50">
|
||||||
|
<button class="bg-gray-500 text-white px-4 py-2 rounded" onclick="browseDirectory()">
|
||||||
|
Explorar
|
||||||
|
</button>
|
||||||
|
<button id="open-in-explorer-btn"
|
||||||
|
class="bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded"
|
||||||
|
title="Abrir directorio actual en el explorador de archivos">
|
||||||
|
Abrir Carpeta
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="setWorkingDirectory()">
|
||||||
|
Confirmar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Add directory history dropdown -->
|
||||||
|
<div class="mt-2">
|
||||||
|
<select id="directory-history" class="w-full p-2 border rounded text-gray-600"
|
||||||
|
onchange="loadHistoryDirectory(this.value)">
|
||||||
|
<option value="">-- Directorios recientes --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Level 3 Configuration -->
|
||||||
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h2 class="text-xl font-bold">Configuración del Directorio</h2>
|
||||||
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="toggleConfig('level3-content')">
|
||||||
|
Ocultar Configuración
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="level3-content">
|
||||||
|
<div id="level3-form"></div>
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(3)">
|
||||||
|
Modificar Esquema
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scripts List -->
|
||||||
|
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Scripts Disponibles</h2>
|
||||||
|
<div id="scripts-list" class="space-y-4"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Level 3 Configuration -->
|
<!-- Tab Content: Nuevo Launcher GUI -->
|
||||||
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
<div id="launcher-content" class="tab-content hidden">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<!-- Launcher Controls -->
|
||||||
<h2 class="text-xl font-bold">Configuración del Directorio</h2>
|
<div class="mb-6 bg-white p-6 rounded-lg shadow">
|
||||||
<button class="bg-blue-500 text-white px-4 py-2 rounded"
|
<div class="flex justify-between items-center mb-4">
|
||||||
onclick="toggleConfig('level3-content')">
|
<h2 class="text-xl font-bold">Launcher GUI - Scripts Independientes</h2>
|
||||||
Ocultar Configuración
|
<button onclick="openGroupEditor()"
|
||||||
</button>
|
class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
|
||||||
</div>
|
Gestionar Grupos
|
||||||
<div id="level3-content">
|
|
||||||
<div id="level3-form"></div>
|
|
||||||
<div class="flex justify-end mt-4">
|
|
||||||
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(3)">
|
|
||||||
Modificar Esquema
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Group Selector -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium mb-2">Seleccionar Grupo de Scripts</label>
|
||||||
|
<div class="relative">
|
||||||
|
<select id="launcher-group-select" class="w-full p-3 border rounded-lg pl-12"
|
||||||
|
onchange="loadLauncherScripts()">
|
||||||
|
<option value="">-- Seleccionar Grupo --</option>
|
||||||
|
</select>
|
||||||
|
<div class="absolute left-3 top-1/2 transform -translate-y-1/2">
|
||||||
|
<div id="selected-group-icon"
|
||||||
|
class="w-6 h-6 bg-gray-200 rounded flex items-center justify-center text-sm">📁</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Category Filter -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<h3 class="text-sm font-medium mb-2">Filtrar por Categoría</h3>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<button class="category-btn active px-3 py-1 rounded-full text-sm border" data-category="all"
|
||||||
|
onclick="filterByCategory('all')">
|
||||||
|
Todas
|
||||||
|
</button>
|
||||||
|
<button class="category-btn px-3 py-1 rounded-full text-sm border" data-category="Herramientas"
|
||||||
|
onclick="filterByCategory('Herramientas')">
|
||||||
|
🔧 Herramientas
|
||||||
|
</button>
|
||||||
|
<button class="category-btn px-3 py-1 rounded-full text-sm border" data-category="Análisis"
|
||||||
|
onclick="filterByCategory('Análisis')">
|
||||||
|
📊 Análisis
|
||||||
|
</button>
|
||||||
|
<button class="category-btn px-3 py-1 rounded-full text-sm border" data-category="Utilidades"
|
||||||
|
onclick="filterByCategory('Utilidades')">
|
||||||
|
⚙️ Utilidades
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Favorites Panel -->
|
||||||
|
<div id="favorites-panel" class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<h3 class="text-lg font-semibold text-yellow-800">
|
||||||
|
⭐ Scripts Favoritos
|
||||||
|
</h3>
|
||||||
|
<span class="text-sm text-yellow-600" id="favorites-count">
|
||||||
|
0 favoritos
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="favorites-list" class="space-y-2">
|
||||||
|
<!-- Lista dinámica de favoritos -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scripts Grid -->
|
||||||
|
<div class="mb-6 bg-white p-6 rounded-lg shadow">
|
||||||
|
<h2 class="text-xl font-bold mb-4">Scripts Disponibles</h2>
|
||||||
|
<div id="launcher-scripts-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
<!-- Scripts cards dinámicos -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- History Panel -->
|
||||||
|
<div class="mb-6 bg-white p-6 rounded-lg shadow">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h3 class="text-lg font-semibold">📝 Historial Reciente</h3>
|
||||||
|
<button onclick="clearLauncherHistory()" class="text-red-500 hover:text-red-700 text-sm">
|
||||||
|
Limpiar Historial
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="history-list" class="space-y-2 max-h-64 overflow-y-auto">
|
||||||
|
<!-- Lista dinámica de historial -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts List -->
|
<!-- Logs (común para ambos sistemas) -->
|
||||||
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
|
||||||
<h2 class="text-xl font-bold mb-4">Scripts Disponibles</h2>
|
|
||||||
<div id="scripts-list" class="space-y-4"></div> <!-- Añadido space-y-4 para separación -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Logs -->
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow">
|
<div class="bg-white p-6 rounded-lg shadow">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-xl font-bold">Logs</h2>
|
<h2 class="text-xl font-bold">Logs</h2>
|
||||||
|
@ -168,12 +304,13 @@
|
||||||
Limpiar
|
Limpiar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="log-area" class="bg-gray-100 p-4 rounded h-64 overflow-y-auto font-mono text-sm whitespace-pre-wrap">
|
<div id="log-area"
|
||||||
|
class="bg-gray-100 p-4 rounded h-64 overflow-y-auto font-mono text-sm whitespace-pre-wrap">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Schema Editor Modal -->
|
<!-- Schema Editor Modal (existente) -->
|
||||||
<div id="schema-editor" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center">
|
<div id="schema-editor" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center">
|
||||||
<div class="modal-content bg-white rounded-lg shadow-lg max-h-screen overflow-auto">
|
<div class="modal-content bg-white rounded-lg shadow-lg max-h-screen overflow-auto">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
@ -184,22 +321,23 @@
|
||||||
class="bg-gray-500 text-white px-4 py-2 rounded">
|
class="bg-gray-500 text-white px-4 py-2 rounded">
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
<button onclick="saveSchema()"
|
<button onclick="saveSchema()" class="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
class="bg-blue-500 text-white px-4 py-2 rounded">
|
|
||||||
Guardar
|
Guardar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex mt-4" id="editor-tabs">
|
<div class="flex mt-4" id="editor-tabs">
|
||||||
<button id="visual-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('visual')">Visual</button>
|
<button id="visual-tab" class="px-4 py-2 border-b-2"
|
||||||
|
onclick="switchEditorMode('visual')">Visual</button>
|
||||||
<button id="json-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('json')">JSON</button>
|
<button id="json-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('json')">JSON</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div id="visual-editor" class="hidden">
|
<div id="visual-editor" class="hidden">
|
||||||
<div id="schema-fields"></div>
|
<div id="schema-fields"></div>
|
||||||
<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar Campo</button>
|
<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar
|
||||||
|
Campo</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="json-editor" class="w-full h-96 font-mono p-2 border rounded"></textarea>
|
<textarea id="json-editor" class="w-full h-96 font-mono p-2 border rounded"></textarea>
|
||||||
<input type="hidden" id="schema-level">
|
<input type="hidden" id="schema-level">
|
||||||
|
@ -211,8 +349,7 @@
|
||||||
class="bg-gray-500 text-white px-4 py-2 rounded">
|
class="bg-gray-500 text-white px-4 py-2 rounded">
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
<button onclick="saveSchema()"
|
<button onclick="saveSchema()" class="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
class="bg-blue-500 text-white px-4 py-2 rounded">
|
|
||||||
Guardar
|
Guardar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -220,13 +357,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Script Details Editor Modal -->
|
<!-- Script Details Editor Modal (existente) -->
|
||||||
<div id="script-editor-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50">
|
<div id="script-editor-modal"
|
||||||
|
class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div class="modal-content bg-white rounded-lg shadow-lg w-full max-w-lg max-h-[90vh] overflow-auto">
|
<div class="modal-content bg-white rounded-lg shadow-lg w-full max-w-lg max-h-[90vh] overflow-auto">
|
||||||
<div class="modal-header sticky top-0 bg-white border-b p-4">
|
<div class="modal-header sticky top-0 bg-white border-b p-4">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h3 class="text-xl font-bold">Editar Detalles del Script</h3>
|
<h3 class="text-xl font-bold">Editar Detalles del Script</h3>
|
||||||
<button onclick="closeScriptEditorModal()" class="text-gray-500 hover:text-gray-700">×</button>
|
<button onclick="closeScriptEditorModal()"
|
||||||
|
class="text-gray-500 hover:text-gray-700">×</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6 space-y-4">
|
<div class="p-6 space-y-4">
|
||||||
|
@ -234,7 +373,8 @@
|
||||||
<input type="hidden" id="edit-script-filename">
|
<input type="hidden" id="edit-script-filename">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-bold mb-1">Nombre del Archivo</label>
|
<label class="block text-sm font-bold mb-1">Nombre del Archivo</label>
|
||||||
<p id="edit-script-filename-display" class="text-sm text-gray-600 bg-gray-100 p-2 rounded border"></p>
|
<p id="edit-script-filename-display" class="text-sm text-gray-600 bg-gray-100 p-2 rounded border">
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -242,29 +382,155 @@
|
||||||
<input type="text" id="edit-script-display-name" class="w-full p-2 border rounded">
|
<input type="text" id="edit-script-display-name" class="w-full p-2 border rounded">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="edit-script-short-description" class="block text-sm font-bold mb-2">Descripción Corta</label>
|
<label for="edit-script-short-description" class="block text-sm font-bold mb-2">Descripción
|
||||||
|
Corta</label>
|
||||||
<input type="text" id="edit-script-short-description" class="w-full p-2 border rounded">
|
<input type="text" id="edit-script-short-description" class="w-full p-2 border rounded">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="edit-script-long-description" class="block text-sm font-bold mb-2">Descripción Larga / Ayuda</label>
|
<label for="edit-script-long-description" class="block text-sm font-bold mb-2">Descripción Larga /
|
||||||
|
Ayuda</label>
|
||||||
<textarea id="edit-script-long-description" class="w-full p-2 border rounded" rows="5"></textarea>
|
<textarea id="edit-script-long-description" class="w-full p-2 border rounded" rows="5"></textarea>
|
||||||
<p class="text-xs text-gray-500 mt-1">Usa Markdown. Doble Enter para párrafo nuevo, dos espacios + Enter para salto de línea simple.</p>
|
<p class="text-xs text-gray-500 mt-1">Usa Markdown. Doble Enter para párrafo nuevo, dos espacios +
|
||||||
|
Enter para salto de línea simple.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" id="edit-script-hidden" class="form-checkbox h-5 w-5 mr-2">
|
<input type="checkbox" id="edit-script-hidden" class="form-checkbox h-5 w-5 mr-2">
|
||||||
<label for="edit-script-hidden" class="text-sm font-bold">Ocultar script (no se podrá ejecutar desde la UI)</label>
|
<label for="edit-script-hidden" class="text-sm font-bold">Ocultar script (no se podrá ejecutar desde
|
||||||
|
la UI)</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer sticky bottom-0 bg-white border-t p-4 flex justify-end gap-4">
|
<div class="modal-footer sticky bottom-0 bg-white border-t p-4 flex justify-end gap-4">
|
||||||
<button onclick="closeScriptEditorModal()" class="bg-gray-500 text-white px-4 py-2 rounded">Cancelar</button>
|
<button onclick="closeScriptEditorModal()"
|
||||||
<button onclick="saveScriptDetails()" class="bg-blue-500 text-white px-4 py-2 rounded">Guardar Cambios</button>
|
class="bg-gray-500 text-white px-4 py-2 rounded">Cancelar</button>
|
||||||
|
<button onclick="saveScriptDetails()" class="bg-blue-500 text-white px-4 py-2 rounded">Guardar
|
||||||
|
Cambios</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Corregir la ruta del script -->
|
<!-- Group Editor Modal (NUEVO) -->
|
||||||
<script src="https://unpkg.com/markdown-it@14.1.0/dist/markdown-it.min.js"></script> <!-- Librería Markdown-it (unpkg) -->
|
<div id="group-editor-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
|
||||||
|
<div class="flex items-center justify-center min-h-screen p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-screen overflow-y-auto">
|
||||||
|
<div class="p-6">
|
||||||
|
<h3 class="text-xl font-semibold mb-6">Gestionar Grupos de Scripts GUI</h3>
|
||||||
|
|
||||||
|
<!-- Lista de grupos existentes -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h4 class="font-medium mb-3">Grupos Existentes</h4>
|
||||||
|
<div id="existing-groups-list" class="space-y-2 max-h-40 overflow-y-auto border rounded p-2">
|
||||||
|
<!-- Lista dinámica -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Formulario de edición -->
|
||||||
|
<form id="group-form" class="space-y-4">
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">ID del Grupo</label>
|
||||||
|
<input type="text" id="group-id" class="w-full p-2 border rounded" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Nombre</label>
|
||||||
|
<input type="text" id="group-name" class="w-full p-2 border rounded" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Descripción</label>
|
||||||
|
<textarea id="group-description" class="w-full p-2 border rounded h-20"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Categoría</label>
|
||||||
|
<select id="group-category" class="w-full p-2 border rounded">
|
||||||
|
<option value="Herramientas">🔧 Herramientas</option>
|
||||||
|
<option value="Análisis">📊 Análisis</option>
|
||||||
|
<option value="Utilidades">⚙️ Utilidades</option>
|
||||||
|
<option value="Desarrollo">💻 Desarrollo</option>
|
||||||
|
<option value="Visualización">📈 Visualización</option>
|
||||||
|
<option value="Otros">📁 Otros</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Versión</label>
|
||||||
|
<input type="text" id="group-version" class="w-full p-2 border rounded" value="1.0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">Directorio</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input type="text" id="group-directory" class="flex-1 p-2 border rounded" required>
|
||||||
|
<button type="button" onclick="browseGroupDirectory()"
|
||||||
|
class="bg-gray-500 text-white px-4 py-2 rounded">
|
||||||
|
Explorar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-3 pt-4">
|
||||||
|
<button type="button" onclick="closeGroupEditor()"
|
||||||
|
class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button type="button" onclick="deleteGroup()"
|
||||||
|
class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600" id="delete-group-btn"
|
||||||
|
style="display: none;">
|
||||||
|
Eliminar
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
|
||||||
|
Guardar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Script Arguments Modal (NUEVO) -->
|
||||||
|
<div id="script-args-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
|
||||||
|
<div class="flex items-center justify-center min-h-screen p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
|
||||||
|
<div class="p-6">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">Argumentos del Script</h3>
|
||||||
|
<div id="script-info" class="mb-4 p-3 bg-gray-50 rounded">
|
||||||
|
<div class="font-medium" id="script-display-name"></div>
|
||||||
|
<div class="text-sm text-gray-600" id="script-args-description"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium mb-1">
|
||||||
|
Argumentos de Línea de Comandos
|
||||||
|
</label>
|
||||||
|
<textarea id="script-args-input" class="w-full p-2 border rounded h-20"
|
||||||
|
placeholder="--input data.xlsx --output results.csv"></textarea>
|
||||||
|
<p class="text-xs text-gray-500 mt-1">
|
||||||
|
Separar argumentos con espacios. Usar comillas para valores con espacios.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-3 mt-6">
|
||||||
|
<button onclick="closeArgsModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button onclick="executeWithArgs()"
|
||||||
|
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
|
||||||
|
Ejecutar Script
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
|
||||||
<script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script>
|
<script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/launcher.js') }}" defer></script>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
console.log('Window loaded, initializing app...');
|
console.log('Window loaded, initializing app...');
|
||||||
|
@ -276,4 +542,5 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue