Merge remote-tracking branch 'origin/master'
# Conflicts: # config/machine_settings/hostnames.ini
This commit is contained in:
commit
fca98bdd0f
@ -17,6 +17,7 @@ balance_blue: 1.5
|
||||
time_format: %Y-%m-%d_%H-%M-%S
|
||||
path: ./data/images
|
||||
minimum_disk_free_space_gb: 20
|
||||
resize_resolution: 612x512
|
||||
|
||||
[archive_synchronizer]
|
||||
archive_endpoint: https://r5portal.it/api/echo/
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from PyQt5.QtCore import QThread
|
||||
|
||||
if "--sim-serial" in sys.argv:
|
||||
from components.dummies.serial import serial
|
||||
else:
|
||||
@ -16,16 +17,8 @@ C_UINT32_MAX = c_uint32(-1).value
|
||||
|
||||
class NeoPixels(Component):
|
||||
def __init__(self, config=None, name=None):
|
||||
self.conn = None
|
||||
super().__init__(config=config, name=name, threaded=False)
|
||||
self.conn = serial.Serial(
|
||||
self.port,
|
||||
baudrate=self.baudrate,
|
||||
stopbits=self.stopbits,
|
||||
parity=self.parity,
|
||||
bytesize=self.bytesize,
|
||||
)
|
||||
time.sleep(5)
|
||||
self.set_all_pixel_color("#000000")
|
||||
|
||||
def config_changed(self):
|
||||
self.port = self.config[self.name]["port"]
|
||||
@ -33,6 +26,17 @@ class NeoPixels(Component):
|
||||
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())
|
||||
if self.conn is not None:
|
||||
self.conn.close()
|
||||
self.conn = serial.Serial(
|
||||
self.port,
|
||||
baudrate=self.baudrate,
|
||||
stopbits=self.stopbits,
|
||||
parity=self.parity,
|
||||
bytesize=self.bytesize,
|
||||
)
|
||||
QThread.msleep(5000)
|
||||
self.set_all_pixel_color("#000000")
|
||||
|
||||
def set(self, pixel, color):
|
||||
if type(pixel) is not int or pixel < 0 or pixel > C_UINT32_MAX:
|
||||
@ -44,7 +48,7 @@ class NeoPixels(Component):
|
||||
response = self.conn.readline()
|
||||
if response != b"ok\n":
|
||||
self.log.error(response.strip())
|
||||
# conn.close()
|
||||
# self.conn.close()
|
||||
except serial.serialutil.SerialException:
|
||||
self.log.exception(traceback.format_exc())
|
||||
|
||||
|
||||
@ -180,7 +180,7 @@ class TecnaMarpossProvasetT3P(ModbusComponent):
|
||||
"Running test: test type",
|
||||
"Running test: sequence index",
|
||||
]}
|
||||
if info["Running test: active phase"] == "FINE TEST":
|
||||
if info["Running test: active phase"] == "FINE TEST": # "END TEST, WAITING THE START OF A NEW TEST":
|
||||
info.update(self.get_test_results())
|
||||
self.log.debug(str(info))
|
||||
super()._get([info])
|
||||
|
||||
@ -7,7 +7,7 @@ registers = {
|
||||
"Life counter: TOTAL TESTS": [8 - 1, {"dt": "32bit_uint", }],
|
||||
"Real time test pressure output": [11 - 1, {"dt": "32bit_int", "f": 21, }],
|
||||
"Real time differential pressure output": [13 - 1, {"dt": "32bit_int", "f": 22, }],
|
||||
"Real time pressure line regulator": [15 - 1, {"dt": "16bit_int", "f": 23,"g":0.1 }],
|
||||
"Real time pressure line regulator": [15 - 1, {"dt": "16bit_int", "f": 23, "g": 0.1}], # TODO: REMOVE "g"
|
||||
"Active alarm flags": [16 - 1, {"dt": "16bit_uint", }],
|
||||
# | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
# | | | | A5 | A4 | A3 | A2 | A1 | | | | | | E3 | E2 | E1 |
|
||||
@ -28,9 +28,9 @@ registers = {
|
||||
"Running test: flow rate format": [27 - 1, {"dt": "16bit_uint", }], # white
|
||||
"Active test program number": [31 - 1, {"dt": "16bit_uint", }],
|
||||
"Running test: active phase": [32 - 1, {"dt": "16bit_uint", "decoding": {
|
||||
0: "ATTESA START",
|
||||
10: "CONTROLLO BARCODE",
|
||||
20: "ATTESA BARCODE",
|
||||
0: "ATTESA START", # "WAITING START",
|
||||
10: "CONTROLLO BARCODE", # "CHECK BARCODE",
|
||||
20: "ATTESA BARCODE", # "WAITING BARCODE",
|
||||
30: "START TEST",
|
||||
40: "START CAGE",
|
||||
50: "START PLUG",
|
||||
@ -38,10 +38,10 @@ registers = {
|
||||
70: "WAIT DELAY ACKNOWLEDGE COMMAND",
|
||||
80: "WAIT DELAY",
|
||||
90: "WAIT ACKNOWLEDGE",
|
||||
100: "T0 PRE-RIEMPIMENTO",
|
||||
110: "T1 RIEMPIMENTO",
|
||||
120: "T2 ASSESTAMENTO",
|
||||
130: "T3 MISURA",
|
||||
100: "T0 PRE-RIEMPIMENTO", # "T0 PRE-FILL",
|
||||
110: "T1 RIEMPIMENTO", # "T1 FILLING",
|
||||
120: "T2 ASSESTAMENTO", # "T2 SETTLING",
|
||||
130: "T3 MISURA", # "T3 MEASURE",
|
||||
140: "WAITING OPERATOR RESULT ACKNOWLEDGE",
|
||||
150: "RESULT PRESENT",
|
||||
160: "MARKING",
|
||||
@ -49,7 +49,7 @@ registers = {
|
||||
180: "END SEQUENCE PROGRAM",
|
||||
190: "END PLUG",
|
||||
200: "END CAGE",
|
||||
210: "FINE TEST",
|
||||
210: "FINE TEST", # WAITING THE START OF A NEW TEST",
|
||||
}, }],
|
||||
"Running test: phase backwards time": [33 - 1, {"dt": "16bit_uint", "f": 26, }],
|
||||
"Running test: filling pressure": [34 - 1, {"dt": "32bit_int", "f": 21, }],
|
||||
|
||||
@ -1,29 +1,26 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PyQt5.QtCore import (QFileSystemWatcher, QLineF, QMutex, QPointF, QRectF,
|
||||
Qt, QThread, pyqtSignal)
|
||||
from PyQt5.QtGui import (QBrush, QColor, QFont, QImage, QPainter, QPainterPath,
|
||||
QPen, QPixmap)
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from lib.helpers import time_this
|
||||
from .component import Component
|
||||
from .consumer import Consumer
|
||||
|
||||
if "--no-gpu" in sys.argv:
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
|
||||
|
||||
import tensorflow as tf
|
||||
from lib.helpers.object_detection.utils import label_map_util
|
||||
from PyQt5.QtCore import (QFileSystemWatcher, QLineF, QMutex, QPointF, QRectF,
|
||||
Qt, QThread, pyqtSignal)
|
||||
from PyQt5.QtGui import (QBrush, QColor, QFont, QImage, QPainter, QPainterPath,
|
||||
QPen, QPixmap)
|
||||
|
||||
from .component import Component
|
||||
from .consumer import Consumer
|
||||
|
||||
if "--no-edgetpu" not in sys.argv:
|
||||
try:
|
||||
@ -317,8 +314,7 @@ class Vision(Component):
|
||||
if tf_mode is None and "normal" in self.allowed_modes:
|
||||
try:
|
||||
# create tensorflow model
|
||||
model = tf.saved_model.load(str(self.models_dir / model_name))
|
||||
model = model.signatures['serving_default']
|
||||
model = tf.saved_model.load(str(self.models_dir / model_name)).signatures["serving_default"]
|
||||
tf_mode = "normal"
|
||||
except Exception:
|
||||
self.log.exception(traceback.format_exc())
|
||||
@ -339,7 +335,6 @@ class Vision(Component):
|
||||
self.log.info(f"initialized model {self.model!r} with mode {self.tf_mode!r}")
|
||||
self.loading_model_signal.emit({"status": "done"})
|
||||
|
||||
@time_this
|
||||
def check_features(self, frame, lock=True):
|
||||
if self.interpreter is not None and frame.shape != self.interpreter.get_input_details()[0]["shape"][1:3]:
|
||||
tensor = np.expand_dims(cv2.resize(frame, self.interpreter.get_input_details()[0]["shape"][1:3], interpolation=cv2.INTER_LINEAR), axis=0)
|
||||
@ -347,6 +342,7 @@ class Vision(Component):
|
||||
frame_resized = cv2.resize(frame, (256, 256), interpolation=cv2.INTER_LINEAR)
|
||||
tensor = tf.convert_to_tensor(np.asarray(frame_resized))
|
||||
tensor = tensor[tf.newaxis, ...]
|
||||
# tensor = np.expand_dims(frame, axis=0)
|
||||
# Run inference
|
||||
if lock:
|
||||
self.lock.lock()
|
||||
@ -388,33 +384,36 @@ class Vision(Component):
|
||||
if lock:
|
||||
self.lock.unlock()
|
||||
detections = {
|
||||
"detection_scores": [scores][0:1],
|
||||
"detection_boxes": [boxes][0:1],
|
||||
"detection_classes": [map(lambda class_id: class_id + 1, class_ids)][0:1],
|
||||
"detection_scores": [scores],
|
||||
"detection_boxes": [boxes],
|
||||
"detection_classes": [map(lambda class_id: class_id + 1, class_ids)],
|
||||
}
|
||||
else:
|
||||
detections = self.model(tensor)
|
||||
if lock:
|
||||
self.lock.unlock()
|
||||
detections = {
|
||||
"detection_scores": detections["detection_scores"].numpy().tolist(),
|
||||
"detection_boxes": detections["detection_boxes"].numpy().tolist(),
|
||||
"detection_classes": detections["detection_classes"],
|
||||
}
|
||||
# WARNING: results other than the ones related to tensor[-1] will be discarded
|
||||
parsed_detections = []
|
||||
for d_box, d_class, d_score in zip( # , d_mask in zip(
|
||||
for d_score, d_box, d_class in zip( # , d_mask in zip(
|
||||
detections["detection_scores"][-1],
|
||||
detections["detection_boxes"][-1],
|
||||
detections["detection_classes"][-1],
|
||||
detections["detection_scores"][-1],
|
||||
# detections["detection_masks"][-1],
|
||||
):
|
||||
if d_score < self.detection_threshold:
|
||||
continue
|
||||
box = list(d_box)
|
||||
# box = d_box.numpy().tolist()
|
||||
box = [i * s for i, s in zip(box, frame.shape[:2] * 2)] # rescale detection to frame size
|
||||
detection = {
|
||||
"class": self.category_index[int(d_class)],
|
||||
"score": float(d_score),
|
||||
# "score": d_score.numpy().tolist(),
|
||||
# "mask": d_mask.numpy().tolist(),
|
||||
"score": d_score,
|
||||
"box": box,
|
||||
"class": self.category_index[int(d_class)],
|
||||
# "mask": d_mask,
|
||||
"center": self.get_center(box),
|
||||
"size": self.get_size(box),
|
||||
}
|
||||
@ -477,7 +476,6 @@ class Vision(Component):
|
||||
else:
|
||||
raise NotImplementedError(f"invalid zone_detection_filter_mode: {self.zone_detection_filter_mode!r}")
|
||||
elif zone["shape"] == "ellipse": # it's a circle
|
||||
distance = self.get_distance(detection["center"], zone["center"])
|
||||
if self.zone_detection_filter_mode == "center_inside":
|
||||
outside_zone = distance > zone["size"][0] / 2
|
||||
elif self.zone_detection_filter_mode == "box_inside":
|
||||
|
||||
@ -17,19 +17,30 @@ class VisionSaver(Component):
|
||||
def config_changed(self):
|
||||
self.location = Path(self.config[self.name]["path"])
|
||||
os.makedirs(self.location, exist_ok=True)
|
||||
self.resize_resolution = self.config[self.name].get("resize_resolution", None)
|
||||
if self.resize_resolution is not None:
|
||||
self.resize_resolution = list(map(int, self.resize_resolution.split("x")))
|
||||
self.mask_zones = self.config[self.name].get("mask_zones", None)
|
||||
self.minimum_disk_free_space_gb = self.config[self.name].get("minimum_disk_free_space_gb", None)
|
||||
if self.minimum_disk_free_space_gb is not None:
|
||||
self.minimum_disk_free_space_gb = float(self.minimum_disk_free_space_gb)
|
||||
self.time_format = self.config[self.name]["time_format"]
|
||||
|
||||
def save(self, save_time, img, mask=True):
|
||||
timestamp = datetime.fromtimestamp(save_time).strftime(self.time_format)
|
||||
def save(self, save_time, img, mask=True, resize=True):
|
||||
if type(save_time) is float:
|
||||
save_time = int(save_time)
|
||||
if type(save_time) is int:
|
||||
save_time = datetime.fromtimestamp(save_time)
|
||||
if type(save_time) is not datetime:
|
||||
raise ValueError(f"save_time must be float int or datetime, not {type(save_time)}")
|
||||
timestamp = save_time.strftime(self.time_format)
|
||||
save_dir = self.location / save_time.strftime("%Y") / save_time.strftime("%m")
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
out_path = save_dir / f"{timestamp}.png"
|
||||
self.log.info(f"saving {out_path}")
|
||||
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
||||
if resize and self.resize_resolution is not None:
|
||||
img = cv2.resize(img, self.resize_resolution, interpolation=cv2.INTER_LINEAR)
|
||||
if mask and self.mask_zones is not None:
|
||||
height, width, channels = img.shape
|
||||
out = np.full(
|
||||
@ -41,7 +52,7 @@ class VisionSaver(Component):
|
||||
out[zone[1]:zone[3], zone[0]:zone[2]] = img[zone[1]:zone[3], zone[0]:zone[2]]
|
||||
else:
|
||||
out = img
|
||||
cv2.imwrite(out_path, out)
|
||||
cv2.imwrite(str(out_path), out)
|
||||
return out_path
|
||||
|
||||
def remove_older_images_if_needed(self):
|
||||
|
||||
@ -15,7 +15,7 @@ conn = serial.Serial(
|
||||
)
|
||||
pixel = C_UINT32_MAX
|
||||
time.sleep(5)
|
||||
color = "#000000"
|
||||
color = "#ff00ff"
|
||||
conn.write(int.to_bytes(pixel, length=4, byteorder="big") + bytes.fromhex(color[1:7]) + b"\n")
|
||||
response = conn.readline()
|
||||
print(response)
|
||||
|
||||
@ -46,19 +46,19 @@ class Archive(Widget):
|
||||
replace_widget(self, "crud_w", self.crud)
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud.db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.crud().db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud().db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud().db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.print_b.clicked.connect(self.print_label)
|
||||
|
||||
def check(self):
|
||||
if not self.crud.modified:
|
||||
selected = self.crud.get_selected_rows()
|
||||
if not self.crud().modified:
|
||||
selected = self.crud().get_selected_rows()
|
||||
if len(selected) == 1:
|
||||
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
|
||||
if selected >= 0 and selected < len(self.crud.data_index):
|
||||
selected = self.crud.data_index[selected]
|
||||
self.selected = self.crud.db.table_model.get_by_id(selected)
|
||||
if selected >= 0 and selected < len(self.crud().data_index):
|
||||
selected = self.crud().data_index[selected]
|
||||
self.selected = self.crud().db.table_model.get_by_id(selected)
|
||||
self.print_b.setEnabled(True)
|
||||
return
|
||||
self.selected = None
|
||||
|
||||
@ -46,19 +46,19 @@ class Autotests_Archive(Widget):
|
||||
replace_widget(self, "crud_w", self.crud)
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud.db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.crud().db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud().db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud().db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.print_b.clicked.connect(self.print_label)
|
||||
|
||||
def check(self):
|
||||
if not self.crud.modified:
|
||||
selected = self.crud.get_selected_rows()
|
||||
if not self.crud().modified:
|
||||
selected = self.crud().get_selected_rows()
|
||||
if len(selected) == 1:
|
||||
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
|
||||
if selected >= 0 and selected < len(self.crud.data_index):
|
||||
selected = self.crud.data_index[selected]
|
||||
self.selected = self.crud.db.table_model.get_by_id(selected)
|
||||
if selected >= 0 and selected < len(self.crud().data_index):
|
||||
selected = self.crud().data_index[selected]
|
||||
self.selected = self.crud().db.table_model.get_by_id(selected)
|
||||
self.print_b.setEnabled(True)
|
||||
return
|
||||
self.selected = None
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
from PyQt5.QtCore import QEvent, Qt
|
||||
from PyQt5.QtWidgets import QApplication, QTableWidget
|
||||
@ -28,7 +29,7 @@ class CopyPastableCrudQTableWidget(QTableWidget):
|
||||
copy_sparse[row] = {}
|
||||
cell = self.cellWidget(row, col)
|
||||
if cell is not None:
|
||||
copy_sparse[row][col] = cell.parse(row_number=row, crud=self.crud)
|
||||
copy_sparse[row][col] = cell.parse(row_number=row, crud=weakref.ref(self.crud))
|
||||
copy_dense = [["" for col in range(max_col - min_col + 1)] for row in range(max_row - min_row + 1)]
|
||||
for row, row_cells in copy_sparse.items():
|
||||
for col, cell_text in row_cells.items():
|
||||
@ -54,7 +55,7 @@ class CopyPastableCrudQTableWidget(QTableWidget):
|
||||
c = min_col + col
|
||||
cell = self.cellWidget(r, c)
|
||||
if cell is not None:
|
||||
cell.render(cell_text, field_name=self.crud.select[c] if self.crud is not None else None, row_number=r, crud=self.crud)
|
||||
cell.render(cell_text, field_name=self.crud.select[c] if self.crud is not None else None, row_number=r, crud=weakref.ref(self.crud))
|
||||
super().keyPressEvent(event)
|
||||
QApplication.sendEvent(self, event)
|
||||
return True
|
||||
|
||||
@ -6,7 +6,7 @@ from datetime import datetime
|
||||
|
||||
from lib.db import Crud_DB
|
||||
from peewee import TextField
|
||||
from PyQt5.QtCore import QObject, Qt, pyqtSignal
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtWidgets import (QAbstractItemView, QComboBox, QDialog,
|
||||
QGridLayout, QHeaderView, QLineEdit, QMessageBox,
|
||||
QPlainTextEdit, QPushButton)
|
||||
@ -46,7 +46,12 @@ class Cell:
|
||||
self.field_alias = field_alias
|
||||
self.field = field
|
||||
self.row_number = row_number
|
||||
if crud is None:
|
||||
self.crud = None
|
||||
elif type(crud) is weakref.ReferenceType:
|
||||
self.crud = crud
|
||||
else:
|
||||
self.crud = weakref.ref(crud)
|
||||
self.value = None
|
||||
self.is_modified = False
|
||||
self.connected_modified = False
|
||||
@ -181,7 +186,7 @@ class Json_External_Dialog_Editor_Cell_Widget(QPushButton, Cell):
|
||||
def __init__(self, editor_widget_class, *args, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None, **kwargs):
|
||||
if not issubclass(editor_widget_class, Editor):
|
||||
raise AssertionError(f"editor_widget_class {editor_widget_class!r} must be a subclass of {Editor!r}")
|
||||
self.editor = editor_widget_class(*args, cell_widget=self, **kwargs)
|
||||
self.editor = editor_widget_class(*args, cell_widget=weakref.ref(self), **kwargs)
|
||||
super().__init__(u"\u238B modifica")
|
||||
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
|
||||
self.dialog = Dialog()
|
||||
@ -335,7 +340,7 @@ class Crud(Widget):
|
||||
for fn, cn in self.select_index.items():
|
||||
w = self.db_tw.cellWidget(rn, cn)
|
||||
try:
|
||||
r[fn] = w._parse(row_number=rn, crud=self)
|
||||
r[fn] = w._parse(row_number=rn, crud=weakref.ref(self))
|
||||
except Exception as e:
|
||||
self.log.exception(traceback.format_exc())
|
||||
self.set_row_color(rn, "red")
|
||||
@ -427,12 +432,12 @@ class Crud(Widget):
|
||||
r = self.row_upgrader(r, rn, self)
|
||||
for fn, cn in self.select_index.items():
|
||||
readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly)
|
||||
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self)
|
||||
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=weakref.ref(self))
|
||||
w.set_readonly(readonly=w.readonly)
|
||||
w.do_autocomplete(w.autocomplete)
|
||||
w.modified.connect(self.set_modified)
|
||||
if fn in r:
|
||||
w._render(data=r[fn], row_number=rn, crud=self)
|
||||
w._render(data=r[fn], row_number=rn, crud=weakref.ref(self))
|
||||
self.db_tw.setCellWidget(rn, cn, w)
|
||||
# REENABLE EVENTS AFTER UPDATE
|
||||
self.db_tw.blockSignals(False)
|
||||
@ -452,7 +457,7 @@ class Crud(Widget):
|
||||
self.db_tw.setRowCount(rn + 1)
|
||||
for fn, cn in self.select_index.items():
|
||||
readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly)
|
||||
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self)
|
||||
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=weakref.ref(self))
|
||||
w.set_readonly(readonly=w.readonly)
|
||||
w.do_autocomplete(w.autocomplete)
|
||||
w.modified.connect(self.set_modified)
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import weakref
|
||||
|
||||
from PyQt5.QtCore import QObject
|
||||
from PyQt5.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit,
|
||||
QListWidget, QPlainTextEdit, QRadioButton,
|
||||
@ -8,7 +10,12 @@ from ui.widget import Widget
|
||||
class Editor(Widget):
|
||||
def __init__(self, cell_widget=None):
|
||||
super().__init__()
|
||||
if cell_widget is None:
|
||||
self.cell_widget = None
|
||||
elif type(cell_widget) is weakref.ReferenceType:
|
||||
self.cell_widget = cell_widget
|
||||
else:
|
||||
self.cell_widget = weakref.ref(cell_widget)
|
||||
self.spec = {}
|
||||
|
||||
def set_readonly(self, readonly=True):
|
||||
|
||||
@ -22,12 +22,12 @@ class Step_Spec_JEDECW(QPushButton, Cell):
|
||||
super().__init__(u"\u238B modifica")
|
||||
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
|
||||
self.update_editor()
|
||||
self.crud.modified.connect(self.update_editor)
|
||||
self.crud().modified.connect(self.update_editor)
|
||||
self.dialog.setWindowTitle(self.field_alias)
|
||||
self.clicked.connect(self.dialog.show)
|
||||
|
||||
def update_editor(self):
|
||||
self.editor_type = self.crud.db_tw.cellWidget(self.row_number, self.crud.select_index["type"])._parse()
|
||||
self.editor_type = self.crud().db_tw.cellWidget(self.row_number, self.crud().select_index["type"])._parse()
|
||||
self.editor = self.editors[self.editor_type]
|
||||
if self.dialog.centralWidget is not self.editor:
|
||||
self.dialog.setCentralWidget(self.editor)
|
||||
|
||||
@ -252,26 +252,32 @@ class Test(Widget):
|
||||
|
||||
def set_leak(self, leak=None):
|
||||
if "leak" not in self.data:
|
||||
self.data["leak"] = []
|
||||
self.data["leak"] = {}
|
||||
leak["step"] = model_to_dict(self.step)
|
||||
self.data["leak"].append(leak)
|
||||
self.data["leak"][len(self.data["leak"])] = leak
|
||||
self.data["overridden"] = self.data["overridden"] or leak.get("overridden", False)
|
||||
self.data["ok"] = self.data["ok"] and leak.get("ok", False)
|
||||
self.next()
|
||||
|
||||
def set_vision(self, vision=None):
|
||||
if "vision" not in self.data:
|
||||
self.data["vision"] = []
|
||||
self.data["vision"] = {}
|
||||
vision["step"] = model_to_dict(self.step)
|
||||
self.data["vision"].append(vision)
|
||||
self.data["vision"][len(self.data["vision"])] = vision
|
||||
self.data["overridden"] = self.data["overridden"] or vision.get("overridden", False)
|
||||
self.data["ok"] = self.data["ok"] and vision.get("ok", False)
|
||||
self.next()
|
||||
|
||||
def done(self, ok=True):
|
||||
self.log.info("cycle done")
|
||||
self.data["vision"][0].pop("detections")
|
||||
self.data["vision"][0].pop("results")
|
||||
t = self.data.get("vision", {}).get(0, {}).get("time", None)
|
||||
f = self.data.get("vision", {}).get(0, {}).get("frame", None)
|
||||
if t is not None and f is not None:
|
||||
file_path = self.components["vision_saver"].save(t, f)
|
||||
self.log.info(f"cycle vision image saved locally: {file_path!r}")
|
||||
for vision in self.data["vision"].values():
|
||||
vision.pop("frame", None)
|
||||
vision.pop("render", None)
|
||||
archived = Archive.archive(self.recipe, self.data, ok and self.data["ok"], overridden=self.data["overridden"])
|
||||
self.log.info(f"cycle archived locally: {archived!r}")
|
||||
# COUNT OK
|
||||
@ -281,29 +287,28 @@ class Test(Widget):
|
||||
def print(self, archived=None):
|
||||
self.log.info("cycle print")
|
||||
# LABEL PRINT
|
||||
datamatrix= archived.recipe.part_number+ archived.time.strftime("%m%y")+f"{archived.id:0>5}"
|
||||
dt_label=archived.time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
pmax = self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("test_pressure", "-")
|
||||
pmin=self.data.get("leak", [{}, {}])[0].get("results", {}).get("data", {}).get("Running test: pressure at the end of settling", "-")
|
||||
leak=self.data.get("leak", [{}, {}])[0].get("results", {}).get("data", {}).get("Running test: measured leak", "-")
|
||||
datamatrix = str(archived.recipe.part_number) + archived.time.strftime("%m%y") + f"{archived.id:0>5}"
|
||||
pmax = self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure", "-")
|
||||
pmin = self.data.get("leak", {}).get(0, {}).get("results", {}).get("data", {}).get("Running test: pressure at the end of settling", "-")
|
||||
leak = self.data.get("leak", {}).get(0, {}).get("results", {}).get("data", {}).get("Running test: measured leak", "-")
|
||||
context = {
|
||||
"DATAMATRIX": datamatrix,
|
||||
"DATAMATRIX_TEXT": datamatrix,
|
||||
"PIECE_NUM": str(archived.id),
|
||||
"DATETIME": dt_label,
|
||||
"DATETIME": archived.time.strftime("%d/%m/%Y %H:%M:%S"),
|
||||
"SHIFT": str(get_shift(archived.time)),
|
||||
"STATION": str(self.machine_id),
|
||||
"OPERATOR": str(Users.get_session().user.username),
|
||||
"PMAX": f"{pmax:.3f}",
|
||||
"PMIN": f"{pmin:.3f}",
|
||||
"LEAK": f"{leak:.3f}",
|
||||
"TFILL": str(self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("filling_time", "-")),
|
||||
"TSTAB": str(self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("settling_time", "-")),
|
||||
"TTEST": str(self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("test_time", "-")),
|
||||
"MAXLEAK": str(self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("test_pressure_max_delta", "-")),
|
||||
"PTESTMIN": str(self.data.get("leak", [{}, {}])[0].get("step", {}).get("spec", {}).get("test_pressure_min_delta", "-")),
|
||||
"PMAX": f"{pmax:.3f}" if type(pmax) is not str else pmax,
|
||||
"PMIN": f"{pmin:.3f}" if type(pmin) is not str else pmin,
|
||||
"LEAK": f"{leak:.3f}" if type(leak) is not str else leak,
|
||||
"TFILL": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("filling_time", "-")),
|
||||
"TSTAB": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("settling_time", "-")),
|
||||
"TTEST": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_time", "-")),
|
||||
"MAXLEAK": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure_max_delta", "-")),
|
||||
"PTESTMIN": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure_min_delta", "-")),
|
||||
"RESULT_L1": "ESITO" + str(" FORZATO" if self.data.get("overridden", False) else ""),
|
||||
"RESULT_L2": str("CONFORME" if self.data.get("leak", [{}, {}])[0].get("results", {}).get("ok", False) else "SCARTO"),
|
||||
"RESULT_L2": str("CONFORME" if self.data.get("leak", {}).get(0, {}).get("results", {}).get("ok", False) else "SCARTO"),
|
||||
}
|
||||
self.components["label_printer"].print_label("EtichettaR5", context=context)
|
||||
self.log.info(f"cycle printed: {context!r}")
|
||||
|
||||
@ -1,18 +1,9 @@
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import cv2
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtGui import QImage, QPixmap
|
||||
from ui.test_test import Test_Test
|
||||
|
||||
TMP_DIR = "tmp"
|
||||
ARCHIVE_DIR = "data/vision_saves"
|
||||
INFERENCE_INPUT_W = 256
|
||||
INFERENCE_INPUT_H = 256
|
||||
|
||||
|
||||
|
||||
class Test_Vision(Test_Test):
|
||||
request_frame = pyqtSignal()
|
||||
@ -36,7 +27,6 @@ class Test_Vision(Test_Test):
|
||||
self.components["vision"].resume()
|
||||
self.components["galaxy_camera"].resume()
|
||||
self.request_frame.emit() # request first frame
|
||||
self.save_b.clicked.connect(lambda: self.save_frame_all(position="tmp"))
|
||||
|
||||
def stop(self):
|
||||
# disable camera-vision loop
|
||||
@ -96,38 +86,12 @@ class Test_Vision(Test_Test):
|
||||
def save_last(self):
|
||||
if self.last is None:
|
||||
return
|
||||
self.components["vision_saver"].save(self.last["time"], self.last["frame"])
|
||||
|
||||
def emit_ok(self):
|
||||
self.last.pop("frame", None)
|
||||
self.last.pop("render", None)
|
||||
super().emit_ok()
|
||||
self.components["neo_pixels"].set_all_pixel_color("#000000")
|
||||
|
||||
def emit_ko(self):
|
||||
self.last.pop("frame", None)
|
||||
self.last.pop("render", None)
|
||||
super().emit_ko()
|
||||
self.components["neo_pixels"].set_all_pixel_color("#000000")
|
||||
|
||||
def save_frame_detection(self, position=None, date=None):
|
||||
if date is None:
|
||||
date = datetime.now()
|
||||
if position == "archive":
|
||||
save_dir = ARCHIVE_DIR
|
||||
elif position == "tmp":
|
||||
save_dir = TMP_DIR
|
||||
elif position is None:
|
||||
save_dir = self.config["image saver"]["location"]
|
||||
else:
|
||||
save_dir = position
|
||||
save_dir += "/img_archive"
|
||||
save_dir += f"/{date.strftime('%Y')}/{date.strftime('%m')}"
|
||||
self.remove_older_images_if_needed()
|
||||
if not os.path.exists(save_dir):
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
filename = f"{save_dir}/{date.strftime('%Y-%m-%d_%H_%M_%S')}_{self.config['image saver']['suffix']}.png"
|
||||
print(f"saving {filename}")
|
||||
image_np_bgr = cv2.cvtColor(self.detection_image, cv2.COLOR_RGB2BGR)
|
||||
cv2.imwrite(filename, image_np_bgr)
|
||||
return filename
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ class Users_Management(Widget):
|
||||
user = Users.generate(username=row["username"], password=row["password"], roles=row["roles"])
|
||||
except AssertionError as e:
|
||||
self.log.exception(traceback.format_exc())
|
||||
crud.set_row_color(row_number, "red")
|
||||
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(user)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user