diff --git a/config/machine_settings/st-ten-1-linux.ini b/config/machine_settings/st-ten-1-linux.ini index 7561095..b88254e 100644 --- a/config/machine_settings/st-ten-1-linux.ini +++ b/config/machine_settings/st-ten-1-linux.ini @@ -1,66 +1,107 @@ [machine] -description = ST-TEN-2 - COLLAUDO TUBI RISCALDATI VOLPIANO -instruction_folder = st-ten-2 -image_for_warning= st-ten-2 +description = ST-TEN-1 +instruction_folder = st-ten-1 +image_for_warning= st-ten-1 [hardware_config] archive_synchronizer: present +galaxy_camera: absent +uvc_camera: absent label_printer: present +neo_pixels: absent remote_api: absent tecna_t3: present - -[digital_io] -# OUTPUT MAP FOR EXTERNAL FLUSH/BLOW UNIT -id_flush_blow: USB-5860,BID#0 -blow_on: 0 # INPUT VALVE TO SERVICE AIR -flush_on: 1 # OUTPUT VALVE TO DIRT COLLECTOR -blow_led:2 # CLEAN INDICATOR -test_on_led:3 # LEAK TEST ACTIVE INDICATOR -flush_led:4 # FLUSH INDICATOR +vision_saver: absent +vision: absent +screwdriver: absent +fixture_id: absent +digital_io: present +external_flush_blow: absent [tecna_t3] 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_name_field: cod finito cliente -part_number_field: cod finito r5 -barcode_enable_field: verifica_codice_a_barre_abilitata -barcode_serial_field: codice_a_barre -label_template_field: etichetta -description_field: cod semilavorato costampato +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] -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: + +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: 1000 -tempo_riempimento: 10 +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: 15000 +pressione_di_test: 5000 pressione_di_test_delta_massimo: 30 tempo_svuotamento: 1 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] enabled: true pre_filling_time: 0 pre_filling_pressure: 1000 -filling_time: 5 +filling_time: 10 settling_time: 10 settling_pressure_min_percent: 5 settling_pressure_max_percent: 5 +test_pressure: 7000 test_time: 10 -test_pressure_qneg: 5 -test_pressure: 9000 -test_pressure_qpos: 5 +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 \ No newline at end of file diff --git a/config/machine_settings/test-linux.ini b/config/machine_settings/test-linux.ini new file mode 100644 index 0000000..490c522 --- /dev/null +++ b/config/machine_settings/test-linux.ini @@ -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 \ No newline at end of file diff --git a/src/components/tecna_marposs_provaset_t3.py b/src/components/tecna_marposs_provaset_t3.py index e138835..65fea7c 100644 --- a/src/components/tecna_marposs_provaset_t3.py +++ b/src/components/tecna_marposs_provaset_t3.py @@ -361,27 +361,29 @@ class TecnaMarpossProvasetT3(ModbusComponent): self.log.info(f"saved {min(len(recipes), max(self.max_program_number - 1, 0))} recipes") 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 = { - "T0 - Pre-filling time": step.spec["pre_filling_time"], - "P0 - Pre-filling pressure": step.spec["pre_filling_pressure"], - "T1 - Filling time": step.spec["filling_time"], - "T2 - Settling time": step.spec["settling_time"], - "PR- - Min pressure tolerance %": step.spec["settling_pressure_min_percent"], - "PR+ - Max pressure tolerance % (P+)": step.spec["settling_pressure_max_percent"], - "T3 - Measure time": step.spec["test_time"], - "Q- Lower test leak limit": step.spec["test_pressure_qneg"], - "Q+ Upper test leak limit": step.spec["test_pressure_qpos"], - "FST - Discharge time": step.spec["flush_time"], - "FSL - Discharge limit": step.spec["flush_pressure"], + "T0 - Pre-filling time": "pre_filling_time", + "P0 - Pre-filling pressure": "pre_filling_pressure", + "T1 - Filling time": "filling_time", + "T2 - Settling time": "settling_time", + "PR- - Min pressure tolerance %": "settling_pressure_min_percent", + "PR+ - Max pressure tolerance % (P+)": "settling_pressure_max_percent", + "T3 - Measure time": "test_time", + "Q- Lower test leak limit": "test_pressure_qneg", + "Q+ Upper test leak limit": "test_pressure_qpos", + "FST - Discharge time": "flush_time", + "FSL - Discharge limit": "flush_pressure", } if self.model == "t3p": spec.update({ - "PREL - Nominal test pressure": step.spec["test_pressure"], + "PREL - Nominal test pressure": "test_pressure", }) elif self.model == "t3l": spec.update({ - "Nominal peak pressure": step.spec["test_pressure"], + "Nominal peak pressure": "test_pressure", }) else: raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.") @@ -389,9 +391,9 @@ class TecnaMarpossProvasetT3(ModbusComponent): # SET RECIPE NUMBER self.write("Active test program number", recipe_number) - read_recipe={} + recipe_data={} # READ ALL PARAMETERS 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 diff --git a/src/scripts/save_tecna_recipes_to_csv.py b/src/scripts/save_tecna_recipes_to_csv.py index 4f46a48..3e6dfb6 100644 --- a/src/scripts/save_tecna_recipes_to_csv.py +++ b/src/scripts/save_tecna_recipes_to_csv.py @@ -1,11 +1,16 @@ #!/usr/bin/env python3 import argparse +import csv import faulthandler import logging import os import platform import signal import sys +sys.argv.append("--debug") +sys.argv.append("--sim-io") +sys.argv.append("--system-id=test-linux") + import traceback import weakref from datetime import datetime @@ -24,7 +29,6 @@ parser = argparse.ArgumentParser(prog='ST-TEN', description='Leak test system') parser.add_argument('-s', '--system-id') args, unspec = parser.parse_known_args() - def quit_app(signalnum=None, handler=None): logging.info(f"quitting app. signal: {signalnum!r}, handler: {handler!r}") global app @@ -68,7 +72,7 @@ try: from lib.db import Users from lib.helpers import ConfigReader 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, \ Barcode_Recipe_Selection @@ -79,6 +83,7 @@ try: def __init__(self, parent=None): # print(f"MAIN {int(QThread.currentThreadId())}", flush=True) super().__init__() + self.recipes = None try: # READ CONFIG @@ -116,20 +121,145 @@ try: QMessageBox.critical(None, "Errore", f"Errore di avvio del programma di collaudo:\n\n{e}") quit() - def save_recipes(self): - self.components["tecna_t3"].read_recipes + def read_recipes(self): + 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__": app = QApplication(sys.argv) 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.read_recipes() + main.export_recipes() + if "--no-gui" not in sys.argv: app.exec() diff --git a/src/ui/crud/crud.py b/src/ui/crud/crud.py index 57b633c..2b4181c 100755 --- a/src/ui/crud/crud.py +++ b/src/ui/crud/crud.py @@ -1,5 +1,6 @@ import ast import json +import logging import traceback import weakref from datetime import datetime @@ -40,6 +41,7 @@ class Cell: 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): + self.log = logging.getLogger(f"{self.__class__.__name__} ({id(self)})") self.readonly = readonly self.autocomplete = autocomplete self.field_name = field_name diff --git a/src/ui/recipe_selection/recipe_selection.py b/src/ui/recipe_selection/recipe_selection.py index 98d6675..614400d 100755 --- a/src/ui/recipe_selection/recipe_selection.py +++ b/src/ui/recipe_selection/recipe_selection.py @@ -182,6 +182,7 @@ class Recipe_Selection(Widget): val = dict.get(key, self.defaults[key]) return val if val != "" else self.defaults[key] + # READ RECIPE STEPS FROM CSV ROW def read_steps(self, row, defaults=None): if defaults is None: 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): if defaults is None: global noner @@ -341,6 +343,7 @@ class Recipe_Selection(Widget): self.log.info(f"recipes: imported {count} rows.") self.crud.refresh() + # EXPORT RECIPES TABLE TO CSV FILE def export_recipes(self, csv_path=None): if csv_path is None: csv_path, _ = QFileDialog.getSaveFileName( diff --git a/src/ui/test_leak/test_leak.py b/src/ui/test_leak/test_leak.py index 24b77b1..0728174 100644 --- a/src/ui/test_leak/test_leak.py +++ b/src/ui/test_leak/test_leak.py @@ -43,7 +43,7 @@ class Test_Leak(Test_Test): def start_test(self): # print extra labels - if self.step.type == "leak_1": + if self.step.step_type == "leak_1": self.parent.print_extra_labels() # SELECT TEST CHANNEL @@ -93,8 +93,8 @@ class Test_Leak(Test_Test): if show is False: return show - if "leak_2" in [s.type for s in self.parent.cycle_steps]: - if self.step.type=="leak_1": + if "leak_2" in [s.step_type for s in self.parent.cycle_steps]: + if self.step.step_type=="leak_1": self.test_num_l.setText("1/2") else: self.test_num_l.setText("2/2") @@ -143,7 +143,7 @@ class Test_Leak(Test_Test): time.sleep(2) # AUTO START SECOND TEST - if step.type == "leak_2": + if step.step_type == "leak_2": self.recipe_written = False time.sleep(1) self.start_b.setEnabled(True)