test leak

This commit is contained in:
matteo porta 2022-07-12 10:48:04 +02:00
parent 02cf4797eb
commit 817d62625e
21 changed files with 1061 additions and 582 deletions

View File

@ -31,7 +31,7 @@ platform: cups
printer:
[tecna_marposs_provaset_t3]
[tecna_t3]
address: COM3
baudrate: 115200

View File

@ -18,6 +18,7 @@ python -B -u "./src/main.py" \
--no-autotest \
--no-edgetpu \
--no-gpu \
--panel \
--sim-camera \
--sim-modbus \
--sim-os-label-printer \

View File

@ -105,22 +105,45 @@ class TecnaMarpossProvasetT3(ModbusComponent):
]}
if info["Running test: type of test"] is None:
info.update(self.get_test_results())
self.start_test()
self.log.debug(str(info))
super()._get([info])
def start_test(self):
self.log.info("starting test")
self.write("Start testing", 1)
def start_test(self, table=1):
self.log.info(f"starting test table {table!r}")
self.write("Start testing", table)
def stop_test(self):
self.log.warining("stopping test")
self.log.warning("stopping test")
self.write("Stop the test in progress", 0)
def get_test_results(self):
self.log.info("getting test results")
return {r: self.read(r) for r in [
"Running test: measured leak",
"Running test: calculated leak flow rate",
"Running test: calculate RVP%",
"Running test: result",
]}
def write_recipe(self, recipe, table=1):
# "pressure_ramp": self.pressure_ramp_sb,
# "stabilization_cycles": self.stabilization_cycles_sb,
recipe = {
"Type of test": "LEAK TEST",
# "Test flags": recipe.spec[""],
"T0 - Pre-filling time": recipe.spec["pre_filling_time"],
"P0 - Pre-filling pressure": recipe.spec["pre_filling_pressure"],
"T1 - Filling time": recipe.spec["filling_time"],
"T2 - Settling time": recipe.spec["settling_time"],
"PR%- Lower tolerance on pressure / P- Minimum pressure": recipe.spec["settling_pressure_min_percent"],
"PR%+ Superior tolerance on pressure / P+ Pressure max": recipe.spec["settling_pressure_max_percent"],
"T3 - Measure time": recipe.spec["test_time"],
"Q- Leak limit": recipe.spec["test_pressure_min_delta"],
"PREL - Nominal test pressure": recipe.spec["test_pressure"],
"Q+ Upper limit": recipe.spec["test_pressure_max_delta"],
"FST - discharge time": recipe.spec["flush_time"],
"FSL - discharge limit": recipe.spec["flush_pressure"],
}
self.log.debug(str(recipe))
for register, value in recipe.items():
self.write(register, value)

View File

