save recipes wip

This commit is contained in:
neo 2024-10-04 11:39:16 +02:00
parent e3db54583d
commit dd88b1eafa
7 changed files with 342 additions and 57 deletions

View File

@ -1,66 +1,107 @@
[machine] [machine]
description = ST-TEN-2 - COLLAUDO TUBI RISCALDATI VOLPIANO description = ST-TEN-1
instruction_folder = st-ten-2 instruction_folder = st-ten-1
image_for_warning= st-ten-2 image_for_warning= st-ten-1
[hardware_config] [hardware_config]
archive_synchronizer: present archive_synchronizer: present
galaxy_camera: absent
uvc_camera: absent
label_printer: present label_printer: present
neo_pixels: absent
remote_api: absent remote_api: absent
tecna_t3: present tecna_t3: present
vision_saver: absent
[digital_io] vision: absent
# OUTPUT MAP FOR EXTERNAL FLUSH/BLOW UNIT screwdriver: absent
id_flush_blow: USB-5860,BID#0 fixture_id: absent
blow_on: 0 # INPUT VALVE TO SERVICE AIR digital_io: present
flush_on: 1 # OUTPUT VALVE TO DIRT COLLECTOR external_flush_blow: absent
blow_led:2 # CLEAN INDICATOR
test_on_led:3 # LEAK TEST ACTIVE INDICATOR
flush_led:4 # FLUSH INDICATOR
[tecna_t3] [tecna_t3]
port: /dev/ttyUSB0 port: /dev/ttyUSB0
model: t3l
[fixture_rfid]
port: COM5
[digital_io]
# OUTPUT MAP FOR FIXTURE CONNECTOR
id_fixture: USB-5862,BID#0
discard_idx:12 # BIT NUMBER OF THE I/0 MODULE USED FOR DISCARD SENSING
[recipe] [recipe]
recipe_name_field: cod finito cliente recipe_name_field: codice_ricetta
part_number_field: cod finito r5 part_number_field: codice_prodotto
barcode_enable_field: verifica_codice_a_barre_abilitata label_template_field: modello_etichetta
barcode_serial_field: codice_a_barre description_field: descrizione
label_template_field: etichetta
description_field: cod semilavorato costampato [label_printer]
platform: windows
printer: zd420
[recipes_defaults] [recipes_defaults]
prova_tenuta_abilitata: x tester_discharge_enable: yes
codice_ricetta: specificare ricetta
cliente: IVECO
part_number: specificare part number
canale_di_prova: 0
warning_img: warning_img:
dimensione_lotto_abilitata:
n_componenti:1 n_componenti:1
istruzione_abilitata: x
numero nastri (n):0
numero sensori anello (sa):0
numero sensori presenza (sp):0
prova_tenuta_abilitata: x
tempo_pre_riempimento: 0 tempo_pre_riempimento: 0
pressione_pre_riempimento: 1000 pressione_pre_riempimento: 5000
tempo_riempimento: 10 tempo_riempimento: 5
tempo_assestamento: 10 tempo_assestamento: 10
percentuale_minima_pressione_assestamento: 5 percentuale_minima_pressione_assestamento: 5
percentuale_massima_pressione_assestamento: 5 percentuale_massima_pressione_assestamento: 5
tempo_di_test: 10 tempo_di_test: 10
pressione_di_test_delta_minimo: 30 pressione_di_test_delta_minimo: 30
pressione_di_test: 15000 pressione_di_test: 5000
pressione_di_test_delta_massimo: 30 pressione_di_test_delta_massimo: 30
tempo_svuotamento: 1 tempo_svuotamento: 1
pressione_svuotamento: 100 pressione_svuotamento: 100
canale_di_prova: 0
tester_discharge_enable: yes prova_tenuta_abilitata_2:
tempo_pre_riempimento_2: 0
pressione_pre_riempimento_2: 1000
tempo_riempimento_2: 5
tempo_assestamento_2: 5
percentuale_minima_pressione_assestamento_2: 5
percentuale_massima_pressione_assestamento_2: 5
tempo_di_test_2: 5
pressione_di_test_delta_minimo_2: 200
pressione_di_test_2: 1000
pressione_di_test_delta_massimo_2: 200
tempo_svuotamento_2: 1
pressione_svuotamento_2: 100
stampa_etichetta_abilitata: x
modello_etichetta: ETA30x16_203dpi.prn
descrizione: inserire descrizione ricetta
[autotest_leak] [autotest_leak]
enabled: true enabled: true
pre_filling_time: 0 pre_filling_time: 0
pre_filling_pressure: 1000 pre_filling_pressure: 1000
filling_time: 5 filling_time: 10
settling_time: 10 settling_time: 10
settling_pressure_min_percent: 5 settling_pressure_min_percent: 5
settling_pressure_max_percent: 5 settling_pressure_max_percent: 5
test_pressure: 7000
test_time: 10 test_time: 10
test_pressure_qneg: 5 test_pressure_qpos: 10 #Q+ Upper test leak limit
test_pressure: 9000 test_pressure_qneg: 30 #Q- Lower test leak limit
test_pressure_qpos: 5 test_pressure_tt_qpos: 1 # Q+ Upper test leak limit (tube-tube)
test_pressure_tt_qneg: 5 # Q- Lower test leak limit (tube-tube)
flush_time: 1 flush_time: 1
flush_pressure: 100 flush_pressure: 100
relay_config: 1 relay_config: 1

