Add machine status tracking and update for ArchiveSynchronizer

The ArchiveSynchronizer now tracks machine status and sends updates about it. Options of status are tracked as "working", "logged-in", and "logged-out". This implementation includes configuration changes for the st-ten-10 machine and error reporting for failures during remote archive operations.
This commit is contained in:
Eduardo 2024-04-23 11:15:23 +02:00
parent 414d8431c1
commit 85c1ec2d2d
6 changed files with 64 additions and 14 deletions

View File

@ -8,3 +8,4 @@ st-ten-6: st-ten-6
st-ten-7: st-ten-7 st-ten-7: st-ten-7
st-ten-8: st-ten-8 st-ten-8: st-ten-8
ST-TEN-9: st-ten-9 ST-TEN-9: st-ten-9
st-ten-10: st-ten-10

View File

@ -3,7 +3,7 @@ description = ST-TEN-10
instruction_folder = st-ten-10 instruction_folder = st-ten-10
[hardware_config] [hardware_config]
archive_synchronizer: absent archive_synchronizer: present
label_printer: present label_printer: present
label_printer_2: present # Brother label printer used only in st-ten-10 label_printer_2: present # Brother label printer used only in st-ten-10
remote_api: absent remote_api: absent
@ -16,8 +16,8 @@ leak_disabled: active #disable the tecna_t3 and furness_controls from starting
[archive_synchronizer] [archive_synchronizer]
archive_endpoint: https://satilportal.it/api/st-ten-save/ archive_endpoint: https://satilportal.it/api/st-ten-save/
poll_time: 60 poll_time: 10
hold_time: 1 hold_time: 10
#[furness_controls] #[furness_controls]
#port: COM3 #port: COM3

View File

@ -2,6 +2,9 @@ import json
import os import os
import re import re
import sys import sys
import threading
import time
import requests
import traceback import traceback
import requests import requests
@ -13,19 +16,22 @@ from requests.adapters import HTTPAdapter, Retry
from urllib3.exceptions import InsecureRequestWarning from urllib3.exceptions import InsecureRequestWarning
from .component import Component from .component import Component
# Suppress insecure request warning # Suppress insecure request warning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
class ArchiveSynchronizer(Component): class ArchiveSynchronizer(Component):
machine_status = "offline"
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True): def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded) super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
self.simulate = "--sim-archiver" in sys.argv self.simulate = "--sim-archiver" in sys.argv
self.status= ArchiveSynchronizer.machine_status
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"] if "--dev-portal" in sys.argv:
self.archive_endpoint =f"https://dev.r5portal.it/api/st-ten-save/"
else:
self.archive_endpoint = self.config[self.name]["archive_endpoint"]
#self.status_endpoint = f"https://dev.r5portal.it/api/device-info-update?machine-id={self.machine_id_endpoint}&status={self.status}"
# 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)
@ -49,9 +55,48 @@ class ArchiveSynchronizer(Component):
if self.hold_time > 0: if self.hold_time > 0:
QThread.msleep(self.hold_time) QThread.msleep(self.hold_time)
self.gcs_bucket = None self.gcs_bucket = None
if "--dev-portal" in sys.argv:
self.update_machine_status()
super()._get() super()._get()
def update_machine_status(self):
self.status = ArchiveSynchronizer.machine_status
#print(f"machine_status_global: {ArchiveSynchronizer.machine_status}") # TESTING
self.status_endpoint = f"https://dev.r5portal.it/api/device-info-update?machine-id={self.machine_id.upper()}&status={self.status}"
if self.status not in ["working", "logged-in", "logged-out"]:
status_dict = {"last_status": "offline"}
else:
status_dict = {"last_status": self.status}
response = None
try:
if not self.simulate:
with requests.Session() as s:
s.mount("", HTTPAdapter(max_retries=Retry(total=0))) # this disables retries
response = requests.post(self.status_endpoint, json=status_dict, timeout=5, verify=False)
if response.status_code != 200:
raise AssertionError("bad status response")
except AssertionError as e:
self.log.warning(
f"Status: {self.status}: failed to update machine status: {str(e)}: {response.status_code if response else 'no response'}: {response.content if response else 'no response'}"
)
return False
except (requests.ConnectionError, requests.Timeout) as e:
self.log.warning(
f"Status: {self.status}: failed to update machine status, archive_endpoint might be unreachable: {str(e)}"
)
return False
except Exception:
self.log.error(
f"Status: {self.status}: failed to update machine status:\n{traceback.format_exc()}:\n{response.status_code if response else 'no response'}: {response.content if response else 'no response'}"
)
return False
self.log.info(f"Status: {self.status}: Machine Status Updated Successfully")
return True
def remote_archive(self, record): def remote_archive(self, record):
r = None
try: try:
if not self.simulate: if not self.simulate:
with requests.Session() as s: with requests.Session() as s:
@ -74,10 +119,6 @@ class ArchiveSynchronizer(Component):
except (requests.ConnectionError, requests.Timeout) as e: 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)}") self.log.warning(f"id: {record.id}: failed to archive remotely, archive_endpoint might be unreachable: {str(e)}")
return False return False
except Exception:
self.log.error(f"id: {record.id}: failed to archive remotely:\n{traceback.format_exc()}:\n{r.status_code}: {r.content}")
return False
self.log.info(f"id: {record.id}: archived remotely")
return True return True
def remote_store(self, record): def remote_store(self, record):

