Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
311726303c
101
config/csv_import/EXTRACTIONS/T3P stten12 10 02 2025.csv
Normal file
101
config/csv_import/EXTRACTIONS/T3P stten12 10 02 2025.csv
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
codice_ricetta,part_number,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,template_di_stampa,campo1,campo2
|
||||||
|
5803148256,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148256,
|
||||||
|
5803148257,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148257,
|
||||||
|
5803148261,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148261,
|
||||||
|
5803148263,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148263,
|
||||||
|
5803148264,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148264,
|
||||||
|
5803148265,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148265,
|
||||||
|
5803148266,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148266,
|
||||||
|
5803148267,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148267,
|
||||||
|
5803148268,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148268,
|
||||||
|
5803148269,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803148269,
|
||||||
|
5803166717,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803166717,
|
||||||
|
5803167128,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5803167128,
|
||||||
|
5802966801,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5802966801 R.01,
|
||||||
|
A68507016000,FUEL HOSE PUMP CPL,0,0,0,1,10,10,0,0,7200,0,0.1,0,4,A68507016000,KTM
|
||||||
|
16LA573CP REV01, HOSE-MCU-TO-MOTOR,0,0,0,0,20,20,2,0,3000,0,0.1,0,2, HOSE-MCU-TO-MO,P16
|
||||||
|
PIPE-IN-PRE-COOL,16LA372CP,0,0,0,0,20,20,3,0,3500,0,0.1,0,2,16LA372CP REV01,P16
|
||||||
|
5803083561, TUBO ASPIRAZIONE,0,3000,0,1,20,20,2,0,3000,0,0.1,0,6,5803083561,IVECO
|
||||||
|
5803083634, TUBO ASPIRAZIONE,0,3000,0,1,20,20,2,0,2000,0,0.1,0,6,5803083634,IVECO
|
||||||
|
16LA642CP REV01,HOSE-WCACS LINK PIPE,0,0,0,0,20,20,3,0,3500,0,0.1,0,2,HOSE-WCACS LINK ,P16
|
||||||
|
16LA353CP REV02,HOSE-RH-HT-RADIATOR-TOP,0,2000,1,1,20,20,3,0,3500,0,0.1,0,2,HOSE-RH-HT-RADIA,P16
|
||||||
|
16LA336CP REV02,16LA336CP,0,0,0,1,20,20,3,0,3500,0,0.1,0,2, HOSE-E-MOTOR,P16
|
||||||
|
16L0180CP REV04,16L0180CP,0,0,0,1,20,20,3,0,3000,0,0.1,0,2,HOSE LH LTR OUT,P16
|
||||||
|
HOSE-JNCTN TO W,16L0177CP,0,0,0,1,20,20,3,0,3500,0,0.1,0,2,16L0177CP REV03,P16
|
||||||
|
HOSE-IPU-OUTLET,16L0170CP,0,0,0,1,20,20,2,0,3000,0,0.1,0,2,16L0170CP REV03,P16
|
||||||
|
HOSE-TO-MCU,16L0167CP,0,0,0,1,20,20,2,0,3000,0,0.1,0,2,16L0167CP REV02,P16
|
||||||
|
HOSE-LH-LTR-OUTL,16L0164CP,0,0,0,1,20,20,3,0,3000,0,0.1,0,2,16L0164CP REV02,P16
|
||||||
|
16L0007CP REV03,16L0007CP,0,0,1,0,20,20,3,0,3000,0,0.1,0,2,HOSE-IPU-JNCTN-T,P16
|
||||||
|
16FC246CP REV01,16FC246CP,0,0,0,0,20,20,3,0,1000,0,0.1,0,2,ASSY-OIL FILL,P16
|
||||||
|
HOSE-WCACS-TO,16L0161CP,0,0,0,1,20,20,2,0,3000,0,0.1,0,2,16L0161CP REV04,P16
|
||||||
|
HOSE-RH-LTR-OUT,16L0154CP,0,0,1,1,20,20,2,0,3000,0,0.1,0,2,16L0154CP REV03,P16
|
||||||
|
16LA661CP REV01,HOSE-RH-HT-RADIATOR-BOTT,0,2000,1,0,20,20,3,0,3500,0,0.1,0,2,HOSE-RH-HT-RADIA,P16
|
||||||
|
16L0058CP REV02,16L0058CP,0,0,0,0,20,20,3,0,3500,0,0.1,0,2,HOSE-LT-TUNEL-TO,P16
|
||||||
|
16FC069CP REV01,ASSY CCV-INTAKE,0,0,0,0,20,20,2,0,1000,0,0.1,0,2,ASSY CCV-INTAKE,P16
|
||||||
|
16L0145CP REV3,16L0145CP,0,0,0,1,20,20,2,0,3000,0,0.1,0,2,HOSE-HOSE-PUMP-T,P16
|
||||||
|
16FC056CP REV02,ASSY-MAKE UP AIR,0,0,0,0,20,20,2,0,1000,0,0.1,0,2,ASSY-MAKE UP AIR,P16
|
||||||
|
HOSE-IPU-OUTLET,16L0151CP,0,0,1,1,20,20,2,0,3000,0,0.1,0,2,16L0151CP REV03,P16
|
||||||
|
16L0046CP REV03,HOSE-LH-HT-RADIATOR-TOP-,0,2000,1,0,20,20,3,0,3500,0,0.1,0,2,HOSE-LH-HT-RADIA,P16
|
||||||
|
16L0075CP REV03,16L0075CP,0,0,0,1,20,20,3,0,2500,0,0.1,0,2,HOSE-HEADER-TANK,P16
|
||||||
|
16FC250CP REV01,16FB640CP,0,0,0,0,20,20,1,0,200,0,0.1,0,2,ASSY CCV - PCV,P16
|
||||||
|
MY83-6B748-BA,ENGINE OIL ASY-OUTLET,0,0,1,1,10,10,3,0,7000,0,0.0,0,4,MY83-6B748-BA,AST0N MARTIN
|
||||||
|
6689099A1,ARGO TRACT,0,0,0,1,10,10,2,0,6000,0,0.0,0,4,6689099A1,
|
||||||
|
6689101A1,ARGO TRACT,0,0,0,1,10,10,2,0,6000,0,0.0,0,4,6689101A1,
|
||||||
|
6689098A1,ARGO TRACT,0,0,0,1,10,10,2,0,6000,0,0.0,0,4,6689098A1,
|
||||||
|
5803031876,OIL FILLER PIPE,0,0,1,1,10,10,1,0,500,0,0.0,0,4,5803031876,IVECO
|
||||||
|
FUGA CALIBRATA,24003,0,7000,0,0,5,5,1,0,7000,0,0.1,0,5,24003 12MBAR,FUGA CALIBRATA
|
||||||
|
5801384842 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801384842 REV 0,LANCIA BOLZANO
|
||||||
|
5802729983 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5802729983 REV 0,LANCIA BOLZANO
|
||||||
|
5802731910 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5802731910 REV 0,LANCIA BOLZANO
|
||||||
|
5801384841 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801384841 REV0,LANCIA BOLZANO
|
||||||
|
5802915821,COMPL.MAN,0,0,0,1,15,15,1,0,5000,0,0.1,0,6,5802915821 R.02,
|
||||||
|
5802915822,COMPL.MAN,0,0,0,1,15,15,1,0,5000,0,0.1,0,6,5802915822 R.02,
|
||||||
|
504321532 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,504321532 REV 0,LANCIA BOLZANO
|
||||||
|
5801970063 REV0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801970063 REV0,LANCIA BOLZANO
|
||||||
|
60192054 REV01,TUBO PER COMBUSTIBILE,0,0,1,1,10,10,1,0,8000,0,0.0,0,4,60192054 REV01,LANCIA BOLZANO
|
||||||
|
5801862227,TUBO PER COMBUSTIBILE,0,0,1,1,10,10,1,0,8000,0,0.0,0,4,5801862227 REV0,LANCIA BOLZANO
|
||||||
|
5801970064 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801970064 REV 0,LANCIA BOLZANO
|
||||||
|
5801970066 REV 1,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801970066 REV01,LANCIA BOLZANO
|
||||||
|
5801970065 REV0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801970065 REV0,LANCIA BOLZANO
|
||||||
|
504321531 REV 0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,504321531 REV 0,LANCIA BOLZANO
|
||||||
|
5801384840 REV0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801384840 REV0,LANCIA BOLZANO
|
||||||
|
5801384839 REV0,TUBO PER COMBUSTIBILE,0,0,1,1,10,10,1,0,8000,0,0.0,0,4,5801384839 REV0,LANCIA BOLZANO
|
||||||
|
12970-13-2382,TUBEASSY-CLUTCH COOLING,0,0,0,0,10,10,3,0,7000,0,0.0,0,4,12970-13-2382,MULTIMATIC
|
||||||
|
12970-13-2384,TUBEASSY-CLUTCH COOLING,0,0,0,0,10,10,3,0,7000,0,0.0,0,4,12970-13-2384,MULTIMATIC
|
||||||
|
12970-13-2386,TUBEASSY-CLUTCH COOLING,0,0,0,0,10,10,2,0,7000,0,0.0,0,4,12970-13-2386,MULTIMATIC
|
||||||
|
5801970067 REV0,TUBO PER COMBUSTIBILE,0,0,2,1,10,10,1,0,8000,0,0.0,0,4,5801970067 REV0,LANCIA BOLZANO
|
||||||
|
61907016000 IN.D,FUEL HOSE PUMP CPL,0,0,0,1,10,10,0,0,7000,0,0.1,0,4,61907016000 IN.D,KTM
|
||||||
|
5802831451,TUBAZIONE RITORNO,0,0,0,1,10,10,1,0,5000,0,0.1,0,6,5802831451 R.03,
|
||||||
|
5802831453,TUBAZIONE MANDATA,0,0,0,1,10,10,1,0,5000,0,0.1,0,6,5802831453 R.03,
|
||||||
|
5802860939,TUBAZIONE TRAVERSA RADI,0,0,0,1,10,10,1,0,5000,0,0.1,0,6,5802860939 R.03,
|
||||||
|
5802831450,TUBAZIONE INTERMEDIE,0,0,0,1,10,10,1,0,5000,0,0.1,0,6,5802831450 R.03,
|
||||||
|
5802831452,TUBAZIONE INTERMEDIE,0,0,0,1,10,10,1,0,5000,0,0.1,0,6,5802831452 R.03,
|
||||||
|
5802860512,TUBAZIONE RITORNO RADIA,0,5000,0,1,10,10,1,0,5000,0,0.1,0,6,5802860512 R.03,
|
||||||
|
PY83-6B747-BA,ENGINE OIL ASSY-INLET,0,0,1,1,10,10,3,0,7000,0,0.0,0,4,PY83-6B747-BA,AST0N MARTIN
|
||||||
|
PY83-6B748-BA,ENGINE OIL ASY-OUTLET,0,0,1,1,10,10,3,0,7000,0,0.0,0,4,PY83-6B748-BA,AST0N MARTIN
|
||||||
|
5802908563,CONNECTION PIPE,0,0,1,2,20,20,1,0,2000,0,0.1,0,5,5802908563,FPT - 13535
|
||||||
|
5802726236 REV02,OIL FILLER PIPE,0,0,1,1,10,10,1,0,500,0,0.1,0,4,5802726236 REV02,IVECO
|
||||||
|
5802726232 REV02,OIL FILLER PIPE,0,300,1,1,10,10,1,0,500,0,0.1,0,4,5802726232 REV02,IVECO
|
||||||
|
TBG-915988,TUBO PLASTICO SX,0,0,0,0,15,15,2,0,3100,0,0.0,0,4,TUBO PLASTICO SX,SAGOM TUBI
|
||||||
|
TBG-915989,TUBO PLASTICO DX,0,0,0,0,15,15,2,0,3100,0,0.0,0,4,TUBO PLASTICO DX,SAGOM TUBI
|
||||||
|
TBG-919634,TUBO PLASTICO SX,0,0,1,1,15,15,2,0,3100,0,0.0,0,1,TUBO PLASTICO ,SAGOM TUBI
|
||||||
|
16EA019SP,,0,0,2,1,10,10,6,0,2000,0,0.0,0,2,P16 MCLAREN,HOSE
|
||||||
|
91752479,HOSE FUEL,0,0,0,0,10,10,5,0,2000,0,0.0,0,5,91752479 REV.A,CNH - BNJ31
|
||||||
|
91713309,HOSE FUEL,0,0,0,1,10,10,1,0,2000,0,0.0,0,5,91713309 REV.A,CNH - BNJ31
|
||||||
|
TBG-919635,TUBO PLASTICO SX,0,0,1,1,15,15,2,0,3100,0,0.1,0,1,TUBO PLASTICO ,SAGOM TUBI
|
||||||
|
5803030264,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030264 REV.0,IVECO
|
||||||
|
5803030758,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030758 REV.0,IVECO
|
||||||
|
5803030799,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030799 REV.0,IVECO
|
||||||
|
5803030800,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030800 REV.0,IVECO
|
||||||
|
5803030840,DELIVERY PIPE COMPLETE,0,0,0,1,10,10,1,0,4000,0,0.0,0,4,5803030840 REV.0,IVECO
|
||||||
|
5803030842,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030842 REV.0,IVECO
|
||||||
|
5803030878,DELIVERY PIPE COMPLETE,0,0,0,0,10,10,1,0,4000,0,0.0,0,4,5803030878 REV.0,IVECO
|
||||||
|
5802878056,,0,0,0,1,10,10,1,0,5000,0,0.0,0,6,5802878056 ,
|
||||||
|
5802831451,,0,0,0,1,10,10,1,0,5000,0,0.0,0,6,5802831451 R.03,
|
||||||
|
5802967436,,0,0,0,1,10,10,1,0,5000,0,0.0,0,6,5802967436 R.03,
|
||||||
|
5802860512,,0,0,0,1,10,10,1,0,5000,0,0.0,0,6,5802860512 R.03,
|
||||||
|
5802989226,FPT,0,0,0,2,10,10,1,0,2000,0,0.0,0,4,5802989226,
|
||||||
|
5803028303,FPT,0,0,0,1,10,10,2,0,2000,0,0.0,0,4,5803028303,
|
||||||
|
5802989036,FPT,0,0,0,1,10,10,2,0,2000,0,0.0,0,4,5802989036,
|
||||||
|
5802269836,FPT,0,0,0,1,10,10,2,0,3000,0,0.0,0,4,5802269836,
|
||||||
|
000952054,j000952054,0,2200,0,0,5,5,3,0,2200,0,0.0,100,1,000952054,LANCIA BOLZANO
|
||||||
|
5
config/csv_import/RICETTE-ST-TEN-11-FERRARI.csv
Normal file
5
config/csv_import/RICETTE-ST-TEN-11-FERRARI.csv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
verifica_codice_a_barre_abilitata,pressione_di_test_2,cliente,tempo_di_test_2,pressione_pre_riempimento,prova_tenuta_abilitata,modello_etichetta,tempo_di_test,pressione_pre_riempimento_2,prova_tenuta_abilitata_2,stampa_etichetta_abilitata,part_number,tempo_pre_riempimento_2,tempo_pre_riempimento,pressione_di_test,codice_a_barre,codice_ricetta
|
||||||
|
x,15000,Ferrari,10,1000,x,ferrari_30x16_203.prn,30,1000,x,x,000990684,0,0,2000,,000990684
|
||||||
|
x,15000,Ferrari,10,1000,x,ferrari_30x16_203.prn,30,1000,x,x,000990685,0,0,2000,,000990685
|
||||||
|
x,15000,Ferrari,10,1000,x,ferrari_30x16_203.prn,30,1000,x,x,000990686,0,0,2000,,000990686
|
||||||
|
x,15000,Ferrai,10,1000,x,ferrari_30x16_203.prn,30,1000,x,x,000990708,0,0,5000,,000990708
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[machine]
|
[machine]
|
||||||
description = ST-TEN-12 AUTOMATICO
|
description = ST-TEN-12 M2 C04
|
||||||
instruction_folder = st-ten-12
|
instruction_folder = st-ten-12
|
||||||
image_for_warning= st-ten-12
|
image_for_warning= st-ten-12
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ neo_pixels: absent
|
|||||||
remote_api: absent
|
remote_api: absent
|
||||||
tecna_t3: present
|
tecna_t3: present
|
||||||
vision_saver: absent
|
vision_saver: absent
|
||||||
vision: absent
|
vision: present
|
||||||
screwdriver: absent
|
screwdriver: absent
|
||||||
fixture_id: present
|
fixture_id: present
|
||||||
barcode_recipe_selection: present
|
barcode_recipe_selection: present
|
||||||
@ -26,11 +26,11 @@ poll_time: 10
|
|||||||
hold_time: 10
|
hold_time: 10
|
||||||
|
|
||||||
[tecna_t3]
|
[tecna_t3]
|
||||||
port: /dev/ttyUSB1
|
port: /dev/ttyUSB0
|
||||||
model: t3p
|
model: t3l
|
||||||
|
|
||||||
[fixture_rfid]
|
[fixture_rfid]
|
||||||
port: USB0
|
port: USB1
|
||||||
|
|
||||||
[digital_io]
|
[digital_io]
|
||||||
# OUTPUT MAP FOR VALVE CONTROL UNITS
|
# OUTPUT MAP FOR VALVE CONTROL UNITS
|
||||||
@ -54,7 +54,6 @@ cliente: IVECO
|
|||||||
part_number: specificare part number
|
part_number: specificare part number
|
||||||
canale_di_prova: 0
|
canale_di_prova: 0
|
||||||
warning_img:
|
warning_img:
|
||||||
pid_pressure_correction: 105
|
|
||||||
dimensione_lotto_abilitata:
|
dimensione_lotto_abilitata:
|
||||||
n_componenti:1
|
n_componenti:1
|
||||||
istruzione_abilitata: x
|
istruzione_abilitata: x
|
||||||
@ -89,6 +88,8 @@ pressione_di_test_2: 1000
|
|||||||
pressione_di_test_delta_massimo_2: 200
|
pressione_di_test_delta_massimo_2: 200
|
||||||
tempo_svuotamento_2: 1
|
tempo_svuotamento_2: 1
|
||||||
pressione_svuotamento_2: 100
|
pressione_svuotamento_2: 100
|
||||||
|
pid_pressure_correction: 105
|
||||||
|
pid_pressure_correction_2: 105
|
||||||
|
|
||||||
stampa_etichetta_abilitata: x
|
stampa_etichetta_abilitata: x
|
||||||
modello_etichetta: ETA30x16_203dpi.prn
|
modello_etichetta: ETA30x16_203dpi.prn
|
||||||
|
|||||||
BIN
config/vision/recipes/952054_dx.scsln
Normal file
BIN
config/vision/recipes/952054_dx.scsln
Normal file
Binary file not shown.
BIN
config/vision/recipes/952054_sx.scsln
Normal file
BIN
config/vision/recipes/952054_sx.scsln
Normal file
Binary file not shown.
26
make_desktop_file_developer_test.sh
Normal file
26
make_desktop_file_developer_test.sh
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Define the temporary file name for the .desktop file
|
||||||
|
SCRIPT="/tmp/shortcut-$(date +%s).desktop"
|
||||||
|
|
||||||
|
# Create the .desktop file with the required content
|
||||||
|
echo "[Desktop Entry]" >> $SCRIPT
|
||||||
|
echo "Version=1.0" >> $SCRIPT
|
||||||
|
echo "Name=AVVIO PROGRAMMA COLLAUDO" >> $SCRIPT
|
||||||
|
echo "Comment=Shortcut to AVVIO PROGRAMMA COLLAUDO" >> $SCRIPT
|
||||||
|
echo "Exec=$HOME/PycharmProjects/st-ten-1/runmetest.sh" >> $SCRIPT
|
||||||
|
echo "Icon=$HOME/PycharmProjects/st-ten-1/src/ui/imgs/neo_red.ico" >> $SCRIPT
|
||||||
|
echo "Terminal=false" >> $SCRIPT
|
||||||
|
echo "Type=Application" >> $SCRIPT
|
||||||
|
echo "Categories=Utility;" >> $SCRIPT
|
||||||
|
|
||||||
|
# Prompt for sudo password and copy the .desktop file to the user's Desktop
|
||||||
|
sudo cp $SCRIPT "$HOME/Desktop/AVVIO PROGRAMMA TEST.desktop"
|
||||||
|
|
||||||
|
# Make the .desktop file executable
|
||||||
|
sudo chmod +x "$HOME/Desktop/AVVIO PROGRAMMA TEST.desktop"
|
||||||
|
|
||||||
|
# Remove the temporary .desktop file
|
||||||
|
rm $SCRIPT
|
||||||
|
|
||||||
|
echo "Shortcut created on Desktop"
|
||||||
4
runmetest.sh
Executable file
4
runmetest.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
source "./venv/bin/activate" || source "./venv/Scripts/activate" || :
|
||||||
|
python -O "./src/main.py" --no-edgetpu --no-tflite --no-autotest --sim-archiver --sim-printer --system-id=test-linux --no-autotest --no-lock --sim-io --dev-portal --auto-login-admin $*
|
||||||
@ -273,7 +273,7 @@ class ArchiveSynchronizer(Component):
|
|||||||
self.log.error(f"id {record.id}: {path_in}: file not found.")
|
self.log.error(f"id {record.id}: {path_in}: file not found.")
|
||||||
return False
|
return False
|
||||||
except Forbidden as e:
|
except Forbidden as e:
|
||||||
if re.match("^Object '.*?' is subject to bucket's retention policy and cannot be deleted, overwritten or archived until .*?$", e._response.json()["error"]["message"]) is not None:
|
if re.match("^Object '.*?' is subject .*?$", e._response.json()["error"]["message"]) is not None:
|
||||||
self.log.info(f"id {record.id}: {path_in}: already stored.")
|
self.log.info(f"id {record.id}: {path_in}: already stored.")
|
||||||
else:
|
else:
|
||||||
self.log.warning(f"id: {record.id}: failed to store remotely: {str(e)}: {e._response.json()}")
|
self.log.warning(f"id: {record.id}: failed to store remotely: {str(e)}: {e._response.json()}")
|
||||||
|
|||||||
@ -174,7 +174,7 @@ class Component(QObject):
|
|||||||
self._lock.release()
|
self._lock.release()
|
||||||
return running
|
return running
|
||||||
|
|
||||||
def wait_completion(self, timeout=5):
|
def wait_completion(self, timeout=10):
|
||||||
"""
|
"""
|
||||||
waits until the requested action has been completed by the component
|
waits until the requested action has been completed by the component
|
||||||
this will return immediately if threaded=False was passed at component initialization
|
this will return immediately if threaded=False was passed at component initialization
|
||||||
|
|||||||
@ -14,11 +14,14 @@ from .hikrobot_dll import *
|
|||||||
|
|
||||||
|
|
||||||
class HikrobotSmartCamera(Component):
|
class HikrobotSmartCamera(Component):
|
||||||
def __init__(self, config=None, name=None, period=0.5, lazy=True, paused=False, threaded=True):
|
def __init__(self, config=None, name=None, period=0.2, lazy=True, paused=False, threaded=True):
|
||||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||||
self.simulate = "--sim-camera" in sys.argv
|
self.simulate = "--sim-camera" in sys.argv
|
||||||
self.num_cameras=1
|
self.num_cameras=1
|
||||||
self.connected = False
|
self.connected = False
|
||||||
|
self.ok_memory = True
|
||||||
|
self.ok_frames = []
|
||||||
|
self.ok_results =[]
|
||||||
|
|
||||||
def config_changed(self):
|
def config_changed(self):
|
||||||
self.connected = False
|
self.connected = False
|
||||||
@ -99,39 +102,53 @@ class HikrobotSmartCamera(Component):
|
|||||||
for cam_idx in range(self.num_cameras):
|
for cam_idx in range(self.num_cameras):
|
||||||
cam = self.cam_list[cam_idx]
|
cam = self.cam_list[cam_idx]
|
||||||
self.log.info(f"GET FRAME CAMERA # {cam_idx}")
|
self.log.info(f"GET FRAME CAMERA # {cam_idx}")
|
||||||
|
if self.ok_results[cam_idx]:
|
||||||
# Trigger acquisition & test execution
|
# keep last good result
|
||||||
nRet = mv_lib.MV_VS_SetCommandValue(cam["handle"], b"AcquisitionStart")
|
frame = self.ok_frames[cam_idx]
|
||||||
if nRet != MV_VS_OK:
|
res = False
|
||||||
self.log.error(f"CAM{cam_idx} AcquisitionStart failed! [{nRet&0xFFFFFFFF:#x}]")
|
|
||||||
self.connected = False
|
|
||||||
# get frame
|
|
||||||
frame_res = MV_VS_GetFrame(cam["handle"])
|
|
||||||
if frame_res is not None:
|
|
||||||
res = frame_res["res"]["ScDeviceSolutionRunningResult"]
|
|
||||||
frame = frame_res["frame"]
|
|
||||||
# rotate frame
|
|
||||||
if rot[cam_idx] == "1":
|
|
||||||
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
|
|
||||||
if rot[cam_idx] == "2":
|
|
||||||
frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
|
||||||
if rot[cam_idx] == "3":
|
|
||||||
frame = cv2.rotate(frame, cv2.ROTATE_180)
|
|
||||||
|
|
||||||
if concat_frame is None:
|
|
||||||
concat_frame = copy.deepcopy(frame)
|
|
||||||
else:
|
|
||||||
concat_frame = cv2.hconcat([concat_frame, frame])
|
|
||||||
concat_results.append(not res)
|
|
||||||
else:
|
else:
|
||||||
self.connected = False
|
# Trigger acquisition & test execution
|
||||||
super()._get([concat_frame, concat_results])
|
nRet = mv_lib.MV_VS_SetCommandValue(cam["handle"], b"AcquisitionStart")
|
||||||
|
if nRet != MV_VS_OK:
|
||||||
|
self.log.error(f"CAM{cam_idx} AcquisitionStart failed! [{nRet&0xFFFFFFFF:#x}]")
|
||||||
|
self.connected = False
|
||||||
|
frame_res = None
|
||||||
|
else:
|
||||||
|
# get frame
|
||||||
|
frame_res = MV_VS_GetFrame(cam["handle"])
|
||||||
|
|
||||||
|
if frame_res is not None:
|
||||||
|
res = frame_res["res"]["ScDeviceSolutionRunningResult"]
|
||||||
|
frame = frame_res["frame"]
|
||||||
|
# rotate frame
|
||||||
|
if rot[cam_idx] == "1":
|
||||||
|
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
|
||||||
|
if rot[cam_idx] == "2":
|
||||||
|
frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
||||||
|
if rot[cam_idx] == "3":
|
||||||
|
frame = cv2.rotate(frame, cv2.ROTATE_180)
|
||||||
|
else:
|
||||||
|
self.connected = False
|
||||||
|
if concat_frame is None:
|
||||||
|
concat_frame = copy.deepcopy(frame)
|
||||||
|
else:
|
||||||
|
concat_frame = cv2.hconcat([concat_frame, frame])
|
||||||
|
concat_results.append(not res)
|
||||||
|
if not res:
|
||||||
|
self.ok_results[cam_idx] = True
|
||||||
|
self.ok_frames[cam_idx] = copy.deepcopy(frame)
|
||||||
|
|
||||||
|
super()._get([concat_frame, self.ok_results])
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
self.log.info(f"RESUMING")
|
self.log.info(f"RESUMING")
|
||||||
if not self.connected:
|
if not self.connected:
|
||||||
self.config_changed()
|
self.config_changed()
|
||||||
if self.connected:
|
if self.connected:
|
||||||
|
# init OK result memory
|
||||||
|
self.ok_frames = [None for n in range(self.num_cameras)]
|
||||||
|
self.ok_results = [False for n in range(self.num_cameras)]
|
||||||
|
|
||||||
if len(self.cam_list) == self.num_cameras:
|
if len(self.cam_list) == self.num_cameras:
|
||||||
# Dummy acquisition for each camera to avoid reading past images
|
# Dummy acquisition for each camera to avoid reading past images
|
||||||
for cam_idx in range(self.num_cameras):
|
for cam_idx in range(self.num_cameras):
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
import pymodbus
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
if "--sim-serial" in sys.argv:
|
if "--sim-serial" in sys.argv:
|
||||||
@ -18,113 +19,190 @@ if "--sim-modbus" not in sys.argv:
|
|||||||
else:
|
else:
|
||||||
from components.dummies.pymodbus import ModbusClient
|
from components.dummies.pymodbus import ModbusClient
|
||||||
|
|
||||||
from PyQt5.QtCore import QMutex
|
from PyQt5.QtCore import QMutex, pyqtSignal
|
||||||
|
|
||||||
from .component import Component
|
from .component import Component
|
||||||
|
|
||||||
|
|
||||||
class ModbusComponent(Component):
|
class ModbusComponent(Component):
|
||||||
|
modbus_error_signal = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True, registers=None):
|
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True, registers=None):
|
||||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||||
self.registers = registers if registers is not None else {}
|
self.registers = registers if registers is not None else {}
|
||||||
self.lock = QMutex()
|
self.lock = QMutex()
|
||||||
|
|
||||||
def config_changed(self):
|
def config_changed(self):
|
||||||
self.method = self.config[self.name].get("method", "rtu").lower()
|
|
||||||
self.port = self.config[self.name]["port"]
|
# Read configuration values
|
||||||
self.baudrate = int(self.config[self.name]["baudrate"])
|
self.method = self.config[self.name].get("method", "rtu").lower()
|
||||||
self.stopbits = getattr(serial, self.config[self.name].get("stopbits", "stopbits_one").upper())
|
self.port = self.config[self.name]["port"]
|
||||||
self.parity = getattr(serial, self.config[self.name].get("parity", "parity_none").upper())
|
self.baudrate = int(self.config[self.name]["baudrate"])
|
||||||
self.bytesize = getattr(serial, self.config[self.name].get("bytesize", "eightbits").upper())
|
self.stopbits = getattr(serial, self.config[self.name].get("stopbits", "stopbits_one").upper())
|
||||||
self.byteorder = getattr(Endian, self.config[self.name].get("byteorder", "Big").upper())
|
self.parity = getattr(serial, self.config[self.name].get("parity", "parity_none").upper())
|
||||||
self.wordorder = getattr(Endian, self.config[self.name].get("wordorder", "Little").upper())
|
self.bytesize = getattr(serial, self.config[self.name].get("bytesize", "eightbits").upper())
|
||||||
self.timeout = int(self.config[self.name].get("timeout", 1))
|
self.byteorder = getattr(Endian, self.config[self.name].get("byteorder", "Big").upper())
|
||||||
|
self.wordorder = getattr(Endian, self.config[self.name].get("wordorder", "Little").upper())
|
||||||
|
self.timeout = int(self.config[self.name].get("timeout", 1))
|
||||||
|
|
||||||
|
# Lock the interaction to ensure thread safety
|
||||||
|
self.lock.lock()
|
||||||
|
try:
|
||||||
|
# Initialize the Modbus client
|
||||||
|
self.client = ModbusClient(
|
||||||
|
method=self.method,
|
||||||
|
port=self.port,
|
||||||
|
stopbits=self.stopbits,
|
||||||
|
bytesize=self.bytesize,
|
||||||
|
parity=self.parity,
|
||||||
|
baudrate=self.baudrate,
|
||||||
|
timeout=self.timeout,
|
||||||
|
strict=False,
|
||||||
|
)
|
||||||
|
if not self.client.connect():
|
||||||
|
raise ConnectionError(f"Cannot connect to Modbus on port {self.port}")
|
||||||
|
|
||||||
|
if not self.client.is_socket_open():
|
||||||
|
raise ConnectionError(f"Connection socket not open on port {self.port}")
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
error_message = f"Serial port error: {self.port} not found. Check your configuration."
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Configuration error: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
finally:
|
||||||
|
self.lock.unlock()
|
||||||
|
|
||||||
|
def _read(self, register, count=1, **kwargs):
|
||||||
|
"""Read holding registers with error handling."""
|
||||||
self.lock.lock()
|
self.lock.lock()
|
||||||
self.client = ModbusClient(
|
try:
|
||||||
method=self.method,
|
read = self.client.read_holding_registers(register, count=count, **kwargs)
|
||||||
port=self.port,
|
|
||||||
stopbits=self.stopbits,
|
|
||||||
bytesize=self.bytesize,
|
|
||||||
parity=self.parity,
|
|
||||||
baudrate=self.baudrate,
|
|
||||||
timeout=self.timeout,
|
|
||||||
strict=False,
|
|
||||||
)
|
|
||||||
if not self.client.connect():
|
|
||||||
QMessageBox.critical(None, "ERRORE", f"ERRORE MODBUS - VERIFICARE CONNESSIONE USB")
|
|
||||||
exit(-1)
|
|
||||||
#raise ConnectionError("device not reachable (could not connect): {} ({})".format(self.name, self.port))
|
|
||||||
|
|
||||||
if not self.client.is_socket_open():
|
|
||||||
raise ConnectionError("device not reachable (socket not open): {} ({})".format(self.name, self.port))
|
|
||||||
self.lock.unlock()
|
|
||||||
|
|
||||||
def _read(self, register, count=1,**kwargs):
|
|
||||||
while True:
|
|
||||||
self.lock.lock()
|
|
||||||
read = self.client.read_holding_registers(register, count=count,**kwargs)
|
|
||||||
self.lock.unlock()
|
|
||||||
if read.isError():
|
if read.isError():
|
||||||
self.log.exception(traceback.format_exception(type(read), read, read.__traceback__))
|
error_message = f"Modbus read failed: Could not read Modbus register {register}"
|
||||||
# raise read
|
self.modbus_error_signal.emit(error_message)
|
||||||
else:
|
raise ValueError(f"Modbus read error at register {register}")
|
||||||
break
|
return read
|
||||||
return read
|
except pymodbus.exceptions.ConnectionException:
|
||||||
|
error_message = f"Modbus read failed: Connection error at port {self.port}"
|
||||||
def _write(self, register, value,**kwargs):
|
#self.log.error(error_message, exc_info=True)
|
||||||
while True:
|
self.modbus_error_signal.emit(error_message)
|
||||||
self.lock.lock()
|
return None # Return None to signal failure
|
||||||
wrote = self.client.write_registers(register, value, skip_encode=True,**kwargs)
|
except Exception as e:
|
||||||
|
error_message = f"Error reading Modbus register {register}: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
self.lock.unlock()
|
self.lock.unlock()
|
||||||
|
|
||||||
|
def _write(self, register, value, **kwargs):
|
||||||
|
"""Write to holding registers with error handling."""
|
||||||
|
self.lock.lock()
|
||||||
|
try:
|
||||||
|
wrote = self.client.write_registers(register, value, skip_encode=True, **kwargs)
|
||||||
|
|
||||||
|
# Check if the response indicates an error
|
||||||
if wrote.isError():
|
if wrote.isError():
|
||||||
self.log.exception(traceback.format_exception(type(wrote), wrote, wrote.__traceback__))
|
raise ValueError(f"Modbus write error at register {register}")
|
||||||
# raise wrote
|
return wrote
|
||||||
else:
|
except pymodbus.exceptions.ConnectionException as ce:
|
||||||
break
|
error_message = f"Modbus write failed: Connection error at port {self.port}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error writing Modbus register {register} with value {value}: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
finally:
|
||||||
|
self.lock.unlock()
|
||||||
|
|
||||||
def _decode(self, read, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
def _decode(self, read, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(read.registers, byteorder=self.byteorder, wordorder=self.wordorder)
|
"""Decode data safely."""
|
||||||
data = getattr(decoder, f"decode_{data_type}")(*args, **kwargs)
|
if read is None:
|
||||||
data = (data - offset) / gain
|
error_message = "Error decoding Modbus data: No data to decode (read returned None)"
|
||||||
if data_type.endswith("uint"):
|
#self.log.error(error_message)
|
||||||
data = int(abs(data))
|
self.modbus_error_signal.emit(error_message)
|
||||||
elif data_type.endswith("int"):
|
return None
|
||||||
data = int(data)
|
try:
|
||||||
else:
|
decoder = BinaryPayloadDecoder.fromRegisters(
|
||||||
raise NotImplementedError(f"data_type {data_type!r} is not supported")
|
read.registers, byteorder=self.byteorder, wordorder=self.wordorder
|
||||||
return data
|
)
|
||||||
|
data = getattr(decoder, f"decode_{data_type}")(*args, **kwargs)
|
||||||
|
data = (data - offset) / gain
|
||||||
|
return int(abs(data)) if "uint" in data_type else int(data)
|
||||||
|
except AttributeError:
|
||||||
|
error_message = "Modbus read returned invalid data (NoneType encountered)"
|
||||||
|
#self.log.error(error_message)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error decoding Modbus data: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
return None
|
||||||
|
|
||||||
def _encode(self, data, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
def _encode(self, data, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
||||||
builder = BinaryPayloadBuilder(byteorder=self.byteorder, wordorder=self.wordorder)
|
"""Encode data for Modbus write with error handling."""
|
||||||
data = data * gain + offset
|
try:
|
||||||
if data_type.endswith("uint"):
|
builder = BinaryPayloadBuilder(byteorder=self.byteorder, wordorder=self.wordorder)
|
||||||
data = int(abs(data))
|
data = data * gain + offset
|
||||||
elif data_type.endswith("int"):
|
if data_type.endswith("uint"):
|
||||||
data = int(data)
|
data = int(abs(data))
|
||||||
else:
|
elif data_type.endswith("int"):
|
||||||
raise NotImplementedError(f"data_type {data_type!r} is not supported")
|
data = int(data)
|
||||||
getattr(builder, f"add_{data_type}")(data, *args, **kwargs)
|
else:
|
||||||
return builder.build()
|
raise NotImplementedError(f"Data type {data_type!r} is not supported")
|
||||||
|
getattr(builder, f"add_{data_type}")(data, *args, **kwargs)
|
||||||
|
return builder.build()
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error encoding Modbus data: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
return None
|
||||||
|
|
||||||
def read(self, register, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
def read(self, register, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
||||||
if data_type.startswith("16bit_"):
|
"""Read and decode Modbus register data with error handling."""
|
||||||
count = 1
|
try:
|
||||||
elif data_type.startswith("32bit_"):
|
if data_type.startswith("16bit_"):
|
||||||
count = 2
|
count = 1
|
||||||
else:
|
elif data_type.startswith("32bit_"):
|
||||||
raise NotImplementedError(f"data_type {data_type!r} is not supported")
|
count = 2
|
||||||
|
else:
|
||||||
return self._decode(self._read(register, count=count, **kwargs), *args, data_type=data_type, gain=gain,offset=offset)
|
raise NotImplementedError(f"Data type {data_type!r} is not supported")
|
||||||
|
return self._decode(
|
||||||
|
self._read(register, count=count, **kwargs),
|
||||||
|
*args,
|
||||||
|
data_type=data_type,
|
||||||
|
gain=gain,
|
||||||
|
offset=offset,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error inside Modbus read: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
return None
|
||||||
|
|
||||||
def write(self, register, data, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
def write(self, register, data, *args, data_type="16bit_uint", gain=1, offset=0, **kwargs):
|
||||||
self._write(register, self._encode(data, *args, data_type=data_type, gain=gain, offset=offset),**kwargs)
|
"""Encode and write data to Modbus registers with error handling."""
|
||||||
|
try:
|
||||||
# def _get(self, data):
|
encoded_data = self._encode(data, *args, data_type=data_type, gain=gain, offset=offset)
|
||||||
# # print("MODBUS", str(int(QThread.currentThreadId())), flush=True)
|
if encoded_data is not None:
|
||||||
# super()._get(data)
|
self._write(register, encoded_data, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error inside Modbus write: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
self.modbus_error_signal.emit(error_message)
|
||||||
|
|
||||||
def __del__(self, event=None):
|
def __del__(self, event=None):
|
||||||
self.lock.lock()
|
try:
|
||||||
if self.client.is_socket_open():
|
self.lock.lock()
|
||||||
self.client.close()
|
if self.client.is_socket_open():
|
||||||
self.lock.unlock()
|
self.client.close()
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error during Modbus cleanup: {str(e)}"
|
||||||
|
#self.log.error(error_message, exc_info=True)
|
||||||
|
finally:
|
||||||
|
self.lock.unlock()
|
||||||
@ -85,5 +85,39 @@ class RFID_PN532(Component):
|
|||||||
self.rfid_error_signal.emit(False)
|
self.rfid_error_signal.emit(False)
|
||||||
self.mutex.unlock()
|
self.mutex.unlock()
|
||||||
|
|
||||||
def write_tag(self,data):
|
def write_tag(self, data):
|
||||||
self.data_to_write=copy.deepcopy(data)
|
self.data_to_write = copy.deepcopy(data)
|
||||||
|
if not self.simulate:
|
||||||
|
if not self.connected:
|
||||||
|
self.open_device()
|
||||||
|
|
||||||
|
if not self.connected:
|
||||||
|
self.log.error("Failed to connect to device")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.mutex.tryLock():
|
||||||
|
try:
|
||||||
|
target = self.clf.sense(RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F'))
|
||||||
|
if target is not None:
|
||||||
|
tag = src.lib.nfc.tag.activate(self.clf, target)
|
||||||
|
if tag is not None and tag.ndef is not None:
|
||||||
|
self.log.info("Detected NFC tag, writing data...")
|
||||||
|
ndef_record = ndef.TextRecord(self.data_to_write)
|
||||||
|
|
||||||
|
# IMPORTANT: DON'T CLOSE DEVICE BEFORE WRITING
|
||||||
|
tag.ndef.records = [ndef_record] # Write the record to the tag
|
||||||
|
self.log.info(f"Data '{self.data_to_write}' written successfully to tag.")
|
||||||
|
else:
|
||||||
|
self.log.error("Tag is not NDEF-compatible or invalid")
|
||||||
|
else:
|
||||||
|
self.log.debug("No tag present for writing")
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error(f"Error during tag writing: {e}")
|
||||||
|
finally:
|
||||||
|
# Move closing outside the try block
|
||||||
|
self.mutex.unlock() # Unlock mutex always
|
||||||
|
|
||||||
|
# Close AFTER writing and mutex release
|
||||||
|
if self.connected:
|
||||||
|
self.close_device() # close_device can raise exception
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
from lib.db import Recipes, db
|
from lib.db import Recipes, db
|
||||||
from PyQt5.QtCore import QSemaphore, pyqtSignal
|
from PyQt5.QtCore import QSemaphore, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from .component import Component
|
from .component import Component
|
||||||
@ -11,9 +13,11 @@ from .tecna_marposs_provaset_t3p_registers import registers as t3p_registers
|
|||||||
class TecnaMarpossProvasetT3(ModbusComponent):
|
class TecnaMarpossProvasetT3(ModbusComponent):
|
||||||
_store_recipes_signal = pyqtSignal(object)
|
_store_recipes_signal = pyqtSignal(object)
|
||||||
_store_recipes_lock = QSemaphore(0)
|
_store_recipes_lock = QSemaphore(0)
|
||||||
|
tecna_error_signal = pyqtSignal(bool, str) # Emits (True, error_message) if error exists, else (False, "")
|
||||||
|
|
||||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
|
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
|
||||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded, registers=None)
|
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded, registers=None)
|
||||||
|
self.modbus_error_signal.connect(self.handle_modbus_error)
|
||||||
|
|
||||||
def config_changed(self):
|
def config_changed(self):
|
||||||
super().config_changed()
|
super().config_changed()
|
||||||
@ -26,13 +30,17 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
|||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.")
|
raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.")
|
||||||
self.set_measure_units()
|
self.set_measure_units()
|
||||||
self.units = self.get_measure_units()
|
try:
|
||||||
self.max_program_number = self.read("Max number of programs")
|
self.units = self.get_measure_units()
|
||||||
self.saver_label_count = min(abs(int(self.config[self.name].get("saver_label_count", 1))), 0b1111)
|
self.max_program_number = self.read("Max number of programs")
|
||||||
self.saver_print_on_fail = 1 if self.config[self.name].get("saver_print_on_fail", "no").lower() in {"yes", "y", "on", "true", "1", "x"} else 0
|
self.saver_label_count = min(abs(int(self.config[self.name].get("saver_label_count", 1))), 0b1111)
|
||||||
self.saver_label_template = min(abs(int(self.config[self.name].get("saver_label_template", 1))), 0b11111111)
|
self.saver_print_on_fail = 1 if self.config[self.name].get("saver_print_on_fail", "no").lower() in {"yes", "y", "on", "true", "1", "x"} else 0
|
||||||
self.model = self.config[self.name]["model"].lower()
|
self.saver_label_template = min(abs(int(self.config[self.name].get("saver_label_template", 1))), 0b11111111)
|
||||||
self.log.info(f"units: {self.units}")
|
self.model = self.config[self.name]["model"].lower()
|
||||||
|
self.log.info(f"units: {self.units}")
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error during config_changed operation: {str(e)}"
|
||||||
|
self.tecna_error_signal.emit(True, error_message)
|
||||||
|
|
||||||
_pressure_units = {"mH2O": 0, "mbar": 1, "kPa": 2, "mmHg": 3, "inH2O": 4, "psi": 5, "mmH2O": 6, } # (se fondoscala <=6 bar)
|
_pressure_units = {"mH2O": 0, "mbar": 1, "kPa": 2, "mmHg": 3, "inH2O": 4, "psi": 5, "mmH2O": 6, } # (se fondoscala <=6 bar)
|
||||||
_leak_units = {"mmH2O": 0, "mbar": 1, "Pa": 2, "mmHg": 3, "inH2O": 4, "psi": 5, }
|
_leak_units = {"mmH2O": 0, "mbar": 1, "Pa": 2, "mmHg": 3, "inH2O": 4, "psi": 5, }
|
||||||
@ -125,111 +133,133 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@Component.reconfig_on_error
|
@Component.reconfig_on_error
|
||||||
def read(self, register, *args, data_type=None, gain=None, offset=None, formatting=None, decoding_map=None, **kwargs):
|
def read(self, register, *args, data_type=None, gain=None, offset=None, formatting=None, decoding_map=None,
|
||||||
if type(register) is str:
|
**kwargs):
|
||||||
register, s = self.registers[register]
|
try:
|
||||||
|
if type(register) is str:
|
||||||
|
register, s = self.registers[register]
|
||||||
|
if data_type is None:
|
||||||
|
data_type = s.get("dt", None)
|
||||||
|
if gain is None:
|
||||||
|
gain = s.get("g", None)
|
||||||
|
if offset is None:
|
||||||
|
offset = s.get("o", None)
|
||||||
|
if formatting is None:
|
||||||
|
formatting = s.get("f", None)
|
||||||
|
if decoding_map is None:
|
||||||
|
decoding_map = s.get("decoding", None)
|
||||||
|
if not len(args):
|
||||||
|
args = s.get("a", [])
|
||||||
|
if not len(kwargs):
|
||||||
|
kwargs = s.get("k", {})
|
||||||
if data_type is None:
|
if data_type is None:
|
||||||
data_type = s.get("dt", None)
|
data_type = "16bit_uint"
|
||||||
if gain is None:
|
if gain is None:
|
||||||
gain = s.get("g", None)
|
gain = 1
|
||||||
if offset is None:
|
if offset is None:
|
||||||
offset = s.get("o", None)
|
offset = 0
|
||||||
if formatting is None:
|
return self._convert_from_format(
|
||||||
formatting = s.get("f", None)
|
super().read(
|
||||||
if decoding_map is None:
|
register,
|
||||||
decoding_map = s.get("decoding", None)
|
*args,
|
||||||
if not len(args):
|
data_type=data_type,
|
||||||
args = s.get("a", [])
|
gain=gain,
|
||||||
if not len(kwargs):
|
offset=offset,
|
||||||
kwargs = s.get("k", {})
|
**kwargs,
|
||||||
if data_type is None:
|
),
|
||||||
data_type = "16bit_uint"
|
formatting=formatting,
|
||||||
if gain is None:
|
decoding_map=decoding_map,
|
||||||
gain = 1
|
)
|
||||||
if offset is None:
|
except Exception as e:
|
||||||
offset = 0
|
error_message = f"Error during read operation on register {register}: {str(e)}"
|
||||||
return self._convert_from_format(
|
self.log.error(error_message, exc_info=True)
|
||||||
super().read(
|
self.tecna_error_signal.emit(True, error_message)
|
||||||
|
raise # Re-raise the exception for further upstream handling if needed
|
||||||
|
|
||||||
|
@Component.reconfig_on_error
|
||||||
|
@Component.reconfig_on_error
|
||||||
|
def write(self, register, data, *args, data_type=None, gain=None, offset=None, formatting=None, encoding_map=None,
|
||||||
|
**kwargs):
|
||||||
|
try:
|
||||||
|
if type(register) is str:
|
||||||
|
register, s = self.registers[register]
|
||||||
|
if data_type is None:
|
||||||
|
data_type = s.get("dt", None)
|
||||||
|
if gain is None:
|
||||||
|
gain = s.get("g", None)
|
||||||
|
if offset is None:
|
||||||
|
offset = s.get("o", None)
|
||||||
|
if formatting is None:
|
||||||
|
formatting = s.get("f", None)
|
||||||
|
if encoding_map is None:
|
||||||
|
encoding_map = s.get("encoding", None)
|
||||||
|
if not len(args):
|
||||||
|
args = s.get("a", [])
|
||||||
|
if not len(kwargs):
|
||||||
|
kwargs = s.get("k", {})
|
||||||
|
if data_type is None:
|
||||||
|
data_type = "16bit_uint"
|
||||||
|
if gain is None:
|
||||||
|
gain = 1
|
||||||
|
if offset is None:
|
||||||
|
offset = 0
|
||||||
|
return super().write(
|
||||||
register,
|
register,
|
||||||
|
self._convert_to_format(
|
||||||
|
data,
|
||||||
|
formatting=formatting,
|
||||||
|
encoding_map=encoding_map,
|
||||||
|
),
|
||||||
*args,
|
*args,
|
||||||
data_type=data_type,
|
data_type=data_type,
|
||||||
gain=gain,
|
gain=gain,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
),
|
)
|
||||||
formatting=formatting,
|
except Exception as e:
|
||||||
decoding_map=decoding_map,
|
error_message = f"Error during write operation on register {register} with data {data}: {str(e)}"
|
||||||
)
|
self.log.error(error_message, exc_info=True)
|
||||||
|
self.tecna_error_signal.emit(True, error_message)
|
||||||
@Component.reconfig_on_error
|
raise # Re-raise the exception for further upstream handling if needed
|
||||||
def write(self, register, data, *args, data_type=None, gain=None, offset=None, formatting=None, encoding_map=None, **kwargs):
|
|
||||||
if type(register) is str:
|
|
||||||
register, s = self.registers[register]
|
|
||||||
if data_type is None:
|
|
||||||
data_type = s.get("dt", None)
|
|
||||||
if gain is None:
|
|
||||||
gain = s.get("g", None)
|
|
||||||
if offset is None:
|
|
||||||
offset = s.get("o", None)
|
|
||||||
if formatting is None:
|
|
||||||
formatting = s.get("f", None)
|
|
||||||
if encoding_map is None:
|
|
||||||
encoding_map = s.get("encoding", None)
|
|
||||||
if not len(args):
|
|
||||||
args = s.get("a", [])
|
|
||||||
if not len(kwargs):
|
|
||||||
kwargs = s.get("k", {})
|
|
||||||
if data_type is None:
|
|
||||||
data_type = "16bit_uint"
|
|
||||||
if gain is None:
|
|
||||||
gain = 1
|
|
||||||
if offset is None:
|
|
||||||
offset = 0
|
|
||||||
return super().write(
|
|
||||||
register,
|
|
||||||
self._convert_to_format(
|
|
||||||
data,
|
|
||||||
formatting=formatting,
|
|
||||||
encoding_map=encoding_map,
|
|
||||||
),
|
|
||||||
*args,
|
|
||||||
data_type=data_type,
|
|
||||||
gain=gain,
|
|
||||||
offset=offset,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Component.reconfig_on_error
|
@Component.reconfig_on_error
|
||||||
def _get(self):
|
def _get(self):
|
||||||
# print("TECNA", str(int(QThread.currentThreadId())), flush=True)
|
try:
|
||||||
# READ INFO
|
# READ INFO
|
||||||
info = {r: self.read(r) for r in [
|
info = {r: self.read(r) for r in [
|
||||||
"Real time test pressure output",
|
"Real time test pressure output",
|
||||||
"Real time differential pressure output",
|
"Real time differential pressure output",
|
||||||
"Real time pressure line regulator",
|
"Real time pressure line regulator",
|
||||||
"Active alarm flags",
|
"Active alarm flags",
|
||||||
"Active test program number",
|
"Active test program number",
|
||||||
"Running test: active phase",
|
"Running test: active phase",
|
||||||
"Running test: test type",
|
"Running test: test type",
|
||||||
"Running test: sequence index",
|
"Running test: sequence index",
|
||||||
"Digital inputs status (mask)",
|
"Digital inputs status (mask)",
|
||||||
# "Digital outputs status (mask)",
|
# "Digital outputs status (mask)",
|
||||||
]}
|
]}
|
||||||
if self.model == "t3p":
|
if self.model == "t3p":
|
||||||
pass
|
pass
|
||||||
elif self.model == "t3l":
|
elif self.model == "t3l":
|
||||||
info.update({r: self.read(r) for r in [
|
info.update({r: self.read(r) for r in [
|
||||||
"Active not severe alarm flags",
|
"Active not severe alarm flags",
|
||||||
]})
|
]})
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Tecna t3 model {self.model!r} not implemented.")
|
raise NotImplementedError(f"Tecna t3 model {self.model!r} not implemented.")
|
||||||
if info["Running test: active phase"] == "FINE TEST": # "END TEST, WAITING THE START OF A NEW TEST":
|
|
||||||
info.update(self.get_test_results())
|
if info["Running test: active phase"] == "FINE TEST": # "END TEST, WAITING THE START OF A NEW TEST":
|
||||||
for round_me in ["measured leak"]:
|
info.update(self.get_test_results())
|
||||||
if round_me in info.keys():
|
|
||||||
info.update({round_me:float(f"{info[round_me]:.2f}")})
|
for round_me in ["measured leak"]:
|
||||||
self.log.debug(str(info))
|
if round_me in info.keys():
|
||||||
super()._get([info])
|
info.update({round_me: float(f"{info[round_me]:.2f}")})
|
||||||
|
self.log.debug(str(info))
|
||||||
|
super()._get([info])
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error during _get operation: {str(e)}"
|
||||||
|
self.log.error(error_message, exc_info=True)
|
||||||
|
self.tecna_error_signal.emit(True, error_message)
|
||||||
|
raise # Re-raise the exception for further upstream handling
|
||||||
|
|
||||||
@Component.reconfig_on_error
|
@Component.reconfig_on_error
|
||||||
def _set(self, val=None):
|
def _set(self, val=None):
|
||||||
@ -299,7 +329,7 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
|||||||
"FSL - Discharge limit": step.spec["flush_pressure"],
|
"FSL - Discharge limit": step.spec["flush_pressure"],
|
||||||
"PSQ - Next sequence program PSOUT mode": 0,
|
"PSQ - Next sequence program PSOUT mode": 0,
|
||||||
"RAMPS: T1 configuration": pid_ramps,
|
"RAMPS: T1 configuration": pid_ramps,
|
||||||
"PID: pressure correction": step.spec["pid_pressure_correction"],
|
"PID: pressure correction": step.spec.get("pid_pressure_correction",0),
|
||||||
"Various flags": 0b0000000000010000 if self.config["recipes_defaults"]["tester_discharge_enable"] == "yes" else 0b0000000000000000
|
"Various flags": 0b0000000000010000 if self.config["recipes_defaults"]["tester_discharge_enable"] == "yes" else 0b0000000000000000
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -402,6 +432,7 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
|||||||
# READ ALL PARAMETERS
|
# READ ALL PARAMETERS
|
||||||
for register_name, field_name in spec.items():
|
for register_name, field_name in spec.items():
|
||||||
recipe_data[field_name] = self.read(register_name)
|
recipe_data[field_name] = self.read(register_name)
|
||||||
|
time.sleep(0.01)
|
||||||
recipe_name=self.int_array_to_str([recipe_data[f"R{wn}"] for wn in range(8)])
|
recipe_name=self.int_array_to_str([recipe_data[f"R{wn}"] for wn in range(8)])
|
||||||
recipe_code=self.int_array_to_str([recipe_data[f"C{wn}"] for wn in range(12)])
|
recipe_code=self.int_array_to_str([recipe_data[f"C{wn}"] for wn in range(12)])
|
||||||
recipe_f1=self.int_array_to_str([recipe_data[f"F1_{wn}"] for wn in range(8)])
|
recipe_f1=self.int_array_to_str([recipe_data[f"F1_{wn}"] for wn in range(8)])
|
||||||
@ -446,3 +477,19 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
|||||||
break
|
break
|
||||||
result+=char2
|
result+=char2
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def handle_modbus_error(self, error_message):
|
||||||
|
"""
|
||||||
|
Handle errors received from the ModbusComponent and send
|
||||||
|
appropriate error signals for Tecna Marposs.
|
||||||
|
"""
|
||||||
|
if error_message:
|
||||||
|
# Emit the Tecna-specific error signal with True and the error message
|
||||||
|
self.tecna_error_signal.emit(True, error_message)
|
||||||
|
self.log.error(f"Modbus error encountered: {error_message}")
|
||||||
|
return True, error_message
|
||||||
|
else:
|
||||||
|
# Emit no error (False)
|
||||||
|
self.tecna_error_signal.emit(False, "")
|
||||||
|
return False, None
|
||||||
@ -125,11 +125,21 @@ try:
|
|||||||
def read_recipes(self):
|
def read_recipes(self):
|
||||||
self.recipes = {}
|
self.recipes = {}
|
||||||
max_num_recipes=self.components["tecna_t3"].max_program_number
|
max_num_recipes=self.components["tecna_t3"].max_program_number
|
||||||
max_num_recipes=300
|
#max_num_recipes=100
|
||||||
logging.info(f"NUMBER OF RECIPES TO BE READ: {max_num_recipes}")
|
logging.info(f"NUMBER OF RECIPES TO BE READ: {max_num_recipes}")
|
||||||
for recipe_num in range(1, max_num_recipes+1):
|
for recipe_num in range(1, max_num_recipes+1):
|
||||||
logging.info(f"READING RECIPE #{recipe_num}")
|
recipe_ok=False
|
||||||
self.recipes[recipe_num] = self.components["tecna_t3"].read_recipe(recipe_num)
|
for retry in range(3):
|
||||||
|
logging.info(f"READING RECIPE #{recipe_num}")
|
||||||
|
try:
|
||||||
|
self.recipes[recipe_num] = self.components["tecna_t3"].read_recipe(recipe_num)
|
||||||
|
recipe_ok=True
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f"ERROR - RETRYING RECIPE #{recipe_num}")
|
||||||
|
if not recipe_ok:
|
||||||
|
self.recipes[recipe_num] = {"recipe_name":"READ_ERROR"}
|
||||||
|
|
||||||
|
|
||||||
def export_recipes(self, csv_path=None):
|
def export_recipes(self, csv_path=None):
|
||||||
if csv_path is None:
|
if csv_path is None:
|
||||||
|
|||||||
@ -6,14 +6,52 @@ from PyQt5 import Qt
|
|||||||
from lib.helpers import timing
|
from lib.helpers import timing
|
||||||
from PyQt5.QtCore import Qt, QTimer, QThread
|
from PyQt5.QtCore import Qt, QTimer, QThread
|
||||||
from PyQt5.QtGui import QKeySequence, QPixmap, QPalette, QColor
|
from PyQt5.QtGui import QKeySequence, QPixmap, QPalette, QColor
|
||||||
from PyQt5.QtWidgets import QShortcut, QApplication
|
from PyQt5.QtWidgets import QShortcut, QApplication, QDialog, QVBoxLayout, QLabel, QPushButton, QPlainTextEdit, \
|
||||||
|
QHBoxLayout, QMessageBox
|
||||||
|
|
||||||
from ui.test_test import Test_Test
|
from ui.test_test import Test_Test
|
||||||
from ui.widget import Widget
|
from ui.widget import Widget
|
||||||
from lib import db
|
from lib import db
|
||||||
from lib.db.models import Recipes
|
from lib.db.models import Recipes , Users
|
||||||
test_scan="xxx\nyyy\nzzz"
|
test_scan="xxx\nyyy\nzzz"
|
||||||
|
|
||||||
|
|
||||||
|
class TagWindow(QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setWindowTitle("Scrittura NFC")
|
||||||
|
self.setFixedSize(400, 300)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
|
||||||
|
self.text_edit = QPlainTextEdit(self)
|
||||||
|
self.text_edit.setPlaceholderText("Scanerizza il QRcode della Dima")
|
||||||
|
self.text_edit.setFixedHeight(100)
|
||||||
|
|
||||||
|
|
||||||
|
layout.addWidget(self.text_edit)
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
|
||||||
|
self.ok_button = QPushButton("Conferma Tag", self)
|
||||||
|
self.ok_button.clicked.connect(self.on_ok_clicked)
|
||||||
|
|
||||||
|
# Create the Close button
|
||||||
|
self.close_button = QPushButton("Chiudi", self)
|
||||||
|
self.close_button.clicked.connect(self.close)
|
||||||
|
|
||||||
|
button_layout.addWidget(self.ok_button)
|
||||||
|
button_layout.addWidget(self.close_button)
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
def on_ok_clicked(self):
|
||||||
|
entered_text = self.text_edit.toPlainText()
|
||||||
|
print(f"Text Entered: {entered_text}")
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
class Barcode_Recipe_Selection(Test_Test):
|
class Barcode_Recipe_Selection(Test_Test):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -34,6 +72,8 @@ class Barcode_Recipe_Selection(Test_Test):
|
|||||||
self.status_palettes["warning"].setColor(QPalette.Base, QColor(255, 165, 0))
|
self.status_palettes["warning"].setColor(QPalette.Base, QColor(255, 165, 0))
|
||||||
self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0))
|
self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0))
|
||||||
|
|
||||||
|
self.tag_b.clicked.connect(self.open_tag_window)
|
||||||
|
|
||||||
self.recipe_selection_b.clicked.connect(self.parent.set_recipe_mode_table)
|
self.recipe_selection_b.clicked.connect(self.parent.set_recipe_mode_table)
|
||||||
self.barcode_input_l.setFocus()
|
self.barcode_input_l.setFocus()
|
||||||
|
|
||||||
@ -49,6 +89,12 @@ class Barcode_Recipe_Selection(Test_Test):
|
|||||||
self.ok_timer.setSingleShot(True)
|
self.ok_timer.setSingleShot(True)
|
||||||
self.ok_timer.timeout.connect(self.set_recipe)
|
self.ok_timer.timeout.connect(self.set_recipe)
|
||||||
|
|
||||||
|
session= Users.get_session()
|
||||||
|
|
||||||
|
if session.is_admin:
|
||||||
|
self.tag_b.setVisible(True)
|
||||||
|
else:
|
||||||
|
self.tag_b.setVisible(False)
|
||||||
|
|
||||||
def start(self, recipe=None, step=None, pieces=None):
|
def start(self, recipe=None, step=None, pieces=None):
|
||||||
|
|
||||||
@ -113,4 +159,35 @@ class Barcode_Recipe_Selection(Test_Test):
|
|||||||
self.parent_assembly_widget().set_text("SCANSIONARE BARCODE SELEZIONE RICETTA")
|
self.parent_assembly_widget().set_text("SCANSIONARE BARCODE SELEZIONE RICETTA")
|
||||||
|
|
||||||
|
|
||||||
|
def open_tag_window(self):
|
||||||
|
|
||||||
|
# Open the tag writing window
|
||||||
|
self.tag_window = TagWindow(self)
|
||||||
|
result = self.tag_window.exec_() # Get the result of the dialog (OK or Cancel)
|
||||||
|
if result != QDialog.Accepted:
|
||||||
|
return # If canceled, return
|
||||||
|
|
||||||
|
entered_text = self.tag_window.text_edit.toPlainText().strip()
|
||||||
|
if not entered_text:
|
||||||
|
QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"Warning",
|
||||||
|
"The text is empty. Please enter text to write to the tag.",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if the tag is empty
|
||||||
|
if self.parent.components["fixture_id"].current_data:
|
||||||
|
# Tag already contains data, ask for confirmation to overwrite
|
||||||
|
confirmation_result = QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"Tag Already Written",
|
||||||
|
"The tag already contains data. Do you want to overwrite it?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No,
|
||||||
|
)
|
||||||
|
if confirmation_result == QMessageBox.No:
|
||||||
|
return # User does not want to overwrite, so return without writing
|
||||||
|
|
||||||
|
# Write the entered text to the tag
|
||||||
|
self.parent.components["fixture_id"].write_tag(entered_text)
|
||||||
|
QMessageBox.information(self, "Success", "The tag has been successfully written.")
|
||||||
|
|||||||
@ -13,54 +13,146 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Test Connector</string>
|
<string>Test Connector</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<property name="leftMargin">
|
<item row="0" column="0">
|
||||||
<number>0</number>
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
</property>
|
<item row="0" column="0">
|
||||||
<property name="topMargin">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<number>0</number>
|
<item row="1" column="3">
|
||||||
</property>
|
<spacer name="horizontalSpacer_2">
|
||||||
<property name="rightMargin">
|
<property name="orientation">
|
||||||
<number>0</number>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="sizeHint" stdset="0">
|
||||||
<number>0</number>
|
<size>
|
||||||
</property>
|
<width>308</width>
|
||||||
<item row="2" column="0" alignment="Qt::AlignHCenter">
|
<height>20</height>
|
||||||
<widget class="QPlainTextEdit" name="barcode_input_l">
|
</size>
|
||||||
<property name="minimumSize">
|
</property>
|
||||||
<size>
|
</spacer>
|
||||||
<width>600</width>
|
</item>
|
||||||
<height>0</height>
|
<item row="4" column="1" colspan="2">
|
||||||
</size>
|
<widget class="QPushButton" name="recipe_selection_b">
|
||||||
</property>
|
<property name="font">
|
||||||
<property name="maximumSize">
|
<font>
|
||||||
<size>
|
<pointsize>20</pointsize>
|
||||||
<width>600</width>
|
</font>
|
||||||
<height>200</height>
|
</property>
|
||||||
</size>
|
<property name="text">
|
||||||
</property>
|
<string>TORNA ALLA TABELLA RICETTE</string>
|
||||||
<property name="font">
|
</property>
|
||||||
<font>
|
</widget>
|
||||||
<pointsize>20</pointsize>
|
</item>
|
||||||
</font>
|
<item row="1" column="1" rowspan="2" colspan="2">
|
||||||
</property>
|
<widget class="QPlainTextEdit" name="barcode_input_l">
|
||||||
<property name="plainText">
|
<property name="minimumSize">
|
||||||
<string/>
|
<size>
|
||||||
</property>
|
<width>600</width>
|
||||||
</widget>
|
<height>0</height>
|
||||||
</item>
|
</size>
|
||||||
<item row="3" column="0" alignment="Qt::AlignHCenter">
|
</property>
|
||||||
<widget class="QPushButton" name="recipe_selection_b">
|
<property name="maximumSize">
|
||||||
<property name="font">
|
<size>
|
||||||
<font>
|
<width>600</width>
|
||||||
<pointsize>20</pointsize>
|
<height>200</height>
|
||||||
</font>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="font">
|
||||||
<string>TORNA ALLA TABELLA RICETTE</string>
|
<font>
|
||||||
</property>
|
<pointsize>20</pointsize>
|
||||||
</widget>
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="plainText">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>78</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>378</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>78</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>168</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="tag_b">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>SCRIVI TAG NFC</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@ -28,7 +28,6 @@ from ui.test_vision import Test_Vision
|
|||||||
from ui.test_warning_img import Test_Warning_Img
|
from ui.test_warning_img import Test_Warning_Img
|
||||||
from ui.widget import Widget
|
from ui.widget import Widget
|
||||||
from components import ArchiveSynchronizer
|
from components import ArchiveSynchronizer
|
||||||
from src.components.rfid_pn532 import RFID_PN532
|
|
||||||
|
|
||||||
|
|
||||||
class Test(Widget):
|
class Test(Widget):
|
||||||
@ -58,17 +57,28 @@ class Test(Widget):
|
|||||||
else:
|
else:
|
||||||
self.flag_label.setVisible(False)
|
self.flag_label.setVisible(False)
|
||||||
|
|
||||||
|
self.active_errors = [] # List to hold current errors (type: tuples of (message, is_error))
|
||||||
|
self.current_error_index = 0 # Keeps track of the current error index during alternation
|
||||||
|
|
||||||
|
# Timer for alternating errors
|
||||||
|
self.error_timer = QTimer()
|
||||||
|
self.error_timer.setInterval(2000) # Fire every 2 seconds
|
||||||
|
self.error_timer.timeout.connect(self.display_current_error) # Connect the timer to the display logic
|
||||||
|
|
||||||
# SHOW AND UPDATE TIME CLOCK
|
# SHOW AND UPDATE TIME CLOCK
|
||||||
self.refresh_time(init=True)
|
self.refresh_time(init=True)
|
||||||
# INIT RECIPE
|
# INIT RECIPE
|
||||||
self.recipe = None
|
self.recipe = None
|
||||||
self.auto_import = Recipe_Selection
|
if "fixture_id" in self.components:
|
||||||
self.archive_synch = ArchiveSynchronizer(config=config)
|
self.rfid = self.components["fixture_id"]
|
||||||
|
self.rfid.rfid_error_signal.connect(self.handle_rfid_error)
|
||||||
self.rfid = RFID_PN532(config=config)
|
if "tecna_t3" in self.components:
|
||||||
|
self.tecna = self.components["tecna_t3"]
|
||||||
|
self.tecna.tecna_error_signal.connect(self.handle_modbus_error)
|
||||||
self.error_label.setText("")
|
self.error_label.setText("")
|
||||||
self.error_label.setStyleSheet("QLabel { color: red; }")
|
self.error_label.setStyleSheet("QLabel { color: red; }")
|
||||||
self.rfid.rfid_error_signal.connect(self.handle_rfid_error)
|
|
||||||
|
|
||||||
|
|
||||||
if self.config["hardware_config"]["barcode_recipe_selection"] == "present":
|
if self.config["hardware_config"]["barcode_recipe_selection"] == "present":
|
||||||
self.recipe_selection_mode = "barcode"
|
self.recipe_selection_mode = "barcode"
|
||||||
@ -78,6 +88,7 @@ class Test(Widget):
|
|||||||
self.tester_component = None
|
self.tester_component = None
|
||||||
if self.config["hardware_config"]["tecna_t3"] == "present":
|
if self.config["hardware_config"]["tecna_t3"] == "present":
|
||||||
self.tester_component = "tecna_t3"
|
self.tester_component = "tecna_t3"
|
||||||
|
self.components["tecna_t3"].tecna_error_signal.connect(self.handle_modbus_error)
|
||||||
elif self.config["hardware_config"]["furness_controls"] == "present":
|
elif self.config["hardware_config"]["furness_controls"] == "present":
|
||||||
self.tester_component = "furness_control"
|
self.tester_component = "furness_control"
|
||||||
|
|
||||||
@ -828,13 +839,89 @@ class Test(Widget):
|
|||||||
self.fail_cycle()
|
self.fail_cycle()
|
||||||
self.change_recipe()
|
self.change_recipe()
|
||||||
|
|
||||||
|
def add_error(self, message, is_error):
|
||||||
|
"""
|
||||||
|
Add a new error message to the list of active errors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (str): The error message to add.
|
||||||
|
is_error (bool): True if it's an error that should show in red, False for non-errors.
|
||||||
|
"""
|
||||||
|
# Avoid duplicates
|
||||||
|
if (message, is_error) not in self.active_errors:
|
||||||
|
self.active_errors.append((message, is_error))
|
||||||
|
|
||||||
|
# Start the timer if it's not already active
|
||||||
|
if not self.error_timer.isActive():
|
||||||
|
self.error_timer.start()
|
||||||
|
|
||||||
|
def remove_error(self, message):
|
||||||
|
"""
|
||||||
|
Remove an error message from the list of active errors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (str): The error message to remove.
|
||||||
|
"""
|
||||||
|
self.active_errors = [err for err in self.active_errors if err[0] != message]
|
||||||
|
|
||||||
|
# Reset the error index if necessary
|
||||||
|
if self.current_error_index >= len(self.active_errors):
|
||||||
|
self.current_error_index = 0
|
||||||
|
|
||||||
|
# Stop the timer if there are no more errors
|
||||||
|
if not self.active_errors:
|
||||||
|
self.error_label.setText("")
|
||||||
|
self.error_label.setStyleSheet("")
|
||||||
|
self.error_timer.stop()
|
||||||
|
|
||||||
|
def display_current_error(self):
|
||||||
|
"""
|
||||||
|
Display the current error from the active errors list. If there are multiple errors,
|
||||||
|
it alternates between them every time this function is called.
|
||||||
|
"""
|
||||||
|
if self.active_errors:
|
||||||
|
# Get the current error message and update the label
|
||||||
|
message, is_error = self.active_errors[self.current_error_index]
|
||||||
|
self.error_label.setText(message)
|
||||||
|
if is_error:
|
||||||
|
self.error_label.setStyleSheet("color: red;")
|
||||||
|
else:
|
||||||
|
self.error_label.setStyleSheet("color: green;")
|
||||||
|
|
||||||
|
# Move to the next error for the next call to this method
|
||||||
|
self.current_error_index = (self.current_error_index + 1) % len(self.active_errors)
|
||||||
|
else:
|
||||||
|
# Clear the label if there are no errors
|
||||||
|
self.error_label.setText("")
|
||||||
|
self.error_label.setStyleSheet("")
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def handle_rfid_error(self, connected):
|
def handle_rfid_error(self, connected):
|
||||||
|
"""
|
||||||
|
Handle errors related to the RFID system.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
connected (bool): True if connected, False if not.
|
||||||
|
"""
|
||||||
if connected:
|
if connected:
|
||||||
self.error_label.setText("") # Update the GUI label
|
self.remove_error("Errore RFID.") # Remove the RFID error
|
||||||
else:
|
else:
|
||||||
self.error_label.setText("Errore RFID.") # Update the GUI label
|
self.add_error("Errore RFID.", True) # Add the RFID error
|
||||||
self.error_label.setStyleSheet("color: red;")
|
|
||||||
|
@pyqtSlot(bool, str)
|
||||||
|
def handle_modbus_error(self, has_error, error_message):
|
||||||
|
"""
|
||||||
|
Handle Tecna/Modbus errors and manage the error list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
has_error (bool): True if there is an error, False otherwise.
|
||||||
|
error_message (str): The error message to add.
|
||||||
|
"""
|
||||||
|
#print(f"DEBUG: Modbus error handler called - has_error={has_error}, error_message={error_message}") # Debugging
|
||||||
|
if has_error:
|
||||||
|
self.add_error(f"Errore Tecna", True) # Add the Modbus error
|
||||||
|
else:
|
||||||
|
self.remove_error(f"Errore Tecna") # Remove the Modbus error
|
||||||
|
|
||||||
def update_label_with_args(self, extra_info=None):
|
def update_label_with_args(self, extra_info=None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1192</width>
|
<width>1192</width>
|
||||||
<height>114</height>
|
<height>120</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -412,7 +412,7 @@ CONTATORE</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="9">
|
||||||
<widget class="QLabel" name="flag_label">
|
<widget class="QLabel" name="flag_label">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import weakref
|
|||||||
|
|
||||||
from PyQt5.QtGui import QPixmap
|
from PyQt5.QtGui import QPixmap
|
||||||
from PyQt5.QtWidgets import QMessageBox, QDialog, QApplication
|
from PyQt5.QtWidgets import QMessageBox, QDialog, QApplication
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt, pyqtSlot
|
||||||
from ui import Dialog
|
from ui import Dialog
|
||||||
from ui.test_instructions_reminder import Test_Instructions_Reminder
|
from ui.test_instructions_reminder import Test_Instructions_Reminder
|
||||||
from ui.test_test import Test_Test
|
from ui.test_test import Test_Test
|
||||||
@ -26,6 +26,8 @@ class Test_Leak(Test_Test):
|
|||||||
self.show_instruction_b.setVisible("show_instructions" in self.parent.config["hardware_config"].keys())
|
self.show_instruction_b.setVisible("show_instructions" in self.parent.config["hardware_config"].keys())
|
||||||
self.show_instruction_b.clicked.connect(self.show_instruction)
|
self.show_instruction_b.clicked.connect(self.show_instruction)
|
||||||
|
|
||||||
|
self.components[self.tester_component].tecna_error_signal.connect(self.handle_modbus_error)
|
||||||
|
|
||||||
def show_instruction(self):
|
def show_instruction(self):
|
||||||
dialog=Dialog()
|
dialog=Dialog()
|
||||||
dialog.setCentralWidget(Test_Instructions_Reminder(recipe=self.parent.recipe,bench_name=self.parent.config.machine_id))
|
dialog.setCentralWidget(Test_Instructions_Reminder(recipe=self.parent.recipe,bench_name=self.parent.config.machine_id))
|
||||||
@ -312,3 +314,15 @@ class Test_Leak(Test_Test):
|
|||||||
def save_last(self):
|
def save_last(self):
|
||||||
if self.last is None:
|
if self.last is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool, str)
|
||||||
|
def handle_modbus_error(self, error, error_message):
|
||||||
|
"""
|
||||||
|
Handle errors received from the Modbus component and stop the test gracefully.
|
||||||
|
"""
|
||||||
|
if error:
|
||||||
|
self.display_text(
|
||||||
|
text=f"ERRORE TECNA TEST INTERROTTO.",
|
||||||
|
bg_color="red", text_color="white"
|
||||||
|
)
|
||||||
|
self.stop_test()
|
||||||
|
|||||||
@ -15,7 +15,7 @@ class Test_Vision(Test_Test):
|
|||||||
def __init__(self, components=None, recipe=None, step=None, pieces=None, run_once=False, reset_on_start=True, enable_override=True):
|
def __init__(self, components=None, recipe=None, step=None, pieces=None, run_once=False, reset_on_start=True, enable_override=True):
|
||||||
super().__init__(components=components, recipe=recipe, step=step, pieces=pieces, run_once=run_once, reset_on_start=reset_on_start, enable_override=enable_override)
|
super().__init__(components=components, recipe=recipe, step=step, pieces=pieces, run_once=run_once, reset_on_start=reset_on_start, enable_override=enable_override)
|
||||||
if "--sim-camera" not in sys.argv:
|
if "--sim-camera" not in sys.argv:
|
||||||
self.ok_counter_limit = 2
|
self.ok_counter_limit = 3
|
||||||
else:
|
else:
|
||||||
self.ok_counter_limit = 1
|
self.ok_counter_limit = 1
|
||||||
# DETECTIONS TABLE
|
# DETECTIONS TABLE
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user