@ -56,7 +56,7 @@ registers = {
4: "not used",
5: "BLOCKAGE TEST PASSED",
100: "LEAK TEST FAILED - UPPER LIMIT",
101: "LEAK TEST FAILED ANOMALY",
101: "LEAK TEST - FAILED ANOMALY",
102: "LEAK TEST - MAXIMUM LEAK FAILED",
103: "BURST - BREAKAGE PRESSURE DEFLECTION",
104: "VOLUMETRIC CONTROL - RVP% FAILED",
@ -65,14 +65,14 @@ registers = {
107: "BLOCKAGE - MAX PRESSURE FAILED",
108: "BLOCKAGE - MIN PRESSURE FAILED",
109: "BURST - MINIMUM PRESSURE FAILED",
200: "LEAK TEST FAILED PR% PRESSURE MINUM",
200: "LEAK TEST FAILED - PR% PRESSURE MINUM",
201: "LEAK TEST FAILED - PR% PRESSURE MAX",
202: "LEAK TEST FAILED P0% PRESSURE MINUM",
202: "LEAK TEST FAILED - P0% PRESSURE MINUM",
203: "LEAK TEST FAILED - P0% PRESSURE MAX",
204: "ERROR - INTERNAL ALARMS",
205: "ERROR - RELATIVE PRESSURE OUT OF RANGE",
206: "ERROR - DIFFERENTIAL PRESSURE OUT OF RANGE",
207: "ERROR PRE-FILLING VALVE NOT OPENED",
207: "ERROR - PRE-FILLING VALVE NOT OPENED",
250: "TEST ABORTED",
}, }],
"Running test: type of test": [47, {"dt": "16bit_uint", "decoding": {
@ -130,13 +130,20 @@ registers = {
# 0= Only passed
# 1= Only failed
# 2=All
"AUTOMATION: Marking driving time": [624, {"dt": "16bit_uint", }],
"AUTOMATION: Marking - driving time": [624, {"dt": "16bit_uint", }],
# Format: x.x seconds
"Type of test": [701, {"dt": "16bit_uint", }],
# 1=LEAK TEST
# 2=BLOCKAGE TEST
# 3=LEAK TEST WITH VOLUME
# 4=BURST TEST
"Selection number of test table on which you intend to work": [700, {"dt": "16bit_uint", }],
"Type of test": [701, {"dt": "16bit_uint", "encoding": {
"LEAK TEST": 1,
"BLOCKAGE TEST": 2,
"LEAK TEST WITH VOLUME": 3,
"BURST TEST": 4,
}, "decoding": {
1: "LEAK TEST",
2: "BLOCKAGE TEST",
3: "LEAK TEST WITH VOLUME",
4: "BURST TEST",
}, }],
"Test flags": [702, {"dt": "16bit_uint", }],
# T0/Pr: Phase filling mode T0 0= TIME 1= PRESSURE
# T1/Pr: Phase filling mode T1 0= TIME 1=PRESSURE
@ -149,39 +156,39 @@ registers = {
# AT: 0=Tare pressure disabled 1=Tare pressure enabled
# HR: 0=Resolution on loss 1 Pa 1=Resolution on loss 0.1 Pa (models with full scale <= 2 bar)
# CH: Selected test channel (2-channel T3P2C model only)
"T0 Pre-filling time": [704, {"dt": "16bit_uint", "f": 26, }],
"P0 Pre-filling pressure": [705, {"dt": "16bit_uint", "f": 23, }],
"T0 - Pre-filling time": [704, {"dt": "16bit_uint", "f": 26, }],
"P0 - Pre-filling pressure": [705, {"dt": "16bit_uint", "f": 23, }],
# In order to use a negative (vacuum) value, this parameter must however be written positively, but the P0- bit must
# also be selected in register 702
"T1 Filling time": [706, {"dt": "16bit_uint", "f": 26, }],
"T2 Settling time": [707, {"dt": "16bit_uint", "f": 26, }],
"T3 Measure time": [708, {"dt": "16bit_uint", "f": 26, }],
"PREL Nominal test pressure": [709, {"dt": "16bit_uint", "f": 23, }],
"T1 - Filling time": [706, {"dt": "16bit_uint", "f": 26, }],
"T2 - Settling time": [707, {"dt": "16bit_uint", "f": 26, }],
"T3 - Measure time": [708, {"dt": "16bit_uint", "f": 26, }],
"PREL - Nominal test pressure": [709, {"dt": "16bit_uint", "f": 23, }],
# To set a negative pressure (vacuum) this parameters must be written in absolute value and then set
# bit Pr- in register 702
"Pxx Minimum Pressure": [710, {"dt": "16bit_uint", }],
"PR%- Lower tolerance on pressure / P- Minimum pressure": [710, {"dt": "16bit_uint", }],
# PR%- Lower tolerance on pressure: LEAK: Format: x.x %
# P- Minimum pressure: BLOCKAGE: Format as indicated in register 23
# PR- Minimum pressure%: BURST: Format x.x%
"Q + Upper limit": [711, {"dt": "16bit_uint", "f": 22, }],
"Q+ Upper limit": [711, {"dt": "16bit_uint", "f": 22, }],
# Format as indicated in the register 22
"Q- Leak limit": [712, {"dt": "16bit_uint", "f": 22, }],
# Format as indicated in the register 22
"FST discharge time": [713, {"dt": "16bit_uint", "f": 26, }],
"FST - discharge time": [713, {"dt": "16bit_uint", "f": 26, }],
# Format as indicated in the register 26
"VP Equivalent volume": [714, {"dt": "16bit_uint", "f": 25, }],
"VP - Equivalent volume": [714, {"dt": "16bit_uint", "f": 25, }],
# Format as indicated in the register 25
"P% Pressure tolerance": [717, {"dt": "16bit_uint", }],
# BLOCKAGE: Format x.x %
"PB Minimum burst pressure": [743, {"dt": "16bit_uint", "f": 23, }],
"PB - Minimum burst pressure": [743, {"dt": "16bit_uint", "f": 23, }],
# BURST: Format as indicated in the register 23
"BD Delta burst": [744, {"dt": "16bit_uint", "f": 23, }],
"BD - Delta burst": [744, {"dt": "16bit_uint", "f": 23, }],
# BURST: Format as indicated in the register 23
"FSL discharge limit": [745, {"dt": "16bit_uint", "f": 23, }],
"FSL - discharge limit": [745, {"dt": "16bit_uint", "f": 23, }],
# Format as indicated in the register 23
"PR% + Superior tolerance on pressure": [747, {"dt": "16bit_uint", }],
# LEAK: Format: x.x %
# P + Pressure max BURST: Format as indicated in the register 23
"PR%+ Superior tolerance on pressure / P+ Pressure max": [747, {"dt": "16bit_uint", }],
# PR% + Superior tolerance on pressure: LEAK: Format: x.x %
# P + Pressure max: BURST: Format as indicated in the register 23
"RVP%: volumetric ratio": [750, {"dt": "16bit_uint", }],
# VOLUME+LEAK: Format x.xx (from 0.00 to 649.99)
"RVP%: max tolerance": [751, {"dt": "16bit_uint", }],

View File

@ -64,24 +64,26 @@ Recipes.replace(id=0, name="TEST", spec={
"client": "TEST_CLIENT",
"part_number": "TEST_PART_NUMBER",
"station": "TEST_STATION",
# pressure
"pressure_min": 0,
"pressure_test": 1,
"pressure_max": 2,
"pressure_ramp": 3,
# test
"cleaning_time": 4,
"tolerance": 5,
"test_duration": 6,
"flush_duration": 7,
# stabilizarion
"stabilization_time": 8,
"stabilization_level_min": 9,
"stabilization_level_max": 10,
"stabilization_settling_time": 11,
"stabilization_cycles": 12,
# description
"description": "TEST_DESCRIPTION",
# pre-filling
"pre_filling_time": 5,
"pre_filling_pressure": 3000,
# filling
"filling_time": 5,
"settling_time": 10,
"settling_pressure_min_percent": 20,
"settling_pressure_max_percent": 20,
# test
"test_time": 20,
"test_pressure_min_delta": 3.00,
"test_pressure": 3000,
"test_pressure_max_delta": 0.25,
"cycles": 1,
# flush
"flush_time": 2,
"flush_pressure": 5,
# vision
"vision_recipe": 0,
}, archived=False).execute()
if True:

View File

@ -84,7 +84,7 @@ try:
"label_printer": {"c": Os_Label_Printer, "t": False},
"neo_pixels": {"c": NeoPixels, "t": False},
"remote_api": {"c": RemoteAPI, "k": {"main": self}},
"tecna_marposs_provaset_t3": {"c": TecnaMarpossProvasetT3},
"tecna_t3": {"c": TecnaMarpossProvasetT3, "k": {"paused": True}},
"vision_saver": {"c": VisionSaver, "t": False},
"vision": {"c": Vision, "k": {"paused": True}},
}

View File

@ -14,6 +14,8 @@ from .test import Test
from .test_admin_permission import Test_Admin_Permission
from .test_autotest import Test_Autotest
from .test_home import Test_Home
from .test_leak import Test_Leak
from .test_test import Test_Test
from .test_vision import Test_Vision
from .users_management import Users_Management
from .widget import Widget

View File

@ -12,8 +12,8 @@ dialogs = {}
class Dialog(QDialog):
_closing = pyqtSignal()
def __init__(self):
super().__init__()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
global dialogs
dialogs[id(self)] = self
self.setAttribute(Qt.WA_DeleteOnClose)

View File

@ -1,5 +1,5 @@
from PyQt5.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit,
QRadioButton, QSpinBox, QPlainTextEdit)
QPlainTextEdit, QRadioButton, QSpinBox)
from ui.widget import Widget
@ -11,26 +11,26 @@ class Recipe_Editor(Widget):
"client": self.client_le,
"part_number": self.part_number_le,
"station": self.station_le,
# pressure
"pressure_min": self.pressure_min_sb,
"pressure_test": self.pressure_test_sb,
"pressure_max": self.pressure_max_sb,
"pressure_ramp": self.pressure_ramp_sb,
"description": self.description_pte,
# pre-filling
"pre_filling_time": self.pre_filling_time_sb,
"pre_filling_pressure": self.pre_filling_pressure_sb,
# filling
"filling_time": self.filling_time_sb,
"settling_time": self.settling_time_sb,
"settling_pressure_min_percent": self.settling_pressure_min_percent_sb,
"settling_pressure_max_percent": self.settling_pressure_max_percent_sb,
# test
"cleaning_time": self.cleaning_time_sb,
"tolerance": self.tolerance_sb,
"test_duration": self.test_duration_sb,
"flush_duration": self.flush_duration_sb,
"test_time": self.test_time_sb,
"test_pressure_min_delta": self.test_pressure_min_delta_sb,
"test_pressure": self.test_pressure_sb,
"test_pressure_max_delta": self.test_pressure_max_delta_sb,
"cycles": self.cycles_sb,
# flush
"flush_time": self.flush_time_sb,
"flush_pressure": self.flush_pressure_sb,
# vision
"vision_recipe": self.vision_recipe_cb,
# stabilizarion
"stabilization_time": self.stabilization_time_sb,
"stabilization_level_min": self.stabilization_level_min_sb,
"stabilization_level_max": self.stabilization_level_max_sb,
"stabilization_settling_time": self.stabilization_settling_time_sb,
"stabilization_cycles": self.stabilization_cycles_sb,
# description
"description": self.description_pte,
}
def set_readonly(self, readonly):

View File

@ -6,66 +6,42 @@
<rect>
<x>0</x>
<y>0</y>
<width>494</width>
<height>733</height>
<width>845</width>
<height>696</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="1" colspan="2">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Descrizione</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="description_pte"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Test</string>
<string>Pre-Riempimento</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="4" column="1">
<widget class="QSpinBox" name="test_duration_sb"/>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="tolerance_sb"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Tempo di prova</string>
<widget class="QSpinBox" name="pre_filling_pressure_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Tempo di scarico</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="flush_duration_sb"/>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="cleaning_time_sb"/>
<widget class="QSpinBox" name="pre_filling_time_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Tempo di pulizia</string>
<string>Tempo</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_19">
<property name="text">
<string>bar</string>
<string>mbar</string>
</property>
</widget>
</item>
@ -76,31 +52,290 @@
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_20">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_21">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Tolleranza</string>
<string>Pressione</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<item row="2" column="2">
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>Riempimento</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Soglia min</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Tempo</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_20">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="settling_pressure_min_percent_sb">
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="filling_time_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Soglia max</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="settling_pressure_max_percent_sb">
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_26">
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_27">
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Assestamento</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="settling_time_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_25">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="3">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Test</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="6" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Cicli</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="test_pressure_min_delta_sb">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_28">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QSpinBox" name="test_pressure_max_delta_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSpinBox" name="test_time_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="label_23">
<property name="text">
<string>mbar</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="label_22">
<property name="text">
<string>mbar</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Delta min</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Delta max</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Tempo</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QLabel" name="label_24">
<property name="text">
<string>mbar</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QSpinBox" name="test_pressure_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Pressione</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QSpinBox" name="cycles_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="4">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Scarico</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tempo</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Pressione</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="flush_pressure_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="flush_time_sb">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_18">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_31">
<property name="text">
<string>mbar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1" colspan="4">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Visione</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Ricetta visione</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="vision_recipe_cb"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Ricetta</string>
@ -120,12 +355,6 @@
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="station_le"/>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="client_le"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
@ -133,191 +362,26 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="part_number_le"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Stabilizzazione</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Soglia max</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Soglia min</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="stabilization_cycles_sb"/>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="stabilization_time_sb"/>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="stabilization_settling_time_sb"/>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="stabilization_level_max_sb"/>
<widget class="QLineEdit" name="station_le"/>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="stabilization_level_min_sb"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Tempo assestamento</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Numero di cicli</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Tempo</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_25">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_26">
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_27">
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_28">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Pressione</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="4" column="2">
<widget class="QSpinBox" name="pressure_max_sb"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Min</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Max</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QSpinBox" name="pressure_min_sb"/>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_22">
<property name="text">
<string>bar</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="pressure_test_sb"/>
</item>
<item row="5" column="2">
<widget class="QSpinBox" name="pressure_ramp_sb"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="label_24">
<property name="text">
<string>bar</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Rampa di salita</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="label_23">
<property name="text">
<string>bar</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QLabel" name="label_18">
<property name="text">
<string>bar/min</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Visione</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Ricetta visione</string>
</property>
</widget>
<widget class="QLineEdit" name="part_number_le"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="vision_recipe_cb"/>
<widget class="QLineEdit" name="client_le"/>
</item>
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Descrizione</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="description_pte"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>

View File

@ -9,6 +9,7 @@ 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
from ui.test_leak import Test_Leak
from ui.test_vision import Test_Vision
from ui.widget import Widget
@ -39,13 +40,13 @@ class Test(Widget):
"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\""),
"fail": Test_Assembly(self.select_step_img("fail"), u"CICLO INTERROTTO - RIMUOVERE IL SENSORE"),
"run_test": Test_Assembly(self.select_step_img("wait"), u"ESECUZIONE TEST IN CORSO - ATTENDERE", Test_Leak(components=self.components, recipe=self.recipe)),
"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)),
"vision": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Vision(components=self.components, recipe=self.recipe)),
"wait": Test_Assembly(self.select_step_img("wait"), u"ATTENDERE - PAUSA INTER CICLO"),
}
self.cycle_loop = ["vision", "done", "wait"]
self.cycle_loop = ["run_test", "vision", "done", "wait"]
self.cycle_index = -1
self.cycle_changing_state = False
# SETUP AUTOTEST
self.autotest_request = False
if "--no-autotest" not in sys.argv:
@ -54,23 +55,25 @@ class Test(Widget):
else:
self.autotest_period = None
# INIT TEST DATA
self.data = None
self.data = {}
# 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)
elif type(w.widget) is Test_Leak:
w.ok.connect(self.set_leak)
elif type(w.widget) is Test_Vision:
w.ok.connect(self.set_vision)
else:
w.ok.connect(self.next)
if hasattr(w, "ko"):
w.ko.connect(self.fail_cycle)
# TESTING
if "--test" in sys.argv:
self.testing = True
@ -78,6 +81,9 @@ class Test(Widget):
self.testing = False
# /TESTING
# START CYCLE
self.next_timer = QTimer()
self.next_timer.setSingleShot(True)
self.next_timer.timeout.connect(self.next)
self.next()
def refresh_time(self, init=False):
@ -113,90 +119,6 @@ class Test(Widget):
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":
@ -219,11 +141,10 @@ class Test(Widget):
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"
self.cycle_index = -1
# RESET TEST DATA
self.data = None
self.data.clear()
elif action == "fail":
self.log.info(f"cycle next: action: {action!r}")
if self.cycle_state in self.cycle_loop:
@ -231,32 +152,29 @@ class Test(Widget):
# COUNT FAIL
self.pieces[1] += 1
# FAIL AND RESTART TEST
self.cycle_changing_state = True
self.cycle_state = "fail"
self.cycle_index = -1
# RESET TEST DATA
self.data = None
self.data.clear()
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"
if self.recipe is None:
# if recipe not set: select_recipe
self.cycle_state = "select_recipe"
else:
if self.cycle_index == -1 and self.autotest_request is not False:
# 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:
if self.cycle_index == -1 and self.autotest_request is not False:
# 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
self.cycle_index = (self.cycle_index + 1) % len(self.cycle_loop)
self.cycle_state = self.cycle_loop[self.cycle_index]
# goto next step in cycle_loop
self.cycle_index = (self.cycle_index + 1) % len(self.cycle_loop)
self.cycle_state = self.cycle_loop[self.cycle_index]
# enable/disable cycle controls
self.change_recipe_b.setEnabled(self.recipe is not None)
self.cancel_b.setEnabled(self.cycle_state not in {
@ -268,16 +186,18 @@ class Test(Widget):
self.log.info(f"cycle next: next cycle_state: {self.cycle_state!r}")
# INIT TEST DATA IF STARTING CYCLE LOOP
if self.cycle_index == 0:
self.data = {}
if self.cycle_state == "done":
self.done()
self.data.clear()
w = self.cycle_states[self.cycle_state]
if hasattr(w, "start"):
w.start()
w.start(recipe=self.recipe)
self.setCentralWidget(w)
if self.cycle_state == "done":
self.done()
self.next_timer.start(2000)
elif self.cycle_state == "wait":
self.next_timer.start(6000)
# 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
@ -296,7 +216,13 @@ class Test(Widget):
self.data["ok"] = self.data.get("ok", True) and self.data["vision"].get("ok", False)
self.next()
def done(self, ok=False):
def set_leak(self, leak=None):
self.data["leak"] = leak
self.data["overridden"] = self.data.get("overridden", False) or self.data["leak"].get("overridden", False)
self.data["ok"] = self.data.get("ok", True) and self.data["leak"].get("ok", False)
self.next()
def done(self, ok=False, next=False):
self.log.info("cycle done")
archived = Archive.archive(self.recipe, self.data, ok and self.data["ok"], overridden=self.data["overridden"])
self.log.info(f"cycle archived locally: {archived!r}")

View File

@ -0,0 +1 @@
from .test_leak import Test_Leak

View File

@ -0,0 +1,55 @@
from ui.test_test import Test_Test
class Test_Leak(Test_Test):
def start(self, recipe=None):
super().start(recipe=recipe)
# setup test loop
self.components["tecna_t3"].write_recipe(self.recipe)
self.get_connection = self.components["tecna_t3"].out.connect(self.get)
self.components["tecna_t3"].resume()
self.components["tecna_t3"].start_test()
def stop(self):
# disable tes loop
self.components["tecna_t3"].stop_test()
self.components["tecna_t3"].pause()
self.disconnect(self.get_connection)
super().stop()
def get(self, data=None, override=False):
if self.ok_timer.isActive():
# avoid proccessing if vision was completed
return
if data is None or data[-1] is None:
super().get(None, override=override)
return
data = data[-1]
super().get([{
"time": data.get("time", None),
"results": {
"ok": type(data["tecna_t3"]["Running test: result"]) is str and "passed" in data["tecna_t3"]["Running test: result"].lower(),
"result": data["tecna_t3"]["Running test: result"],
"data": data["tecna_t3"],
},
}], override=override)
def visualize(self, data=None):
if data is None:
data = {}
d = data.get("results", {}).get("data", {})
for register, label in {
"Instrument status: active phase": self.test_phase_l,
"Test circuit pressure, in real time": self.circuit_pressure_l,
"Measured leak, in real time": self.leak_l,
"Regulated pressure, in real time": self.regulated_pressure_l,
# "Active alarm flags": self._l,
"Running test: type of test": self.test_type_l,
"Testing in progress: progressive sequence index": self.sequence_index_l,
}.items():
label.setText(str(d.get(register, "-")))
super().visualize(data)
def save_last(self):
if self.last is None:
return

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Leak</class>
<widget class="QWidget" name="Leak">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>322</width>
<height>329</height>
</rect>
</property>
<property name="windowTitle">
<string>Leak</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="9" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Test sequence index:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Regulated pressure:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Status:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Leak:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="leak_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="sequence_index_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="circuit_pressure_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Circuit pressure:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="regulated_pressure_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="instrument_status_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QPushButton" name="save_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SALVA IMMAGINE</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Instrument status:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Type of test:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Risultato</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QProgressBar" name="ok_counter_pb">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="format">
<string>%v / %m</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="state_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="test_type_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QPushButton" name="override_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FORZA ACCETTAZIONE</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="img_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Test phase</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="test_phase_l">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1 @@
from .test_test import Test_Test

View File

@ -0,0 +1,167 @@
import sys
from lib.helpers import timing
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QColor, QPalette, QPixmap
from ui import Dialog
from ui.test_admin_permission import Test_Admin_Permission
from ui.widget import Widget
class Test_Test(Widget):
ok = pyqtSignal(dict)
ko = pyqtSignal(dict)
def __init__(self, components=None, recipe=None):
super().__init__()
self.components = components
self.recipe = recipe
# setup variables
self.ok_counter = 0
self.ok_counter_limit = 1
# setup controls
self.ok_timer = QTimer()
self.ok_timer.setSingleShot(True)
self.ok_timer.setInterval(2000)
self.ok_timer.timeout.connect(self.emit_ok)
# setup save frame button
self.last = None
self.save_b.setEnabled(False)
self.save_b.clicked.connect(self.save_last)
# setup override
self.admin_challenged = False
self.override_b.clicked.connect(self.override)
self.override_b.setEnabled(True)
# setup vision staus gui
self.status_imgs_full = {
True: QPixmap("src/ui/imgs/success.png"),
"": QPixmap("src/ui/imgs/neo.ico"),
"warning": QPixmap("src/ui/imgs/warning.png"),
False: QPixmap("src/ui/imgs/fail.png"),
None: QPixmap("src/ui/imgs/wait.png"),
}
self.status_imgs_small = {k: i.scaled(32, 32, Qt.KeepAspectRatio, Qt.SmoothTransformation) for k, i in self.status_imgs_full.items()}
self.status_palettes = {
True: QPalette(),
"": QPalette(),
"warning": QPalette(),
False: QPalette(),
None: QPalette(),
}
self.status_palettes[True].setColor(QPalette.Base, Qt.green)
self.status_palettes[False].setColor(QPalette.Base, Qt.red)
self.status_palettes["warning"].setColor(QPalette.Base, QColor(255, 165, 0))
self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0))
self.visualize()
def start(self, recipe=None):
self.recipe = recipe
self.visualize()
# start test
self.start_time = timing()
# TESTING
if "--test" in sys.argv:
if hasattr(self, "_test_overridden_test_"):
del self._test_overridden_test_
# /TESTING
def stop(self):
pass
def get(self, data=None, override=False):
if self.ok_timer.isActive():
# avoid proccessing if test was completed
return
if data is None:
if self.last is not None:
data = self.last
else:
data = {}
else:
data = data[-1]
if not override:
result_ok = data.get("results", {}).get("ok", False)
else:
result_ok = True
self.last = {
**data,
"overridden": override,
"duration": timing() - self.start_time,
"ok": result_ok,
}
if not override:
if result_ok is True:
self.ok_counter += 1
else:
self.ok_counter = 0
else:
self.ok_counter = self.ok_counter_limit
# check if completed
if self.ok_counter >= self.ok_counter_limit:
self.stop()
self.ok_timer.start()
self.visualize(self.last)
# TESTING
if "--test" in sys.argv:
if not hasattr(self, "_test_overridden_test_"):
self._test_overridden_test_ = True
self.override_b.click()
# /TESTING
def visualize(self, data=None, img=None):
if data is None:
data = {}
overridden = data.get("overridden", False)
results = data.get("results", None)
self.save_b.setEnabled(self.last is not None)
if overridden:
self.state_l.setPixmap(self.status_imgs_small["warning"])
elif results is None:
self.state_l.setPixmap(self.status_imgs_small[None])
elif results.get("ok", False) is True:
self.state_l.setPixmap(self.status_imgs_small[True])
else:
self.state_l.setPixmap(self.status_imgs_small[False])
self.ok_counter_pb.setMaximum(self.ok_counter_limit)
self.ok_counter_pb.setValue(min(self.ok_counter, self.ok_counter_limit))
if self.ok_counter >= self.ok_counter_limit:
self.ok_counter_pb.setPalette(self.status_palettes[True])
else:
self.ok_counter_pb.setPalette(self.status_palettes[False])
if img is None:
if overridden:
self.img = self.status_imgs_full["warning"]
else:
self.img = self.status_imgs_full[None]
else:
self.img = img
self.resizeEvent()
def save_last(self):
if self.last is None:
return
def resizeEvent(self, event=None):
self.img_l.setPixmap(self.img.scaled(self.img_l.width(), self.img_l.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
def challenge_admin(self, info):
if not self.admin_challenged:
d = Dialog(parent=self)
d.setCentralWidget(Test_Admin_Permission(info))
d.setModal(True)
d.show()
r = d.exec()
if r == d.Accepted:
self.admin_challenged = True
elif r == d.Rejected:
self.admin_challenged ^= False
else:
raise AssertionError("Bad admin challenge dialog return code")
return self.admin_challenged
def override(self):
if self.challenge_admin("Si sta tentando di bypassare il test"):
self.get(override=True)
def emit_ok(self):
self.ok.emit(self.last)

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Test</class>
<widget class="QWidget" name="Test">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>835</height>
</rect>
</property>
<property name="windowTitle">
<string>Test</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QPushButton" name="override_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FORZA ACCETTAZIONE</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Risultato</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QProgressBar" name="ok_counter_pb">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>50</number>
</property>
<property name="format">
<string>%v / %m</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="state_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="img_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>600</width>
<height>700</height>
</size>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="save_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SALVA IMMAGINE</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,158 +1,68 @@
import sys
from lib.helpers import timing
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QColor, QImage, QPalette, QPixmap
from ui import Dialog
from ui.test_admin_permission import Test_Admin_Permission
from ui.widget import Widget
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from ui.test_test import Test_Test
class Test_Vision(Widget):
ok = pyqtSignal(dict)
class Test_Vision(Test_Test):
request_frame = pyqtSignal()
def __init__(self, components, recipe):
super().__init__()
self.components = components
self.recipe = recipe
# setup vision variables
self.vision = {}
self.vision_ok_counter = 0
def __init__(self, components=None, recipe=None):
if "--sim-camera" not in sys.argv:
self.vision_ok_counter_limit = 2
self.ok_counter_limit = 2
else:
self.vision_ok_counter_limit = 1
# setup vision controls
self.ok_timer = QTimer()
self.ok_timer.setSingleShot(True)
self.ok_timer.setInterval(2000)
self.ok_timer.timeout.connect(self.emit_ok)
# setup save frame button
self.last_vision = None
self.save_frame_b.setEnabled(False)
self.save_frame_b.clicked.connect(self.save_last_vision)
# setup vision override
self.admin_challenged = False
self.vision_overridden = False
self.override_b.clicked.connect(self.override_vision)
self.override_b.setEnabled(True)
# setup vision staus gui
self.status_imgs_full = {
True: QPixmap("src/ui/imgs/success.png"),
"": QPixmap("src/ui/imgs/neo.ico"),
"warning": QPixmap("src/ui/imgs/warning.png"),
False: QPixmap("src/ui/imgs/fail.png"),
None: QPixmap("src/ui/imgs/wait.png"),
}
self.status_imgs_small = {k: i.scaled(32, 32, Qt.KeepAspectRatio, Qt.SmoothTransformation) for k, i in self.status_imgs_full.items()}
self.status_palettes = {
True: QPalette(),
"": QPalette(),
"warning": QPalette(),
False: QPalette(),
None: QPalette(),
}
self.status_palettes[True].setColor(QPalette.Base, Qt.green)
self.status_palettes[False].setColor(QPalette.Base, Qt.red)
self.status_palettes["warning"].setColor(QPalette.Base, QColor(255, 165, 0))
self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0))
self.visualize_vision()
self.ok_counter_limit = 1
super().__init__(components, recipe)
def start(self):
self.visualize_vision()
# TESTING
if "--test" in sys.argv:
self.override_b.click()
# /TESTING
def start(self, recipe=None):
super().start(recipe=recipe)
# setup camera-vision loop
self.components["galaxy_camera"].set_period(period=None) # only get frame on request
self.components["galaxy_camera"].add_sources({"test_vision": self.request_frame})
self.request_frame_connection = self.components["vision"].out.connect(self.request_frame) # request new frame as soon as vision finishes
self.process_vision_connection = self.components["vision"].out.connect(self.process_vision)
self.get_connection = self.components["vision"].out.connect(self.get)
self.components["vision"].resume()
self.components["galaxy_camera"].resume()
# start test
self.start_time = timing()
self.request_frame.emit() # request first frame
def stop(self):
# disable camera-vision loop
self.components["galaxy_camera"].pause()
self.components["vision"].pause()
self.disconnect(self.process_vision_connection)
self.disconnect(self.get_connection)
self.disconnect(self.request_frame_connection)
self.components["galaxy_camera"].remove_sources(["test_vision", ])
super().stop()
def process_vision(self, data=None, override=False):
def get(self, data=None, override=False):
if self.ok_timer.isActive():
# avoid proccessing if vision was completed
return
if data is None:
data = [{"vision": {}}]
if data is None or data[-1] is None:
super().get(None, override=override)
return
data = data[-1]
time = data.get("time", None)
data = data["vision"]
frame = data.get("frame", None)
detections = data.get("detections", None)
results = data.get("results", None)
render = data.get("render", None)
if not override:
result_ok = data.get("vision", {}).get("ok", False)
else:
result_ok = True
self.last_vision = {
"time": time,
"frame": frame,
"detections": detections,
"results": results,
"render": render,
"overridden": override,
"vision_duration": timing() - self.start_time,
"ok": result_ok,
}
if not override:
if result_ok is True:
self.vision_ok_counter += 1
else:
self.vision_ok_counter = 0
else:
self.vision_ok_counter = self.vision_ok_counter_limit
# check if completed
if self.vision_ok_counter >= self.vision_ok_counter_limit:
self.stop()
self.ok_timer.start()
self.visualize_vision(
time=time,
frame=frame,
detections=detections,
results=results,
render=render,
overridden=override,
)
super().get([{
"time": data.get("time", None),
"frame": data["vision"].get("frame", None),
"detections": data["vision"].get("detections", None),
"results": data["vision"].get("results", None),
"render": data["vision"].get("render", None),
}], override=override)
def visualize_vision(self, time=None, frame=None, detections=None, results=None, render=None, overridden=False):
self.save_frame_b.setEnabled(self.last_vision is not None)
def visualize(self, data=None):
if data is None:
data = {}
overridden = data.get("overridden", False)
frame = data.get("frame", None)
render = data.get("render", None)
if overridden:
self.state_l.setPixmap(self.status_imgs_small["warning"])
elif results is None:
self.state_l.setPixmap(self.status_imgs_small[None])
elif results.get("ok", False) is True:
self.state_l.setPixmap(self.status_imgs_small[True])
else:
self.state_l.setPixmap(self.status_imgs_small[False])
self.ok_counter_pb.setMaximum(self.vision_ok_counter_limit)
self.ok_counter_pb.setValue(min(self.vision_ok_counter, self.vision_ok_counter_limit))
if self.vision_ok_counter >= self.vision_ok_counter_limit:
self.ok_counter_pb.setPalette(self.status_palettes[True])
else:
self.ok_counter_pb.setPalette(self.status_palettes[False])
if overridden:
self.img = self.status_imgs_full["warning"]
img = self.status_imgs_full["warning"]
elif render is not None:
self.img = render
img = render
elif frame is not None:
self.img = QPixmap.fromImage(QImage(
img = QPixmap.fromImage(QImage(
frame.data,
frame.shape[1], # width
frame.shape[0], # height
@ -160,34 +70,14 @@ class Test_Vision(Widget):
QImage.Format_RGB888
))
else:
self.img = self.status_imgs_full[None]
self.resizeEvent()
img = self.status_imgs_full[None]
super().visualize(data, img=img)
def save_last_vision(self):
if self.last_vision is None:
def save_last(self):
if self.last is None:
return
def resizeEvent(self, event=None):
self.frame_l.setPixmap(self.img.scaled(self.frame_l.width(), self.frame_l.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
def challenge_admin(self, info):
if not self.admin_challenged:
d = Dialog()
d.setCentralWidget(Test_Admin_Permission(info))
d.setModal(True)
d.show()
r = d.exec()
if r == d.Accepted:
self.admin_challenged = True
elif r == d.Rejected:
self.admin_challenged ^= False
else:
raise AssertionError("Bad admin challenge dialog return code")
return self.admin_challenged
def override_vision(self):
if self.challenge_admin("Si sta tentando di bypassare il test di visione"):
self.process_vision(override=True)
def emit_ok(self):
self.ok.emit(self.last_vision)
self.last.pop("frame", None)
self.last.pop("render", None)
self.ok.emit(self.last)

View File

@ -72,7 +72,7 @@
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="frame_l">
<widget class="QLabel" name="img_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -91,7 +91,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="save_frame_b">
<widget class="QPushButton" name="save_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>

View File

@ -8,8 +8,8 @@ from ui.dialog import Dialog
class Widget(QWidget):
def __init__(self):
super().__init__()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAttribute(Qt.WA_DeleteOnClose)
me = self.__class__.__name__
u = get_resource("ui/{0}/{0}.ui".format(me.lower()))

View File

@ -11,8 +11,8 @@ from ui.dialog import Dialog
class Window(QMainWindow):
_closing = pyqtSignal()
def __init__(self):
super().__init__()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowFlags(Qt.Window)
me = self.__class__.__name__