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

684 lines
36 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
2022-06-01 16:37:19 +00:00
2022-07-19 09:59:00 +00:00
from lib.db import Archive, Steps, Users
2022-07-26 14:18:44 +00:00
from lib.helpers import get_shift
from playhouse.shortcuts import model_to_dict
2023-06-23 08:20:26 +00:00
from PyQt5.QtCore import QTimer, pyqtSlot
from PyQt5.QtWidgets import QMessageBox
2022-06-01 16:37:19 +00:00
from ui.helpers import replace_widget
from ui.recipe_selection import Recipe_Selection
2023-03-03 17:51:35 +00:00
from ui.barcode_recipe_selection import Barcode_Recipe_Selection
2022-06-01 16:37:19 +00:00
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
2023-01-05 11:16:04 +00:00
from ui.test_warning_img import Test_Warning_Img
2023-01-12 21:15:29 +00:00
from ui.test_instructions import Test_Instructions
2022-09-20 15:42:59 +00:00
from ui.test_fail import Test_Fail
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
2022-06-01 16:37:19 +00:00
from ui.widget import Widget
class Test(Widget):
2023-05-22 16:39:28 +00:00
def __init__(self, config, components=None,main_window=None):
2022-06-01 16:37:19 +00:00
super().__init__()
2023-05-22 16:39:28 +00:00
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("")
# SHOW AND UPDATE TIME CLOCK
self.refresh_time(init=True)
# INIT RECIPE
self.recipe = None
2023-03-25 14:53:25 +00:00
if self.config["hardware_config"]["barcode_recipe_selection"]=="present":
self.recipe_selection_mode = "barcode"
else:
self.recipe_selection_mode = "table"
2022-07-19 09:59:00 +00:00
self.step = None
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", },
2023-01-31 14:02:27 +00:00
"instruction":{"digital_io"},
2022-10-18 09:57:08 +00:00
"screws": {"screwdriver", "tecna_t3", },
"resistance": {"multicomp", },
2023-02-17 16:38:03 +00:00
"leak_1": {"tecna_t3", },
"leak_2": {"tecna_t3", },
2022-10-11 13:30:53 +00:00
"vision": {("uvc_camera", "galaxy_camera", ), "vision", "vision_saver", }, # "neo_pixels", },
2022-10-18 09:57:08 +00:00
"print": {"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)),
2022-10-18 09:57:08 +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)),
2023-01-05 17:57:44 +00:00
"warning_img": Test_Assembly(img_path=None, text=u"ATTENZIONE - PER QUESTO CODICE ESEGUIRE LE OPERAZIONI INDICATE IN FIGURA", widget=Test_Warning_Img(components=self.components, recipe=self.recipe, step=self.step,run_once=True)),
2022-10-18 09:57:08 +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),
"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)),
2023-03-30 10:57:49 +00:00
"leak_1": Test_Assembly(img_path=None, text=None, widget=Test_Leak(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces,parent=self)),
"leak_2": Test_Assembly(img_path=None, text=None, widget=Test_Leak(components=self.components, recipe=self.recipe, step=self.step, pieces=self.pieces,parent=self)),
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)),
2023-02-20 16:59:07 +00:00
"instruction": Test_Assembly(img_path=None, text=u"ESEGUIRE LE OPERAZIONI DI MONTAGGIO INDICATE IN FIGURA", widget=Test_Instructions(components=self.components, recipe=self.recipe,bench_name=self.config.machine_id, step=self.step)),
2023-07-26 19:29:14 +00:00
"piece_removal": Test_Assembly(img_path=None, text=u"RIMUOVERE IL PEZZO APRENDO TUTTE LE CHIUSURE", widget=Test_Instructions(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),
2022-10-18 09:57:08 +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)),
"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)),
2023-03-25 15:09:23 +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
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:
2023-07-06 10:59:06 +00:00
self.autotest_period = 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
2023-07-06 10:59:06 +00:00
#if not self.config["autotest_done"]:
# 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)
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
2022-08-23 14:00:04 +00:00
if isinstance(w.widget, (Recipe_Selection)):
2022-06-01 16:37:19 +00:00
w.ok.connect(self.set_recipe)
else:
2022-08-24 10:59:16 +00:00
w.ok.connect(lambda data=None, step_name=step_name, self=weakref.ref(self): self().set_step(step_name, 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)
2023-01-05 17:57:44 +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)
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):
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()
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}")
if reason in ("init","login"):
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"
self.autotest_request = reason
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:
self.log.debug(f"cycle next: cycle step: {model_to_dict(self.step)!r} action: {action!r}")
else:
self.log.debug(f"cycle next: cycle step: {self.step!r} action: {action!r}")
2022-07-25 09:16:14 +00:00
current_w = self.centralWidget
if 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)
2023-07-30 19:42:26 +00:00
self.cycle_available_steps["leak_1"].recipe_written=False
self.cycle_available_steps["leak_2"].recipe_written=False
2022-07-19 09:59:00 +00:00
self.step = Steps(type="select_recipe")
2022-06-28 10:31:27 +00:00
self.cycle_index = -1
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
2023-06-29 17:09:16 +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
2022-07-19 09:59:00 +00:00
self.step = Steps(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-03-03 17:51:35 +00:00
self.step = Steps(type="barcode_recipe_selection")
else:
self.step = Steps(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.autotesting = True
self.autotesting_reason = self.autotest_request
2022-07-12 08:48:04 +00:00
self.autotest_request = False
if self.autotest_period is not None: # reset periodic autotest timer
self.time_timer.start(self.autotest_period)
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))
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))
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
self.step = Steps(type=None)
2022-06-01 16:37:19 +00:00
# enable/disable cycle controls
self.change_recipe_b.setEnabled(self.recipe is not None)
2022-07-19 09:59:00 +00:00
self.cancel_b.setEnabled(self.step.type is not None and self.step.type not in {
2022-06-01 16:37:19 +00:00
"emergency",
"fail",
"select_recipe",
"wait",
})
2022-10-18 09:57:08 +00:00
self.log.info(f"cycle next: next cycle step: {model_to_dict(self.step)!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)
2022-07-19 09:59:00 +00:00
w = self.cycle_available_steps[self.step.type]
2022-10-18 09:57:08 +00:00
show = None
2023-07-25 13:44:45 +00:00
if self.step.type=="leak_2":
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)
2022-07-19 09:59:00 +00:00
if self.step.type == "done":
2022-07-26 10:24:53 +00:00
self.archived = self.done()
self.next_timer.start(2000)
elif self.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.label = compiled_label
self.archived.save()
2022-07-12 08:48:04 +00:00
self.next_timer.start(2000)
2022-07-19 09:59:00 +00:00
elif self.step.type == "wait":
2022-10-19 12:09:15 +00:00
self.next_timer.start(2000)
2022-06-01 16:37:19 +00:00
# UPDATE PIECES DISPLAY
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
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
2022-07-26 10:24:53 +00:00
for i, step in enumerate(steps):
2022-09-07 15:24:40 +00:00
if i in skip:
continue
2023-05-02 17:18:58 +00:00
if step.type == "vision":
self.components["vision"].config_changed(vision_recipe=self.recipe.name)
2022-10-18 09:57:08 +00:00
if step.type == "count":
count_found = True
2023-01-18 18:06:16 +00:00
if "warning_img" in step.spec:
if step.spec["warning_img"]:
steps.insert(i,Steps(type="warning_img", spec={"warning_img":step.spec["warning_img"]}))
skip.add(i + 1)
2023-01-26 13:13:03 +00:00
if "assembly" in step.spec:
if step.spec["assembly"]:
steps.insert(i, Steps(type="instructions", spec={"num_tape": step.spec["num_tape"],
"num_piece": step.spec["num_piece"],
"num_ring": step.spec["num_ring"]}))
2023-01-18 18:06:16 +00:00
skip.add(i + 1)
2022-09-07 15:24:40 +00:00
if step.type == "resistance":
steps.insert(i + 1, Steps(type="resistance", spec={
"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)
2022-07-26 10:24:53 +00:00
if step.type == "print":
steps.insert(i, Steps(type="done"))
print_found = True
self.print_step=step
2022-09-07 15:24:40 +00:00
skip.add(i + 1)
2022-10-18 09:57:08 +00:00
if count_found:
steps.insert(i + 2, Steps(type="count_end"))
2023-07-26 19:29:14 +00:00
2023-07-27 16:19:05 +00:00
if self.config["hardware_config"].get("enforce_piece_removal", "no") == "yes":
2023-07-26 19:29:14 +00:00
steps.append(Steps(type="piece_removal"))
2023-07-27 16:19:05 +00:00
2022-07-26 10:24:53 +00:00
if not print_found:
steps.append(Steps(type="done"))
2022-10-18 09:57:08 +00:00
if count_found:
steps.append(Steps(type="count_end"))
2022-07-26 10:24:53 +00:00
steps.append(Steps(type="wait"))
self.cycle_steps = steps
self.check_steps_dependencies(self.cycle_steps)
2023-02-26 18:09:46 +00:00
leak_autotest_steps=[]
2023-05-31 13:02:06 +00:00
# CONFIGURE LEAK AUTOTEST PARAMETERS
2023-02-26 18:09:46 +00:00
if self.config["autotest_leak"]["enabled"]=="true":
l_at_1=copy.deepcopy(self.config["autotest_leak"])
l_at_1.pop("enabled")
l_at_1={a: float(x) for a, x in l_at_1.items()}
2023-03-31 16:16:41 +00:00
l_at_1["autotest"]="ko_check"
2023-02-26 18:09:46 +00:00
l_at_2=copy.deepcopy(self.config["autotest_leak"])
l_at_2.pop("enabled")
l_at_2={a: float(x) for a, x in l_at_2.items()}
2023-06-10 09:22:26 +00:00
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"]
2023-03-31 16:16:41 +00:00
l_at_2["autotest"]="ok_check"
2023-02-26 18:09:46 +00:00
leak_autotest_steps=[Steps(type="leak_1",spec=l_at_1),Steps(type="leak_1",spec=l_at_2)]
2022-11-15 16:17:59 +00:00
self.autotest_cycle_steps = [
*([
Steps(type="resistance", spec={
"scale": 500,
"expected": 1,
2022-11-15 16:53:15 +00:00
"tolerance_pos": 5,
"tolerance_neg": 5,
2022-11-15 16:17:59 +00:00
"autotest": True,
}),
Steps(type="resistance", spec={
"scale": 500,
"expected": float("+inf"),
2022-11-15 16:53:15 +00:00
"tolerance_pos": 0,
"tolerance_neg": 0,
2022-11-15 16:17:59 +00:00
"autotest": True,
}),
Steps(type="resistance", spec={
"scale": 500,
2023-01-05 13:15:48 +00:00
"expected": 10,
"tolerance_pos": 1,
"tolerance_neg": 1,
2022-11-15 16:17:59 +00:00
"autotest": True,
}),
Steps(type="resistance", spec={
"scale": 500,
"expected": float("+inf"),
2022-11-15 16:53:15 +00:00
"tolerance_pos": 0,
"tolerance_neg": 0,
2022-11-15 16:17:59 +00:00
"autotest": True,
}),
] if "resistance" not in self.unsupported_steps else []),
2023-02-26 18:09:46 +00:00
*(leak_autotest_steps),
2022-11-15 16:17:59 +00:00
Steps(type="done"),
Steps(type="wait"),
]
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:
2022-10-18 09:57:08 +00:00
self.log.info(f"cycle recipe: cycle recipe: {model_to_dict(self.recipe)!r} cycle steps: {[model_to_dict(s) 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("")
self.next()
else:
2022-10-18 09:57:08 +00:00
self.log.info(f"cycle recipe: cycle 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:
if step.type in self.unsupported_steps or step.type not in self.cycle_available_steps:
unsupported_steps.add(step.type)
else:
for dependency in self.steps_dependencies.get(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]):
unsupported_steps.add(step.type)
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:
unsupported_steps.add(step.type)
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):
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):
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:
data["step"] = model_to_dict(self.step)
2023-05-18 14:12:40 +00:00
data["step"].pop("name",None)
# MAKE ARRAY ONLY IF MORE THAN ONE TEST OF SAME TYPE
if len(self.data[step_name])>1:
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...")
#remove useless info
2023-05-20 07:13:03 +00:00
self.data.get("recipe",{}).get("spec",{}).pop("steps",None)
self.data.get("recipe",{}).get("spec",{}).pop("available_steps",None)
2023-05-20 13:28:57 +00:00
for leak in ["leak_1","leak_2"]:
if leak in self.data.keys():
self.data[leak]["results"]={k:self.data[leak]["results"]["tecna_t3"][k] for k in ["Running test: filling pressure",
"Running test: measured leak",
"Running test: pressure at the end of settling",
"Running test: result"]
}
2022-08-23 14:00:04 +00:00
if "vision" in self.data:
2022-10-03 11:48:59 +00:00
vision_test_1 = self.data.get("vision", {}).get("0", {})
2022-08-23 14:00:04 +00:00
out_paths = self.components["vision_saver"].save(
save_time=vision_test_1.get("time", None),
frame=vision_test_1.get("frame", None),
# vision=vision_test_1.get("detections", None),
)
2022-10-03 11:48:59 +00:00
self.data.get("vision", {}).get("0", {})["files"] = out_paths
2022-08-23 14:00:04 +00:00
self.log.info(f"cycle vision saved locally: {out_paths!r}")
for vision in self.data.get("vision", {}).values():
vision.pop("frame", None)
vision.pop("render", None)
2023-05-02 17:18:58 +00:00
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")
2022-09-14 14:53:55 +00:00
if archived.label is not None:
2023-02-14 15:15:22 +00:00
raise AssertionError("this should never happen")
2022-09-14 14:53:55 +00:00
self.components["label_printer"].print_label(archived.label, context=None)
self.log.info("cycle printed already compiled label")
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
2023-06-10 09:22:26 +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)
2023-02-16 17:58:01 +00:00
printer_fields = self.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", "-")),
2022-09-14 14:53:55 +00:00
# STEP SPEC
"TPREFILL": self.labellify(leak_test_1_step_spec.get("pre_filling_time", "-")),
"PPREFILL": self.labellify(leak_test_1_step_spec.get("pre_filling_pressure", "-")),
"TFILL": self.labellify(leak_test_1_step_spec.get("filling_time", "-")),
"TSET": self.labellify(leak_test_1_step_spec.get("settling_time", "-")),
2023-02-17 11:26:18 +00:00
"TPREFILL2": self.labellify(leak_test_2_step_spec.get("pre_filling_time", "-")),
"PPREFILL2": self.labellify(leak_test_2_step_spec.get("pre_filling_pressure", "-")),
"TFILL2": self.labellify(leak_test_2_step_spec.get("filling_time", "-")),
"TSET2": self.labellify(leak_test_2_step_spec.get("settling_time", "-")),
2022-09-14 14:53:55 +00:00
"PSETMINP": self.labellify(leak_test_1_step_spec.get("settling_pressure_min_percent", " -")),
"PSETMAXP": self.labellify(leak_test_1_step_spec.get("settling_pressure_max_percent", " -")),
2023-02-16 12:16:05 +00:00
"PSETMINP2": self.labellify(leak_test_2_step_spec.get("settling_pressure_min_percent", " -")),
"PSETMAXP2": self.labellify(leak_test_2_step_spec.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),
2022-09-14 14:53:55 +00:00
"TTEST": self.labellify(leak_test_1_step_spec.get("test_time", "-")),
2023-02-17 11:26:18 +00:00
"TTEST2": self.labellify(leak_test_2_step_spec.get("test_time", "-")),
2023-06-10 09:22:26 +00:00
"PMIN": self.labellify(leak_test_1_step_spec.get("test_pressure_qneg", "-")),
"PMIN2": self.labellify(leak_test_2_step_spec.get("test_pressure_qneg", "-")),
2022-09-14 14:53:55 +00:00
"PTEST": self.labellify(leak_test_1_step_spec.get("test_pressure", "-")),
2023-02-17 11:26:18 +00:00
"PTEST2": self.labellify(leak_test_2_step_spec.get("test_pressure", "-")),
2023-06-10 09:22:26 +00:00
"PMAX": self.labellify(leak_test_1_step_spec.get("test_pressure_qpos", "-")),
2022-09-14 14:53:55 +00:00
"TFLUSH": self.labellify(leak_test_1_step_spec.get("flush_time", "-")),
"PFLUSH": self.labellify(leak_test_1_step_spec.get("flush_pressure", "-")),
# 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", "-")),
"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}",
# 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"),
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),
2022-09-14 14:53:55 +00:00
# RESULT
"RESULT": str("CONFORME" if leak_test_1_results.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 ""),
2022-08-02 16:15:30 +00:00
"RESULT_L2": str("CONFORME" if leak_test_1_results.get("ok", False) else "SCARTO"),
2022-07-26 10:24:53 +00:00
}
2023-02-14 15:15:22 +00:00
for n in range(5):
2023-02-14 16:03:17 +00:00
field=f"labeltxt_{n+1}"
2023-02-14 15:15:22 +00:00
if field in printer_fields.keys():
2023-02-14 16:03:17 +00:00
if printer_fields[field] !="":
context[field.upper()]=printer_fields[field]
2023-02-07 16:37:09 +00:00
# PRINT MAIN PRODUCT LABEL
2022-09-14 14:53:55 +00:00
compiled_label = self.components["label_printer"].print_label(label, context=context)
self.log.info(f"Main label printed: {context!r}")
2022-09-14 14:53:55 +00:00
return compiled_label
2023-03-30 10:57:49 +00:00
def print_extra_labels(self):
# PRINT EXTRA LABELS IF NEEDED (BEFORE LEAK TEST)
if "extra_label_printer" in self.components.keys() and self.print_step is not None:
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)
2023-06-23 08:20:26 +00:00
def load_recipe_from_rfid(self,data):
if self.step.type == "barcode_recipe_selection":
2023-06-26 17:21:12 +00:00
if data is not None:
self.cycle_available_steps["barcode_recipe_selection"].widget.get(data)
else:
# fixture removed
2023-07-27 16:19:05 +00:00
self.fail_cycle()
2023-07-27 10:02:37 +00:00
self.change_recipe()
2023-06-26 17:21:12 +00:00
else:
if data is not None:
self.set_recipe_mode_barcode()