View File

@ -0,0 +1,107 @@
[machine]
description = TEST-LINUX
instruction_folder = st-ten-1
image_for_warning= st-ten-1
[hardware_config]
archive_synchronizer: absent
galaxy_camera: absent
uvc_camera: absent
label_printer: present
neo_pixels: absent
remote_api: absent
tecna_t3: present
vision_saver: absent
vision: absent
screwdriver: absent
fixture_id: absent
digital_io: absent
external_flush_blow: absent
[tecna_t3]
port: /dev/ttyUSB0
model: t3p
[fixture_rfid]
port: COM5
[digital_io]
# OUTPUT MAP FOR FIXTURE CONNECTOR
id_fixture: USB-5862,BID#0
discard_idx:12 # BIT NUMBER OF THE I/0 MODULE USED FOR DISCARD SENSING
[recipe]
recipe_name_field: codice_ricetta
part_number_field: codice_prodotto
label_template_field: modello_etichetta
description_field: descrizione
[label_printer]
platform: windows
printer: zd420
[recipes_defaults]
tester_discharge_enable: yes
codice_ricetta: specificare ricetta
cliente: IVECO
part_number: specificare part number
canale_di_prova: 0
warning_img:
dimensione_lotto_abilitata:
n_componenti:1
istruzione_abilitata: x
numero nastri (n):0
numero sensori anello (sa):0
numero sensori presenza (sp):0
prova_tenuta_abilitata: x
tempo_pre_riempimento: 0
pressione_pre_riempimento: 5000
tempo_riempimento: 5
tempo_assestamento: 10
percentuale_minima_pressione_assestamento: 5
percentuale_massima_pressione_assestamento: 5
tempo_di_test: 10
pressione_di_test_delta_minimo: 30
pressione_di_test: 5000
pressione_di_test_delta_massimo: 30
tempo_svuotamento: 1
pressione_svuotamento: 100
prova_tenuta_abilitata_2:
tempo_pre_riempimento_2: 0
pressione_pre_riempimento_2: 1000
tempo_riempimento_2: 5
tempo_assestamento_2: 5
percentuale_minima_pressione_assestamento_2: 5
percentuale_massima_pressione_assestamento_2: 5
tempo_di_test_2: 5
pressione_di_test_delta_minimo_2: 200
pressione_di_test_2: 1000
pressione_di_test_delta_massimo_2: 200
tempo_svuotamento_2: 1
pressione_svuotamento_2: 100
stampa_etichetta_abilitata: x
modello_etichetta: ETA30x16_203dpi.prn
descrizione: inserire descrizione ricetta
[autotest_leak]
enabled: true
pre_filling_time: 0
pre_filling_pressure: 1000
filling_time: 10
settling_time: 10
settling_pressure_min_percent: 5
settling_pressure_max_percent: 5
test_pressure: 7000
test_time: 10
test_pressure_qpos: 10 #Q+ Upper test leak limit
test_pressure_qneg: 30 #Q- Lower test leak limit
test_pressure_tt_qpos: 1 # Q+ Upper test leak limit (tube-tube)
test_pressure_tt_qneg: 5 # Q- Lower test leak limit (tube-tube)
flush_time: 1
flush_pressure: 100
relay_config: 1

