Merge remote-tracking branch 'origin/master'
49
config/instruction_images/st-ten-15/DEFAULT.svg
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1200"
|
||||
height="700"
|
||||
viewBox="0 0 1200 700"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.2.2 (1:1.2.2+202212051550+b0a8486541)"
|
||||
sodipodi:docname="DEFAULT.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0570734"
|
||||
inkscape:cx="365.15913"
|
||||
inkscape:cy="357.59106"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1023"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs2" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff7f2a;fill-opacity:1;stroke:#aa4400"
|
||||
x="277.31964"
|
||||
y="373.9111"
|
||||
id="text226"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan224"
|
||||
x="277.31964"
|
||||
y="373.9111"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke:#aa4400;fill:#ff7f2a">DISEGNO NON DISPONIBILE</tspan></text></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
BIN
config/instruction_images/st-ten-15/img/arw-yel-down.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
config/instruction_images/st-ten-15/img/mb1.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
config/instruction_images/st-ten-15/img/mi1.PNG
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
config/instruction_images/st-ten-15/img/mi2.PNG
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
config/instruction_images/st-ten-15/img/ok.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
config/instruction_images/st-ten-15/img/rb1.PNG
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
config/instruction_images/st-ten-15/img/rb2.PNG
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
config/instruction_images/st-ten-15/img/rm1.PNG
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
config/instruction_images/st-ten-15/img/rm2.PNG
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
config/instruction_images/st-ten-15/img/tape_black.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
config/instruction_images/st-ten-15/img/tape_white.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
|
|
@ -56,18 +56,20 @@ status_root:api/device-info-update/
|
|||
download_root:media/uploads/
|
||||
|
||||
images_path: data/images
|
||||
poll_time: 60
|
||||
hold_time: 30
|
||||
poll_time: 10
|
||||
hold_time: 10
|
||||
service_account_json: config/machine_settings/gcloud_default.json
|
||||
bucket_id: st_ten_img
|
||||
|
||||
[archive_synchronizer_extra]
|
||||
archive_endpoint: https://r5portal.it/api/st-ten-save/
|
||||
portal_address: https://r5portal.it/
|
||||
archive_root:api/st-ten-save/
|
||||
status_root:api/device-info-update/
|
||||
download_root:media/uploads/
|
||||
archive_endpoint: http://172.20.3.13/api/st-ten-save/
|
||||
images_path: data/images
|
||||
poll_time: 60
|
||||
hold_time: 30
|
||||
service_account_json: config/machine_settings/gcloud_default.json
|
||||
bucket_id: st_ten_img
|
||||
poll_time: 10
|
||||
hold_time: 10
|
||||
|
||||
[label_printer]
|
||||
platform: windows
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ image_for_warning= st-ten-1
|
|||
|
||||
[hardware_config]
|
||||
archive_synchronizer: present
|
||||
archive_synchronizer_extra: present
|
||||
galaxy_camera: absent
|
||||
uvc_camera: absent
|
||||
label_printer: present
|
||||
|
|
@ -18,6 +19,12 @@ screwdriver: absent
|
|||
digital_io: present
|
||||
external_flush_blow: absent
|
||||
|
||||
# VERO PROJECT LOCAL SERVER
|
||||
[archive_synchronizer_extra]
|
||||
portal_address: http://172.20.3.13:45006/
|
||||
poll_time: 10
|
||||
hold_time: 10
|
||||
|
||||
[tecna_t3]
|
||||
port: COM4
|
||||
model: t3l
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ image_for_warning= st-ten-14
|
|||
|
||||
[hardware_config]
|
||||
archive_synchronizer: present
|
||||
archive_synchronizer_extra: present
|
||||
uvc_camera: absent
|
||||
label_printer: present
|
||||
neo_pixels: absent
|
||||
|
|
@ -19,13 +20,19 @@ fixture_id: absent
|
|||
discard_box: absent
|
||||
enforce_piece_removal: no
|
||||
|
||||
# VERO PROJECT LOCAL SERVER
|
||||
[archive_synchronizer_extra]
|
||||
portal_address: http://172.20.3.13:45007/
|
||||
poll_time: 10
|
||||
hold_time: 10
|
||||
|
||||
[tecna_t3]
|
||||
port: /dev/ttyUSB0
|
||||
model: t3l
|
||||
|
||||
[label_printer]
|
||||
platform: linux
|
||||
printer: ZTC-ZD421-203dpi-ZPL
|
||||
printer: Zebra_Technologies_ZTC_ZD421-203dpi_ZPL
|
||||
|
||||
[digital_io]
|
||||
# OUTPUT MAP FOR FIXTURE CONNECTOR
|
||||
|
|
|
|||
98
config/machine_settings/st-ten-15.ini
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
[machine]
|
||||
description = ST-TEN-15 AUTOMATICO
|
||||
instruction_folder = st-ten-15
|
||||
image_for_warning= st-ten-15
|
||||
|
||||
[hardware_config]
|
||||
archive_synchronizer: present
|
||||
uvc_camera: absent
|
||||
label_printer: present
|
||||
neo_pixels: absent
|
||||
remote_api: absent
|
||||
tecna_t3: present
|
||||
vision_saver: absent
|
||||
vision: absent
|
||||
screwdriver: absent
|
||||
digital_io: present
|
||||
barcode_recipe_selection: present
|
||||
fixture_id: present
|
||||
discard_box: absent
|
||||
second_leak_test: present
|
||||
dual_channel: absent
|
||||
#enforce_piece_removal: yes
|
||||
|
||||
[tecna_t3]
|
||||
port: /dev/ttyUSB0
|
||||
model: t3p
|
||||
|
||||
[label_printer]
|
||||
platform: linux
|
||||
printer: zd421
|
||||
|
||||
[digital_io]
|
||||
# OUTPUT MAP FOR FIXTURE CONNECTOR
|
||||
|
||||
id_fixture: USB-5860,BID#0
|
||||
discard_idx:12 # BIT NUMBER OF THE I/0 MODULE USED FOR DISCARD SENSING
|
||||
|
||||
[fixture_rfid]
|
||||
port: dev/ttyUSB1
|
||||
|
||||
[recipe]
|
||||
recipe_name_field: codice_ricetta
|
||||
part_number_field: codice_prodotto
|
||||
label_template_field: modello_etichetta
|
||||
description_field: descrizione
|
||||
|
||||
[recipes_defaults]
|
||||
tester_discharge_enable: yes
|
||||
dimensione_lotto_abilitata: x
|
||||
tempo_pre_riempimento: 0
|
||||
pressione_pre_riempimento: 1000
|
||||
tempo_riempimento: 15
|
||||
tempo_assestamento: 15
|
||||
tempo_di_test: 10
|
||||
n_componenti:1
|
||||
percentuale_minima_pressione_assestamento: 5
|
||||
percentuale_massima_pressione_assestamento: 5
|
||||
pressione_di_test_delta_minimo: 30
|
||||
pressione_di_test: 7000
|
||||
pressione_di_test_delta_massimo: 30
|
||||
tempo_svuotamento: 0
|
||||
pressione_svuotamento: 100
|
||||
canale_di_prova: 1
|
||||
prova_tenuta_abilitata_2:
|
||||
tempo_pre_riempimento_2: 0
|
||||
pressione_pre_riempimento_2: 1000
|
||||
tempo_riempimento_2: 20
|
||||
tempo_assestamento_2: 20
|
||||
tempo_di_test_2: 10
|
||||
percentuale_minima_pressione_assestamento_2: 5
|
||||
percentuale_massima_pressione_assestamento_2: 5
|
||||
pressione_di_test_delta_minimo_2: 30
|
||||
pressione_di_test_2: 15000
|
||||
pressione_di_test_delta_massimo_2: 30
|
||||
tempo_svuotamento_2: 0
|
||||
pressione_svuotamento_2: 100
|
||||
canale_di_prova_2: 2
|
||||
modello_etichetta: EtichettaR5_Montaggio_1prova.prn
|
||||
pid_pressure_correction: 105
|
||||
istruzione_abilitata_extra:
|
||||
|
||||
[autotest_leak]
|
||||
enabled: true
|
||||
pre_filling_time: 0
|
||||
pre_filling_pressure: 1000
|
||||
filling_time: 10
|
||||
settling_time: 10
|
||||
settling_pressure_min_percent: 5
|
||||
settling_pressure_max_percent: 5
|
||||
test_pressure: 7000
|
||||
test_time: 10
|
||||
test_pressure_qpos: 10 #Q+ Upper test leak limit
|
||||
test_pressure_qneg: 30 #Q- Lower test leak limit
|
||||
test_pressure_tt_qpos: 1 # Q+ Upper test leak limit (tube-tube)
|
||||
test_pressure_tt_qneg: 5 # Q- Lower test leak limit (tube-tube)
|
||||
flush_time: 1
|
||||
flush_pressure: 100
|
||||
relay_config: 1
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
description = ST-TEN-4 - REPARTO PREFORMATURA - MAPPANO
|
||||
instruction_folder = st-ten-4
|
||||
image_for_warning= st-ten-4
|
||||
|
||||
[hardware_config]
|
||||
archive_synchronizer: present
|
||||
archive_synchronizer_extra: present
|
||||
uvc_camera: absent
|
||||
label_printer: present
|
||||
neo_pixels: absent
|
||||
|
|
@ -13,6 +15,12 @@ vision_saver: absent
|
|||
vision: absent
|
||||
screwdriver: absent
|
||||
|
||||
# VERO PROJECT LOCAL SERVER
|
||||
[archive_synchronizer_extra]
|
||||
portal_address: http://172.20.3.13:45008/
|
||||
poll_time: 10
|
||||
hold_time: 10
|
||||
|
||||
[tecna_t3]
|
||||
port: COM4
|
||||
model: t3p
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ screwdriver: absent
|
|||
#digital_io: present
|
||||
digital_io_flush_blow: present
|
||||
second_leak_test: present
|
||||
barcode_recipe_selection: present
|
||||
external_flush_blow: present # EXTERNAL BOX CONTROLLING MULTI-CHANNEL TEST (IF PRESENT), BLOW-CLEANING AND EXTERNAL FLUSH
|
||||
dual_channel: present
|
||||
#fixture_id: present
|
||||
|
|
|
|||
BIN
config/warning_images/st-ten-10/Img-G-8606466.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8606706.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8606707.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8612523.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8615927.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8615928.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8626311.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
config/warning_images/st-ten-10/Img-G-8626312.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55680.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55681.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55682.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55683.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55794.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55795.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55796.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55865.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55866.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55916.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R55918.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56061.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56063.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56215.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56217.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56219.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56401.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56583.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56585.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56632.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56636.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56638.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56738.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R56739.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R57023.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R57025.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
config/warning_images/st-ten-10/Img-G-R57027.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -4,6 +4,7 @@ import re
|
|||
import sys
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
|
@ -12,13 +13,14 @@ import requests
|
|||
from google.api_core.exceptions import Forbidden
|
||||
from google.cloud import storage
|
||||
from requests import JSONDecodeError
|
||||
|
||||
from lib.db import Archive, db
|
||||
from lib.db.models import Log
|
||||
from PyQt5.QtCore import QThread
|
||||
from requests.adapters import HTTPAdapter, Retry
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
from .component import Component
|
||||
from ui.helpers import get_main_window
|
||||
# Suppress insecure request warning
|
||||
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
||||
|
||||
|
|
@ -26,6 +28,7 @@ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
|||
class ArchiveSynchronizer(Component):
|
||||
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)
|
||||
self.main_window = None
|
||||
self.simulate = "--sim-archiver" in sys.argv
|
||||
self.machine_status = "logged-in"
|
||||
self.machine_id = None
|
||||
|
|
@ -43,23 +46,50 @@ class ArchiveSynchronizer(Component):
|
|||
|
||||
self._do_set_period({"period": float(self.config[self.name]["poll_time"])})
|
||||
self.hold_time = round(float(self.config[self.name]["hold_time"]) * 1000)
|
||||
self.gcs_client = storage.Client.from_service_account_json(self.config[self.name]["service_account_json"])
|
||||
self.gcs_client._http.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this seems to be useless
|
||||
self.gcs_client._http.adapters.move_to_end("", last=False) # this seems to be useless
|
||||
self.bucket_id = self.config[self.name]["bucket_id"]
|
||||
if self.name == "archive_synchronizer":
|
||||
self.gcs_client = storage.Client.from_service_account_json(self.config[self.name]["service_account_json"])
|
||||
self.gcs_client._http.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this seems to be useless
|
||||
self.gcs_client._http.adapters.move_to_end("", last=False) # this seems to be useless
|
||||
self.bucket_id = self.config[self.name]["bucket_id"]
|
||||
self.gcs_bucket = None
|
||||
|
||||
@db.connection_context()
|
||||
def _get(self):
|
||||
for record in list(Archive.select().where((Archive.archived != True) | (Archive.uploaded != True))): # using "is not True" breaks the query.. # list() forces the complete execution of the query unlocking the db unlike __enter__()
|
||||
if self.main_window is None:
|
||||
self.main_window = get_main_window()
|
||||
|
||||
if self.name != "archive_synchronizer_extra":
|
||||
# MAIN SERVER
|
||||
bit_pos = 0
|
||||
unsaved_records = Archive.select().where((Archive.archived == 0) |
|
||||
(Archive.archived == 2) |
|
||||
(Archive.uploaded == 0))
|
||||
else:
|
||||
# EXTRA SERVER (VERO PROJECT SPA)
|
||||
bit_pos = 1
|
||||
unsaved_records = Archive.select().where((Archive.archived == 0) |
|
||||
(Archive.archived == 1))
|
||||
for record in unsaved_records:
|
||||
if not self.simulate:
|
||||
if record.archived is not True:
|
||||
record.archived = self.remote_archive(record) is True
|
||||
s = time.time()
|
||||
save_ok = self.remote_archive(record) is True
|
||||
e = time.time()
|
||||
else:
|
||||
save_ok=True
|
||||
if record.uploaded is not True:
|
||||
record.uploaded = self.remote_store(record) is True
|
||||
else:
|
||||
self.log.info("simulated archive synchronizer cycle")
|
||||
record.save()
|
||||
save_ok=True
|
||||
|
||||
if save_ok:
|
||||
record.archived |= (1 << bit_pos)
|
||||
self.log.info(f"({self.name}) id {record.id}: archived remotely")
|
||||
else:
|
||||
self.log.info(f"({self.name}) id {record.id}: failed to archive remotely")
|
||||
self.main_window.run_request.emit(record.save, [], {})
|
||||
|
||||
if self.hold_time > 0:
|
||||
QThread.msleep(self.hold_time)
|
||||
self.gcs_bucket = None
|
||||
|
|
@ -68,7 +98,9 @@ class ArchiveSynchronizer(Component):
|
|||
self.machine_status="working"
|
||||
|
||||
super()._get()
|
||||
self.update_machine_status()
|
||||
|
||||
if self.name == "archive_synchronizer":
|
||||
self.update_machine_status()
|
||||
|
||||
def update_machine_status(self):
|
||||
status_call = f"{self.status_endpoint}?machine-id={self.machine_id.upper()}&status={self.machine_status}"
|
||||
|
|
@ -132,16 +164,28 @@ class ArchiveSynchronizer(Component):
|
|||
if not self.simulate:
|
||||
with requests.Session() as s:
|
||||
s.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this disables retries
|
||||
r = requests.post(self.archive_endpoint, params={
|
||||
"data": json.dumps(record.test_data),
|
||||
"machine_id": self.machine_id,
|
||||
"overridden": record.overridden,
|
||||
"recipe": record.test_data.get("recipe", {}).get("name", None),
|
||||
"result": "OK" if record.result else "KO",
|
||||
"serial": record.id,
|
||||
"time": record.time.isoformat(),
|
||||
"user": record.user.username,
|
||||
}, timeout=5, verify=False)
|
||||
if self.name == "archive_synchronizer":
|
||||
r = requests.post(self.archive_endpoint, params={
|
||||
"data": json.dumps(record.test_data),
|
||||
"machine_id": self.machine_id,
|
||||
"overridden": record.overridden,
|
||||
"recipe": record.test_data.get("recipe", {}).get("name", None),
|
||||
"result": "OK" if record.result else "KO",
|
||||
"serial": record.id,
|
||||
"time": record.time.isoformat(),
|
||||
"user": record.user.username,
|
||||
}, timeout=5, verify=False)
|
||||
else:
|
||||
r = requests.get(self.archive_endpoint, params={
|
||||
"machine_id": self.machine_id,
|
||||
"overridden": record.overridden,
|
||||
"recipe": record.test_data.get("recipe", {}).get("name", None),
|
||||
"result": "OK" if record.result else "KO",
|
||||
"serial": record.id,
|
||||
"time": record.time.isoformat(),
|
||||
"user": record.user.username,
|
||||
}, timeout=5, verify=False)
|
||||
|
||||
if r.status_code != 200:
|
||||
raise AssertionError("bad status response")
|
||||
except AssertionError as e:
|
||||
|
|
@ -150,6 +194,8 @@ class ArchiveSynchronizer(Component):
|
|||
except (requests.ConnectionError, requests.Timeout) as e:
|
||||
self.log.warning(f"id: {record.id}: failed to archive remotely, archive_endpoint might be unreachable: {str(e)}")
|
||||
return False
|
||||
self.log.info(f"Archived successfully: {record.id}")
|
||||
|
||||
return True
|
||||
|
||||
def remote_store(self, record):
|
||||
|
|
@ -182,16 +228,16 @@ class ArchiveSynchronizer(Component):
|
|||
return False
|
||||
except Exception:
|
||||
self.log.error(f"id: {record.id}: failed to store remotely:\n{traceback.format_exc()}")
|
||||
self.log.info(f"id: {record.id}: stored remotely")
|
||||
self.log.info(f"Stored successfully: {record.id}")
|
||||
return True
|
||||
|
||||
def remote_fetch(self, remote_path=None, local_path=None):
|
||||
"""
|
||||
download a single file from the server.
|
||||
Download a single file from the server.
|
||||
|
||||
:param remote_path: path of where to download the file from
|
||||
:param local_path: path of where to save the file to
|
||||
:return: a dictionary with errors if any occur.
|
||||
:param remote_path: Path of where to download the file from
|
||||
:param local_path: Path of where to save the file to
|
||||
:return: A dictionary with errors if any occur.
|
||||
"""
|
||||
if remote_path is None:
|
||||
raise ValueError("remote_path cannot be None")
|
||||
|
|
@ -199,48 +245,68 @@ class ArchiveSynchronizer(Component):
|
|||
raise ValueError("local_path cannot be None")
|
||||
|
||||
call_url = f"https://dev.r5portal.it/{remote_path}"
|
||||
log_info_type = "Download"
|
||||
log_time = datetime.now()
|
||||
log_info = f"Attempted to download from {call_url}"
|
||||
|
||||
try:
|
||||
if not self.simulate:
|
||||
with requests.Session() as s:
|
||||
self.log.info(f"Fetching file from: {call_url}")
|
||||
|
||||
# Make the HTTP GET request to fetch the file
|
||||
response = s.get(call_url, timeout=5, verify=False)
|
||||
|
||||
# Log response details
|
||||
self.log.info(f"HTTP Status Code: {response.status_code}")
|
||||
#self.log.info(f"Response Headers: {response.headers}")
|
||||
#self.log.info(f"Response Content: {response.content}")
|
||||
|
||||
# Handle HTTP errors
|
||||
if response.status_code == 404:
|
||||
self.log.warning(f"File not found: {call_url}. Please check the URL path.")
|
||||
log_info += " - File not found"
|
||||
self.log.warning(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "File not found"}
|
||||
elif response.status_code == 403 or response.status_code == 401:
|
||||
self.log.warning(f"Access forbidden or not logged in for file: {call_url}")
|
||||
elif response.status_code in [403, 401]:
|
||||
log_info += " - Access forbidden or not logged in"
|
||||
self.log.warning(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "Access forbidden or not logged in"}
|
||||
elif response.status_code != 200:
|
||||
self.log.error(f"Unexpected HTTP response status: {response.status_code} for URL: {call_url}")
|
||||
log_info += f" - Unexpected HTTP response status: {response.status_code}"
|
||||
self.log.error(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "Unexpected HTTP response status"}
|
||||
|
||||
# Ensure the directory exists
|
||||
os.makedirs(local_path, exist_ok=True)
|
||||
|
||||
# Save the file to the local path
|
||||
local_file_path = os.path.join(local_path, os.path.basename(remote_path))
|
||||
with open(local_file_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
|
||||
self.log.info(f"File downloaded successfully: {local_file_path}")
|
||||
log_info += f" - File downloaded successfully: {local_file_path}"
|
||||
self.log.info(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"downloaded_file": local_file_path}
|
||||
|
||||
except requests.ConnectionError as e:
|
||||
self.log.error(f"Connection error occurred while fetching the file: {str(e)}")
|
||||
log_info += f" - Connection error: {str(e)}"
|
||||
self.log.error(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "Connection error"}
|
||||
except requests.Timeout as e:
|
||||
self.log.error(f"Timeout error occurred while fetching the file: {str(e)}")
|
||||
log_info += f" - Timeout error: {str(e)}"
|
||||
self.log.error(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "Timeout error"}
|
||||
except Exception as e:
|
||||
self.log.error(f"An unexpected error occurred: {str(e)}")
|
||||
log_info += f" - Unexpected error: {str(e)}"
|
||||
self.log.error(log_info)
|
||||
self.log_to_db(log_time, log_info_type, log_info)
|
||||
return {"error": "Unexpected error"}
|
||||
|
||||
def log_to_db(self, log_time, log_info_type, log_info):
|
||||
"""Save log information to the database."""
|
||||
try:
|
||||
# Use the Log class instead of the log instance
|
||||
new_log_entry = Log(time=log_time, info_type=log_info_type, info=log_info)
|
||||
new_log_entry.save() # Save the log entry to the database
|
||||
except Exception as e:
|
||||
self.log.error(f"Failed to save log to database: {str(e)}")
|
||||
self.log.error(traceback.format_exc())
|
||||
|
|
|
|||
|
|
@ -63,3 +63,4 @@ Users.register(username="USER", password="user")
|
|||
if True:
|
||||
# crud_db must be imported after db and models_reference are available
|
||||
from .crud_db import Crud_DB
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
|
||||
from peewee import (AutoField, BooleanField, DateTimeField, ForeignKeyField,
|
||||
TextField)
|
||||
TextField, IntegerField)
|
||||
from playhouse.sqlite_ext import JSONField
|
||||
|
||||
from .base_model import BaseModel, db
|
||||
|
|
@ -16,7 +16,7 @@ class Archive(BaseModel):
|
|||
overridden = BooleanField(null=False)
|
||||
test_data = JSONField(null=False)
|
||||
label = TextField(null=True)
|
||||
archived = BooleanField(null=False, default=False)
|
||||
archived = IntegerField(null=False, default=False)
|
||||
uploaded = BooleanField(null=False, default=False)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from peewee import AutoField, DateTimeField, TextField
|
|||
|
||||
from .base_model import BaseModel, db
|
||||
|
||||
log = logging.getLogger("db_log")
|
||||
db_logger = logging.getLogger("db_log")
|
||||
|
||||
|
||||
class Log(BaseModel):
|
||||
|
|
@ -18,7 +18,7 @@ class Log(BaseModel):
|
|||
@db.atomic()
|
||||
def log(cls, info_type, info=None):
|
||||
cls.create(info_type=info_type, info=info)
|
||||
log.info(f"{info_type}: {info}")
|
||||
db_logger.info(f"{info_type}: {info}")
|
||||
|
||||
class Meta:
|
||||
table_name = "log"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from datetime import datetime
|
|||
from pathlib import Path
|
||||
from ui.diagnostics import Diagnostics
|
||||
from lib.helpers.single_process import SingleProcess
|
||||
from ui.logs_management.info import Logs_Management
|
||||
|
||||
if platform.system().lower() == "windows":
|
||||
sys.path.append(f"{os.getcwd()}\src\components")
|
||||
|
|
@ -71,7 +72,7 @@ try:
|
|||
from lib.helpers import ConfigReader
|
||||
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||
from ui import About, Archive, Login, Main_Window, Test, Users_Management, Recipe_Selection, \
|
||||
from ui import About, Archive, Login, Main_Window, Test, Users_Management,Logs_Management ,Recipe_Selection, \
|
||||
Barcode_Recipe_Selection
|
||||
|
||||
if "--vision" in sys.argv:
|
||||
|
|
@ -100,6 +101,7 @@ try:
|
|||
# INIT COMPONENT
|
||||
self.components_specs = {
|
||||
"archive_synchronizer": {"c": ArchiveSynchronizer},
|
||||
"archive_synchronizer_extra": {"c": ArchiveSynchronizer},
|
||||
"label_printer": {"c": Os_Label_Printer, "t": False},
|
||||
"extra_label_printer": {"c": Os_Label_Printer, "t": False},
|
||||
"label_printer_2": {"c": BrotherLabelPrinter, "t": False},
|
||||
|
|
@ -173,7 +175,6 @@ try:
|
|||
|
||||
# GUI INIT
|
||||
if "--no-gui" not in sys.argv:
|
||||
# self.main_window = Main_Window(self.bench)
|
||||
self.main_window = Main_Window()
|
||||
# CONNECT MAIN WINDOW ACTIONS
|
||||
self.main_window.logout_a.triggered.connect(lambda checked, selfie=weakref.ref(self): selfie().logout())
|
||||
|
|
@ -190,6 +191,8 @@ try:
|
|||
|
||||
self.main_window.about_a.triggered.connect(
|
||||
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(About()))
|
||||
self.main_window.download_a.triggered.connect(
|
||||
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Logs_Management()))
|
||||
self.main_window.quit_a.triggered.connect(quit_app)
|
||||
self.main_window.users_management_a.triggered.connect(
|
||||
lambda checked, selfie=weakref.ref(self): selfie().main_window.open_dialog(Users_Management()))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from .widget import Widget
|
|||
from .window import Window
|
||||
from .dialog import Dialog
|
||||
from .about import About
|
||||
from .logs_management import Logs_Management
|
||||
from .archive import Archive
|
||||
from .barcodes_step_editor import Barcodes_Step_Editor
|
||||
from .barcode_recipe_selection import Barcode_Recipe_Selection
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class Barcode_Recipe_Selection(Test_Test):
|
|||
else:
|
||||
lines = data.splitlines()
|
||||
#lines = data.split("-")
|
||||
candidates = [i for i in lines if len(i)==10]
|
||||
candidates = [i for i in lines if len(i) in(10,12)]
|
||||
if len(candidates)>0:
|
||||
# RECIPE CODE FOUND
|
||||
self.recipe=candidates[-1]
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
from .calc_foreground_color import calc_foreground_color
|
||||
from .get_main_window import get_main_window
|
||||
from .replace_widget import replace_widget
|
||||
|
|
|
|||
8
src/ui/helpers/get_main_window.py
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
from PyQt5.QtWidgets import QApplication, QMainWindow
|
||||
|
||||
|
||||
def get_main_window():
|
||||
tws = QApplication.topLevelWidgets()
|
||||
for w in tws:
|
||||
if isinstance(w, QMainWindow):
|
||||
return w
|
||||
1
src/ui/logs_management/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .info import Logs_Management
|
||||
73
src/ui/logs_management/info.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import traceback
|
||||
|
||||
from lib.db import log # Presumendo che esista un modulo per accedere alla tabella "log"
|
||||
from PyQt5.QtWidgets import QMessageBox, QTableWidget
|
||||
from ui.crud import Crud, Line_Edit_Cell_Widget
|
||||
from ui.widget import Widget
|
||||
|
||||
|
||||
class Logs_Management(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
class Info_Line_Edit_Cell_Widget(Line_Edit_Cell_Widget):
|
||||
def render(self, data, row_number=None, crud=None):
|
||||
super().render(data, row_number=row_number, crud=crud)
|
||||
|
||||
def parse(self, action=None, row_number=None, crud=None):
|
||||
return self.text()
|
||||
|
||||
crud_aliases = {
|
||||
"time": "Data",
|
||||
"info_type": "Operazione",
|
||||
"info": "Info",
|
||||
}
|
||||
self.crud = Crud(
|
||||
"log",
|
||||
display_name="LOG MANAGEMENT",
|
||||
readonly=["id"],
|
||||
select=list(crud_aliases.keys()),
|
||||
fields_aliases=crud_aliases,
|
||||
autocomplete={},
|
||||
widget_classes={
|
||||
"time": Info_Line_Edit_Cell_Widget,
|
||||
"info_type": Info_Line_Edit_Cell_Widget,
|
||||
"info": Info_Line_Edit_Cell_Widget,
|
||||
},
|
||||
row_filter=self.row_filter
|
||||
)
|
||||
|
||||
self.layout().addWidget(self.crud, 0, 0, -1, -1)
|
||||
|
||||
# Adjust the column widths based on content
|
||||
self.adjust_column_widths()
|
||||
|
||||
def adjust_column_widths(self):
|
||||
"""Adjust the widths of columns to fit their content."""
|
||||
# Ensure that Crud has db_tw which is a QTableWidget
|
||||
if hasattr(self.crud, 'db_tw') and isinstance(self.crud.db_tw, QTableWidget):
|
||||
# Optionally, resize all columns to fit their contents initially
|
||||
self.crud.db_tw.resizeColumnsToContents()
|
||||
|
||||
# Set a specific column width for columns; for example, set 'Info' column
|
||||
for column in range(self.crud.db_tw.columnCount()):
|
||||
self.crud.db_tw.setColumnWidth(column, 200) # Set width to 300 pixels
|
||||
|
||||
def row_filter(self, row, row_number, crud):
|
||||
try:
|
||||
|
||||
|
||||
log_entry = log.generate(
|
||||
id=row["id"],
|
||||
time=row["time"],
|
||||
info_type=row["info_type"],
|
||||
info=row["info"]
|
||||
)
|
||||
except AssertionError as e:
|
||||
self.log.exception(traceback.format_exc())
|
||||
self.crud.set_row_color(row_number, "red")
|
||||
QMessageBox.critical(None, "Errore Salvataggio DB", f"Errore alla riga {row_number}:\n{str(e)}")
|
||||
return False, None, True
|
||||
|
||||
row.update(log_entry)
|
||||
return True, row, False
|
||||
23
src/ui/logs_management/logs_management.ui
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Users_Management</class>
|
||||
<widget class="QWidget" name="Users_Management">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>94</width>
|
||||
<height>18</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Logs Management</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -5,10 +5,16 @@ from ui.window import Window
|
|||
|
||||
class Main_Window(Window):
|
||||
do = pyqtSignal(dict)
|
||||
run_request = pyqtSignal(object, list, dict)
|
||||
|
||||
@staticmethod
|
||||
def run_request_handler(fn, a, ka):
|
||||
fn(*a, **ka)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.do.connect(self._do)
|
||||
self.run_request.connect(self.run_request_handler)
|
||||
# print("MAIN_WINDOW ", str(int(QThread.currentThreadId())), flush=True)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<string>Informazioni</string>
|
||||
</property>
|
||||
<addaction name="about_a"/>
|
||||
<addaction name="download_a"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="admin_m">
|
||||
<property name="font">
|
||||
|
|
@ -134,6 +135,11 @@
|
|||
<string>Diagnostica</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="download_a">
|
||||
<property name="text">
|
||||
<string>Aggiornamento</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
|||
|
|
@ -368,7 +368,9 @@ class Test(Widget):
|
|||
elif self.step.step_type == "print":
|
||||
compiled_label = self.print(self.archived, self.step.spec.get("template", "EtichettaR5"))
|
||||
self.archived.label = compiled_label
|
||||
self.archived.save()
|
||||
self.log.info(f"Label printed. Saving...")
|
||||
#self.archived.save()
|
||||
self.main_window.main_window.run_request.emit(self.archived.save, [], {})
|
||||
self.next_timer.start(500)
|
||||
elif self.step.step_type == "wait":
|
||||
self.next_timer.start(500)
|
||||
|
|
|
|||