st-ten-1/src/ui/test/test.py

980 lines
50 KiB
Python
Raw Normal View History

2023-02-26 18:09:46 +00:00
import copy
2022-06-01 16:37:19 +00:00
import logging
import os
import sys
2022-08-23 14:00:04 +00:00
import weakref
2023-07-06 10:59:06 +00:00
from datetime import datetime, timedelta
from PyQt5.QtCore import QTimer, pyqtSlot, pyqtSignal
2024-01-16 18:12:06 +00:00
from PyQt5.QtWidgets import QMessageBox
2024-09-18 12:18:07 +00:00
from lib.db import Archive, Recipes, Users
2022-07-26 14:18:44 +00:00
from lib.helpers import get_shift
2024-09-18 12:18:07 +00:00
from lib.helpers.step import Step
2022-07-26 14:18:44 +00:00
from playhouse.shortcuts import model_to_dict
2024-01-16 18:12:06 +00:00
from ui.barcode_recipe_selection import Barcode_Recipe_Selection
2022-06-01 16:37:19 +00:00
from ui.helpers import replace_widget
from ui.recipe_selection import Recipe_Selection
from ui.test_assembly import Test_Assembly
2022-07-25 13:36:42 +00:00
from ui.test_barcodes import Test_Barcodes
2022-08-24 10:59:16 +00:00
from ui.test_connector import Test_Connector
2022-10-18 09:57:08 +00:00
from ui.test_count import Test_Count
from ui.test_count_end import Test_Count_End
2022-09-20 15:42:59 +00:00
from ui.test_fail import Test_Fail
2024-01-16 18:12:06 +00:00
from ui.test_instructions import Test_Instructions
2025-01-23 14:35:10 +00:00
from src.ui.test_pipe_cutter import Test_Pipe_Cutter
2022-07-12 08:48:04 +00:00
from ui.test_leak import Test_Leak
2022-08-23 14:00:04 +00:00
from ui.test_resistance import Test_Resistance
2022-10-04 11:51:36 +00:00
from ui.test_screws import Test_Screws
2022-06-22 15:18:29 +00:00
from ui.test_vision import Test_Vision
2024-01-16 18:12:06 +00:00
from ui.test_warning_img import Test_Warning_Img
2022-06-01 16:37:19 +00:00
from ui.widget import Widget
from components import ArchiveSynchronizer
2022-06-01 16:37:19 +00:00
class Test(Widget):
2024-01-16 18:12:06 +00:00
def __init__(self, config, components=None, main_window=None):
2022-06-01 16:37:19 +00:00
super().__init__()
2024-01-16 18:12:06 +00:00
self.autotest_timer = None
self.main_window = main_window
2022-09-20 15:42:59 +00:00
self.config = config
2022-06-22 15:18:29 +00:00
self.components = components
2022-06-01 16:37:19 +00:00
# GET LOGGER
self.log = logging.getLogger("Test")
2022-10-12 14:23:34 +00:00
# SHOW MACHINE DESCRIPTION
self.machine_description_l.setText(self.config.get("machine", {}).get("description", "N/A"))
2022-06-01 16:37:19 +00:00
# SHOW USERNAME
session = Users.get_session()
self.user_l.setText(session.username)
if session.is_admin:
self.user_l.setStyleSheet("QLabel { color: red; }")
else:
self.user_l.setStyleSheet("")
2025-01-31 10:06:41 +00:00
self.flag_label.setVisible(False)
if len(sys.argv) > 1:
self.flag_label.setVisible(True)
self.update_label_with_args() # Initial update
else:
self.flag_label.setVisible(False)
self.active_errors = [] # List to hold current errors (type: tuples of (message, is_error))
self.current_error_index = 0 # Keeps track of the current error index during alternation
# Timer for alternating errors
self.error_timer = QTimer()
self.error_timer.setInterval(2000) # Fire every 2 seconds
self.error_timer.timeout.connect(self.display_current_error) # Connect the timer to the display logic
2022-06-01 16:37:19 +00:00
# SHOW AND UPDATE TIME CLOCK
self.refresh_time(init=True)
# INIT RECIPE
self.recipe = None
2025-02-11 10:39:03 +00:00
if "fixture_id" in self.components:
self.rfid = self.components["fixture_id"]
self.rfid.rfid_error_signal.connect(self.handle_rfid_error)
if "tecna_t3" in self.components:
self.tecna = self.components["tecna_t3"]
2025-02-25 08:10:18 +00:00
#self.tecna.tecna_error_signal.connect(self.handle_modbus_error)
2025-01-13 10:56:47 +00:00
self.error_label.setText("")
self.error_label.setStyleSheet("QLabel { color: red; }")
2025-02-11 10:39:03 +00:00
2024-06-05 09:20:28 +00:00
2024-01-16 18:12:06 +00:00
if self.config["hardware_config"]["barcode_recipe_selection"] == "present":
2023-03-25 14:53:25 +00:00
self.recipe_selection_mode = "barcode"
else:
self.recipe_selection_mode = "table"
2022-07-19 09:59:00 +00:00
self.step = None
2024-03-12 12:48:00 +00:00
self.tester_component = None
if self.config["hardware_config"]["tecna_t3"] == "present":
self.tester_component = "tecna_t3"
2025-02-12 14:37:27 +00:00
#self.components["tecna_t3"].tecna_error_signal.connect(self.handle_modbus_error)
2024-03-12 12:48:00 +00:00
elif self.config["hardware_config"]["furness_controls"] == "present":
self.tester_component = "furness_control"
self.unsupported_steps = set()
self.steps_dependencies = {
2022-10-18 09:57:08 +00:00
"count": set(),
2022-09-23 17:07:36 +00:00
"connector": {"multicomp", },
2024-01-16 18:12:06 +00:00
"instruction": {"digital_io"},
2022-10-18 09:57:08 +00:00
"screws": {"screwdriver", "tecna_t3", },
"resistance": {"multicomp", },
2023-10-12 17:37:39 +00:00
"leak_1": {self.tester_component, },
"leak_2": {self.tester_component, },
2025-01-17 14:14:28 +00:00
"pipe_cutter": {"pipe_cutter"},
2025-01-23 15:49:51 +00:00
"vision": {("uvc_camera", "galaxy_camera","hikrobot_sc"), "vision", "vision_saver", }, # "neo_pixels", },
2024-03-12 12:48:00 +00:00
"print": {"label_printer_2"} if self.config["hardware_config"]["label_printer"] != "present" else {"label_printer"},
}
self.unsupported_steps = set()
for step_name, dependencies in self.steps_dependencies.items():
for dependency in dependencies:
2022-10-11 13:30:53 +00:00
if isinstance(dependency, tuple):
2022-11-09 16:18:11 +00:00
# if all([d not in self.components or not self.components[d].ready for d in dependency]):
if all([d not in self.components for d in dependency]):
2022-10-11 13:30:53 +00:00
self.unsupported_steps.add(step_name)
else:
2022-11-09 16:18:11 +00:00
# if dependency not in self.components or not self.components[dependency].ready:
if dependency not in self.components:
2022-10-11 13:30:53 +00:00
self.unsupported_steps.add(step_name)
2022-10-18 09:57:08 +00:00
# INIT PIECES COUNTER
self.pieces = {"ok": 0, "ko": 0}
2022-06-01 16:37:19 +00:00
# INIT CYCLE STATES
2022-07-19 09:59:00 +00:00
self.cycle_available_steps = {
2022-08-02 16:15:30 +00:00
# "assembly_1": Test_Assembly(img_path=self.select_step_img("assembly_1"), text=u"INSERIRE SENSORE", widget=None),
"barcodes": Test_Assembly(img_path=self.select_step_img("scan"), text=u"LEGGERE IL BARCODE DEL PEZZO DA COLLAUDARE", widget=Test_Barcodes()),
2022-08-24 10:59:16 +00:00
"connector": Test_Assembly(img_path=self.select_step_img("scan"), text=u"COLLEGARE IL CONNETTORE INDICATO AL PEZZO E LEGGERE IL SUO BARCODE", widget=Test_Connector(run_once=True)),
2024-01-16 18:12:06 +00:00
"count": Test_Assembly(img_path=None, text=u"INSERIRE IL NUMERO DI PEZZI ATTESI PER IL LOTTO",
widget=Test_Count(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces, run_once=True)),
"warning_img": Test_Assembly(img_path=None, text=u"ATTENZIONE - PER QUESTO CODICE ESEGUIRE LE OPERAZIONI INDICATE IN FIGURA",
2024-06-05 09:20:28 +00:00
widget=Test_Warning_Img(components=self.components, recipe=self.recipe,bench_name=self.config["machine"]["image_for_warning"], step=self.step, run_once=True)),
2024-01-16 18:12:06 +00:00
"count_end": Test_Assembly(img_path=None, text=u"LOTTO TERMINATO, PREMERE CONTINUA PERCOMINCIARNE UNO NUOVO",
widget=Test_Count_End(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces)),
2022-08-02 16:15:30 +00:00
"done": Test_Assembly(img_path=self.select_step_img("success"), text=u"COLLAUDO COMPLETATO", widget=None),
2024-01-16 18:12:06 +00:00
"emergency": Test_Assembly(img_path=self.select_step_img("reset_emergency"),
text=u"EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \"RESET EMERGENZA\" DAL MEN\u00d9 \"STRUMENTI\"", widget=None),
2023-05-18 07:58:36 +00:00
"fail": Test_Assembly(img_path=self.select_step_img("fail"), text=u"CICLO INTERROTTO, PREMERE CONTINUA PER COMINCIARE UN NUOVO CICLO", widget=Test_Fail(parent=self)),
2023-07-14 15:09:29 +00:00
"blow": Test_Assembly(img_path=None, text=u"SOFFIAGGIO TUBO IN CORSO - ATTENDERE...", widget=Test_Warning_Img(components=self.components, recipe=self.recipe, step=self.step)),
"leak_1": Test_Assembly(img_path=None, text=None, widget=Test_Leak(config=self.config,components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces, parent=self))
2024-03-12 12:48:00 +00:00
if self.config["hardware_config"]["tecna_t3"] != "absent" or self.config["hardware_config"]["furness_controls"] !="absent" else None,
"leak_2": Test_Assembly(img_path=None, text=None, widget=Test_Leak(config=self.config,components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces, parent=self))
2024-03-12 12:48:00 +00:00
if self.config["hardware_config"]["tecna_t3"] != "absent" or self.config["hardware_config"]["furness_controls"] != "absent" else None,
2023-07-14 15:09:29 +00:00
"flush": Test_Assembly(img_path=None, text=u"SCARICO ARIA IN CORSO - ATTENDERE...", widget=Test_Warning_Img(components=self.components, recipe=self.recipe, step=self.step)),
2024-01-16 18:12:06 +00:00
"instruction": Test_Assembly(img_path=None, text=u"ESEGUIRE LE OPERAZIONI DI MONTAGGIO INDICATE IN FIGURA",
2024-02-01 13:53:00 +00:00
widget=Test_Instructions(config=self.config,components=self.components, recipe=self.recipe, bench_name=self.config.machine_id, step=self.step)),
2025-01-23 14:35:10 +00:00
"pipe_cutter": Test_Assembly(img_path=None, text=u"ATTENZIONE TAGLIO CORRUGATO IN CORSO",widget=Test_Pipe_Cutter(config=self.config, components=self.components,recipe=self.recipe, bench_name=self.config.machine_id,step=self.step)),
"instruction_extra": Test_Assembly(img_path=None, text=u"ESEGUIRE LE OPERAZIONI DI MONTAGGIO EXTRA INDICATE IN FIGURA",
widget=Test_Instructions(config=self.config, components=self.components,recipe=self.recipe, bench_name=self.config.machine_id,step=self.step)),
2024-01-16 18:12:06 +00:00
"piece_removal": Test_Assembly(img_path=None, text=u"RIMUOVERE IL PEZZO APRENDO TUTTE LE CHIUSURE",
2024-02-01 13:53:00 +00:00
widget=Test_Instructions(config=self.config,components=self.components, recipe=self.recipe, bench_name=self.config.machine_id, step=self.step)),
2022-08-02 16:15:30 +00:00
"print": Test_Assembly(img_path=self.select_step_img("print"), text=u"STAMPA ETICHETTA IN CORSO", widget=None),
2024-01-16 18:12:06 +00:00
"resistance": Test_Assembly(img_path=None, text=u"COLLEGARE CONNETTORE ELETTRICO PER EFFETTUARE PROVA RESISTENZA",
widget=Test_Resistance(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces)),
2022-10-18 09:57:08 +00:00
"screws": Test_Assembly(img_path=None, text=u"AVVITARE TUTE LE VITI COME INDICATO", widget=Test_Screws(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces)),
2022-09-21 14:15:04 +00:00
"select_recipe": Test_Assembly(img_path=None, text=u"SELEZIONARE IL CODICE DA COLLAUDARE", widget=Recipe_Selection(config=self.config, unsupported_steps=self.unsupported_steps)),
2024-01-16 18:12:06 +00:00
"barcode_recipe_selection": Test_Assembly(img_path=self.select_step_img("scan"), text=u"LEGGERE IL BARCODE SULLA DIMA DEL COMPONENTE DA COLLAUDARE",
widget=Barcode_Recipe_Selection(parent=self)),
2022-10-18 09:57:08 +00:00
"vision": Test_Assembly(img_path=None, text=u"VERIFICARE CONTROLLO CON TELECAMERA", widget=Test_Vision(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces)),
2022-08-02 16:15:30 +00:00
"wait": Test_Assembly(img_path=self.select_step_img("wait"), text=u"ATTENDERE - PAUSA INTER CICLO", widget=None),
None: Test_Assembly(img_path=self.select_step_img("warning"), text=u"ATTENZIONE - LA RICETTA SELEZIONATA NON CONTIENE FASI DI TEST", widget=None),
2022-06-01 16:37:19 +00:00
}
2022-07-19 09:59:00 +00:00
self.cycle_steps = None
2022-06-28 10:31:27 +00:00
self.cycle_index = -1
self.print_step = None
2023-10-13 15:52:41 +00:00
self.last_label = None
2023-08-25 16:51:03 +00:00
self.require_discard_piece = False
2022-06-01 16:37:19 +00:00
# SETUP AUTOTEST
self.autotest_request = False
2022-11-15 16:17:59 +00:00
self.autotesting = False
self.autotesting_reason = None
self.autotest_cycle_steps = None
if "--no-autotest" not in sys.argv:
if "--test-autotest" in sys.argv:
self.autotest_period = int(60 * 1000) # 1 min
else:
self.autotest_period = int(8.5 * 60 * 60 * 1000) # 8.5 HOURS
2023-05-12 15:37:25 +00:00
# self.autotest_period = 12 * 60 * 60 * 1000 # 12 HOURS
2024-01-16 18:12:06 +00:00
# if not self.config["autotest_done"]:
2023-07-06 10:59:06 +00:00
# self.request_autotest("init")
2022-11-15 16:17:59 +00:00
else:
self.autotest_period = None
2022-06-28 10:31:27 +00:00
# INIT TEST DATA
2022-07-26 10:24:53 +00:00
self.data = {"ok": True, "overridden": False}
self.archived = None
2022-06-01 16:37:19 +00:00
# CONNECT CYCLE CONTROLS
self.cancel_b.clicked.connect(self.fail_cycle)
self.change_recipe_b.clicked.connect(self.change_recipe)
2023-09-02 21:29:09 +00:00
self.reset_count_b.clicked.connect(self.reset_count)
2022-08-23 14:00:04 +00:00
for step_name, w in self.cycle_available_steps.items():
2022-06-01 16:37:19 +00:00
if hasattr(w, "ok"):
# custom ok handlers should call next again
2024-01-16 18:12:06 +00:00
if isinstance(w.widget, Recipe_Selection):
2022-06-01 16:37:19 +00:00
w.ok.connect(self.set_recipe)
else:
2024-01-16 18:12:06 +00:00
w.ok.connect(lambda data=None, step_namel=step_name, selfie=weakref.ref(self): selfie().set_step(step_namel, data))
2022-07-12 08:48:04 +00:00
if hasattr(w, "ko"):
w.ko.connect(self.fail_cycle)
2022-10-18 09:57:08 +00:00
# CUSTOM STEP CONNECTIONS
self.cycle_available_steps["count"].ok.connect(self.cycle_available_steps["count_end"].widget.set_amount)
2024-01-16 18:12:06 +00:00
# self.cycle_available_steps["warning_img"].ok.connect(self.cycle_available_steps["warning_img"].widget.set_done)
2023-06-26 17:21:12 +00:00
if "fixture_id" in self.components.keys():
self.components["fixture_id"].new_id_signal.connect(self.load_recipe_from_rfid)
2025-01-13 10:56:47 +00:00
self.components["fixture_id"].rfid_error_signal.connect(self.handle_rfid_error)
self.tag_loaded_recipe = self.main_window.tag_loaded_recipe
2023-06-26 17:21:12 +00:00
2022-06-01 16:37:19 +00:00
# TESTING
if "--test" in sys.argv:
self.testing = True
else:
self.testing = False
# /TESTING
# START CYCLE
2022-07-12 08:48:04 +00:00
self.next_timer = QTimer()
self.next_timer.setSingleShot(True)
self.next_timer.timeout.connect(self.next)
2022-06-01 16:37:19 +00:00
self.next()
2022-06-01 16:37:19 +00:00
def refresh_time(self, init=False):
if init:
self.time_timer = QTimer()
self.time_timer.setSingleShot(True)
self.time_timer.timeout.connect(self.refresh_time)
t = datetime.now()
self.time_l.setText("{d}/{mo}/{y}\n{h}:{m}".format(y=t.year, mo=t.month, d=t.day, h=t.hour, m=t.minute))
self.time_timer.start(60 - t.second)
def select_step_img(self, step, suffix=None):
img_path = "./src/ui/imgs"
names = []
if suffix is not None:
2022-09-20 15:42:59 +00:00
names.append(f"{step}_{suffix}_{self.config.machine_id}")
2022-06-01 16:37:19 +00:00
names.append(f"{step}_{suffix}")
2022-09-20 15:42:59 +00:00
names.append(f"{step}_{self.config.machine_id}")
2022-06-01 16:37:19 +00:00
names.append(f"{step}")
for name in names:
for ext in ["png", "jpg"]:
path = f"{img_path}/{name}.{ext}"
if os.path.isfile(path):
return path
raise FileNotFoundError(f"No image was found for step {step}")
def change_recipe(self):
self.next(action="change_recipe")
2023-03-25 14:53:25 +00:00
def set_recipe_mode_table(self):
self.recipe_selection_mode = "table"
self.change_recipe()
def set_recipe_mode_barcode(self):
self.recipe_selection_mode = "barcode"
self.change_recipe()
2024-01-16 18:12:06 +00:00
2025-02-17 10:29:31 +00:00
def cut_tube(self):
self.components["pipe_cutter"].to_calibrate()
self.components["pipe_cutter"].start_cutting()
2023-09-23 10:58:55 +00:00
def reprint_label(self):
2023-10-13 15:52:41 +00:00
self.print(self.last_label, self.print_step.spec.get("template", "EtichettaR5"))
2023-03-25 14:53:25 +00:00
2022-06-01 16:37:19 +00:00
def fail_cycle(self):
self.next(action="fail")
def setCentralWidget(self, widget):
replace_widget(self, "centralWidget", widget)
def request_autotest(self, reason): # you can cancel the request calling request_autotest(False)
2023-07-14 06:24:10 +00:00
if "--no-autotest" not in sys.argv:
self.log.info(f"cycle request autotest: reason: {reason!r} autotest_request: {self.autotest_request!r}")
2024-01-16 18:12:06 +00:00
if reason in ("init", "login"):
2023-07-14 06:24:10 +00:00
self.autotest_timer = QTimer()
self.autotest_timer.setSingleShot(False)
self.autotest_timer.timeout.connect(self.request_periodic_autotest)
if self.autotest_period is not None:
self.autotest_timer.start(self.autotest_period)
reason = "boot"
2024-09-18 16:03:13 +00:00
if self.config["autotest_leak"]["enabled"] == "true":
self.autotest_request = reason
else:
self.autotest_request = False
else:
self.log.info(f"Autotest request ignored (reason: {reason!r}) --no-autotest flag detected")
2023-06-29 17:09:16 +00:00
if reason == "logout":
self.next(action="abort")
2022-06-01 16:37:19 +00:00
def request_periodic_autotest(self):
self.request_autotest("periodic")
def next(self, action=None):
2022-10-18 09:57:08 +00:00
if self.step is not None:
2024-09-18 16:03:13 +00:00
self.log.info(f"cycle step: {self.step.step_type!r} action: {action!r} current index:{self.cycle_index}")
2022-10-18 09:57:08 +00:00
else:
2023-09-23 10:58:55 +00:00
self.log.info(f"cycle step: {self.step!r} action: {action!r} current index:{self.cycle_index}")
2022-07-25 09:16:14 +00:00
current_w = self.centralWidget
2022-07-25 09:16:14 +00:00
if hasattr(current_w, "stop"):
self.log.info(f"stopping widget {self.step.step_type}")
2022-07-25 09:16:14 +00:00
current_w.stop()
2022-06-01 16:37:19 +00:00
if action == "change_recipe":
self.log.info(f"cycle next: action: {action!r}")
self.set_recipe(recipe=None)
2024-03-12 12:48:00 +00:00
if self.config["hardware_config"]["tecna_t3"] == "present" or self.config["hardware_config"]["furness_controls"] == "present":
self.cycle_available_steps["leak_1"].widget.recipe_written = False
self.cycle_available_steps["leak_2"].widget.recipe_written = False
2024-09-18 12:18:07 +00:00
self.step = Step(step_type="select_recipe")
2022-06-28 10:31:27 +00:00
self.cycle_index = -1
2024-01-16 18:12:06 +00:00
self.recipe = None
self.cycle_steps = None
2022-08-02 16:15:30 +00:00
# COUNT RESET
2022-10-18 09:57:08 +00:00
self.pieces["ok"] = 0
self.pieces["ko"] = 0
2024-01-16 18:12:06 +00:00
elif action in ("fail", "abort"):
2022-06-01 16:37:19 +00:00
self.log.info(f"cycle next: action: {action!r}")
# FAIL AND RESTART TEST
2024-09-18 12:18:07 +00:00
self.step = Step(step_type="fail")
2022-06-28 10:31:27 +00:00
self.cycle_index = -1
2022-08-02 16:15:30 +00:00
# COUNT FAIL
2023-06-29 17:09:16 +00:00
if action == "fail":
self.done(ok=False)
2022-06-01 16:37:19 +00:00
elif action is not None:
raise NotImplementedError(f"cycle next: action {action!r} is not a valid action")
2022-07-19 09:59:00 +00:00
# if action did not set the next cycle step
# set next cycle step normally
if self.recipe is None or self.cycle_steps is None:
2022-07-12 08:48:04 +00:00
# if recipe not set: select_recipe
2023-03-25 14:53:25 +00:00
if self.recipe_selection_mode == "barcode":
2023-09-22 22:13:31 +00:00
self.log.info(f"returning to barcode recipe selection")
2024-10-25 07:29:22 +00:00
self.step = Step(step_type="barcode_recipe_selection")
2023-03-03 17:51:35 +00:00
else:
2023-09-22 22:13:31 +00:00
self.log.info(f"returning to recipe selection table")
2024-09-18 12:18:07 +00:00
self.step = Step(step_type="select_recipe")
2022-07-19 09:59:00 +00:00
elif action is None:
2022-11-15 16:17:59 +00:00
if self.autotest_request is not False and self.autotest_cycle_steps is not None and not self.autotesting and (self.cycle_index == -1 or self.cycle_index + 1 >= len(self.cycle_steps)):
# if autotest was requested
# and if cycle_steps is not started or has ended
self.cycle_index = -1
2022-11-15 16:17:59 +00:00
self.autotesting = True
self.autotesting_reason = self.autotest_request
2022-07-12 08:48:04 +00:00
self.autotest_request = False
2024-09-18 16:03:13 +00:00
self.log.info(f"Autotest requested (reason: {self.autotesting_reason})")
2022-07-12 08:48:04 +00:00
if self.autotest_period is not None: # reset periodic autotest timer
self.time_timer.start(self.autotest_period)
2023-09-06 08:35:15 +00:00
self.require_discard_piece = False
2022-11-15 16:17:59 +00:00
if self.autotesting:
if self.cycle_index + 1 < len(self.autotest_cycle_steps):
# goto next step in autotest_cycle_steps
self.cycle_index = (self.cycle_index + 1) % len(self.autotest_cycle_steps)
self.step = self.autotest_cycle_steps[self.cycle_index]
else:
# autotest ended
self.autotesting = False
2023-06-29 17:09:16 +00:00
if self.autotesting_reason == "logout":
Users.logout()
self.main_window.open_login()
2023-07-06 10:59:06 +00:00
else:
t = datetime.now()
self.last_at_l.setText("{d}/{mo}/{y} {h}:{m}".format(y=t.year, mo=t.month, d=t.day, h=t.hour, m=t.minute))
2024-01-16 18:12:06 +00:00
t += timedelta(seconds=int(self.autotest_period / 1000))
2023-07-06 10:59:06 +00:00
self.next_at_l.setText("{d}/{mo}/{y} {h}:{m}".format(y=t.year, mo=t.month, d=t.day, h=t.hour, m=t.minute))
2022-11-15 16:17:59 +00:00
self.autotesting_reason = None
self.cycle_index = -1
2023-01-03 16:57:14 +00:00
self.config["autotest_done"] = True
2022-11-15 16:17:59 +00:00
if not self.autotesting:
if len(self.cycle_steps):
# goto next step in cycle_steps
self.cycle_index = (self.cycle_index + 1) % len(self.cycle_steps)
self.step = self.cycle_steps[self.cycle_index]
2022-11-15 16:17:59 +00:00
else:
self.cycle_index = -1
2024-09-18 12:18:07 +00:00
self.step = Step(step_type=None)
2022-06-01 16:37:19 +00:00
# enable/disable cycle controls
self.change_recipe_b.setEnabled(self.recipe is not None)
2024-09-18 12:18:07 +00:00
self.cancel_b.setEnabled(self.step.step_type is not None and self.step.step_type not in {
2022-06-01 16:37:19 +00:00
"emergency",
"fail",
"select_recipe",
"wait",
})
2024-09-18 12:18:07 +00:00
self.log.info(f"next cycle step: {self.step.step_type!r}")
2022-10-25 13:38:18 +00:00
# INIT TEST DATA IF STARTING CYCLE LOOP OR IF RESET IS NEEDED
2022-11-15 16:17:59 +00:00
if self.cycle_index == 0:
2022-07-26 10:24:53 +00:00
self.data = {"ok": True, "overridden": False}
self.archived = None
2022-10-25 13:38:18 +00:00
if self.recipe is not None and "recipe" not in self.data:
self.data["recipe"] = model_to_dict(self.recipe)
2024-09-18 12:18:07 +00:00
w = self.cycle_available_steps[self.step.step_type]
2022-10-18 09:57:08 +00:00
show = None
2024-09-18 12:18:07 +00:00
if self.step.step_type == "leak_2":
2024-01-16 18:12:06 +00:00
self.setCentralWidget(w) # NEED TO PRESHOW UI
2022-08-24 10:59:16 +00:00
if hasattr(w, "start"):
2022-11-15 16:17:59 +00:00
show = w.start(recipe=self.recipe, step=self.step, pieces=self.pieces)
2022-10-18 09:57:08 +00:00
if show is not False and w is not current_w:
self.setCentralWidget(w)
2022-10-24 14:51:58 +00:00
elif show is False:
self.next_timer.start(0)
2024-09-18 12:18:07 +00:00
if self.step.step_type == "done":
2022-07-26 10:24:53 +00:00
self.archived = self.done()
2024-01-16 18:12:06 +00:00
self.last_label = copy.deepcopy(self.archived)
2023-09-05 14:33:18 +00:00
self.next_timer.start(500)
2024-09-18 12:18:07 +00:00
elif self.step.step_type == "print":
2022-09-14 14:53:55 +00:00
compiled_label = self.print(self.archived, self.step.spec.get("template", "EtichettaR5"))
self.archived.test_data.update({"print": compiled_label})
self.archived.test_data.update({"print_template": self.print_template})
2025-02-28 10:49:40 +00:00
self.archived.test_data.update({"barcode_stampato": self.printed_barcode})
2022-09-14 14:53:55 +00:00
self.archived.label = compiled_label
2024-12-02 16:00:32 +00:00
self.log.info(f"Label printed. Saving...")
#self.archived.save()
2024-12-06 08:12:10 +00:00
self.main_window.main_window.run_request.emit(self.archived.save, [], {})
2023-09-05 14:33:18 +00:00
self.next_timer.start(500)
2024-09-18 12:18:07 +00:00
elif self.step.step_type == "wait":
2023-09-05 14:33:18 +00:00
self.next_timer.start(500)
2023-09-02 21:29:09 +00:00
# UPDATE COUNT DISPLAY
2023-09-05 13:07:28 +00:00
self.update_count_display()
2023-09-02 21:29:09 +00:00
2023-09-05 13:07:28 +00:00
def reset_count(self):
2023-09-02 21:29:09 +00:00
# COUNT RESET
self.pieces["ok"] = 0
self.pieces["ko"] = 0
2023-09-05 13:07:28 +00:00
self.update_count_display()
2023-09-02 21:29:09 +00:00
2023-09-05 13:07:28 +00:00
def update_count_display(self):
2022-10-18 09:57:08 +00:00
self.pieces_count_l.setText(f"{self.pieces['ok']} OK / {self.pieces['ko']} NOK / {sum(self.pieces.values())} TOT")
2022-06-01 16:37:19 +00:00
def set_recipe(self, recipe=None):
self.recipe = recipe
2025-01-23 17:33:40 +00:00
self.config.active_recipe = recipe
inserted_instruction = False
2023-09-05 14:33:18 +00:00
self.require_discard_piece = False
2022-07-19 09:59:00 +00:00
if self.recipe is None:
self.cycle_steps = None
2022-11-15 16:17:59 +00:00
self.autotest_cycle_steps = None
2022-07-19 09:59:00 +00:00
else:
2022-07-26 10:24:53 +00:00
steps = self.recipe.get_steps()
2022-09-07 15:24:40 +00:00
skip = set()
2022-07-26 10:24:53 +00:00
print_found = False
2022-10-18 09:57:08 +00:00
count_found = False
2023-01-12 21:15:29 +00:00
# create step sequence list
2024-05-23 12:26:01 +00:00
barcode_names = ['serial', 'barcode_input_2', 'barcode_input_3', 'barcode_input_4', 'barcode_input_5']
2022-07-26 10:24:53 +00:00
for i, step in enumerate(steps):
2024-09-18 12:18:07 +00:00
if step.step_type == "barcodes":
2025-02-20 15:02:25 +00:00
n_pieces_value = step.spec.get("n_pieces")
2025-02-21 11:12:35 +00:00
# Fix: Handle empty string and None
n_pieces = 1 if n_pieces_value in (None, '') else n_pieces_value
try:
n_pieces_adapted = int(n_pieces)
except ValueError:
self.log.error(f"Invalid value for n_pieces: {n_pieces}") # Log the error
n_pieces_adapted = 1 # Default to 1 if conversion fails
2024-05-24 07:24:06 +00:00
if n_pieces_adapted == 1:
2024-05-23 07:09:05 +00:00
step.spec["barcode_name"] = 'serial'
else:
2024-05-24 07:24:06 +00:00
step.spec["barcode_name"] = barcode_names[(n_pieces_adapted - 1) % len(barcode_names)]
n_pieces_adapted -= 1
2024-06-11 04:58:03 +00:00
new_barcode_step = copy.deepcopy(step)
new_barcode_step.spec["n_pieces"] = str(n_pieces_adapted)
2024-05-23 12:26:01 +00:00
steps.insert(i + 1, new_barcode_step)
2022-09-07 15:24:40 +00:00
if i in skip:
continue
2024-09-18 12:18:07 +00:00
if step.step_type == "vision":
2023-05-02 17:18:58 +00:00
self.components["vision"].config_changed(vision_recipe=self.recipe.name)
2024-09-18 12:18:07 +00:00
if step.step_type == "count":
2022-10-18 09:57:08 +00:00
count_found = True
2023-01-18 18:06:16 +00:00
if "warning_img" in step.spec:
if step.spec["warning_img"]:
2024-09-18 12:18:07 +00:00
steps.insert(i, Step(step_type="warning_img", spec={"warning_img": step.spec["warning_img"]}))
2023-01-18 18:06:16 +00:00
skip.add(i + 1)
2023-01-26 13:13:03 +00:00
if "assembly" in step.spec:
if step.spec["assembly"]:
2024-09-18 12:18:07 +00:00
steps.insert(i, Step(step_type="instructions", spec={}))
2023-01-18 18:06:16 +00:00
skip.add(i + 1)
2023-08-25 16:51:03 +00:00
if "require_discard_piece" in step.spec:
if step.spec["require_discard_piece"]:
self.require_discard_piece = True
2024-09-18 12:18:07 +00:00
if step.step_type == "resistance": # ADD STEP TO ENSURE REMOVAL OF CONNECTOR
steps.insert(i + 1, Step(step_type="resistance", spec={
2022-09-07 15:24:40 +00:00
"scale": 500,
"expected": float("+inf"),
2022-11-15 16:53:15 +00:00
"tolerance_pos": 0,
"tolerance_neg": 0,
2022-09-07 15:24:40 +00:00
}))
skip.add(i + 1)
2024-09-18 12:18:07 +00:00
if step.step_type == "print":
self.print_template = step.spec.get("template", "EtichettaR5") # Store the template
2023-09-05 16:54:54 +00:00
if print_found:
continue
2025-02-20 15:03:30 +00:00
steps.insert(i, Step(step_type="done", spec={}))
2022-07-26 10:24:53 +00:00
print_found = True
2023-09-07 09:53:51 +00:00
self.print_step = step
2025-03-15 07:54:47 +00:00
if self.config["hardware_config"].get("enforce_piece_removal", "no") == "yes":
if recipe.spec.get("instruction",False) is not False:
steps.append(Step(step_type="piece_removal", spec={}))
2025-03-17 08:07:57 +00:00
skip.add(i + 1)
2025-03-15 07:54:47 +00:00
if count_found:
steps.append(Step(step_type="count_end", spec={}))
2025-03-17 08:07:57 +00:00
skip.add(i + 1)
2024-09-18 12:18:07 +00:00
if step.step_type in ("leak_1", "leak_2"):
2023-08-25 16:51:03 +00:00
self.leak_step = step
2023-07-27 16:19:05 +00:00
2024-10-25 08:07:22 +00:00
step_types = [step.step_type for step in steps]
if "leak_1" in step_types and "leak_2" in step_types:
leak1_index = step_types.index("leak_1")
leak2_index = step_types.index("leak_2")
if leak1_index + 1 == leak2_index: # Ensure 'leak_1' is immediately followed by 'leak_2'
2024-12-11 14:16:35 +00:00
if self.config["hardware_config"].get("second_leak_test", "yes") == "no":
steps.insert(leak2_index, Step(step_type="instruction_extra", spec={}))
inserted_instruction = True
# Insert 'instruction_extra' after the first 'instructions' if not inserted between leaks
if not inserted_instruction:
for i, step in enumerate(steps):
2024-10-25 08:07:22 +00:00
if step.step_type == "instructions":
2024-10-25 07:18:10 +00:00
steps.insert(i + 1, Step(step_type="instruction_extra", spec={}))
inserted_instruction = True
break
2022-07-26 10:24:53 +00:00
if not print_found:
2024-09-18 12:18:07 +00:00
steps.append(Step(step_type="done"))
2022-10-18 09:57:08 +00:00
if count_found:
2024-09-18 12:18:07 +00:00
steps.append(Step(step_type="count_end"))
steps.append(Step(step_type="wait"))
2022-07-26 10:24:53 +00:00
self.cycle_steps = steps
self.check_steps_dependencies(self.cycle_steps)
2024-01-16 18:12:06 +00:00
leak_autotest_steps = []
2023-05-31 13:02:06 +00:00
# CONFIGURE LEAK AUTOTEST PARAMETERS
2024-01-16 18:12:06 +00:00
if self.config["autotest_leak"]["enabled"] == "true":
l_at_1 = copy.deepcopy(self.config["autotest_leak"])
2023-02-26 18:09:46 +00:00
l_at_1.pop("enabled")
2024-01-16 18:12:06 +00:00
l_at_1 = {a: float(x) for a, x in l_at_1.items()}
l_at_1["autotest"] = "ko_check"
l_at_2 = copy.deepcopy(self.config["autotest_leak"])
2023-02-26 18:09:46 +00:00
l_at_2.pop("enabled")
2024-01-16 18:12:06 +00:00
l_at_2 = {a: float(x) for a, x in l_at_2.items()}
l_at_2["test_pressure_qneg"] = l_at_2["test_pressure_tt_qneg"]
l_at_2["test_pressure_qpos"] = l_at_2["test_pressure_tt_qpos"]
l_at_2["autotest"] = "ok_check"
2024-09-18 12:18:07 +00:00
leak_autotest_steps = [Step(step_type="leak_1", spec=l_at_1), Step(step_type="leak_1", spec=l_at_2)]
2023-02-26 18:09:46 +00:00
2022-11-15 16:17:59 +00:00
self.autotest_cycle_steps = [
*([
2024-09-18 12:18:07 +00:00
Step(step_type="resistance", spec={
2024-01-16 18:12:06 +00:00
"scale": 500,
"expected": 1,
"tolerance_pos": 5,
"tolerance_neg": 5,
"autotest": True,
}),
2024-09-18 12:18:07 +00:00
Step(step_type="resistance", spec={
2024-01-16 18:12:06 +00:00
"scale": 500,
"expected": float("+inf"),
"tolerance_pos": 0,
"tolerance_neg": 0,
"autotest": True,
}),
2024-09-18 12:18:07 +00:00
Step(step_type="resistance", spec={
2024-01-16 18:12:06 +00:00
"scale": 500,
"expected": 10,
"tolerance_pos": 1,
"tolerance_neg": 1,
"autotest": True,
}),
2024-09-18 12:18:07 +00:00
Step(step_type="resistance", spec={
2024-01-16 18:12:06 +00:00
"scale": 500,
"expected": float("+inf"),
"tolerance_pos": 0,
"tolerance_neg": 0,
"autotest": True,
}),
] if "resistance" not in self.unsupported_steps else []),
2023-02-26 18:09:46 +00:00
*(leak_autotest_steps),
2024-09-18 12:18:07 +00:00
Step(step_type="done"),
Step(step_type="wait"),
2022-11-15 16:17:59 +00:00
]
2022-08-24 10:59:16 +00:00
for w in self.cycle_available_steps.values():
if hasattr(w, "reset"):
w.reset()
2022-06-01 16:37:19 +00:00
# UPDATE RECIPE DISPLAY
if self.recipe is not None:
2024-09-18 16:03:13 +00:00
self.log.info(f"set recipe: {model_to_dict(self.recipe)!r} cycle steps: {[s.step_type for s in self.cycle_steps]}")
2022-06-01 16:37:19 +00:00
self.recipe_l.setText(self.recipe.name)
self.recipe_l.setStyleSheet("")
2023-09-19 16:07:04 +00:00
self.cycle_index = -1
2022-06-01 16:37:19 +00:00
self.next()
else:
self.log.info(f"set recipe: {self.recipe!r} cycle steps: {self.cycle_steps}")
2022-06-01 16:37:19 +00:00
self.recipe_l.setText("NON SELEZIONATA")
self.recipe_l.setStyleSheet("QLabel { color: red; }")
def check_steps_dependencies(self, steps):
unsupported_steps = set()
missing_components = set()
for step in steps:
2024-09-18 12:18:07 +00:00
if step.step_type in self.unsupported_steps or step.step_type not in self.cycle_available_steps:
unsupported_steps.add(step.step_type)
else:
2024-09-18 12:18:07 +00:00
for dependency in self.steps_dependencies.get(step.step_type, []):
2022-10-11 13:30:53 +00:00
if isinstance(dependency, tuple):
if all([d not in self.components for d in dependency]):
2024-09-18 12:18:07 +00:00
unsupported_steps.add(step.step_type)
2022-10-11 13:30:53 +00:00
else:
2022-10-11 14:25:18 +00:00
unready = set()
2022-10-11 13:30:53 +00:00
for d in dependency:
2022-10-11 14:25:18 +00:00
if d in self.components:
2022-10-11 13:30:53 +00:00
if not self.components[d].ready:
2022-10-11 14:25:18 +00:00
self.components[d].reconfigure()
if not self.components[d].ready:
unready.add(d)
else:
unready.add(d)
if unready == set(dependency):
2022-10-11 13:30:53 +00:00
missing_components.add(" or ".join(dependency))
else:
2022-10-11 13:30:53 +00:00
if dependency not in self.components:
2024-09-18 12:18:07 +00:00
unsupported_steps.add(step.step_type)
2022-10-11 13:30:53 +00:00
else:
if not self.components[dependency].ready:
2022-10-11 13:30:53 +00:00
self.components[dependency].reconfigure()
if not self.components[dependency].ready:
missing_components.add(dependency)
if len(unsupported_steps):
2024-01-16 18:12:06 +00:00
QMessageBox.critical(None, "Errore Ricetta",
f"Questa ricetta contiene uno o piu step non supportati da questo banco:\n{', '.join(sorted(unsupported_steps))}\nModificare la ricetta adeguatamente.")
if len(missing_components):
2024-01-16 18:12:06 +00:00
QMessageBox.critical(None, "Errore Componenti Ricetta",
f"Questa ricetta richiede i seguenti componenti per essere eseguita:\n{', '.join(sorted(missing_components))}\nModificare la ricetta adeguatamente o collegare i componenti elencati.")
if len(unsupported_steps) or len(missing_components):
self.change_recipe()
2022-08-23 14:00:04 +00:00
def set_step(self, step_name, data=None):
if step_name not in self.data:
self.data[step_name] = {}
2022-08-24 10:59:16 +00:00
if data is not None:
2024-09-18 16:03:13 +00:00
data["step"] = self.step.spec
2024-01-16 18:12:06 +00:00
data["step"].pop("name", None)
2023-05-18 14:12:40 +00:00
# MAKE ARRAY ONLY IF MORE THAN ONE TEST OF SAME TYPE
2024-01-16 18:12:06 +00:00
if len(self.data[step_name]) > 1:
2023-05-18 14:12:40 +00:00
self.data[step_name][str(len(self.data[step_name]))] = data
else:
self.data[step_name] = data
2022-08-24 10:59:16 +00:00
self.data["overridden"] = self.data["overridden"] or data.get("overridden", False)
self.data["ok"] = self.data["ok"] and data.get("ok", False)
2022-07-12 08:48:04 +00:00
self.next()
2022-07-19 09:59:00 +00:00
def done(self, ok=True):
2023-05-18 08:40:16 +00:00
self.log.info("cycle done, saving data...")
2023-09-22 22:13:31 +00:00
# remove useless info
2024-01-16 18:12:06 +00:00
self.data.get("recipe", {}).get("spec", {}).pop("steps", None)
for leak in ["leak_1", "leak_2"]:
2023-05-20 13:28:57 +00:00
if leak in self.data.keys():
2024-01-16 18:12:06 +00:00
results = {k: self.data[leak]["results"][self.tester_component][k] for k in ["Running test: result"]}
2023-06-21 15:09:16 +00:00
2024-01-16 18:12:06 +00:00
results.update({k: round(float(self.data[leak]["results"][self.tester_component][k]), 2) for k in ["Running test: filling pressure",
"Running test: measured leak",
"Running test: pressure at the end of settling"]}
2023-06-21 15:09:16 +00:00
)
2024-01-16 18:12:06 +00:00
self.data[leak]["results"] = results
2023-05-20 13:28:57 +00:00
2022-08-23 14:00:04 +00:00
if "vision" in self.data:
2025-01-24 15:51:58 +00:00
vision = self.data.get("vision", {})
2022-08-23 14:00:04 +00:00
out_paths = self.components["vision_saver"].save(
2025-01-24 15:51:58 +00:00
save_time=vision.get("time", None),
frame=vision.get("frame", None),
2022-08-23 14:00:04 +00:00
# vision=vision_test_1.get("detections", None),
)
2025-01-24 15:51:58 +00:00
vision["files"] = out_paths
2022-08-23 14:00:04 +00:00
self.log.info(f"cycle vision saved locally: {out_paths!r}")
2025-01-24 15:51:58 +00:00
vision.pop("frame", None)
vision.pop("render", None)
vision.pop("detections", None)
if "results" in vision.keys():
vision["results"].pop("results", None)
2022-11-15 16:17:59 +00:00
if self.autotesting:
self.data["autotest"] = True
self.data["autotest_reason"] = self.autotesting_reason
2023-04-11 12:23:37 +00:00
self.data["recipe"]["name"] = "AUTOTEST"
2022-10-25 13:38:18 +00:00
archived = Archive.archive(self.data, ok and self.data["ok"], overridden=self.data["overridden"])
2022-06-01 16:37:19 +00:00
self.log.info(f"cycle archived locally: {archived!r}")
2022-11-15 16:17:59 +00:00
if not self.autotesting:
# COUNT OK
if ok:
self.pieces["ok"] += 1
else:
self.pieces["ko"] += 1
2023-05-22 16:39:28 +00:00
else:
if self.autotesting_reason == "logout":
if ok:
2023-06-29 17:09:16 +00:00
self.main_window.open_login()
2023-05-22 16:39:28 +00:00
2022-07-26 10:24:53 +00:00
return archived
2022-09-14 14:53:55 +00:00
@staticmethod
def labellify(v):
2022-10-19 10:59:16 +00:00
if v is None:
v = "-"
elif isinstance(v, float):
2022-10-03 14:52:05 +00:00
v = f"{v:.1f}"
2022-10-19 11:02:12 +00:00
if not isinstance(v, str):
v = str(v)
2022-09-14 14:53:55 +00:00
return v
2022-09-06 13:15:01 +00:00
def print(self, archived, label):
2022-07-26 10:24:53 +00:00
self.log.info("cycle print")
2023-10-13 15:52:41 +00:00
if archived is None:
self.log.error("attempting to print empty label")
return None
2022-09-14 14:53:55 +00:00
if archived.label is not None:
2023-09-19 15:22:02 +00:00
# raise AssertionError("this should never happen")
2023-10-13 15:52:41 +00:00
self.log.info("printing already compiled label")
2023-09-19 15:22:02 +00:00
2022-06-01 16:37:19 +00:00
# LABEL PRINT
2022-09-14 14:53:55 +00:00
recipe = archived.test_data.get("recipe", {})
2023-06-10 07:55:19 +00:00
leak_test_1 = archived.test_data.get("leak_1", {})
2022-09-14 14:53:55 +00:00
leak_test_1_step = leak_test_1.get("step", {})
leak_test_1_step_spec = leak_test_1_step.get("spec", {})
2022-08-02 16:15:30 +00:00
leak_test_1_results = leak_test_1.get("results", {})
2023-06-10 07:55:19 +00:00
leak_test_2 = archived.test_data.get("leak_2", {})
2023-02-17 11:26:18 +00:00
leak_test_2_step = leak_test_2.get("step", {})
leak_test_2_step_spec = leak_test_2_step.get("spec", {})
leak_test_2_results = leak_test_2.get("results", {})
2023-02-16 17:58:01 +00:00
2024-01-16 18:12:06 +00:00
psetminp_a = leak_test_1_step_spec.get("test_pressure", 0) * (100 + leak_test_1_step_spec.get("test_pressure_qneg", 0) / 100)
psetmaxp_a = leak_test_1_step_spec.get("settling_pressure_max_percent", 0) * (100 + leak_test_1_step_spec.get("test_pressure_qpos", 0) / 100)
psetminp2_a = leak_test_2_step_spec.get("settling_pressure_min_percent", 0) * (100 + leak_test_2_step_spec.get("test_pressure_qneg", 0) / 100)
psetmaxp2_a = leak_test_2_step_spec.get("settling_pressure_max_percent", 0) * (100 + leak_test_2_step_spec.get("test_pressure_qpos", 0) / 100)
2024-03-12 12:48:00 +00:00
if self.tester_component is not None:
2025-01-24 15:51:58 +00:00
if self.recipe.spec["leak_1"]:
leak_test_1_results["Running test: pressure at the end of measure"] = (
leak_test_1_results["Running test: pressure at the end of settling"]
+ leak_test_1_results["Running test: measured leak"])
2025-01-28 17:29:00 +00:00
if self.recipe.spec["leak_2"]:
2025-01-24 15:51:58 +00:00
leak_test_2_results["Running test: pressure at the end of measure"] = (
leak_test_2_results["Running test: pressure at the end of settling"]
+ leak_test_2_results["Running test: measured leak"])
2023-09-23 10:58:55 +00:00
printer_fields = self.print_step.spec
2022-07-26 10:24:53 +00:00
context = {
2022-09-14 14:53:55 +00:00
# RECIPE DATA
"RECIPE": self.labellify(recipe.get("name", "-")),
"CLIENT": self.labellify(recipe.get("client", "-")),
"PART": self.labellify(recipe.get("part_number", "-")),
2023-05-11 09:19:08 +00:00
"DESCRIPTION": self.labellify(recipe.get("description", "-")),
"RECIPE_TO_PRINT": self.labellify(self.print_step.spec.get("labeltxt_5", "-").replace('{N11}', '')),
2022-09-14 14:53:55 +00:00
# STEP SPEC
2024-11-15 11:28:28 +00:00
"TPREFILL": self.labellify(leak_test_1_step.get("pre_filling_time", "-")),
"PPREFILL": self.labellify(leak_test_1_step.get("pre_filling_pressure", "-")),
"TFILL": self.labellify(leak_test_1_step.get("filling_time", "-")),
"TSET": self.labellify(leak_test_1_step.get("settling_time", "-")),
"TPREFILL2": self.labellify(leak_test_2_step.get("pre_filling_time", "-")),
"PPREFILL2": self.labellify(leak_test_2_step.get("pre_filling_pressure", "-")),
"TFILL2": self.labellify(leak_test_2_step.get("filling_time", "-")),
"TSET2": self.labellify(leak_test_2_step.get("settling_time", "-")),
"PSETMINP": self.labellify(leak_test_1_step.get("settling_pressure_min_percent", " -")),
"PSETMAXP": self.labellify(leak_test_1_step.get("settling_pressure_max_percent", " -")),
"PSETMINP2": self.labellify(leak_test_2_step.get("settling_pressure_min_percent", " -")),
"PSETMAXP2": self.labellify(leak_test_2_step.get("settling_pressure_max_percent", " -")),
2023-02-16 17:58:01 +00:00
"PSETMINP_A": self.labellify(psetminp_a),
"PSETMAXP_A": self.labellify(psetmaxp_a),
"PSETMINP2_A": self.labellify(psetminp2_a),
"PSETMAXP2_A": self.labellify(psetmaxp2_a),
2024-11-15 11:28:28 +00:00
"TTEST": self.labellify(leak_test_1_step.get("test_time", "-")),
"TTEST2": self.labellify(leak_test_2_step.get("test_time", "-")),
"PMIN": self.labellify(leak_test_1_step.get("test_pressure_qneg", "-")),
"PMIN2": self.labellify(leak_test_2_step.get("test_pressure_qneg", "-")),
"PTEST": self.labellify(leak_test_1_step.get("test_pressure", "-")),
"PTEST2": self.labellify(leak_test_2_step.get("test_pressure", "-")),
"PMAX": self.labellify(leak_test_1_step.get("test_pressure_qpos", "-")),
"TFLUSH": self.labellify(leak_test_1_step.get("flush_time", "-")),
"PFLUSH": self.labellify(leak_test_1_step.get("flush_pressure", "-")),
2022-09-14 14:53:55 +00:00
# ACTUAL TESTED VALUES
2023-06-10 07:55:19 +00:00
"RESPFILL": self.labellify(leak_test_1_results.get("Running test: filling pressure", "-")),
"RESPFILL2": self.labellify(leak_test_2_results.get("Running test: filling pressure", "-")),
"RESPSET": self.labellify(leak_test_1_results.get("Running test: pressure at the end of settling", "-")),
"RESPSET2": self.labellify(leak_test_2_results.get("Running test: pressure at the end of settling", "-")),
2023-09-11 11:53:17 +00:00
"RESPEND": self.labellify(leak_test_1_results.get("Running test: pressure at the end of measure", "-")),
"RESPEND2": self.labellify(leak_test_1_results.get("Running test: pressure at the end of measure", "-")),
2023-06-10 07:55:19 +00:00
"RESLEAK": self.labellify(leak_test_1_results.get("Running test: measured leak", "-")),
"RESLEAK2": self.labellify(leak_test_2_results.get("Running test: measured leak", "-")),
"RESRES": self.labellify(leak_test_1_results.get("Running test: result", "-")),
"RESRES2": self.labellify(leak_test_2_results.get("Running test: result", "-")),
2022-09-14 14:53:55 +00:00
# SERIAL DEFINITION
"SN": str(archived.id),
2023-02-16 12:16:05 +00:00
"SN4": f"{archived.id:0>4}",
2022-09-14 14:53:55 +00:00
"SN5": f"{archived.id:0>5}",
"SN6": f"{archived.id:0>6}",
2025-03-24 12:12:13 +00:00
"SN7": f"{archived.id:0>7}",
2022-09-14 14:53:55 +00:00
# TIME DEFINITION
2022-08-01 11:29:12 +00:00
"DATETIME": archived.time.strftime("%d/%m/%Y %H:%M:%S"),
2022-09-22 17:25:31 +00:00
"DATE": archived.time.strftime("%d/%m/%Y"),
"TIME": archived.time.strftime("%H:%M:%S"),
2022-09-14 14:53:55 +00:00
"YYYY": archived.time.strftime("%Y"),
"YY": archived.time.strftime("%y"),
"MO": archived.time.strftime("%m"),
"DD": archived.time.strftime("%d"),
"HH": archived.time.strftime("%H"),
"MI": archived.time.strftime("%M"),
"SS": archived.time.strftime("%S"),
2023-06-29 12:57:21 +00:00
"JJJ": archived.time.strftime("%j"),
2025-03-24 12:12:13 +00:00
"WW": archived.time.strftime("%W"),
2022-09-14 14:53:55 +00:00
# EXTRA DATA
2022-07-26 14:18:44 +00:00
"SHIFT": str(get_shift(archived.time)),
2022-09-20 15:42:59 +00:00
"STATION": str(self.config.machine_id),
2022-09-14 14:53:55 +00:00
"OPERATOR": str(archived.user.username),
2023-02-21 22:36:27 +00:00
"BADGE_NUM": str(archived.user.badge_number),
#BARCODE
2025-03-19 08:21:45 +00:00
"BCODE": str(self.step.spec.get("barcode","")),
2023-02-21 22:36:27 +00:00
2022-09-14 14:53:55 +00:00
# RESULT
2024-05-17 07:09:46 +00:00
"RESULT": str("CONFORME" if leak_test_1.get("ok", False) else "SCARTO") + str(" FORZATO" if self.data.get("overridden", False) else ""),
2022-07-27 12:54:19 +00:00
"RESULT_L1": "ESITO" + str(" FORZATO" if self.data.get("overridden", False) else ""),
2024-11-15 11:28:28 +00:00
"RESULT_L2": str("CONFORME" if leak_test_1.get("ok", False) else "SCARTO"),
2022-07-26 10:24:53 +00:00
}
2024-03-14 12:15:08 +00:00
#TESTING BROTHER
2024-03-19 13:23:58 +00:00
label_brother = context.get("RECIPE_TO_PRINT", "-") + context.get("DD","-") + context.get("MO","-") + context.get("YY","-") + context.get("SN5","-")
barcode = str(label_brother)
2024-03-14 12:15:08 +00:00
2025-02-28 10:49:40 +00:00
barcode_format = self.print_step.spec.get("barcode", "-")
self.printed_barcode = barcode_format.format_map(context)
2025-03-25 08:54:52 +00:00
if self.archived is not None:
self.archived.barcode = self.printed_barcode
2025-02-28 10:49:40 +00:00
2023-02-14 15:15:22 +00:00
for n in range(5):
2024-01-16 18:12:06 +00:00
field = f"labeltxt_{n + 1}"
2023-02-14 15:15:22 +00:00
if field in printer_fields.keys():
2024-01-16 18:12:06 +00:00
if printer_fields[field] != "":
context[field.upper()] = printer_fields[field]
2023-02-07 16:37:09 +00:00
# PRINT MAIN PRODUCT LABEL
2024-03-14 12:15:08 +00:00
if self.config["hardware_config"]["tecna_t3"] == "absent" and self.config["hardware_config"]["furness_controls"] == "absent":
compiled_label = self.components["label_printer_2"].print_label(barcode) # Printing with Brother label printer
2024-03-12 12:48:00 +00:00
else:
compiled_label = self.components["label_printer"].print_label(label, context=context)
self.log.info(f"Main label printed: {context!r}")
2025-01-24 15:51:58 +00:00
# return fields used to print label for saving into test archive
return context
2023-03-30 10:57:49 +00:00
def print_extra_labels(self):
# PRINT EXTRA LABELS IF NEEDED (BEFORE LEAK TEST)
2025-03-20 14:36:03 +00:00
if "extra_label_printer" in self.components.keys() and self.print_step is not None and self.autotesting is False:
2023-03-30 10:57:49 +00:00
printer_fields = self.print_step.spec
if len(printer_fields["extra_label"]) > 0:
labels = printer_fields["extra_label"].split(",")
for label in labels:
self.components["extra_label_printer"].print_label(f"{label}.prn", context=None)
2023-06-23 08:20:26 +00:00
2023-06-26 17:21:12 +00:00
@pyqtSlot(str)
2024-01-16 18:12:06 +00:00
def load_recipe_from_rfid(self, data):
if data not in(None,''):
self.tag_loaded_recipe = data
2024-09-18 12:18:07 +00:00
if self.step.step_type == "barcode_recipe_selection":
if data is not None:
self.cycle_available_steps["barcode_recipe_selection"].widget.get(data)
2023-06-26 17:21:12 +00:00
else:
pass
2023-06-26 17:21:12 +00:00
else:
# fixture removed
self.tag_loaded_recipe = None
self.fail_cycle()
self.change_recipe()
2025-01-13 10:56:47 +00:00
def add_error(self, message, is_error):
"""
Add a new error message to the list of active errors.
Args:
message (str): The error message to add.
is_error (bool): True if it's an error that should show in red, False for non-errors.
"""
# Avoid duplicates
if (message, is_error) not in self.active_errors:
self.active_errors.append((message, is_error))
# Start the timer if it's not already active
if not self.error_timer.isActive():
self.error_timer.start()
def remove_error(self, message):
"""
Remove an error message from the list of active errors.
Args:
message (str): The error message to remove.
"""
self.active_errors = [err for err in self.active_errors if err[0] != message]
# Reset the error index if necessary
if self.current_error_index >= len(self.active_errors):
self.current_error_index = 0
# Stop the timer if there are no more errors
if not self.active_errors:
self.error_label.setText("")
self.error_label.setStyleSheet("")
self.error_timer.stop()
def display_current_error(self):
"""
Display the current error from the active errors list. If there are multiple errors,
it alternates between them every time this function is called.
"""
if self.active_errors:
# Get the current error message and update the label
message, is_error = self.active_errors[self.current_error_index]
self.error_label.setText(message)
if is_error:
self.error_label.setStyleSheet("color: red;")
else:
self.error_label.setStyleSheet("color: green;")
# Move to the next error for the next call to this method
self.current_error_index = (self.current_error_index + 1) % len(self.active_errors)
else:
# Clear the label if there are no errors
self.error_label.setText("")
self.error_label.setStyleSheet("")
2025-01-13 10:56:47 +00:00
@pyqtSlot(bool)
def handle_rfid_error(self, connected):
"""
Handle errors related to the RFID system.
Args:
connected (bool): True if connected, False if not.
"""
2025-01-13 10:56:47 +00:00
if connected:
self.remove_error("Errore RFID.") # Remove the RFID error
else:
self.add_error("Errore RFID.", True) # Add the RFID error
@pyqtSlot(bool, str)
def handle_modbus_error(self, has_error, error_message):
"""
Handle Tecna/Modbus errors and manage the error list.
Args:
has_error (bool): True if there is an error, False otherwise.
error_message (str): The error message to add.
"""
#print(f"DEBUG: Modbus error handler called - has_error={has_error}, error_message={error_message}") # Debugging
if has_error:
self.add_error(f"Errore Tecna", True) # Add the Modbus error
2025-01-13 10:56:47 +00:00
else:
self.remove_error(f"Errore Tecna") # Remove the Modbus error
2025-01-31 10:06:41 +00:00
def update_label_with_args(self, extra_info=None):
"""
Updates the flag label with the string representation of current command-line arguments
and optional extra info, directly displaying it on the label.
2025-01-31 10:06:41 +00:00
Args:
extra_info (str): Optional. Extra information to append to the label text.
"""
# Combine command-line arguments
args_text = " ".join(sys.argv[1:]) if len(
sys.argv) > 1 else "No system arguments provided." # Default to No args
2025-01-31 10:06:41 +00:00
if args_text: # If there are CLI arguments (or default message)
2025-01-31 10:06:41 +00:00
# Combine CLI args and extra info for label text
if extra_info:
args_text += f" | {extra_info}"
2025-01-31 10:06:41 +00:00
# Update the label text directly with args_text
self.flag_label.setText(args_text)
self.flag_label.setStyleSheet("QLabel { color: red; font-weight: bold; }") # Customize color if needed
self.flag_label.setVisible(True) # Ensure the label is visible
2025-01-31 10:06:41 +00:00
else: # No args provided
self.flag_label.setVisible(False) # Hide label