View File

@ -361,27 +361,29 @@ class TecnaMarpossProvasetT3(ModbusComponent):
self.log.info(f"saved {min(len(recipes), max(self.max_program_number - 1, 0))} recipes") self.log.info(f"saved {min(len(recipes), max(self.max_program_number - 1, 0))} recipes")
self._store_recipes_lock.release(1) self._store_recipes_lock.release(1)
def read_recipe(self, recipe_number, step): # SELECT & READ RECIPE FROM TECNA MEMORY
def read_recipe(self, recipe_number):
spec = { spec = {
"T0 - Pre-filling time": step.spec["pre_filling_time"], "T0 - Pre-filling time": "pre_filling_time",
"P0 - Pre-filling pressure": step.spec["pre_filling_pressure"], "P0 - Pre-filling pressure": "pre_filling_pressure",
"T1 - Filling time": step.spec["filling_time"], "T1 - Filling time": "filling_time",
"T2 - Settling time": step.spec["settling_time"], "T2 - Settling time": "settling_time",
"PR- - Min pressure tolerance %": step.spec["settling_pressure_min_percent"], "PR- - Min pressure tolerance %": "settling_pressure_min_percent",
"PR+ - Max pressure tolerance % (P+)": step.spec["settling_pressure_max_percent"], "PR+ - Max pressure tolerance % (P+)": "settling_pressure_max_percent",
"T3 - Measure time": step.spec["test_time"], "T3 - Measure time": "test_time",
"Q- Lower test leak limit": step.spec["test_pressure_qneg"], "Q- Lower test leak limit": "test_pressure_qneg",
"Q+ Upper test leak limit": step.spec["test_pressure_qpos"], "Q+ Upper test leak limit": "test_pressure_qpos",
"FST - Discharge time": step.spec["flush_time"], "FST - Discharge time": "flush_time",
"FSL - Discharge limit": step.spec["flush_pressure"], "FSL - Discharge limit": "flush_pressure",
} }
if self.model == "t3p": if self.model == "t3p":
spec.update({ spec.update({
"PREL - Nominal test pressure": step.spec["test_pressure"], "PREL - Nominal test pressure": "test_pressure",
}) })
elif self.model == "t3l": elif self.model == "t3l":
spec.update({ spec.update({
"Nominal peak pressure": step.spec["test_pressure"], "Nominal peak pressure": "test_pressure",
}) })
else: else:
raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.") raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.")
@ -389,9 +391,9 @@ class TecnaMarpossProvasetT3(ModbusComponent):
# SET RECIPE NUMBER # SET RECIPE NUMBER
self.write("Active test program number", recipe_number) self.write("Active test program number", recipe_number)
read_recipe={} recipe_data={}
# READ ALL PARAMETERS # READ ALL PARAMETERS
for register_name, field_name in spec.items(): for register_name, field_name in spec.items():
read_recipe[field_name] = self.read(register_name) recipe_data[field_name] = self.read(register_name)
return read_recipe return recipe_data

View File

