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]
|
||||
description = ST-TEN-12 AUTOMATICO
|
||||
description = ST-TEN-12 M2 C04
|
||||
instruction_folder = st-ten-12
|
||||
image_for_warning= st-ten-12
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ neo_pixels: absent
|
||||
remote_api: absent
|
||||
tecna_t3: present
|
||||
vision_saver: absent
|
||||
vision: absent
|
||||
vision: present
|
||||
screwdriver: absent
|
||||
fixture_id: present
|
||||
barcode_recipe_selection: present
|
||||
@ -26,11 +26,11 @@ poll_time: 10
|
||||
hold_time: 10
|
||||
|
||||
[tecna_t3]
|
||||
port: /dev/ttyUSB1
|
||||
model: t3p
|
||||
port: /dev/ttyUSB0
|
||||
model: t3l
|
||||
|
||||
[fixture_rfid]
|
||||
port: USB0
|
||||
port: USB1
|
||||
|
||||
[digital_io]
|
||||
# OUTPUT MAP FOR VALVE CONTROL UNITS
|
||||
@ -54,7 +54,6 @@ cliente: IVECO
|
||||
part_number: specificare part number
|
||||
canale_di_prova: 0
|
||||
warning_img:
|
||||
pid_pressure_correction: 105
|
||||
dimensione_lotto_abilitata:
|
||||
n_componenti:1
|
||||
istruzione_abilitata: x
|
||||
@ -89,6 +88,8 @@ pressione_di_test_2: 1000
|
||||
pressione_di_test_delta_massimo_2: 200
|
||||
tempo_svuotamento_2: 1
|
||||
pressione_svuotamento_2: 100
|
||||
pid_pressure_correction: 105
|
||||
pid_pressure_correction_2: 105
|
||||
|
||||
stampa_etichetta_abilitata: x
|
||||
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.")
|
||||
return False
|
||||
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.")
|
||||
else:
|
||||
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()
|
||||
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
|
||||
this will return immediately if threaded=False was passed at component initialization
|
||||
|
||||
@ -14,11 +14,14 @@ from .hikrobot_dll import *
|
||||
|
||||
|
||||
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)
|
||||
self.simulate = "--sim-camera" in sys.argv
|
||||
self.num_cameras=1
|
||||
self.connected = False
|
||||
self.ok_memory = True
|
||||
self.ok_frames = []
|
||||
self.ok_results =[]
|
||||
|
||||
def config_changed(self):
|
||||
self.connected = False
|
||||
@ -99,39 +102,53 @@ class HikrobotSmartCamera(Component):
|
||||
for cam_idx in range(self.num_cameras):
|
||||
cam = self.cam_list[cam_idx]
|
||||
self.log.info(f"GET FRAME CAMERA # {cam_idx}")
|
||||
|
||||
# Trigger acquisition & test execution
|
||||
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
|
||||
# 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)
|
||||
if self.ok_results[cam_idx]:
|
||||
# keep last good result
|
||||
frame = self.ok_frames[cam_idx]
|
||||
res = False
|
||||
else:
|
||||
self.connected = False
|
||||
super()._get([concat_frame, concat_results])
|
||||
# Trigger acquisition & test execution
|
||||
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):
|
||||
self.log.info(f"RESUMING")
|
||||
if not self.connected:
|
||||
self.config_changed()
|
||||
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:
|
||||
# Dummy acquisition for each camera to avoid reading past images
|
||||
for cam_idx in range(self.num_cameras):
|
||||
|
||||
@ -2,6 +2,7 @@ import sys
|
||||
import traceback
|
||||
import warnings
|
||||
|
||||
import pymodbus
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
if "--sim-serial" in sys.argv:
|
||||
@ -18,113 +19,190 @@ if "--sim-modbus" not in sys.argv:
|
||||
else:
|
||||
from components.dummies.pymodbus import ModbusClient
|
||||
|
||||
from PyQt5.QtCore import QMutex
|
||||
from PyQt5.QtCore import QMutex, pyqtSignal
|
||||
|
||||
from .component import 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):
|
||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||
self.registers = registers if registers is not None else {}
|
||||
self.lock = QMutex()
|
||||
|
||||
def config_changed(self):
|
||||
self.method = self.config[self.name].get("method", "rtu").lower()
|
||||
self.port = self.config[self.name]["port"]
|
||||
self.baudrate = int(self.config[self.name]["baudrate"])
|
||||
self.stopbits = getattr(serial, self.config[self.name].get("stopbits", "stopbits_one").upper())
|
||||
self.parity = getattr(serial, self.config[self.name].get("parity", "parity_none").upper())
|
||||
self.bytesize = getattr(serial, self.config[self.name].get("bytesize", "eightbits").upper())
|
||||
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))
|
||||
|
||||
# Read configuration values
|
||||
self.method = self.config[self.name].get("method", "rtu").lower()
|
||||
self.port = self.config[self.name]["port"]
|
||||
self.baudrate = int(self.config[self.name]["baudrate"])
|
||||
self.stopbits = getattr(serial, self.config[self.name].get("stopbits", "stopbits_one").upper())
|
||||
self.parity = getattr(serial, self.config[self.name].get("parity", "parity_none").upper())
|
||||
self.bytesize = getattr(serial, self.config[self.name].get("bytesize", "eightbits").upper())
|
||||
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.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():
|
||||
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()
|
||||
try:
|
||||
read = self.client.read_holding_registers(register, count=count, **kwargs)
|
||||
if read.isError():
|
||||
self.log.exception(traceback.format_exception(type(read), read, read.__traceback__))
|
||||
# raise read
|
||||
else:
|
||||
break
|
||||
return read
|
||||
|
||||
def _write(self, register, value,**kwargs):
|
||||
while True:
|
||||
self.lock.lock()
|
||||
wrote = self.client.write_registers(register, value, skip_encode=True,**kwargs)
|
||||
error_message = f"Modbus read failed: Could not read Modbus register {register}"
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
raise ValueError(f"Modbus read error at register {register}")
|
||||
return read
|
||||
except pymodbus.exceptions.ConnectionException:
|
||||
error_message = f"Modbus read failed: Connection error at port {self.port}"
|
||||
#self.log.error(error_message, exc_info=True)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None # Return None to signal failure
|
||||
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()
|
||||
|
||||
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():
|
||||
self.log.exception(traceback.format_exception(type(wrote), wrote, wrote.__traceback__))
|
||||
# raise wrote
|
||||
else:
|
||||
break
|
||||
raise ValueError(f"Modbus write error at register {register}")
|
||||
return wrote
|
||||
except pymodbus.exceptions.ConnectionException as ce:
|
||||
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):
|
||||
decoder = BinaryPayloadDecoder.fromRegisters(read.registers, byteorder=self.byteorder, wordorder=self.wordorder)
|
||||
data = getattr(decoder, f"decode_{data_type}")(*args, **kwargs)
|
||||
data = (data - offset) / gain
|
||||
if data_type.endswith("uint"):
|
||||
data = int(abs(data))
|
||||
elif data_type.endswith("int"):
|
||||
data = int(data)
|
||||
else:
|
||||
raise NotImplementedError(f"data_type {data_type!r} is not supported")
|
||||
return data
|
||||
"""Decode data safely."""
|
||||
if read is None:
|
||||
error_message = "Error decoding Modbus data: No data to decode (read returned None)"
|
||||
#self.log.error(error_message)
|
||||
self.modbus_error_signal.emit(error_message)
|
||||
return None
|
||||
try:
|
||||
decoder = BinaryPayloadDecoder.fromRegisters(
|
||||
read.registers, byteorder=self.byteorder, wordorder=self.wordorder
|
||||
)
|
||||
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):
|
||||
builder = BinaryPayloadBuilder(byteorder=self.byteorder, wordorder=self.wordorder)
|
||||
data = data * gain + offset
|
||||
if data_type.endswith("uint"):
|
||||
data = int(abs(data))
|
||||
elif data_type.endswith("int"):
|
||||
data = int(data)
|
||||
else:
|
||||
raise NotImplementedError(f"data_type {data_type!r} is not supported")
|
||||
getattr(builder, f"add_{data_type}")(data, *args, **kwargs)
|
||||
return builder.build()
|
||||
"""Encode data for Modbus write with error handling."""
|
||||
try:
|
||||
builder = BinaryPayloadBuilder(byteorder=self.byteorder, wordorder=self.wordorder)
|
||||
data = data * gain + offset
|
||||
if data_type.endswith("uint"):
|
||||
data = int(abs(data))
|
||||
elif data_type.endswith("int"):
|
||||
data = int(data)
|
||||
else:
|
||||
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):
|
||||
if data_type.startswith("16bit_"):
|
||||
count = 1
|
||||
elif data_type.startswith("32bit_"):
|
||||
count = 2
|
||||
else:
|
||||
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)
|
||||
"""Read and decode Modbus register data with error handling."""
|
||||
try:
|
||||
if data_type.startswith("16bit_"):
|
||||
count = 1
|
||||
elif data_type.startswith("32bit_"):
|
||||
count = 2
|
||||
else:
|
||||
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):
|
||||
self._write(register, self._encode(data, *args, data_type=data_type, gain=gain, offset=offset),**kwargs)
|
||||
|
||||
# def _get(self, data):
|
||||
# # print("MODBUS", str(int(QThread.currentThreadId())), flush=True)
|
||||
# super()._get(data)
|
||||
"""Encode and write data to Modbus registers with error handling."""
|
||||
try:
|
||||
encoded_data = self._encode(data, *args, data_type=data_type, gain=gain, offset=offset)
|
||||
if encoded_data is not None:
|
||||
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):
|
||||
self.lock.lock()
|
||||
if self.client.is_socket_open():
|
||||
self.client.close()
|
||||
self.lock.unlock()
|
||||
try:
|
||||
self.lock.lock()
|
||||
if self.client.is_socket_open():
|
||||
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.mutex.unlock()
|
||||
|
||||
def write_tag(self,data):
|
||||
self.data_to_write=copy.deepcopy(data)
|
||||
def write_tag(self, 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 PyQt5.QtCore import QSemaphore, pyqtSignal
|
||||
from PyQt5.QtCore import QSemaphore, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from .component import Component
|
||||
@ -11,9 +13,11 @@ from .tecna_marposs_provaset_t3p_registers import registers as t3p_registers
|
||||
class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
_store_recipes_signal = pyqtSignal(object)
|
||||
_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):
|
||||
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):
|
||||
super().config_changed()
|
||||
@ -26,13 +30,17 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
else:
|
||||
raise NotImplementedError(f"tecna t3 model {self.model!r} not implemented.")
|
||||
self.set_measure_units()
|
||||
self.units = self.get_measure_units()
|
||||
self.max_program_number = self.read("Max number of programs")
|
||||
self.saver_label_count = min(abs(int(self.config[self.name].get("saver_label_count", 1))), 0b1111)
|
||||
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_template = min(abs(int(self.config[self.name].get("saver_label_template", 1))), 0b11111111)
|
||||
self.model = self.config[self.name]["model"].lower()
|
||||
self.log.info(f"units: {self.units}")
|
||||
try:
|
||||
self.units = self.get_measure_units()
|
||||
self.max_program_number = self.read("Max number of programs")
|
||||
self.saver_label_count = min(abs(int(self.config[self.name].get("saver_label_count", 1))), 0b1111)
|
||||
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_template = min(abs(int(self.config[self.name].get("saver_label_template", 1))), 0b11111111)
|
||||
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)
|
||||
_leak_units = {"mmH2O": 0, "mbar": 1, "Pa": 2, "mmHg": 3, "inH2O": 4, "psi": 5, }
|
||||
@ -125,111 +133,133 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
return data
|
||||
|
||||
@Component.reconfig_on_error
|
||||
def read(self, register, *args, data_type=None, gain=None, offset=None, formatting=None, decoding_map=None, **kwargs):
|
||||
if type(register) is str:
|
||||
register, s = self.registers[register]
|
||||
def read(self, register, *args, data_type=None, gain=None, offset=None, formatting=None, decoding_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 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:
|
||||
data_type = s.get("dt", None)
|
||||
data_type = "16bit_uint"
|
||||
if gain is None:
|
||||
gain = s.get("g", None)
|
||||
gain = 1
|
||||
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:
|
||||
data_type = "16bit_uint"
|
||||
if gain is None:
|
||||
gain = 1
|
||||
if offset is None:
|
||||
offset = 0
|
||||
return self._convert_from_format(
|
||||
super().read(
|
||||
offset = 0
|
||||
return self._convert_from_format(
|
||||
super().read(
|
||||
register,
|
||||
*args,
|
||||
data_type=data_type,
|
||||
gain=gain,
|
||||
offset=offset,
|
||||
**kwargs,
|
||||
),
|
||||
formatting=formatting,
|
||||
decoding_map=decoding_map,
|
||||
)
|
||||
except Exception as e:
|
||||
error_message = f"Error during read operation on register {register}: {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 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,
|
||||
self._convert_to_format(
|
||||
data,
|
||||
formatting=formatting,
|
||||
encoding_map=encoding_map,
|
||||
),
|
||||
*args,
|
||||
data_type=data_type,
|
||||
gain=gain,
|
||||
offset=offset,
|
||||
**kwargs,
|
||||
),
|
||||
formatting=formatting,
|
||||
decoding_map=decoding_map,
|
||||
)
|
||||
|
||||
@Component.reconfig_on_error
|
||||
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,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
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)
|
||||
raise # Re-raise the exception for further upstream handling if needed
|
||||
|
||||
@Component.reconfig_on_error
|
||||
def _get(self):
|
||||
# print("TECNA", str(int(QThread.currentThreadId())), flush=True)
|
||||
# READ INFO
|
||||
info = {r: self.read(r) for r in [
|
||||
"Real time test pressure output",
|
||||
"Real time differential pressure output",
|
||||
"Real time pressure line regulator",
|
||||
"Active alarm flags",
|
||||
"Active test program number",
|
||||
"Running test: active phase",
|
||||
"Running test: test type",
|
||||
"Running test: sequence index",
|
||||
"Digital inputs status (mask)",
|
||||
# "Digital outputs status (mask)",
|
||||
]}
|
||||
if self.model == "t3p":
|
||||
pass
|
||||
elif self.model == "t3l":
|
||||
info.update({r: self.read(r) for r in [
|
||||
"Active not severe alarm flags",
|
||||
]})
|
||||
else:
|
||||
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())
|
||||
for round_me in ["measured leak"]:
|
||||
if round_me in info.keys():
|
||||
info.update({round_me:float(f"{info[round_me]:.2f}")})
|
||||
self.log.debug(str(info))
|
||||
super()._get([info])
|
||||
try:
|
||||
# READ INFO
|
||||
info = {r: self.read(r) for r in [
|
||||
"Real time test pressure output",
|
||||
"Real time differential pressure output",
|
||||
"Real time pressure line regulator",
|
||||
"Active alarm flags",
|
||||
"Active test program number",
|
||||
"Running test: active phase",
|
||||
"Running test: test type",
|
||||
"Running test: sequence index",
|
||||
"Digital inputs status (mask)",
|
||||
# "Digital outputs status (mask)",
|
||||
]}
|
||||
if self.model == "t3p":
|
||||
pass
|
||||
elif self.model == "t3l":
|
||||
info.update({r: self.read(r) for r in [
|
||||
"Active not severe alarm flags",
|
||||
]})
|
||||
else:
|
||||
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())
|
||||
|
||||
for round_me in ["measured leak"]:
|
||||
if round_me in info.keys():
|
||||
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
|
||||
def _set(self, val=None):
|
||||
@ -299,7 +329,7 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
"FSL - Discharge limit": step.spec["flush_pressure"],
|
||||
"PSQ - Next sequence program PSOUT mode": 0,
|
||||
"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
|
||||
|
||||
}
|
||||
@ -402,6 +432,7 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
# READ ALL PARAMETERS
|
||||
for register_name, field_name in spec.items():
|
||||
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_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)])
|
||||
@ -446,3 +477,19 @@ class TecnaMarpossProvasetT3(ModbusComponent):
|
||||
break
|
||||
result+=char2
|
||||
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):
|
||||
self.recipes = {}
|
||||
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}")
|
||||
for recipe_num in range(1, max_num_recipes+1):
|
||||
logging.info(f"READING RECIPE #{recipe_num}")
|
||||
self.recipes[recipe_num] = self.components["tecna_t3"].read_recipe(recipe_num)
|
||||
recipe_ok=False
|
||||
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):
|
||||
if csv_path is None:
|
||||
|
||||
@ -6,14 +6,52 @@ from PyQt5 import Qt
|
||||
from lib.helpers import timing
|
||||
from PyQt5.QtCore import Qt, QTimer, QThread
|
||||
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.widget import Widget
|
||||
from lib import db
|
||||
from lib.db.models import Recipes
|
||||
from lib.db.models import Recipes , Users
|
||||
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):
|
||||
def __init__(self, parent):
|
||||
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[""].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.barcode_input_l.setFocus()
|
||||
|
||||
@ -49,6 +89,12 @@ class Barcode_Recipe_Selection(Test_Test):
|
||||
self.ok_timer.setSingleShot(True)
|
||||
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):
|
||||
|
||||
@ -113,4 +159,35 @@ class Barcode_Recipe_Selection(Test_Test):
|
||||
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">
|
||||
<string>Test Connector</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="0" alignment="Qt::AlignHCenter">
|
||||
<widget class="QPlainTextEdit" name="barcode_input_l">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" alignment="Qt::AlignHCenter">
|
||||
<widget class="QPushButton" name="recipe_selection_b">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TORNA ALLA TABELLA RICETTE</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="3">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>308</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QPushButton" name="recipe_selection_b">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TORNA ALLA TABELLA RICETTE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" rowspan="2" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="barcode_input_l">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</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>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@ -28,7 +28,6 @@ from ui.test_vision import Test_Vision
|
||||
from ui.test_warning_img import Test_Warning_Img
|
||||
from ui.widget import Widget
|
||||
from components import ArchiveSynchronizer
|
||||
from src.components.rfid_pn532 import RFID_PN532
|
||||
|
||||
|
||||
class Test(Widget):
|
||||
@ -58,17 +57,28 @@ class Test(Widget):
|
||||
else:
|
||||
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
|
||||
self.refresh_time(init=True)
|
||||
# INIT RECIPE
|
||||
self.recipe = None
|
||||
self.auto_import = Recipe_Selection
|
||||
self.archive_synch = ArchiveSynchronizer(config=config)
|
||||
|
||||
self.rfid = RFID_PN532(config=config)
|
||||
if "fixture_id" in self.components:
|
||||
self.rfid = self.components["fixture_id"]
|
||||
self.rfid.rfid_error_signal.connect(self.handle_rfid_error)
|
||||
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.setStyleSheet("QLabel { color: red; }")
|
||||
self.rfid.rfid_error_signal.connect(self.handle_rfid_error)
|
||||
|
||||
|
||||
|
||||
if self.config["hardware_config"]["barcode_recipe_selection"] == "present":
|
||||
self.recipe_selection_mode = "barcode"
|
||||
@ -78,6 +88,7 @@ class Test(Widget):
|
||||
self.tester_component = None
|
||||
if self.config["hardware_config"]["tecna_t3"] == "present":
|
||||
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":
|
||||
self.tester_component = "furness_control"
|
||||
|
||||
@ -828,13 +839,89 @@ class Test(Widget):
|
||||
self.fail_cycle()
|
||||
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)
|
||||
def handle_rfid_error(self, connected):
|
||||
"""
|
||||
Handle errors related to the RFID system.
|
||||
|
||||
Args:
|
||||
connected (bool): True if connected, False if not.
|
||||
"""
|
||||
if connected:
|
||||
self.error_label.setText("") # Update the GUI label
|
||||
self.remove_error("Errore RFID.") # Remove the RFID error
|
||||
else:
|
||||
self.error_label.setText("Errore RFID.") # Update the GUI label
|
||||
self.error_label.setStyleSheet("color: red;")
|
||||
self.add_error("Errore RFID.", True) # Add the RFID error
|
||||
|
||||
@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):
|
||||
"""
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1192</width>
|
||||
<height>114</height>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -412,7 +412,7 @@ CONTATORE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<item row="0" column="9">
|
||||
<widget class="QLabel" name="flag_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
|
||||
@ -4,7 +4,7 @@ import weakref
|
||||
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import QMessageBox, QDialog, QApplication
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from ui import Dialog
|
||||
from ui.test_instructions_reminder import Test_Instructions_Reminder
|
||||
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.clicked.connect(self.show_instruction)
|
||||
|
||||
self.components[self.tester_component].tecna_error_signal.connect(self.handle_modbus_error)
|
||||
|
||||
def show_instruction(self):
|
||||
dialog=Dialog()
|
||||
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):
|
||||
if self.last is None:
|
||||
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):
|
||||
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:
|
||||
self.ok_counter_limit = 2
|
||||
self.ok_counter_limit = 3
|
||||
else:
|
||||
self.ok_counter_limit = 1
|
||||
# DETECTIONS TABLE
|
||||
|
||||
Loading…
Reference in New Issue
Block a user