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
2025-04-17 09:05:30 +00:00
from distutils . util import change_root
2025-01-27 13:37:48 +00:00
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
2024-10-25 15:11:00 +00:00
from components import ArchiveSynchronizer
2022-06-01 16:37:19 +00:00
class Test ( Widget ) :
2025-07-24 14:35:33 +00:00
# Modulo 43 assignment table for checksum calculation
MODULO43_ASSIGNMENT_TABLE = {
' 0 ' : 0 , ' 1 ' : 1 , ' 2 ' : 2 , ' 3 ' : 3 , ' 4 ' : 4 , ' 5 ' : 5 , ' 6 ' : 6 , ' 7 ' : 7 , ' 8 ' : 8 , ' 9 ' : 9 ,
' A ' : 10 , ' B ' : 11 , ' C ' : 12 , ' D ' : 13 , ' E ' : 14 , ' F ' : 15 , ' G ' : 16 , ' H ' : 17 , ' I ' : 18 ,
' J ' : 19 , ' K ' : 20 , ' L ' : 21 , ' M ' : 22 , ' N ' : 23 , ' O ' : 24 , ' P ' : 25 , ' Q ' : 26 , ' R ' : 27 ,
' S ' : 28 , ' T ' : 29 , ' U ' : 30 , ' V ' : 31 , ' W ' : 32 , ' X ' : 33 , ' Y ' : 34 , ' Z ' : 35 ,
' - ' : 36 , ' . ' : 37 , ' ' : 38 , ' $ ' : 39 , ' / ' : 40 , ' + ' : 41 , ' % ' : 42
}
2025-01-27 13:37:48 +00:00
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 ( )
2025-05-06 07:12:53 +00:00
self . original_username = session . username
# Check if we should set the label to "ADMIN"
if session . username . upper ( ) == " ADMIN " :
self . user_l . setText ( " ADMIN " )
session . _is_admin = True
else :
self . user_l . setText ( session . username )
session . _is_admin = False
2022-06-01 16:37:19 +00:00
if session . is_admin :
self . user_l . setStyleSheet ( " QLabel { color: red; } " )
else :
self . user_l . setStyleSheet ( " " )
2025-05-06 07:12:53 +00:00
# Store original admin status
self . had_admin = session . is_admin
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 )
2025-07-24 13:42:11 +00:00
# Initialize barcode formatting variables
self . barcode_prefix = " "
self . barcode_suffix = " *= "
2025-02-05 10:38:40 +00:00
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
2025-02-05 10:38:40 +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 "
2022-09-06 10:06:43 +00:00
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 " , } ,
2022-09-06 10:06:43 +00:00
" 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 " } ,
2022-09-06 10:06:43 +00:00
}
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 ) ) ,
2024-10-07 09:49:38 +00:00
" 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 ,
2024-10-07 09:49:38 +00:00
" 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 ) ) ,
2024-10-08 07:35:29 +00:00
" instruction_extra " : Test_Assembly ( img_path = None , text = u " ESEGUIRE LE OPERAZIONI DI MONTAGGIO EXTRA INDICATE IN FIGURA " ,
2024-10-07 09:49:38 +00:00
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
2023-03-29 17:05:17 +00:00
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
2025-04-01 14:18:39 +00:00
if " --no-autotest " in sys . argv :
self . setStyleSheet ( " background-color: red; " )
else :
self . setStyleSheet ( " background-color: white; " )
2022-11-15 16:17:59 +00:00
if " --no-autotest " not in sys . argv :
2025-03-18 18:17:01 +00:00
if " --test-autotest " in sys . argv :
self . autotest_period = int ( 60 * 1000 ) # 1 min
else :
2025-03-27 15:16:34 +00:00
#self.autotest_period = int(8.5 * 60 * 60 * 1000)# 8.5 HOURS
self . autotest_period = int ( 4 * 60 * 60 * 1000 ) # 4 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 )
2024-02-23 17:34:17 +00:00
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 ( )
def refresh_time ( self , init = False ) :
2025-04-17 09:05:30 +00:00
if init and not hasattr ( self , ' time_timer ' ) :
2022-06-01 16:37:19 +00:00
self . time_timer = QTimer ( )
2025-04-17 09:30:59 +00:00
self . time_timer . setSingleShot ( False )
2022-06-01 16:37:19 +00:00
self . time_timer . timeout . connect ( self . refresh_time )
2025-04-17 09:05:30 +00:00
2022-06-01 16:37:19 +00:00
t = datetime . now ( )
2025-04-17 09:05:30 +00:00
self . time_l . setText ( " {d} / {mo} / {y} \n {h} : {m:02d} " . format (
y = t . year , mo = t . month , d = t . day , h = t . hour , m = t . minute
) )
# Always restart the timer, ensuring intervals are set correctly
if hasattr ( self , ' time_timer ' ) : # Safeguard for uninitialized timer
2025-04-17 09:30:59 +00:00
self . time_timer . start ( 30000 )
2022-06-01 16:37:19 +00:00
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 )
2025-05-06 07:12:53 +00:00
def enable_temp_admin ( self ) :
""" Enable temporary admin privileges for the current user """
session = Users . get_session ( )
if session is None :
self . log . warning ( " Cannot enable temporary admin privileges: No active session " )
return False
# Save the current label text before changing it
self . saved_user_label_text = self . user_l . text ( )
# Enable temporary admin privileges using the Session class method
session . enable_temp_admin ( )
# Update UI to reflect admin status
self . user_l . setText ( " ADMIN " )
self . user_l . setStyleSheet ( " QLabel { color: red; } " )
self . log . info ( f " Temporary admin privileges enabled for user: { session . username } " )
return True
def disable_temp_admin ( self ) :
""" Disable temporary admin privileges and restore original user status """
session = Users . get_session ( )
if session is None :
self . log . warning ( " Cannot disable temporary admin privileges: No active session " )
return False
# Disable temporary admin privileges using the Session class method
session . disable_temp_admin ( )
# Restore original UI
# Use the saved label text if available, otherwise fall back to original username
if hasattr ( self , ' saved_user_label_text ' ) :
self . user_l . setText ( self . saved_user_label_text )
else :
self . user_l . setText ( self . original_username )
# Set style based on original admin status
if self . had_admin :
self . user_l . setStyleSheet ( " QLabel { color: red; } " )
else :
self . user_l . setStyleSheet ( " " )
self . log . info ( f " Temporary admin privileges disabled for user: { session . username } " )
return True
2022-06-01 16:37:19 +00:00
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
2025-04-17 09:30:59 +00:00
if not hasattr ( self , " refresh_timer " ) :
self . refresh_timer = QTimer ( )
self . refresh_timer . setSingleShot ( False )
self . refresh_timer . timeout . connect ( self . refresh_time )
self . refresh_timer . start ( 30000 )
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 } " )
2024-04-23 09:15:23 +00:00
2022-07-25 09:16:14 +00:00
current_w = self . centralWidget
2025-01-27 13:37:48 +00:00
2022-07-25 09:16:14 +00:00
if hasattr ( current_w , " stop " ) :
2025-01-27 13:37:48 +00:00
self . log . info ( f " stopping widget { self . step . step_type } " )
2022-07-25 09:16:14 +00:00
current_w . stop ( )
2025-04-04 14:24:29 +00:00
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 )
2025-04-04 14:24:29 +00:00
if self . config [ " hardware_config " ] [ " tecna_t3 " ] == " present " or self . config [ " hardware_config " ] [
" furness_controls " ] == " present " :
2024-03-12 12:48:00 +00:00
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
2025-04-04 14:24:29 +00:00
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 )
2025-04-04 14:24:29 +00:00
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 " )
2025-04-04 14:24:29 +00:00
# Set next cycle step normally if no action sets it explicitly
2022-07-19 09:59:00 +00:00
if self . recipe is None or self . cycle_steps is None :
2025-04-04 14:24:29 +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 " )
2024-10-29 11:03:20 +00:00
2022-07-19 09:59:00 +00:00
elif action is None :
2025-04-04 14:24:29 +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
2022-11-15 16:17:59 +00:00
# and if cycle_steps is not started or has ended
2025-03-18 18:17:01 +00:00
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 } ) " )
2025-04-04 14:24:29 +00:00
if self . autotest_period is not None : # Reset periodic autotest timer
2022-07-12 08:48:04 +00:00
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 ) :
2025-04-04 14:24:29 +00:00
# Go to next step in autotest_cycle_steps
self . cycle_index + = 1
2022-11-15 16:17:59 +00:00
self . step = self . autotest_cycle_steps [ self . cycle_index ]
else :
2025-04-04 14:24:29 +00:00
# When autotest ends, check if it needs to restart
if not self . data . get ( " ok " ) : # Autotest failed
self . log . warning ( " Restarting autotest from the first step due to failure. " )
self . cycle_index = 0
self . step = self . autotest_cycle_steps [ self . cycle_index ] # Restart from the first step
2023-07-06 10:59:06 +00:00
else :
2025-04-04 14:24:29 +00:00
# Autotest succeeded; proceed to post-autotest actions
self . autotesting = False
if self . autotesting_reason == " logout " :
Users . logout ( )
self . main_window . open_login ( )
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 ) )
t + = timedelta ( seconds = int ( self . autotest_period / 1000 ) )
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 ) )
self . autotesting_reason = None
self . cycle_index = - 1
self . config [ " autotest_done " ] = True
2022-11-15 16:17:59 +00:00
if not self . autotesting :
if len ( self . cycle_steps ) :
2025-04-04 14:24:29 +00:00
# Go to next step in cycle_steps
2022-11-15 16:17:59 +00:00
self . cycle_index = ( self . cycle_index + 1 ) % len ( self . cycle_steps )
self . step = self . cycle_steps [ self . cycle_index ]
else :
self . cycle_index = - 1
2024-09-18 12:18:07 +00:00
self . step = Step ( step_type = None )
2025-04-04 14:24:29 +00:00
# Enable/disable cycle controls
2022-06-01 16:37:19 +00:00
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 " ,
} )
2025-04-04 14:24:29 +00:00
2024-09-18 12:18:07 +00:00
self . log . info ( f " next cycle step: { self . step . step_type !r} " )
2025-04-04 14:24:29 +00:00
# INIT TEST DATA IF STARTING CYCLE OR RESET IS NEEDED
if self . cycle_index == 0 and not self . autotesting : # Initialize test data for normal cycles
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 )
2025-04-04 14:24:29 +00:00
# Remaining logic for updating widgets, starting timers, etc.
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
2025-01-27 11:54:07 +00:00
2024-09-18 12:18:07 +00:00
if self . step . step_type == " leak_2 " :
2025-04-04 14:24:29 +00:00
self . setCentralWidget ( w ) # Pre-show UI for leak_2
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 " ) )
2025-02-28 09:31:03 +00:00
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... " )
2025-04-04 14:24:29 +00:00
# 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 )
2025-04-04 14:24:29 +00:00
# Update 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
2024-10-08 07:35:29 +00:00
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 " :
2025-02-17 12:01:14 +00:00
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 ]
2024-10-08 07:35:29 +00:00
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
2024-10-08 07:35:29 +00:00
# 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 = { } ) )
2024-10-08 07:35:29 +00:00
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
2022-09-06 10:06:43 +00:00
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 :
2023-09-23 14:08:40 +00:00
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; } " )
2022-09-06 10:06:43 +00:00
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 )
2022-09-06 10:06:43 +00:00
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 ) )
2022-09-06 10:06:43 +00:00
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 :
2022-09-06 10:06:43 +00:00
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 )
2022-09-06 10:06:43 +00:00
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 ) ) } \n Modificare la ricetta adeguatamente. " )
2022-09-06 10:06:43 +00:00
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 ) ) } \n Modificare la ricetta adeguatamente o collegare i componenti elencati. " )
2022-09-06 10:06:43 +00:00
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... " )
2025-04-04 14:24:29 +00:00
# Remove useless info
2024-01-16 18:12:06 +00:00
self . data . get ( " recipe " , { } ) . get ( " spec " , { } ) . pop ( " steps " , None )
2023-06-21 15:09:16 +00:00
2025-04-04 14:24:29 +00:00
for leak in [ " leak_1 " , " leak_2 " ] :
if leak in self . data . keys ( ) and " results " in self . data [ leak ] :
# Check if tester_component exists in results
if self . tester_component in self . data [ leak ] [ " results " ] :
# Safely extract results with necessary checks
results = { k : self . data [ leak ] [ " results " ] [ self . tester_component ] . get ( k , " N/A " )
for k in [ " Running test: result " ] }
results . update (
{
k : round ( float ( self . data [ leak ] [ " results " ] [ self . tester_component ] . get ( k , 0.0 ) ) , 2 )
for k in [
" Running test: filling pressure " ,
" Running test: measured leak " ,
" Running test: pressure at the end of settling " ,
]
}
)
self . data [ leak ] [ " results " ] = results
else :
self . log . warning (
f " Missing tester_component ' { self . tester_component } ' in leak results for ' { leak } ' , skipping... " )
self . data [ leak ] [ " results " ] = { " Running test: result " : " MISSING " }
else :
self . log . warning ( f " Leak ' { leak } ' has no results; skipping... " )
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 " , { } )
2025-04-04 14:24:29 +00:00
# Save vision results if available
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
)
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 "
2025-04-04 14:24:29 +00:00
# Archive and update results
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 :
2025-04-04 14:24:29 +00:00
# Count results based on success or failure
2022-11-15 16:17:59 +00:00
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 :
2025-06-03 13:32:55 +00:00
# Make sure to properly logout the user before opening login screen
from lib . db . models . users import Users
from components import ArchiveSynchronizer
Users . logout ( )
ArchiveSynchronizer . machine_status = " logged-out "
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 " , { } )
2025-07-23 12:07:01 +00:00
# Define barcode format variables
2025-07-24 13:42:11 +00:00
self . module_data_format = " 1IT ECE {SN7} "
2025-07-23 12:16:35 +00:00
# Use suffix_mod_43 from print step editor if available, otherwise use default
2025-07-23 12:07:01 +00:00
self . barcode_format = self . print_step . spec . get ( " barcode " )
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 " , " - " ) ) ,
2024-05-08 09:46:03 +00:00
" 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 ) ,
2025-03-31 16:22:42 +00:00
# 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 )
2025-07-23 12:07:01 +00:00
2025-07-24 13:42:11 +00:00
# Process any {M43:X:Y} patterns in the barcode format
processed_barcode_format = self . process_m43_patterns ( self . barcode_format , context )
formatted_barcode = processed_barcode_format . format ( * * context )
2025-03-27 08:36:03 +00:00
context [ ' BCODE ' ] = formatted_barcode
2025-07-23 12:07:01 +00:00
self . printed_barcode = formatted_barcode
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 )
2023-03-29 17:05:17 +00:00
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 ) :
2024-02-23 17:34:17 +00:00
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 " :
2024-02-23 17:34:17 +00:00
if data is not None :
self . cycle_available_steps [ " barcode_recipe_selection " ] . widget . get ( data )
2023-06-26 17:21:12 +00:00
else :
2024-02-23 17:34:17 +00:00
pass
2023-06-26 17:21:12 +00:00
else :
2024-02-23 17:34:17 +00:00
# fixture removed
self . tag_loaded_recipe = None
self . fail_cycle ( )
self . change_recipe ( )
2025-01-13 10:56:47 +00:00
2025-02-05 10:38:40 +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 ) :
2025-02-05 10:38:40 +00:00
"""
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 :
2025-02-05 10:38:40 +00:00
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 :
2025-02-05 10:38:40 +00:00
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 ) :
"""
2025-02-03 11:32:17 +00:00
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
2025-02-03 11:32:17 +00:00
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
2025-02-03 11:32:17 +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 :
2025-02-03 11:32:17 +00:00
args_text + = f " | { extra_info } "
2025-01-31 10:06:41 +00:00
2025-02-03 11:32:17 +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
2025-05-06 07:12:53 +00:00
self . flag_label . setVisible ( False ) # Hide label
2025-07-23 12:07:01 +00:00
2025-07-24 13:42:11 +00:00
def process_m43_patterns ( self , barcode_format , context ) :
"""
Process any { M43 : X : Y } patterns in the barcode format string .
The pattern { M43 : X : Y } is replaced with the modulo 43 check digit calculated
for the substring of the formatted barcode starting at position X and
containing Y characters .
Args :
barcode_format : The barcode format string that may contain { M43 : X : Y } patterns .
context : The context dictionary used for formatting .
Returns :
The processed barcode format string with { M43 : X : Y } patterns replaced by
placeholders that will be filled with the calculated check digits .
"""
import re
2025-07-24 14:35:33 +00:00
# If barcode_format is None or empty, return it as is
if not barcode_format :
self . log . warning ( " Empty barcode format provided " )
return barcode_format
# Log the input barcode format and context
self . log . info ( f " Processing barcode format: ' { barcode_format } ' " )
self . log . info ( f " Context keys: { list ( context . keys ( ) ) } " )
2025-07-24 13:42:11 +00:00
# Create a temporary copy of the barcode format for processing
processed_format = barcode_format
# Find all {M43:X:Y} patterns in the barcode format
pattern = r ' \ { M43:( \ d+):( \ d+) \ } '
2025-07-24 14:35:33 +00:00
matches = list ( re . finditer ( pattern , barcode_format ) )
self . log . info ( f " Found { len ( matches ) } M43 patterns in barcode format " )
# If no M43 patterns found, return the original format
if not matches :
self . log . info ( " No M43 patterns found in barcode format " )
return barcode_format
2025-07-24 13:42:11 +00:00
# Process each match
for i , match in enumerate ( matches ) :
# Extract X and Y values
x = int ( match . group ( 1 ) )
y = int ( match . group ( 2 ) )
2025-07-24 14:35:33 +00:00
self . log . info ( f " Processing M43 pattern { i + 1 } : X= { x } , Y= { y } " )
2025-07-24 13:42:11 +00:00
# Create a placeholder for this check digit
placeholder = f " {{ m43_check_ { i } }} "
# Replace the {M43:X:Y} pattern with the placeholder
processed_format = processed_format . replace ( match . group ( 0 ) , placeholder )
# Format the barcode without the M43 patterns to get the base string
2025-07-24 14:35:33 +00:00
# First, create a temporary format string with all M43 patterns removed
temp_format = barcode_format
for m in matches :
temp_format = temp_format . replace ( m . group ( 0 ) , " " )
# Format the temporary string with the context
try :
base_string = temp_format . format ( * * context )
self . log . info ( f " Base string for checksum calculation: ' { base_string } ' " )
except KeyError as e :
self . log . error ( f " KeyError when formatting base string: { e } " )
context [ f " m43_check_ { i } " ] = " ? "
continue
2025-07-24 13:42:11 +00:00
# Extract the substring for checksum calculation
if x < len ( base_string ) and x + y < = len ( base_string ) :
substring = base_string [ x : x + y ]
2025-07-24 14:35:33 +00:00
# Log the substring and its length for debugging
self . log . info ( f " M43 substring for checksum calculation: ' { substring } ' , length: { len ( substring ) } " )
# Remove any characters that are not valid for checksum calculation
# Check if the substring contains any invalid characters
invalid_chars = [ c for c in substring if c not in self . MODULO43_ASSIGNMENT_TABLE ]
if invalid_chars :
original_substring = substring
substring = ' ' . join ( c for c in substring if c in self . MODULO43_ASSIGNMENT_TABLE )
self . log . info ( f " Removed invalid characters { invalid_chars } from substring: ' { original_substring } ' -> ' { substring } ' " )
# Check if the substring is empty or contains only whitespace
if not substring or substring . isspace ( ) :
self . log . warning ( f " Empty or whitespace-only substring for M43 pattern { i + 1 } " )
context [ f " m43_check_ { i } " ] = " ? "
continue
2025-07-24 13:42:11 +00:00
# Calculate the check digit
2025-07-24 14:35:33 +00:00
try :
check_digit = self . calculate_modulo43_checksum ( substring )
# Log the calculated check digit
self . log . info ( f " Calculated check digit: ' { check_digit } ' for substring: ' { substring } ' " )
# Add the check digit to the context
context [ f " m43_check_ { i } " ] = check_digit
except ValueError as e :
self . log . error ( f " Error calculating checksum: { e } " )
context [ f " m43_check_ { i } " ] = " ? "
2025-07-24 13:42:11 +00:00
else :
# Handle out-of-range indices
self . log . warning ( f " M43 pattern with X= { x } , Y= { y } is out of range for string of length { len ( base_string ) } " )
context [ f " m43_check_ { i } " ] = " ? "
2025-07-24 14:35:33 +00:00
self . log . info ( f " Processed barcode format: ' { processed_format } ' " )
2025-07-24 13:42:11 +00:00
return processed_format
2025-07-23 12:07:01 +00:00
def calculate_modulo43_checksum ( self , data_sequence : str ) - > str :
"""
Calculates the Modulo 43 checksum for a given data sequence .
The function determines the checksum value for each character based on a
predefined assignment table , calculates the total sum of these values ,
and then finds the remainder of the division by 43. The character
corresponding to this remainder is the check digit .
Args :
data_sequence : The input string for which to calculate the checksum .
Returns :
The Modulo 43 check digit as a single character .
Raises :
ValueError : If the data_sequence contains a character not found in the
assignment table .
"""
2025-07-24 14:35:33 +00:00
# If the data sequence is empty, return a default value
if not data_sequence :
self . log . warning ( " Empty data sequence provided for checksum calculation " )
return " ? "
# Use the class variable for the assignment table
assignment_table = self . MODULO43_ASSIGNMENT_TABLE
# Log the input data sequence
self . log . info ( f " Calculating checksum for data sequence: ' { data_sequence } ' " )
2025-07-23 12:07:01 +00:00
total_sum = 0
2025-07-24 14:35:33 +00:00
char_values = [ ]
2025-07-23 12:07:01 +00:00
for char in data_sequence :
if char in assignment_table :
2025-07-24 14:35:33 +00:00
char_value = assignment_table [ char ]
total_sum + = char_value
char_values . append ( ( char , char_value ) )
2025-07-23 12:07:01 +00:00
else :
2025-07-24 14:35:33 +00:00
self . log . error ( f " Character ' { char } ' is not valid for checksum calculation. " )
2025-07-23 12:07:01 +00:00
raise ValueError ( f " Character ' { char } ' is not valid for checksum calculation. " )
2025-07-24 14:35:33 +00:00
# Log the character values and total sum
self . log . info ( f " Character values: { char_values } " )
self . log . info ( f " Total sum: { total_sum } " )
2025-07-23 12:07:01 +00:00
remainder = total_sum % 43
2025-07-24 14:35:33 +00:00
self . log . info ( f " Remainder (total_sum % 43): { remainder } " )
2025-07-23 12:07:01 +00:00
# Invert the assignment_table to map the remainder back to the character
for char , value in assignment_table . items ( ) :
if value == remainder :
2025-07-24 14:35:33 +00:00
self . log . info ( f " Check digit for remainder { remainder } : ' { char } ' " )
2025-07-23 12:07:01 +00:00
return char
# This part of the code should be unreachable given the logic
2025-07-24 14:35:33 +00:00
self . log . error ( f " No character found for remainder { remainder } " )
2025-07-23 12:07:01 +00:00
return " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "