2022-06-01 16:37:19 +00:00
|
|
|
#!/usr/bin/env python3
|
2023-01-20 16:19:39 +00:00
|
|
|
import argparse
|
2022-06-01 16:37:19 +00:00
|
|
|
import faulthandler
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
2025-09-03 14:00:43 +00:00
|
|
|
|
|
|
|
|
from src.lib.helpers.recipe_manager import backup_current_recipes
|
|
|
|
|
|
2025-01-23 15:49:51 +00:00
|
|
|
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION']="python"
|
2023-02-17 20:19:30 +00:00
|
|
|
import platform
|
2022-06-01 16:37:19 +00:00
|
|
|
import signal
|
|
|
|
|
import sys
|
|
|
|
|
import traceback
|
2022-07-05 16:16:22 +00:00
|
|
|
import weakref
|
2022-06-01 16:37:19 +00:00
|
|
|
from datetime import datetime
|
|
|
|
|
from pathlib import Path
|
2023-03-30 10:06:45 +00:00
|
|
|
if platform.system().lower() == "windows":
|
2023-02-17 20:19:30 +00:00
|
|
|
sys.path.append(f"{os.getcwd()}\src\components")
|
2023-03-30 10:06:45 +00:00
|
|
|
else:
|
|
|
|
|
sys.path.append(f"{os.getcwd()}/src/components")
|
2025-01-14 14:43:52 +00:00
|
|
|
sys.path.append(f"{os.getcwd()}")
|
|
|
|
|
|
|
|
|
|
from ui.diagnostics import Diagnostics
|
|
|
|
|
from lib.helpers.single_process import SingleProcess
|
|
|
|
|
from ui.logs_management.info import Logs_Management
|
|
|
|
|
|
2023-01-31 14:02:27 +00:00
|
|
|
|
2022-07-04 10:36:51 +00:00
|
|
|
app = None
|
2022-06-01 16:37:19 +00:00
|
|
|
|
2023-01-20 16:19:39 +00:00
|
|
|
parser = argparse.ArgumentParser(prog='ST-TEN', description='Leak test system')
|
|
|
|
|
parser.add_argument('-s', '--system-id')
|
2025-01-23 15:49:51 +00:00
|
|
|
parser.add_argument('-p', '--auto-select')
|
2024-01-16 18:12:06 +00:00
|
|
|
args, unspec = parser.parse_known_args()
|
|
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
|
2022-06-29 11:04:31 +00:00
|
|
|
def quit_app(signalnum=None, handler=None):
|
|
|
|
|
logging.info(f"quitting app. signal: {signalnum!r}, handler: {handler!r}")
|
2022-06-01 16:37:19 +00:00
|
|
|
global app
|
2022-07-04 10:36:51 +00:00
|
|
|
if app is not None:
|
|
|
|
|
app.quit()
|
2022-06-01 16:37:19 +00:00
|
|
|
quit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# SETUP QUITTING ON CTRL+C
|
|
|
|
|
signal.signal(signal.SIGINT, quit_app)
|
|
|
|
|
|
|
|
|
|
# SETUP FAULTHANDLER
|
|
|
|
|
faulthandler.enable(file=sys.stderr, all_threads=True)
|
|
|
|
|
|
|
|
|
|
# SETUP LOGS
|
|
|
|
|
logs_dir = Path(".") / "data" / "logs"
|
|
|
|
|
os.makedirs(logs_dir, exist_ok=True)
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
format="{asctime}:{name}:{levelname}:{message}",
|
2023-07-22 10:42:25 +00:00
|
|
|
datefmt="%Y-%m-%d_%H-%M-%S",
|
2022-06-01 16:37:19 +00:00
|
|
|
style="{",
|
2023-06-23 17:25:41 +00:00
|
|
|
level="DEBUG" if "--debug" in sys.argv else "INFO",
|
2022-06-01 16:37:19 +00:00
|
|
|
handlers=[
|
|
|
|
|
logging.StreamHandler(stream=sys.stderr),
|
|
|
|
|
logging.FileHandler(
|
2023-07-22 10:42:25 +00:00
|
|
|
logs_dir / f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log",
|
2022-06-01 16:37:19 +00:00
|
|
|
mode="a",
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
delay=False,
|
2022-07-06 11:23:46 +00:00
|
|
|
**({"errors": "surrogateescape"} if sys.version_info.major >= 3 and sys.version_info.minor >= 10 else {}),
|
2022-06-01 16:37:19 +00:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
force=True,
|
2022-07-06 11:23:46 +00:00
|
|
|
**({"encoding": "utf-8"} if sys.version_info.major >= 3 and sys.version_info.minor >= 10 else {}),
|
|
|
|
|
**({"errors": "surrogateescape"} if sys.version_info.major >= 3 and sys.version_info.minor >= 10 else {}),
|
2022-06-01 16:37:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
try:
|
2023-10-11 13:15:42 +00:00
|
|
|
# IMPORT PROJECT ONLY AFTER SETTING UP SIGNAL, FAULTHANDLER AND LOGGING
|
2023-01-11 10:38:13 +00:00
|
|
|
from components import (ArchiveSynchronizer, Multicomp730424,
|
|
|
|
|
Os_Label_Printer, RemoteAPI,
|
2025-01-17 14:14:28 +00:00
|
|
|
TecnaMarpossProvasetT3, FurnessControlsLeakTester, TecnaScrewdriver, USB_586x, RFID_PN532,BrotherLabelPrinter,PipeCutterComponent)
|
2022-06-01 16:37:19 +00:00
|
|
|
from lib.db import Users
|
|
|
|
|
from lib.helpers import ConfigReader
|
2024-02-23 17:34:17 +00:00
|
|
|
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
|
2025-05-06 07:12:53 +00:00
|
|
|
from PyQt5.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit
|
|
|
|
|
import sip
|
2025-09-03 14:00:43 +00:00
|
|
|
from ui import About, Archive, Login, Main_Window, Test, Users_Management, Logs_Management, Recipes_Management , Recipe_Selection, \
|
2025-05-21 12:29:12 +00:00
|
|
|
Barcode_Recipe_Selection, LastCommit
|
2022-06-01 16:37:19 +00:00
|
|
|
|
2022-11-29 15:54:01 +00:00
|
|
|
if "--vision" in sys.argv:
|
2025-01-23 15:49:51 +00:00
|
|
|
from components import GalaxyCamera, NeoPixels, UVCCamera, HikrobotSmartCamera, Vision, VisionSaver
|
2023-01-11 10:38:13 +00:00
|
|
|
|
2022-11-29 15:54:01 +00:00
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
class Main(QObject):
|
|
|
|
|
do = pyqtSignal(dict)
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _do(config):
|
|
|
|
|
return config["f"](*config.get("a", []), **config.get("k", {}))
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
# print(f"MAIN {int(QThread.currentThreadId())}", flush=True)
|
|
|
|
|
super().__init__()
|
2024-02-23 17:34:17 +00:00
|
|
|
self.tag_loaded_recipe = None
|
2022-06-01 16:37:19 +00:00
|
|
|
self.do.connect(self._do)
|
|
|
|
|
try:
|
|
|
|
|
# READ CONFIG
|
2023-01-20 16:19:39 +00:00
|
|
|
|
|
|
|
|
system_id = args.system_id if "system_id" in args else None
|
2025-01-23 15:49:51 +00:00
|
|
|
auto_select = args.auto_select if "auto_select" in args else None
|
2023-01-20 16:19:39 +00:00
|
|
|
|
2025-01-23 15:49:51 +00:00
|
|
|
self.config = ConfigReader(system_id=system_id,auto_select=auto_select)
|
2023-07-22 10:42:25 +00:00
|
|
|
logging.info(f"STARTING SESSION ON MACHINE {self.config['machine']['description']}")
|
2023-01-11 10:38:13 +00:00
|
|
|
self.config["autotest_done"] = False
|
2022-06-01 16:37:19 +00:00
|
|
|
# INIT COMPONENT
|
|
|
|
|
self.components_specs = {
|
2022-08-24 11:22:16 +00:00
|
|
|
"archive_synchronizer": {"c": ArchiveSynchronizer},
|
2024-12-05 16:31:49 +00:00
|
|
|
"archive_synchronizer_extra": {"c": ArchiveSynchronizer},
|
2025-01-23 09:58:14 +00:00
|
|
|
"pipe_cutter": {"c": PipeCutterComponent, "k": {"paused": False}},
|
2022-06-08 07:11:38 +00:00
|
|
|
"label_printer": {"c": Os_Label_Printer, "t": False},
|
2023-02-17 10:46:30 +00:00
|
|
|
"extra_label_printer": {"c": Os_Label_Printer, "t": False},
|
2025-09-03 09:40:34 +00:00
|
|
|
"label_printer_2": {"c": Os_Label_Printer, "t": False},
|
2022-09-06 10:06:43 +00:00
|
|
|
"multicomp": {"c": Multicomp730424, "k": {"paused": True}},
|
2022-06-21 12:10:52 +00:00
|
|
|
"remote_api": {"c": RemoteAPI, "k": {"main": self}},
|
2022-10-04 11:51:36 +00:00
|
|
|
"screwdriver": {"c": TecnaScrewdriver, "k": {"paused": True}},
|
2022-09-14 12:47:09 +00:00
|
|
|
"tecna_t3": {"c": TecnaMarpossProvasetT3, "k": {"paused": True}},
|
2023-10-11 14:40:46 +00:00
|
|
|
"furness_controls": {"c": FurnessControlsLeakTester, "k": {"paused": True}},
|
2024-01-16 18:12:06 +00:00
|
|
|
"digital_io": {"c": USB_586x, "k": {"paused": True}},
|
|
|
|
|
"digital_io_flush_blow": {"c": USB_586x, "k": {"paused": True}},
|
|
|
|
|
"fixture_id": {"c": RFID_PN532, "k": {"paused": False, "lazy": False}},
|
2022-06-01 16:37:19 +00:00
|
|
|
}
|
2023-01-06 14:42:52 +00:00
|
|
|
# VISION COMPONENT IS OPTIONAL AND DISABLED BY DEFAULT
|
2022-11-29 15:54:01 +00:00
|
|
|
if "--vision" in sys.argv:
|
2023-01-11 10:38:13 +00:00
|
|
|
# merge dicts
|
|
|
|
|
self.components_specs = {**self.components_specs,
|
|
|
|
|
**{
|
|
|
|
|
"galaxy_camera": {"c": GalaxyCamera, "k": {"paused": True}},
|
|
|
|
|
"neo_pixels": {"c": NeoPixels, "t": False},
|
|
|
|
|
"uvc_camera": {"c": UVCCamera, "k": {"paused": True}},
|
2025-01-23 15:49:51 +00:00
|
|
|
"hikrobot_sc": {"c": HikrobotSmartCamera, "k": {"paused": True}},
|
2023-01-11 10:38:13 +00:00
|
|
|
"vision_saver": {"c": VisionSaver, "t": False},
|
|
|
|
|
"vision": {"c": Vision, "k": {"paused": True}}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-29 15:54:01 +00:00
|
|
|
|
2022-08-23 14:00:04 +00:00
|
|
|
for component_name in list(self.components_specs):
|
|
|
|
|
if self.config.get("hardware_config", {}).get(component_name, None) != "present":
|
2022-08-24 11:22:16 +00:00
|
|
|
self.components_specs.pop(component_name, None)
|
|
|
|
|
elif component_name not in self.components_specs:
|
|
|
|
|
raise AssertionError(f"{component_name!r} is not a valid component name")
|
2022-06-01 16:37:19 +00:00
|
|
|
self.components = {}
|
|
|
|
|
self.threads = {}
|
|
|
|
|
for component_name, spec in self.components_specs.items():
|
2023-01-11 10:38:13 +00:00
|
|
|
self.components[component_name] = spec["c"](*spec.get("a", []), config=self.config,
|
|
|
|
|
name=component_name, **spec.get("k", {}))
|
2022-06-08 07:11:38 +00:00
|
|
|
if spec.get("t", True):
|
2022-06-01 16:37:19 +00:00
|
|
|
self.threads[component_name] = QThread()
|
|
|
|
|
self.threads[component_name].setTerminationEnabled(True)
|
|
|
|
|
self.components[component_name].moveToThread(self.threads[component_name])
|
2024-02-23 17:34:17 +00:00
|
|
|
|
|
|
|
|
if "fixture_id" in self.components.keys():
|
|
|
|
|
self.components["fixture_id"].new_id_signal.connect(self.load_recipe_from_rfid)
|
|
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
for component_name, thread in self.threads.items():
|
|
|
|
|
component = self.components[component_name]
|
|
|
|
|
thread.started.connect(component.start)
|
|
|
|
|
thread.start()
|
2023-03-13 17:38:30 +00:00
|
|
|
# DEBUGGER WORKAROUND
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
QThread.msleep(1000)
|
|
|
|
|
QApplication.processEvents()
|
2022-10-05 13:27:05 +00:00
|
|
|
if component_name == "vision":
|
|
|
|
|
component.wait_completion(timeout=60)
|
|
|
|
|
else:
|
|
|
|
|
component.wait_completion()
|
2022-06-01 16:37:19 +00:00
|
|
|
except Exception as e:
|
|
|
|
|
logging.exception(traceback.format_exc())
|
2023-01-11 10:38:13 +00:00
|
|
|
QMessageBox.critical(None, "Errore", f"Errore di avvio del programma di collaudo:\n\n{e}")
|
2022-06-01 16:37:19 +00:00
|
|
|
quit()
|
2023-06-23 08:20:26 +00:00
|
|
|
|
2022-06-22 15:18:29 +00:00
|
|
|
# connect camera frames to vision
|
2022-10-11 13:30:53 +00:00
|
|
|
if "vision" in self.components and "uvc_camera" in self.components:
|
|
|
|
|
self.components["vision"].set_sources({"uvc_camera": self.components["uvc_camera"].out})
|
|
|
|
|
elif "vision" in self.components and "galaxy_camera" in self.components:
|
2022-06-29 11:04:31 +00:00
|
|
|
self.components["vision"].set_sources({"galaxy_camera": self.components["galaxy_camera"].out})
|
2023-06-23 08:20:26 +00:00
|
|
|
|
2025-01-23 15:49:51 +00:00
|
|
|
if "vision" in self.components and "hikrobot_sc" in self.components:
|
|
|
|
|
self.components["vision"].set_sources({"hikrobot_sc": self.components["hikrobot_sc"].out})
|
2025-08-22 08:44:32 +00:00
|
|
|
|
|
|
|
|
|
2025-01-23 15:49:51 +00:00
|
|
|
|
2022-10-04 11:51:36 +00:00
|
|
|
# connect tecna to screwdriver
|
|
|
|
|
if "screwdriver" in self.components and "tecna_t3" in self.components:
|
|
|
|
|
self.components["tecna_t3"].set_requestors({"screwdriver": self.components["screwdriver"].request})
|
|
|
|
|
self.components["screwdriver"].set_sources({"tecna_t3": self.components["tecna_t3"].out})
|
2023-06-23 08:20:26 +00:00
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
# GUI INIT
|
2022-06-29 11:04:31 +00:00
|
|
|
if "--no-gui" not in sys.argv:
|
|
|
|
|
self.main_window = Main_Window()
|
|
|
|
|
# CONNECT MAIN WINDOW ACTIONS
|
2024-01-16 18:12:06 +00:00
|
|
|
self.main_window.logout_a.triggered.connect(lambda checked, selfie=weakref.ref(self): selfie().logout())
|
2023-01-11 10:38:13 +00:00
|
|
|
self.main_window.archive_a.triggered.connect(
|
2024-01-16 18:12:06 +00:00
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(
|
|
|
|
|
Archive(hide_cloud_image="vision_saver" not in selfie().components)))
|
2022-06-29 11:04:31 +00:00
|
|
|
if "--archive" in sys.argv:
|
|
|
|
|
self.main_window.archive_a.trigger()
|
|
|
|
|
if "--about" in sys.argv:
|
|
|
|
|
self.main_window.about_a.trigger()
|
2023-04-23 16:53:05 +00:00
|
|
|
|
2025-05-06 07:12:53 +00:00
|
|
|
# Keep the admin menu always visible
|
|
|
|
|
self.main_window.admin_m.menuAction().setVisible(True)
|
2023-04-23 16:53:05 +00:00
|
|
|
|
2024-01-16 18:12:06 +00:00
|
|
|
self.main_window.about_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(About()))
|
2025-05-21 12:29:12 +00:00
|
|
|
self.main_window.last_commit_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(LastCommit()))
|
2024-12-02 09:54:59 +00:00
|
|
|
self.main_window.download_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Logs_Management()))
|
2022-06-29 11:04:31 +00:00
|
|
|
self.main_window.quit_a.triggered.connect(quit_app)
|
2024-01-16 18:12:06 +00:00
|
|
|
self.main_window.users_management_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Users_Management()))
|
2023-04-23 16:53:05 +00:00
|
|
|
self.main_window.table_selection_a.triggered.connect(self.set_recipe_mode_table)
|
|
|
|
|
self.main_window.barcode_selection_a.triggered.connect(self.set_recipe_mode_barcode)
|
2023-09-23 10:58:55 +00:00
|
|
|
self.main_window.ristampa_etichetta_a.triggered.connect(self.reprint_label)
|
2025-02-17 09:01:56 +00:00
|
|
|
self.main_window.tag_a.triggered.connect(self.tag_write)
|
2025-09-03 14:00:43 +00:00
|
|
|
self.main_window.recipes_export_a.triggered.connect(self.trigger_recipe_backup)
|
2025-02-17 10:29:31 +00:00
|
|
|
if "pipe_cutter" in self.components.keys():
|
|
|
|
|
self.main_window.cut_a.setVisible(True)
|
|
|
|
|
self.main_window.cut_a.triggered.connect(self.cut_tube)
|
|
|
|
|
else:
|
|
|
|
|
self.main_window.cut_a.setVisible(False)
|
2024-09-18 12:47:54 +00:00
|
|
|
self.main_window.diagnostics_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Diagnostics(selfie())))
|
2025-05-06 07:12:53 +00:00
|
|
|
self.main_window.admin_enable_a.triggered.connect(
|
|
|
|
|
lambda checked, selfie=weakref.ref(self): selfie().enable_admin_privileges())
|
2022-06-29 11:04:31 +00:00
|
|
|
if "--users-management" in sys.argv:
|
|
|
|
|
self.main_window.users_management_a.trigger()
|
2023-04-23 16:53:05 +00:00
|
|
|
|
|
|
|
|
# CONFIG-SPECIFIC MENU ENTRY ACTIVATION
|
2023-01-11 10:38:13 +00:00
|
|
|
if "tecna_t3" in self.components and (
|
|
|
|
|
"--enable-saving-tecna-recipes" in sys.argv or self.config.get("tecna_t3", {}).get("saver",
|
|
|
|
|
None) == "present"):
|
2022-10-18 14:41:53 +00:00
|
|
|
self.main_window.save_tecna_recipes_a.triggered.connect(self.components["tecna_t3"].store_recipes)
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(True)
|
|
|
|
|
if "--save-tecna-recipes" in sys.argv:
|
|
|
|
|
self.main_window.save_tecna_recipes_a.trigger()
|
|
|
|
|
else:
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(False)
|
2024-01-16 18:12:06 +00:00
|
|
|
self.main_window.barcode_selection_a.setVisible(
|
|
|
|
|
self.config["hardware_config"]["barcode_recipe_selection"] == "present")
|
2023-03-25 15:00:39 +00:00
|
|
|
|
2022-06-29 11:04:31 +00:00
|
|
|
# OPEN LOGIN TAB
|
|
|
|
|
self.open_login()
|
|
|
|
|
# SHOW MAIN WINDOW
|
|
|
|
|
if "--panel" in sys.argv:
|
|
|
|
|
self.main_window.show()
|
|
|
|
|
elif "--maximized" in sys.argv:
|
|
|
|
|
self.main_window.showMaximized()
|
|
|
|
|
elif "--full-screen" in sys.argv:
|
|
|
|
|
self.main_window.showFullScreen()
|
|
|
|
|
else:
|
|
|
|
|
self.main_window.showFullScreen()
|
2022-06-01 16:37:19 +00:00
|
|
|
|
|
|
|
|
def open_login(self):
|
|
|
|
|
tab = Login()
|
2022-07-26 14:09:01 +00:00
|
|
|
tab.successful_login.connect(self.logged_in)
|
2022-06-01 16:37:19 +00:00
|
|
|
self.main_window.open_tab(tab)
|
|
|
|
|
|
2022-07-26 14:09:01 +00:00
|
|
|
def logged_in(self):
|
2022-06-01 16:37:19 +00:00
|
|
|
session = Users.get_session()
|
2025-05-06 07:12:53 +00:00
|
|
|
# Always make the admin menu visible
|
|
|
|
|
self.main_window.admin_m.menuAction().setVisible(True)
|
|
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
if session is not None:
|
2025-05-06 07:12:53 +00:00
|
|
|
# Check if user has admin privileges (either permanent or temporary)
|
|
|
|
|
# Use session.is_admin instead of checking roles directly to be consistent with user.py
|
2022-06-01 16:37:19 +00:00
|
|
|
if session.is_admin:
|
2025-03-19 10:25:56 +00:00
|
|
|
self.main_window.tag_a.setVisible(True)
|
2025-05-06 07:12:53 +00:00
|
|
|
self.main_window.admin_enable_a.setVisible(False) # Hide admin enable action for admins
|
|
|
|
|
# Show admin features for users with admin privileges (permanent or temporary)
|
|
|
|
|
self.main_window.users_management_a.setVisible(True)
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(True)
|
|
|
|
|
self.main_window.diagnostics_a.setVisible(True)
|
2022-06-01 16:37:19 +00:00
|
|
|
else:
|
2025-05-06 07:12:53 +00:00
|
|
|
# For non-admin users, only show the admin enable button
|
|
|
|
|
self.main_window.admin_enable_a.setVisible(True) # Show admin enable action for non-admins
|
|
|
|
|
self.main_window.users_management_a.setVisible(False) # Hide user management for non-admins
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(False) # Hide tecna recipes for non-admins
|
|
|
|
|
self.main_window.diagnostics_a.setVisible(False) # Hide diagnostics for non-admins
|
2025-03-19 10:25:56 +00:00
|
|
|
self.main_window.tag_a.setVisible(False)
|
|
|
|
|
# open test
|
2025-05-06 07:12:53 +00:00
|
|
|
|
|
|
|
|
# Update background color based on admin privileges
|
|
|
|
|
self.update_window_backgrounds()
|
|
|
|
|
|
2024-01-16 18:12:06 +00:00
|
|
|
self.main_window.open_tab(Test(self.config, self.components, self))
|
2023-07-06 10:59:06 +00:00
|
|
|
self.main_window.centralWidget().request_autotest("login")
|
2022-09-06 13:15:01 +00:00
|
|
|
|
|
|
|
|
def logout(self):
|
2024-01-16 18:12:06 +00:00
|
|
|
# Users.logout()
|
2025-05-06 07:12:53 +00:00
|
|
|
# Keep the admin menu visible
|
|
|
|
|
self.main_window.admin_m.menuAction().setVisible(True)
|
|
|
|
|
# Get the current session (if any)
|
|
|
|
|
session = Users.get_session()
|
|
|
|
|
# Note: We no longer reset temp_admin here to preserve admin status across login/logout
|
|
|
|
|
|
|
|
|
|
# Reset background color to default
|
|
|
|
|
self.main_window.setStyleSheet("")
|
|
|
|
|
for window_name, window in self.main_window.windows.items():
|
|
|
|
|
if window is not None and not sip.isdeleted(window):
|
|
|
|
|
window.setStyleSheet("")
|
|
|
|
|
|
2024-01-16 18:12:06 +00:00
|
|
|
if type(self.main_window.centralWidget().centralWidget.widget) in (
|
|
|
|
|
Recipe_Selection, Barcode_Recipe_Selection):
|
2023-05-22 16:39:28 +00:00
|
|
|
# LOGOUT IMMEDIATELY IF NOT TESTING
|
2023-06-29 17:09:16 +00:00
|
|
|
Users.logout()
|
2024-04-23 09:15:23 +00:00
|
|
|
ArchiveSynchronizer.machine_status = "logged-out"
|
2023-05-22 16:39:28 +00:00
|
|
|
self.open_login()
|
|
|
|
|
else:
|
2025-06-03 13:32:29 +00:00
|
|
|
# ALWAYS REQUEST AUTOTEST BEFORE LOGOUT
|
|
|
|
|
self.main_window.centralWidget().request_autotest("logout")
|
2022-06-29 11:04:31 +00:00
|
|
|
|
2023-03-25 15:03:03 +00:00
|
|
|
def set_recipe_mode_table(self):
|
2023-03-25 15:20:09 +00:00
|
|
|
self.main_window.centralWidget().set_recipe_mode_table()
|
2023-03-25 15:03:03 +00:00
|
|
|
|
|
|
|
|
def set_recipe_mode_barcode(self):
|
2023-03-25 15:20:09 +00:00
|
|
|
self.main_window.centralWidget().set_recipe_mode_barcode()
|
2024-01-16 18:12:06 +00:00
|
|
|
|
2023-09-23 10:58:55 +00:00
|
|
|
def reprint_label(self):
|
|
|
|
|
self.main_window.centralWidget().reprint_label()
|
2024-01-16 18:12:06 +00:00
|
|
|
|
2025-02-17 09:01:56 +00:00
|
|
|
def tag_write(self):
|
|
|
|
|
if isinstance(self.main_window.centralWidget().centralWidget.widget, Barcode_Recipe_Selection):
|
|
|
|
|
barcode_data = self.main_window.centralWidget().centralWidget.widget.barcode_input_l.toPlainText().strip()
|
|
|
|
|
self.main_window.centralWidget().centralWidget.widget.tag_write(barcode_data)
|
2025-02-17 10:29:31 +00:00
|
|
|
def cut_tube(self):
|
|
|
|
|
self.main_window.centralWidget().cut_tube()
|
2025-02-17 09:01:56 +00:00
|
|
|
|
2025-05-06 07:12:53 +00:00
|
|
|
def enable_admin_privileges(self):
|
|
|
|
|
session = Users.get_session()
|
|
|
|
|
if session is None:
|
|
|
|
|
QMessageBox.warning(self.main_window, "Errore", "Nessun utente loggato")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# If user already has temporary admin privileges, toggle them off
|
|
|
|
|
if session.temp_admin:
|
|
|
|
|
# Use the Users class method to disable temp admin
|
|
|
|
|
Users.disable_temp_admin()
|
|
|
|
|
# Keep the admin menu visible
|
|
|
|
|
self.main_window.admin_m.menuAction().setVisible(True)
|
|
|
|
|
self.main_window.users_management_a.setVisible(False)
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(False)
|
|
|
|
|
self.main_window.diagnostics_a.setVisible(False)
|
|
|
|
|
self.main_window.tag_a.setVisible(False)
|
|
|
|
|
self.main_window.admin_enable_a.setVisible(True) # Show admin enable action after removing temp admin privileges
|
|
|
|
|
|
|
|
|
|
# Call disable_temp_admin on the Test widget if it exists
|
|
|
|
|
current_widget = self.main_window.centralWidget()
|
|
|
|
|
if current_widget is not None and hasattr(current_widget, 'disable_temp_admin'):
|
|
|
|
|
current_widget.disable_temp_admin()
|
|
|
|
|
QMessageBox.information(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"Successo",
|
|
|
|
|
"Privilegi di amministratore disabilitati"
|
|
|
|
|
)
|
|
|
|
|
# Update background color for all windows
|
|
|
|
|
self.update_window_backgrounds()
|
|
|
|
|
|
|
|
|
|
# Refresh the current UI component to reflect removed admin privileges
|
|
|
|
|
current_widget = self.main_window.centralWidget()
|
|
|
|
|
if current_widget is not None:
|
|
|
|
|
# If the current widget has a refresh method, call it
|
|
|
|
|
if hasattr(current_widget, 'refresh'):
|
|
|
|
|
current_widget.refresh()
|
|
|
|
|
# If the current widget has a crud attribute, refresh it
|
|
|
|
|
if hasattr(current_widget, 'crud'):
|
|
|
|
|
if callable(current_widget.crud):
|
|
|
|
|
crud = current_widget.crud()
|
|
|
|
|
else:
|
|
|
|
|
crud = current_widget.crud
|
|
|
|
|
if hasattr(crud, 'refresh'):
|
|
|
|
|
crud.refresh()
|
|
|
|
|
|
|
|
|
|
# Refresh all other open windows to reflect removed admin privileges
|
|
|
|
|
for window_name, window in self.main_window.windows.items():
|
|
|
|
|
if window is not None and not sip.isdeleted(window):
|
|
|
|
|
central_widget = window.centralWidget()
|
|
|
|
|
if central_widget is not None:
|
|
|
|
|
# If the central widget has a refresh method, call it
|
|
|
|
|
if hasattr(central_widget, 'refresh'):
|
|
|
|
|
central_widget.refresh()
|
|
|
|
|
# If the central widget has a crud attribute, refresh it
|
|
|
|
|
if hasattr(central_widget, 'crud'):
|
|
|
|
|
if callable(central_widget.crud):
|
|
|
|
|
crud = central_widget.crud()
|
|
|
|
|
else:
|
|
|
|
|
crud = central_widget.crud
|
|
|
|
|
if hasattr(crud, 'refresh'):
|
|
|
|
|
crud.refresh()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# If user is a permanent admin (not temporary), show a message
|
|
|
|
|
if "admin" in Users.parse_roles(session.user.roles):
|
|
|
|
|
QMessageBox.information(self.main_window, "Informazione", "Sei già un amministratore permanente")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
password, ok = QInputDialog.getText(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"Admin abilitazione",
|
|
|
|
|
"Inserisci la password di amministratore:",
|
|
|
|
|
QLineEdit.Password
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not ok or not password:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Find an admin user to verify the password against
|
|
|
|
|
admin_users = [user for user in Users.get_users() if "admin" in Users.parse_roles(user.roles)]
|
|
|
|
|
|
|
|
|
|
if not admin_users:
|
|
|
|
|
QMessageBox.warning(self.main_window, "Errore", "Nessun utente amministratore trovato nel sistema")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Use the Users class method to enable temp admin
|
|
|
|
|
if Users.enable_temp_admin(password):
|
|
|
|
|
self.main_window.admin_m.menuAction().setVisible(True)
|
|
|
|
|
self.main_window.tag_a.setVisible(True)
|
|
|
|
|
# Show admin features for users with temporary admin privileges
|
|
|
|
|
self.main_window.users_management_a.setVisible(True)
|
|
|
|
|
self.main_window.save_tecna_recipes_a.setVisible(True)
|
|
|
|
|
self.main_window.diagnostics_a.setVisible(True)
|
|
|
|
|
|
|
|
|
|
# Call enable_temp_admin on the Test widget if it exists
|
|
|
|
|
current_widget = self.main_window.centralWidget()
|
|
|
|
|
if current_widget is not None and hasattr(current_widget, 'enable_temp_admin'):
|
|
|
|
|
current_widget.enable_temp_admin()
|
|
|
|
|
QMessageBox.information(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"Successo",
|
|
|
|
|
"Privilegi di amministratore abilitati temporaneamente"
|
|
|
|
|
)
|
|
|
|
|
# Update background color for all windows
|
|
|
|
|
self.update_window_backgrounds()
|
|
|
|
|
|
|
|
|
|
# Refresh the current UI component to reflect new admin privileges
|
|
|
|
|
current_widget = self.main_window.centralWidget()
|
|
|
|
|
if current_widget is not None:
|
|
|
|
|
# If the current widget has a refresh method, call it
|
|
|
|
|
if hasattr(current_widget, 'refresh'):
|
|
|
|
|
current_widget.refresh()
|
|
|
|
|
# If the current widget has a crud attribute, refresh it
|
|
|
|
|
if hasattr(current_widget, 'crud'):
|
|
|
|
|
if callable(current_widget.crud):
|
|
|
|
|
crud = current_widget.crud()
|
|
|
|
|
else:
|
|
|
|
|
crud = current_widget.crud
|
|
|
|
|
if hasattr(crud, 'refresh'):
|
|
|
|
|
crud.refresh()
|
|
|
|
|
|
|
|
|
|
# Refresh all other open windows to reflect new admin privileges
|
|
|
|
|
for window_name, window in self.main_window.windows.items():
|
|
|
|
|
if window is not None and not sip.isdeleted(window):
|
|
|
|
|
central_widget = window.centralWidget()
|
|
|
|
|
if central_widget is not None:
|
|
|
|
|
# If the central widget has a refresh method, call it
|
|
|
|
|
if hasattr(central_widget, 'refresh'):
|
|
|
|
|
central_widget.refresh()
|
|
|
|
|
# If the central widget has a crud attribute, refresh it
|
|
|
|
|
if hasattr(central_widget, 'crud'):
|
|
|
|
|
if callable(central_widget.crud):
|
|
|
|
|
crud = central_widget.crud()
|
|
|
|
|
else:
|
|
|
|
|
crud = central_widget.crud
|
|
|
|
|
if hasattr(crud, 'refresh'):
|
|
|
|
|
crud.refresh()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
QMessageBox.warning(self.main_window, "Errore", "Password non valida")
|
|
|
|
|
|
|
|
|
|
def update_window_backgrounds(self):
|
|
|
|
|
"""Update the background color of all windows based on admin privileges"""
|
|
|
|
|
session = Users.get_session()
|
|
|
|
|
if session is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Check if user has admin privileges (permanent or temporary)
|
|
|
|
|
# Use session.is_admin instead of checking roles directly to be consistent with user.py
|
|
|
|
|
has_admin = session.is_admin
|
|
|
|
|
|
|
|
|
|
# Set background color for main window
|
|
|
|
|
if has_admin:
|
|
|
|
|
self.main_window.setStyleSheet("background-color: #ffcccc;") # Light red background
|
|
|
|
|
else:
|
|
|
|
|
self.main_window.setStyleSheet("") # Reset to default
|
|
|
|
|
|
|
|
|
|
# Set background color for all other windows
|
|
|
|
|
for window_name, window in self.main_window.windows.items():
|
|
|
|
|
if window is not None and not sip.isdeleted(window):
|
|
|
|
|
if has_admin:
|
|
|
|
|
window.setStyleSheet("background-color: #ffcccc;") # Light red background
|
|
|
|
|
else:
|
|
|
|
|
window.setStyleSheet("") # Reset to default
|
|
|
|
|
|
2024-02-23 17:34:17 +00:00
|
|
|
@pyqtSlot(str)
|
|
|
|
|
def load_recipe_from_rfid(self, data):
|
|
|
|
|
self.tag_loaded_recipe = data
|
2024-01-16 18:12:06 +00:00
|
|
|
|
2025-09-03 14:00:43 +00:00
|
|
|
def trigger_recipe_backup(self):
|
|
|
|
|
"""
|
|
|
|
|
This method acts as a bridge to call the imported backup function.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# Use the imported function and pass the required objects
|
|
|
|
|
backup_current_recipes(config=self.config, logger=logging)
|
|
|
|
|
QMessageBox.information(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"Esportazione Riuscita",
|
|
|
|
|
"Backup delle ricette creato con successo."
|
|
|
|
|
)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.exception("Error during recipe backup")
|
|
|
|
|
QMessageBox.critical(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"Errore di Esportazione",
|
|
|
|
|
f"Si è verificato un errore durante il salvataggio: {e}"
|
|
|
|
|
)
|
|
|
|
|
|
2022-06-01 16:37:19 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
app = QApplication(sys.argv)
|
2023-08-24 10:10:01 +00:00
|
|
|
|
|
|
|
|
with SingleProcess() as single_process_lock:
|
2024-09-18 12:18:07 +00:00
|
|
|
if not single_process_lock and "--no-lock" not in sys.argv:
|
2023-08-24 10:10:01 +00:00
|
|
|
logging.error(f"Program already opened, exiting...")
|
|
|
|
|
QMessageBox.critical(None, "ERRORE", "IL PROGRAMMA E' GIA' IN ESECUZIONE")
|
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
|
|
main = Main()
|
|
|
|
|
if "--no-gui" not in sys.argv:
|
|
|
|
|
app.exec()
|
|
|
|
|
if "--interact" in sys.argv:
|
|
|
|
|
import code
|
|
|
|
|
import readline
|
|
|
|
|
|
|
|
|
|
variables = globals().copy()
|
|
|
|
|
variables.update(locals())
|
|
|
|
|
shell = code.InteractiveConsole(variables)
|
|
|
|
|
shell.interact()
|
2022-06-01 16:37:19 +00:00
|
|
|
except Exception:
|
|
|
|
|
logging.exception(traceback.format_exc())
|
|
|
|
|
# extype, value, tb = sys.exc_info()
|
|
|
|
|
# pdb.post_mortem(tb)
|