View File

@ -80,7 +80,6 @@ try:
class Main(QObject): class Main(QObject):
do = pyqtSignal(dict) do = pyqtSignal(dict)
@staticmethod @staticmethod
def _do(config): def _do(config):
return config["f"](*config.get("a", []), **config.get("k", {})) return config["f"](*config.get("a", []), **config.get("k", {}))
@ -250,6 +249,7 @@ try:
Recipe_Selection, Barcode_Recipe_Selection): Recipe_Selection, Barcode_Recipe_Selection):
# LOGOUT IMMEDIATELY IF NOT TESTING # LOGOUT IMMEDIATELY IF NOT TESTING
Users.logout() Users.logout()
ArchiveSynchronizer.machine_status = "logged-out"
self.open_login() self.open_login()
else: else:
if not self.main_window.centralWidget().autotesting: if not self.main_window.centralWidget().autotesting:

View File

@ -1,7 +1,7 @@
import platform import platform
import subprocess import subprocess
import sys import sys
from components import ArchiveSynchronizer
from lib.db import Session, Users from lib.db import Session, Users
from PyQt5.QtCore import QTimer, pyqtSignal from PyQt5.QtCore import QTimer, pyqtSignal
from PyQt5.QtGui import QKeySequence, QIcon from PyQt5.QtGui import QKeySequence, QIcon
@ -21,6 +21,9 @@ class Login(Widget):
super().__init__() super().__init__()
self.welcome_l.setText("BENVENUTO, PER INIZIARE IL COLLAUDO, EFFETTUA L'ACCESSO:") self.welcome_l.setText("BENVENUTO, PER INIZIARE IL COLLAUDO, EFFETTUA L'ACCESSO:")
self.user_cb.addItems(Users.get_usernames()) self.user_cb.addItems(Users.get_usernames())
ArchiveSynchronizer.machine_status = "logged-in"
QShortcut(QKeySequence("Return"), self).activated.connect(self.login_b.click) QShortcut(QKeySequence("Return"), self).activated.connect(self.login_b.click)
QShortcut(QKeySequence("Enter"), self).activated.connect(self.login_b.click) QShortcut(QKeySequence("Enter"), self).activated.connect(self.login_b.click)
self.login_b.clicked.connect(self.try_login) self.login_b.clicked.connect(self.try_login)
@ -56,6 +59,7 @@ class Login(Widget):
login_count += 1 login_count += 1
# /TESTING # /TESTING
self.successful_login.emit() self.successful_login.emit()
ArchiveSynchronizer.machine_status = "logged-in"
def show_osk(self): def show_osk(self):
self.password_le.setFocus() self.password_le.setFocus()

View File

@ -4,7 +4,7 @@ import os
import sys import sys
import weakref import weakref
from datetime import datetime, timedelta from datetime import datetime, timedelta
from components import ArchiveSynchronizer
from PyQt5.QtCore import QTimer, pyqtSlot from PyQt5.QtCore import QTimer, pyqtSlot
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from lib.db import Archive, Steps, Users from lib.db import Archive, Steps, Users
@ -245,6 +245,10 @@ class Test(Widget):
self.log.info(f"cycle step: {model_to_dict(self.step)!r} action: {action!r} current index:{self.cycle_index}") self.log.info(f"cycle step: {model_to_dict(self.step)!r} action: {action!r} current index:{self.cycle_index}")
else: else:
self.log.info(f"cycle step: {self.step!r} action: {action!r} current index:{self.cycle_index}") self.log.info(f"cycle step: {self.step!r} action: {action!r} current index:{self.cycle_index}")
ArchiveSynchronizer.machine_status = "working"
# print(f"{ArchiveSynchronizer.machine_status}")#testing
current_w = self.centralWidget current_w = self.centralWidget
if hasattr(current_w, "stop"): if hasattr(current_w, "stop"):
current_w.stop() current_w.stop()