@ -1,11 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import csv
import faulthandler import faulthandler
import logging import logging
import os import os
import platform import platform
import signal import signal
import sys import sys
sys.argv.append("--debug")
sys.argv.append("--sim-io")
sys.argv.append("--system-id=test-linux")
import traceback import traceback
import weakref import weakref
from datetime import datetime from datetime import datetime
@ -24,7 +29,6 @@ parser = argparse.ArgumentParser(prog='ST-TEN', description='Leak test system')
parser.add_argument('-s', '--system-id') parser.add_argument('-s', '--system-id')
args, unspec = parser.parse_known_args() args, unspec = parser.parse_known_args()
def quit_app(signalnum=None, handler=None): def quit_app(signalnum=None, handler=None):
logging.info(f"quitting app. signal: {signalnum!r}, handler: {handler!r}") logging.info(f"quitting app. signal: {signalnum!r}, handler: {handler!r}")
global app global app
@ -68,7 +72,7 @@ try:
from lib.db import Users from lib.db import Users
from lib.helpers import ConfigReader from lib.helpers import ConfigReader
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMessageBox from PyQt5.QtWidgets import QApplication, QMessageBox, QFileDialog
from ui import About, Archive, Login, Main_Window, Test, Users_Management, Recipe_Selection, \ from ui import About, Archive, Login, Main_Window, Test, Users_Management, Recipe_Selection, \
Barcode_Recipe_Selection Barcode_Recipe_Selection
@ -79,6 +83,7 @@ try:
def __init__(self, parent=None): def __init__(self, parent=None):
# print(f"MAIN {int(QThread.currentThreadId())}", flush=True) # print(f"MAIN {int(QThread.currentThreadId())}", flush=True)
super().__init__() super().__init__()
self.recipes = None
try: try:
# READ CONFIG # READ CONFIG
@ -116,20 +121,145 @@ try:
QMessageBox.critical(None, "Errore", f"Errore di avvio del programma di collaudo:\n\n{e}") QMessageBox.critical(None, "Errore", f"Errore di avvio del programma di collaudo:\n\n{e}")
quit() quit()
def save_recipes(self): def read_recipes(self):
self.components["tecna_t3"].read_recipes self.recipes = {}
for recipe_num in range(1,100):
self.recipes[recipe_num] = self.components["tecna_t3"].read_recipe(recipe_num)
def export_recipes(self, csv_path=None):
if csv_path is None:
csv_path, _ = QFileDialog.getSaveFileName(
None,
"Esportazione ricette",
"ricette.csv",
"CSV data (*.csv);;All Files (*)",
)
csv_path = str(csv_path)
if not len(csv_path):
return
if not csv_path.lower().endswith(".csv"):
csv_path += ".csv"
csv_dir = os.path.dirname(csv_path)
if len(csv_dir):
os.makedirs(csv_dir, exist_ok=True)
data = []
fieldnames = [
"ricetta",
"cliente",
"part_number",
"dimensione_lotto_abilitata",
"dimensione_lotto",
"verifica_connettore_abilitata",
"connettore",
"verifica_resistenza_connettore_abilitata",
"scala_resistenza",
"r nominale",
"tolleranza_resistenza_pos",
"tolleranza_resistenza_neg",
"avvitatura_abilitata",
"viti",
"prova_tenuta_abilitata",
"tempo_pre_riempimento",
"pressione_pre_riempimento",
"tempo_riempimento",
"tempo_assestamento",
"percentuale_minima_pressione_assestamento",
"percentuale_massima_pressione_assestamento",
"tempo_di_test",
"pressione_di_test_delta_minimo",
"pressione_di_test",
"pressione_di_test_delta_massimo",
"tempo_svuotamento",
"pressione_svuotamento",
"prova_tenuta_abilitata_2",
"tempo_pre_riempimento_2",
"pressione_pre_riempimento_2",
"tempo_riempimento_2",
"tempo_assestamento_2",
"percentuale_minima_pressione_assestamento_2",
"percentuale_massima_pressione_assestamento_2",
"tempo_di_test_2",
"pressione_di_test_delta_minimo_2",
"pressione_di_test_2",
"pressione_di_test_delta_massimo_2",
"tempo_svuotamento_2",
"pressione_svuotamento_2",
"test_visione_abilitato",
"ricetta_visione",
"stampa_etichetta_abilitata",
print_template_field,
]
for num,recipe in self.recipes.items():
steps = recipe.get_steps_map()
exportable = {
recipe_name_field: recipe.name,
"cliente": recipe.client,
"part_number": recipe.part_number,
# "dimensione_lotto_abilitata": "x" if recipe.spec["count"] else "",
# "dimensione_lotto": steps["count"].spec["amount"],
"verifica_connettore_abilitata": "x" if recipe.spec["connector"] else "",
"connettore": steps["connector"].spec["connector"],
barcode_enable_field: "x" if recipe.spec["barcodes"] else "",
barcode_serial_field: steps["barcodes"].spec["serial"],
"verifica_resistenza_connettore_abilitata": "x" if recipe.spec["resistance"] else "",
"scala_resistenza": steps["resistance"].spec["scale"],
"r nominale": steps["resistance"].spec["expected"],
"tolleranza_resistenza_pos": steps["resistance"].spec["tolerance_pos"],
"tolleranza_resistenza_neg": steps["resistance"].spec["tolerance_neg"],
# "avvitatura_abilitata": "x" if recipe.spec["screws"] else "",
# "viti": steps["screws"].spec["quantity"],
"prova_tenuta_abilitata": "x" if recipe.spec["leak_1"] else "",
"tempo_pre_riempimento": steps["leak_1"].spec["pre_filling_time"],
"pressione_pre_riempimento": steps["leak_1"].spec["pre_filling_pressure"],
"tempo_riempimento": steps["leak_1"].spec["filling_time"],
"tempo_assestamento": steps["leak_1"].spec["settling_time"],
"percentuale_minima_pressione_assestamento": steps["leak_1"].spec["settling_pressure_min_percent"],
"percentuale_massima_pressione_assestamento": steps["leak_1"].spec["settling_pressure_max_percent"],
"tempo_di_test": steps["leak_1"].spec["test_time"],
"pressione_di_test_delta_minimo": steps["leak_1"].spec["test_pressure_qneg"],
"pressione_di_test": steps["leak_1"].spec["test_pressure"],
"pressione_di_test_delta_massimo": steps["leak_1"].spec["test_pressure_qpos"],
"tempo_svuotamento": steps["leak_1"].spec["flush_time"],
"pressione_svuotamento": steps["leak_1"].spec["flush_pressure"],
"prova_tenuta_abilitata_2": "x" if recipe.spec["leak_2"] else "",
"tempo_pre_riempimento_2": steps["leak_2"].spec["pre_filling_time"],
"pressione_pre_riempimento_2": steps["leak_2"].spec["pre_filling_pressure"],
"tempo_riempimento_2": steps["leak_2"].spec["filling_time"],
"tempo_assestamento_2": steps["leak_2"].spec["settling_time"],
"percentuale_minima_pressione_assestamento_2": steps["leak_2"].spec[
"settling_pressure_min_percent"],
"percentuale_massima_pressione_assestamento_2": steps["leak_2"].spec[
"settling_pressure_max_percent"],
"tempo_di_test_2": steps["leak_2"].spec["test_time"],
"pressione_di_test_delta_minimo_2": steps["leak_2"].spec["test_pressure_qneg"],
"pressione_di_test_2": steps["leak_2"].spec["test_pressure"],
"pressione_di_test_delta_massimo_2": steps["leak_2"].spec["test_pressure_qpos"],
"tempo_svuotamento_2": steps["leak_2"].spec["flush_time"],
"pressione_svuotamento_2": steps["leak_2"].spec["flush_pressure"],
"test_visione_abilitato": recipe.spec["vision"],
"ricetta_visione": steps["vision"].spec["recipe"],
"stampa_etichetta_abilitata": "x" if recipe.spec["print"] else "",
"modello_etichetta": steps["print"].spec["template"],
}
data.append(exportable)
if len(data):
self.log.info(f"recipes: exporting recipes to {csv_path}")
with open(csv_path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames, extrasaction="ignore")
w.writeheader()
w.writerows(data)
self.log.info(f"recipes: exported {len(data)} rows.")
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
with SingleProcess() as single_process_lock: with SingleProcess() as single_process_lock:
if not single_process_lock and "--no-lock" not in sys.argv:
logging.error(f"Program already opened, exiting...")
QMessageBox.critical(None, "ERRORE", "IL PROGRAMMA E' GIA' IN ESECUZIONE")
exit(0)
main = Main() main = Main()
main.read_recipes()
main.export_recipes()
if "--no-gui" not in sys.argv: if "--no-gui" not in sys.argv:
app.exec() app.exec()

