st-ten-1/src/main.py

302 lines
14 KiB
Python
Raw Normal View History

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
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
2024-09-18 12:47:54 +00:00
from ui.diagnostics import Diagnostics
2023-08-24 10:10:01 +00:00
from lib.helpers.single_process import SingleProcess
2023-06-23 08:20:26 +00:00
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")
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')
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
from components import (ArchiveSynchronizer, Multicomp730424,
Os_Label_Printer, RemoteAPI,
2024-03-12 12:48:00 +00:00
TecnaMarpossProvasetT3, FurnessControlsLeakTester, TecnaScrewdriver, USB_586x, RFID_PN532,BrotherLabelPrinter)
2022-06-01 16:37:19 +00:00
from lib.db import Users
from lib.helpers import ConfigReader
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
2022-06-01 16:37:19 +00:00
from PyQt5.QtWidgets import QApplication, QMessageBox
2023-06-29 17:09:16 +00:00
from ui import About, Archive, Login, Main_Window, Test, Users_Management, Recipe_Selection, \
Barcode_Recipe_Selection
2022-06-01 16:37:19 +00:00
if "--vision" in sys.argv:
from components import GalaxyCamera, NeoPixels, UVCCamera, Vision, VisionSaver
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__()
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
self.config = ConfigReader(system_id=system_id)
2023-07-22 10:42:25 +00:00
logging.info(f"STARTING SESSION ON MACHINE {self.config['machine']['description']}")
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},
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},
2024-03-12 12:48:00 +00:00
"label_printer_2": {"c": BrotherLabelPrinter, "t": False},
"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
if "--vision" in sys.argv:
# 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}},
"vision_saver": {"c": VisionSaver, "t": False},
"vision": {"c": Vision, "k": {"paused": True}}
}
}
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():
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])
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())
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
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(self.bench)
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())
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
# admin menu should not be visible before an admin logs in
self.main_window.admin_m.menuAction().setVisible(False)
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()))
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)
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())))
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
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()
if session is not None:
if session.is_admin:
self.main_window.admin_m.menuAction().setVisible(True)
else:
self.main_window.admin_m.menuAction().setVisible(False)
2022-07-05 16:16:22 +00:00
# open test
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
else:
self.main_window.admin_m.menuAction().setVisible(False)
def logout(self):
2024-01-16 18:12:06 +00:00
# Users.logout()
2022-09-06 13:15:01 +00:00
self.main_window.admin_m.menuAction().setVisible(False)
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()
ArchiveSynchronizer.machine_status = "logged-out"
2023-05-22 16:39:28 +00:00
self.open_login()
else:
if not self.main_window.centralWidget().autotesting:
2023-06-29 17:09:16 +00:00
# LOGOUT AFTER AUTOTEST IF TESTING
2023-05-22 16:39:28 +00:00
self.main_window.centralWidget().request_autotest("logout")
else:
# LOGOUT IMMEDIATELY IF AUTOTESTING
2023-06-29 17:09:16 +00:00
Users.logout()
2023-05-22 16:39:28 +00:00
self.open_login()
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
@pyqtSlot(str)
def load_recipe_from_rfid(self, data):
self.tag_loaded_recipe = data
2024-01-16 18:12:06 +00:00
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)