2022-06-01 16:37:19 +00:00
import logging
import os
import sys
from datetime import datetime
2022-06-28 10:31:27 +00:00
from lib . db import Archive , Users
from PyQt5 . QtCore import QTimer
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
from ui . test_autotest import Test_Autotest
2022-06-22 15:18:29 +00:00
from ui . test_vision import Test_Vision
2022-06-01 16:37:19 +00:00
from ui . widget import Widget
class Test ( Widget ) :
2022-06-22 15:18:29 +00:00
def __init__ ( self , system_name = None , components = None ) :
2022-06-01 16:37:19 +00:00
super ( ) . __init__ ( )
self . system_name = system_name
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 " )
# 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 ( " " )
# SHOW AND UPDATE TIME CLOCK
self . refresh_time ( init = True )
# INIT RECIPE
self . recipe = None
# INIT CYCLE STATES
self . cycle_state = None
self . cycle_states = {
2022-06-22 15:18:29 +00:00
# "assembly_1": Test_Assembly(self.select_step_img("assembly_1"), u"INSERIRE SENSORE"),
2022-06-01 16:37:19 +00:00
" autotest " : Test_Assembly ( None , u " ESEGUIRE PROCEDURA DI AUTOTEST " , Test_Autotest ( ) ) ,
" done " : Test_Assembly ( self . select_step_img ( " success " ) , u " COLLAUDO COMPLETATO - RIMUOVERE IL SENSORE " ) ,
" emergency " : Test_Assembly ( self . select_step_img ( " reset_emergency " ) , u " EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \" RESET EMERGENZA \" DAL MEN \u00d9 \" STRUMENTI \" " ) ,
2022-06-22 15:18:29 +00:00
" fail " : Test_Assembly ( self . select_step_img ( " fail " ) , u " CICLO INTERROTTO - RIMUOVERE IL SENSORE " ) ,
" select_recipe " : Test_Assembly ( None , u " SELEZIONARE IL CODICE DA COLLAUDARE " , Recipe_Selection ( ) ) ,
" vision " : Test_Assembly ( None , u " ESEGUIRE PROCEDURA DI AUTOTEST " , Test_Vision ( self . components , None ) ) ,
" wait " : Test_Assembly ( self . select_step_img ( " wait " ) , u " ATTENDERE - PAUSA INTER CICLO " ) ,
2022-06-01 16:37:19 +00:00
}
2022-06-22 15:18:29 +00:00
self . cycle_loop = [ " vision " , " done " , " wait " ]
2022-06-28 10:31:27 +00:00
self . cycle_index = - 1
2022-06-01 16:37:19 +00:00
self . cycle_changing_state = False
# SETUP AUTOTEST
self . autotest_request = False
if " --no-autotest " not in sys . argv :
self . autotest_period = 12 * 60 * 60 * 1000
self . request_autotest ( " init " )
else :
self . autotest_period = None
2022-06-28 10:31:27 +00:00
# INIT TEST DATA
self . data = None
2022-06-01 16:37:19 +00:00
# INIT PIECES COUNTER ([pieces_ok, pieces_failed])
self . pieces = [ 0 , 0 ]
# CONNECT CYCLE CONTROLS
self . cancel_b . clicked . connect ( self . fail_cycle )
self . change_recipe_b . clicked . connect ( self . change_recipe )
for w in self . cycle_states . values ( ) :
if hasattr ( w , " ko " ) :
w . ko . connect ( self . fail_cycle )
if hasattr ( w , " ok " ) :
# custom ok handlers should call next again
if type ( w . widget ) is Recipe_Selection :
w . ok . connect ( self . set_recipe )
2022-06-28 10:31:27 +00:00
elif type ( w . widget ) is Test_Vision :
w . ok . connect ( self . set_vision )
2022-06-01 16:37:19 +00:00
else :
w . ok . connect ( self . next )
# TESTING
if " --test " in sys . argv :
self . testing = True
else :
self . testing = False
# /TESTING
# START CYCLE
self . next ( )
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 :
names . append ( f " { step } _ { suffix } _ { self . system_name } " )
names . append ( f " { step } _ { suffix } " )
names . append ( f " { step } _ { self . system_name } " )
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 " )
def fail_cycle ( self ) :
self . next ( action = " fail " )
def setCentralWidget ( self , widget ) :
replace_widget ( self , " centralWidget " , widget )
# def check_next(self, interpreted):
# self.disconnect(self.watched)
# if "digital_io" in interpreted:
# if interpreted["digital_io"]["emergency"] is True and self.cycle_state != "emergency":
# self.cycle_state = "emergency"
# self.next()
# elif interpreted["digital_io"]["emergency"] is False and self.cycle_state == "emergency" and self.is_first_cycle_time:
# self.is_first_cycle_time = False
# # reset cycle
# if self.recipe is None:
# self.cycle_state = -1 # go to recipe selection
# else:
# self.cycle_state = 0 # skip recipe selection
# self.next(fail=True)
# if type(self.centralWidget()) is not Test_Autotest: # IGNORE DURING AUTOTEST
# if self.cycle_state == 1: # WAIT FOR PIECE PRESENCE
# if self.testing is True and self.is_first_cycle_time:
# self.bench.inputs["io"].set_bit(*self.bench.parsers["digital_io"].presence1, True)
# self.bench.inputs["io"].set_bit(*self.bench.parsers["digital_io"].presence2, True)
# # /TESTING
# if self.is_first_cycle_time:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "done", msg_type="test")
# if "digital_io" in interpreted and interpreted["digital_io"]["presence1"] and interpreted["digital_io"]["presence2"]:
# self.bench.parsers["digital_io"].handler({"lock": True})
# self.next()
# elif self.cycle_state == 2: # PIECE INSERTED, LOCK PIECE AFTER DELAY
# if self.is_first_cycle_time:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "done", msg_type="test")
# self.timer.start(2000)
# elif self.cycle_state == 4: # CAMERA TEST & BARCODE READ DONE, START ASSEMBLY PHASE
# if self.is_first_cycle_time:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "done", msg_type="test")
# self.timer.start(2000)
# elif self.cycle_state == 5: # WAIT FOR SCREWDRIVER COUNTS
# if self.is_first_cycle_time and self.bench.parsers["pick_to_light"].request is None:
# self.bench.parsers["pick_to_light"].handler({
# "vtfe10": self.recipe.vtfe10,
# "vtfe11": self.recipe.vtfe11,
# "vtfe13": self.recipe.vtfe13,
# })
# log_msg("SCREWDRIVER ENABLE", msg_type="test")
# elif "pick_to_light" in interpreted and interpreted["pick_to_light"] is not None:
# screws = [interpreted["pick_to_light"][k] for k in ["vtfe10", "vtfe11", "vtfe13"]]
# needed = [self.bench.parsers["pick_to_light"].request[k] for k in ["vtfe10", "vtfe11", "vtfe13"]]
# done = all([screwed >= requested for screwed, requested in zip(screws, needed)])
# self.screw_text(screws)
# if self.is_first_cycle_time and done:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "done", msg_type="test")
# self.bench.parsers["pick_to_light"].handler(None)
# self.screw_text([0, 0, 0])
# self.bench.parsers["digital_io"].handler({"lock": False})
# self.next()
# elif self.cycle_state == 6:
# # TESTING
# if self.testing is True:
# self.bench.inputs["io"].set_bit(*self.bench.parsers["digital_io"].presence1, False)
# self.bench.inputs["io"].set_bit(*self.bench.parsers["digital_io"].presence2, False)
# # /TESTING
# if not self.is_done:
# self.is_done = True
# self.done(True)
# if self.is_done and "digital_io" in interpreted and not interpreted["digital_io"]["presence1"] and not interpreted["digital_io"]["presence2"] and self.is_first_cycle_time:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "done", msg_type="test")
# if self.skip:
# # reset cycle
# self.cycle_state = 0 # skip recipe selection
# self.skip = False
# self.next()
# else:
# self.timer.start(2000)
# elif self.cycle_state == 7: # WAITING BEFORE NEW CYCLE
# if self.is_first_cycle_time:
# self.is_first_cycle_time = False
# log_msg("cycle state:", self.cycle_state, "wait", msg_type="test")
# # reset cycle
# self.cycle_state = 0 # skip recipe selection
# self.timer.start(6000)
# self.watched = self.bench.interpreter.update.connect(self.check_next)
def request_autotest ( self , reason ) : # you can cancel the request calling request_autotest(False)
self . log . info ( f " cycle request autotest: reason: { reason !r} autotest_request: { self . autotest_request !r} " )
if reason == " init " :
self . autotest_timer = QTimer ( )
self . autotest_timer . setSingleShot ( False )
self . autotest_timer . timeout . connect ( self . request_periodic_autotest )
self . time_timer . start ( self . autotest_period )
reason = " boot "
self . autotest_request = reason
self . cycle_states [ " autotest " ] . widget . set_reason ( reason )
def request_periodic_autotest ( self ) :
self . request_autotest ( " periodic " )
def next ( self , action = None ) :
self . log . debug ( f " cycle next: cycle_state: { self . cycle_state !r} action: { action !r} " )
2022-06-28 10:31:27 +00:00
current_w = self . cycle_states . get ( self . cycle_state , None )
if current_w is not None and hasattr ( current_w , " stop " ) :
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 )
self . cycle_changing_state = True
self . cycle_state = " select_recipe "
2022-06-28 10:31:27 +00:00
self . cycle_index = - 1
# RESET TEST DATA
self . data = None
2022-06-01 16:37:19 +00:00
elif action == " fail " :
self . log . info ( f " cycle next: action: { action !r} " )
if self . cycle_state in self . cycle_loop :
pass
# COUNT FAIL
self . pieces [ 1 ] + = 1
# FAIL AND RESTART TEST
self . cycle_changing_state = True
self . cycle_state = " fail "
2022-06-28 10:31:27 +00:00
self . cycle_index = - 1
# RESET TEST DATA
self . data = None
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 " )
# if action did not set the next cycle_state
# set next cycle_state normally
if not self . cycle_changing_state :
self . cycle_changing_state = True
if self . recipe is None :
# if recipe not set: select_recipe
self . cycle_state = " select_recipe "
else :
2022-06-28 10:31:27 +00:00
if self . cycle_index == - 1 and self . autotest_request is not False :
2022-06-01 16:37:19 +00:00
# if cycle_loop is not started or has ended
# and autotest was requested
self . autotest_request = False
self . cycle_state = " autotest "
if self . autotest_period is not None : # reset periodic autotest timer
self . time_timer . start ( self . autotest_period )
else :
# goto next step in cycle_loop
2022-06-28 10:31:27 +00:00
self . cycle_index = ( self . cycle_index + 1 ) % len ( self . cycle_loop )
self . cycle_state = self . cycle_loop [ self . cycle_index ]
2022-06-01 16:37:19 +00:00
# enable/disable cycle controls
self . change_recipe_b . setEnabled ( self . recipe is not None )
self . cancel_b . setEnabled ( self . cycle_state not in {
" emergency " ,
" fail " ,
" select_recipe " ,
" wait " ,
} )
self . log . info ( f " cycle next: next cycle_state: { self . cycle_state !r} " )
2022-06-28 10:31:27 +00:00
# INIT TEST DATA IF STARTING CYCLE LOOP
if self . cycle_index == 0 :
self . data = { }
2022-06-01 16:37:19 +00:00
if self . cycle_state == " done " :
self . done ( )
w = self . cycle_states [ self . cycle_state ]
2022-06-22 15:18:29 +00:00
if hasattr ( w , " start " ) :
w . start ( )
2022-06-01 16:37:19 +00:00
# UPDATE PIECES DISPLAY
self . pieces_count_l . setText ( f " { self . pieces [ 0 ] } OK / { self . pieces [ 1 ] } NOK / { sum ( self . pieces ) } TOT " )
self . setCentralWidget ( w )
self . cycle_changing_state = False
def set_recipe ( self , recipe = None ) :
self . recipe = recipe
# UPDATE RECIPE DISPLAY
if self . recipe is not None :
self . recipe_l . setText ( self . recipe . name )
self . recipe_l . setStyleSheet ( " " )
self . next ( )
else :
self . recipe_l . setText ( " NON SELEZIONATA " )
self . recipe_l . setStyleSheet ( " QLabel { color: red; } " )
2022-06-28 10:31:27 +00:00
def set_vision ( self , vision = None ) :
self . data [ " vision " ] = vision
self . data [ " overridden " ] = self . data . get ( " overridden " , False ) or self . data [ " vision " ] . get ( " overridden " , False )
self . data [ " ok " ] = self . data . get ( " ok " , True ) and self . data [ " vision " ] . get ( " ok " , False )
self . next ( )
2022-06-01 16:37:19 +00:00
def done ( self , ok = False ) :
self . log . info ( " cycle done " )
2022-06-28 10:31:27 +00:00
archived = Archive . archive ( self . recipe , 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} " )
# LABEL PRINT
# self.printer.print_label("1", archived)
# self.log.info(f"cycle printed: {archived!r}")
# COUNT OK
self . pieces [ 0 ] + = 1