View File

@ -1,5 +1,6 @@
import ast import ast
import json import json
import logging
import traceback import traceback
import weakref import weakref
from datetime import datetime from datetime import datetime
@ -40,6 +41,7 @@ class Cell:
modified = pyqtSignal(bool) modified = pyqtSignal(bool)
def __init__(self, action=None, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None): def __init__(self, action=None, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None):
self.log = logging.getLogger(f"{self.__class__.__name__} ({id(self)})")
self.readonly = readonly self.readonly = readonly
self.autocomplete = autocomplete self.autocomplete = autocomplete
self.field_name = field_name self.field_name = field_name

View File

@ -182,6 +182,7 @@ class Recipe_Selection(Widget):
val = dict.get(key, self.defaults[key]) val = dict.get(key, self.defaults[key])
return val if val != "" else self.defaults[key] return val if val != "" else self.defaults[key]
# READ RECIPE STEPS FROM CSV ROW
def read_steps(self, row, defaults=None): def read_steps(self, row, defaults=None):
if defaults is None: if defaults is None:
global noner global noner
@ -273,6 +274,7 @@ class Recipe_Selection(Widget):
}, },
} }
# IMPORT RECIPES FROM CSV FILE TO DATABASE
def import_recipes(self, csv_path=None, defaults=None): def import_recipes(self, csv_path=None, defaults=None):
if defaults is None: if defaults is None:
global noner global noner
@ -341,6 +343,7 @@ class Recipe_Selection(Widget):
self.log.info(f"recipes: imported {count} rows.") self.log.info(f"recipes: imported {count} rows.")
self.crud.refresh() self.crud.refresh()
# EXPORT RECIPES TABLE TO CSV FILE
def export_recipes(self, csv_path=None): def export_recipes(self, csv_path=None):
if csv_path is None: if csv_path is None:
csv_path, _ = QFileDialog.getSaveFileName( csv_path, _ = QFileDialog.getSaveFileName(

View File

@ -43,7 +43,7 @@ class Test_Leak(Test_Test):
def start_test(self): def start_test(self):
# print extra labels # print extra labels
if self.step.type == "leak_1": if self.step.step_type == "leak_1":
self.parent.print_extra_labels() self.parent.print_extra_labels()
# SELECT TEST CHANNEL # SELECT TEST CHANNEL
@ -93,8 +93,8 @@ class Test_Leak(Test_Test):
if show is False: if show is False:
return show return show
if "leak_2" in [s.type for s in self.parent.cycle_steps]: if "leak_2" in [s.step_type for s in self.parent.cycle_steps]:
if self.step.type=="leak_1": if self.step.step_type=="leak_1":
self.test_num_l.setText("1/2") self.test_num_l.setText("1/2")
else: else:
self.test_num_l.setText("2/2") self.test_num_l.setText("2/2")
@ -143,7 +143,7 @@ class Test_Leak(Test_Test):
time.sleep(2) time.sleep(2)
# AUTO START SECOND TEST # AUTO START SECOND TEST
if step.type == "leak_2": if step.step_type == "leak_2":
self.recipe_written = False self.recipe_written = False
time.sleep(1) time.sleep(1)
self.start_b.setEnabled(True) self.start_b.setEnabled(True)