Merge remote-tracking branch 'origin/master'

This commit is contained in:
neo-dl 2022-10-03 17:24:03 +02:00
commit 87e1e18ad3
7 changed files with 50 additions and 36 deletions

View File

@ -32,12 +32,12 @@ minimum_disk_free_space_gb: 20
resize_resolution: 612x512 resize_resolution: 612x512
[archive_synchronizer] [archive_synchronizer]
archive_endpoint: https://r5portal.it/api/echo/ archive_endpoint: https://r5portal.it/api/st-ten-save/
images_path: data/images images_path: data/images
poll_time: 60 poll_time: 60
hold_time: 1 hold_time: 1
service_account_json: data/secrets/errecinque-prodserver-9d4024fe9eed.json service_account_json: data/secrets/errecinque-prodserver-d132c3d469cc.json
bucket_id: MACHINE bucket_id: st_ten_img
[label_printer] [label_printer]
platform: windows platform: windows

View File

@ -1,5 +1,5 @@
[hardware_config] [hardware_config]
archive_synchronizer: absent archive_synchronizer: present
galaxy_camera: present galaxy_camera: present
label_printer: present label_printer: present
multicomp: present multicomp: present
@ -18,3 +18,7 @@ port: /dev/ttyACM0
[neo_pixels] [neo_pixels]
port: /dev/ttyACM1 port: /dev/ttyACM1
[archive_synchronizer]
poll_time: 6
hold_time: 1

View File

@ -1,4 +1,5 @@
import json import json
import os
import re import re
import sys import sys
import traceback import traceback
@ -25,7 +26,7 @@ class ArchiveSynchronizer(Component):
def config_changed(self): def config_changed(self):
self.machine_id = self.config.machine_id self.machine_id = self.config.machine_id
self.archive_endpoint = self.config[self.name]["archive_endpoint"] self.archive_endpoint = self.config[self.name]["archive_endpoint"]
self.images_path = self.config[self.name]["images_path"] # self.images_path = self.config[self.name]["images_path"]
self._do_set_period({"period": float(self.config[self.name]["poll_time"])}) 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.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 = storage.Client.from_service_account_json(self.config[self.name]["service_account_json"])
@ -55,13 +56,14 @@ class ArchiveSynchronizer(Component):
with requests.Session() as s: with requests.Session() as s:
s.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this disables retries s.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this disables retries
r = requests.post(self.archive_endpoint, params={ r = requests.post(self.archive_endpoint, params={
"data": json.dumps(record.test_data),
"machine_id": self.machine_id, "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(), "time": record.time.isoformat(),
"user": record.user.username, "user": record.user.username,
"recipe": record.recipe.name,
"test_data": json.dumps(record.test_data),
"result": record.result,
"overridden": record.overridden,
}, timeout=5, verify=False) }, timeout=5, verify=False)
if r.status_code != 200: if r.status_code != 200:
raise AssertionError("bad status response") raise AssertionError("bad status response")
@ -78,6 +80,8 @@ class ArchiveSynchronizer(Component):
return True return True
def remote_store(self, record): def remote_store(self, record):
if "vision" not in record.test_data:
return True
try: try:
self.gcs_bucket = self.gcs_client.get_bucket(self.bucket_id, timeout=(5, 30), retry=None) # retry=None seems to be ignored self.gcs_bucket = self.gcs_client.get_bucket(self.bucket_id, timeout=(5, 30), retry=None) # retry=None seems to be ignored
except Exception: except Exception:
@ -85,24 +89,25 @@ class ArchiveSynchronizer(Component):
self.gcs_bucket = None self.gcs_bucket = None
if self.gcs_bucket is None: if self.gcs_bucket is None:
return False return False
dt = record.vision_time for path in record.test_data["vision"]["0"]["files"]:
timestamp = dt.strftime(self.time_format) dt = record.time
img_in = f"{self.images_path}/frames/{dt.strftime('%Y')}/{dt.strftime('%m')}/{timestamp}.png" # path_in = f"{self.images_path}/{dt.strftime('%Y')}/{dt.strftime('%m')}/{os.path.basename(path)}"
img_out = f"{self.bench.type}/{dt.strftime('%Y')}/{dt.strftime('%m')}/{timestamp}.png" path_in = path
try: path_out = f"{self.machine_id}/{dt.strftime('%Y')}/{dt.strftime('%m')}/{os.path.basename(path)}"
blob = self.gcs_bucket.blob(img_out) try:
if not self.simulate: blob = self.gcs_bucket.blob(path_out)
blob.upload_from_filename(filename=img_in) if not self.simulate:
except FileNotFoundError: blob.upload_from_filename(filename=path_in)
self.log.error(f"id {record.id}: {img_in}: file not found.") except FileNotFoundError:
return False self.log.error(f"id {record.id}: {path_in}: file not found.")
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:
self.log.info(f"id {record.id}: {img_in}: already stored.")
else:
self.log.warning(f"id: {record.id}: failed to store remotely: {str(e)}: {e._response.json()}")
return False return False
except Exception: except Forbidden as e:
self.log.error(f"id: {record.id}: failed to store remotely:\n{traceback.format_exc()}") 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:
self.log.info(f"id: {record.id}: stored remotely") 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()}")
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")
return True return True

View File

@ -81,7 +81,7 @@ class VisionSaver(Component):
# save vision # save vision
with open(out_paths[-1], "w") as f: with open(out_paths[-1], "w") as f:
json.dump(vision, f) json.dump(vision, f)
return out_paths return list(map(str, out_paths))
def remove_older_images_if_needed(self): def remove_older_images_if_needed(self):
if self.minimum_disk_free_space_gb is None: if self.minimum_disk_free_space_gb is None:

View File

@ -15,7 +15,7 @@ class Archive(Widget):
# "id": "Id", # "id": "Id",
# "time": "Data e ora", # "time": "Data e ora",
# "user": "Operatore", # "user": "Operatore",
# "recipe": "Ricetta", # "test_data.recipe.name": "Ricetta",
# "result": "Esito", # "result": "Esito",
# "overridden": "Esito forzato", # "overridden": "Esito forzato",
# "test_data": "Dati del test", # "test_data": "Dati del test",
@ -27,7 +27,7 @@ class Archive(Widget):
crud_aliases = { crud_aliases = {
"time": "Data e ora", "time": "Data e ora",
"user": "Operatore", "user": "Operatore",
"recipe": "Ricetta", "test_data.recipe.name": "Ricetta",
"result": "Esito", "result": "Esito",
"overridden": "Esito forzato", "overridden": "Esito forzato",
"test_data": "Dati del test", "test_data": "Dati del test",

View File

@ -363,7 +363,7 @@ class Crud(Widget):
if text != self.filters.get(fn, None): if text != self.filters.get(fn, None):
self.filters[fn] = text self.filters[fn] = text
self.db.filter(self.select[cn], text) self.db.filter(self.select[cn], text)
self.refresh() self.refresh(filter_cn=cn)
@staticmethod @staticmethod
def default_row_upgrader(row, row_number, crud): def default_row_upgrader(row, row_number, crud):
@ -393,7 +393,7 @@ class Crud(Widget):
fails.append(str(e)) fails.append(str(e))
return r, fail, fails return r, fail, fails
def refresh(self, action=None, page=0): def refresh(self, action=None, page=0, filter_cn=None):
# IGNORE SIGNALS WHILE UPDATING # IGNORE SIGNALS WHILE UPDATING
self.db_tw.blockSignals(True) self.db_tw.blockSignals(True)
self._modified = False # force reset modified status self._modified = False # force reset modified status
@ -472,6 +472,10 @@ class Crud(Widget):
w.setPlaceholderText("Non filtrabile") w.setPlaceholderText("Non filtrabile")
w.setEnabled(False) w.setEnabled(False)
self.db_tw.setCellWidget(0, cn, w) self.db_tw.setCellWidget(0, cn, w)
if filter_cn is not None:
w = self.db_tw.cellWidget(0, filter_cn)
if w is not None:
w.setFocus()
# INSERT UPDATED DATA # INSERT UPDATED DATA
for rn, r in enumerate(data, start=1): # start=1 because rn starts from 1 (filters line) for rn, r in enumerate(data, start=1): # start=1 because rn starts from 1 (filters line)
r = self.row_upgrader(r, rn, self) r = self.row_upgrader(r, rn, self)

View File

@ -309,7 +309,7 @@ class Test(Widget):
self.data[step_name] = {} self.data[step_name] = {}
if data is not None: if data is not None:
data["step"] = model_to_dict(self.step) data["step"] = model_to_dict(self.step)
self.data[step_name][len(self.data[step_name])] = data self.data[step_name][str(len(self.data[step_name]))] = data
self.data["overridden"] = self.data["overridden"] or data.get("overridden", False) self.data["overridden"] = self.data["overridden"] or data.get("overridden", False)
self.data["ok"] = self.data["ok"] and data.get("ok", False) self.data["ok"] = self.data["ok"] and data.get("ok", False)
self.next() self.next()
@ -317,12 +317,13 @@ class Test(Widget):
def done(self, ok=True): def done(self, ok=True):
self.log.info("cycle done") self.log.info("cycle done")
if "vision" in self.data: if "vision" in self.data:
vision_test_1 = self.data.get("vision", {}).get(0, {}) vision_test_1 = self.data.get("vision", {}).get("0", {})
out_paths = self.components["vision_saver"].save( out_paths = self.components["vision_saver"].save(
save_time=vision_test_1.get("time", None), save_time=vision_test_1.get("time", None),
frame=vision_test_1.get("frame", None), frame=vision_test_1.get("frame", None),
# vision=vision_test_1.get("detections", None), # vision=vision_test_1.get("detections", None),
) )
self.data.get("vision", {}).get("0", {})["files"] = out_paths
self.log.info(f"cycle vision saved locally: {out_paths!r}") self.log.info(f"cycle vision saved locally: {out_paths!r}")
for vision in self.data.get("vision", {}).values(): for vision in self.data.get("vision", {}).values():
vision.pop("frame", None) vision.pop("frame", None)
@ -347,7 +348,7 @@ class Test(Widget):
self.log.info("cycle printed already compiled label") self.log.info("cycle printed already compiled label")
# LABEL PRINT # LABEL PRINT
recipe = archived.test_data.get("recipe", {}) recipe = archived.test_data.get("recipe", {})
leak_test_1 = archived.test_data.get("leak", {}).get(0, {}) leak_test_1 = archived.test_data.get("leak", {}).get("0", {})
leak_test_1_step = leak_test_1.get("step", {}) leak_test_1_step = leak_test_1.get("step", {})
leak_test_1_step_spec = leak_test_1_step.get("spec", {}) leak_test_1_step_spec = leak_test_1_step.get("spec", {})
leak_test_1_results = leak_test_1.get("results", {}) leak_test_1_results = leak_test_1.get("results", {})