Skip to content

Commit 3641e03

Browse files
authored
Merge pull request #41 from DiamondLightSource/I04_1-150_improve_messages
I04 1 150 improve messages
2 parents 0c5e0d9 + 2da87fc commit 3641e03

File tree

14 files changed

+138
-56
lines changed

14 files changed

+138
-56
lines changed

dls_barcode/camera/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .camera_switch import CameraSwitch
22
from .camera_scanner import CameraScanner
3+
from .scanner_message import NoNewBarcodeMessage, ScanErrorMessage
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class NoNewBarcodeMessage:
2+
pass
3+
4+
5+
class ScanErrorMessage:
6+
def __init__(self, content):
7+
self._content = content
8+
9+
def content(self):
10+
return self._content

dls_barcode/camera/scanner_worker.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import time
22

33
from dls_util.image import Image
4-
from dls_util.message import MessageType, Message
54
from dls_util import Beeper
65
from dls_barcode.scan import GeometryScanner, SlotScanner, OpenScanner
76
from dls_barcode.datamatrix import DataMatrix
87
from .camera_position import CameraPosition
98
from .plate_overlay import PlateOverlay
9+
from .scanner_message import NoNewBarcodeMessage, ScanErrorMessage
1010

1111
NO_PUCK_TIME = 2
1212

@@ -20,7 +20,7 @@ class ScannerWorker:
2020
"""
2121
def run(self, task_queue, overlay_queue, result_queue, message_queue, kill_queue, config, cam_position):
2222
print("SCANNER start")
23-
self._last_plate_time = time.time()
23+
self._last_puck_time = time.time()
2424

2525
SlotScanner.DEBUG = config.slot_images.value()
2626
SlotScanner.DEBUG_DIR = config.slot_image_directory.value()
@@ -54,8 +54,8 @@ def _process_frame(self, frame, config, overlay_queue, result_queue, message_que
5454
scan_result.print_summary()
5555

5656
if scan_result.success():
57-
# Record the time so we can see how long its been since we last saw a plate
58-
self._last_plate_time = time.time()
57+
# Record the time so we can see how long its been since we last saw a puck
58+
self._last_puck_time = time.time()
5959

6060
plate = scan_result.plate()
6161
if scan_result.any_valid_barcodes():
@@ -64,8 +64,12 @@ def _process_frame(self, frame, config, overlay_queue, result_queue, message_que
6464

6565
if scan_result.any_new_barcodes():
6666
result_queue.put((plate, image))
67-
elif scan_result.error() is not None and (time.time() - self._last_plate_time > NO_PUCK_TIME):
68-
message_queue.put(Message(MessageType.WARNING, scan_result.error(), lifetime=1))
67+
elif scan_result.any_valid_barcodes():
68+
# We have read valid barcodes but they are not new, so the scanner didn't even output a plate
69+
self._last_puck_time = time.time()
70+
message_queue.put(NoNewBarcodeMessage())
71+
elif scan_result.error() is not None and (time.time() - self._last_puck_time > NO_PUCK_TIME):
72+
message_queue.put(ScanErrorMessage(scan_result.error()))
6973

7074
def _create_scanner(self, cam_position, config):
7175
if cam_position == CameraPosition.SIDE:

dls_barcode/geometry/unipuck_calculator.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ def perform_alignment(self):
3434
num_points = len(self._slot_centers)
3535

3636
if num_points > self._num_slots:
37-
raise GeometryAlignmentError("Too many points to perform Unipuck alignment")
38-
37+
raise GeometryAlignmentError("Too many slots detected to perform Unipuck alignment")
3938
elif num_points < MIN_POINTS_FOR_ALIGNMENT:
40-
raise GeometryAlignmentError("Not enough points to perform Unipuck alignment")
39+
raise GeometryAlignmentError("Not enough slots detected to perform Unipuck alignment")
4140

4241
puck = self._calculate_puck_alignment()
4342
return puck
@@ -57,8 +56,8 @@ def _calculate_puck_alignment(self):
5756

5857
return puck
5958

60-
except Exception as ex:
61-
raise GeometryAlignmentError("Unipuck Alignment failed")
59+
except Exception:
60+
raise GeometryAlignmentError("Unipuck alignment failed")
6261

6362
@staticmethod
6463
def _find_puck_center(pin_centers):

dls_barcode/gui/main_window.py

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import multiprocessing
22
import queue
3+
import time
34

45
from PyQt4 import QtGui, QtCore
56

67
from dls_barcode.config import BarcodeConfig, BarcodeConfigDialog
7-
from dls_barcode.camera import CameraScanner, CameraSwitch
8+
from dls_barcode.camera import CameraScanner, CameraSwitch, NoNewBarcodeMessage
89
from dls_util import Beeper
910
from dls_util.file import FileManager
10-
from dls_util.message import MessageType, Message
1111
from .barcode_table import BarcodeTable
1212
from .image_frame import ImageFrame
1313
from .record_table import ScanRecordTable
14-
from .message_display import MessageDisplay
14+
from .message_box import MessageBox
15+
from .message_factory import MessageFactory
16+
17+
18+
RESULT_TIMER_PERIOD = 1000 # ms
19+
VIEW_TIMER_PERIOD = 1 # ms
20+
MESSAGE_TIMER_PERIOD = 1 # ms
1521

1622

1723
class DiamondBarcodeMainWindow(QtGui.QMainWindow):
@@ -39,19 +45,20 @@ def __init__(self, config_file):
3945
self._view_queue = multiprocessing.Queue()
4046
self._message_queue = multiprocessing.Queue()
4147
self._initialise_scanner()
48+
self._reset_msg_timer()
4249

4350
# Timer that controls how often new scan results are looked for
4451
self._result_timer = QtCore.QTimer()
4552
self._result_timer.timeout.connect(self._read_result_queue)
46-
self._result_timer.start(1000)
53+
self._result_timer.start(RESULT_TIMER_PERIOD)
4754

4855
self._view_timer = QtCore.QTimer()
4956
self._view_timer.timeout.connect(self._read_view_queue)
50-
self._view_timer.start(1)
57+
self._view_timer.start(VIEW_TIMER_PERIOD)
5158

5259
self._message_timer = QtCore.QTimer()
5360
self._message_timer.timeout.connect(self._read_message_queue)
54-
self._message_timer.start(1)
61+
self._message_timer.start(MESSAGE_TIMER_PERIOD)
5562

5663
self._camera_switch.restart_live_capture_from_side()
5764

@@ -74,8 +81,8 @@ def _init_ui(self):
7481
self._record_table = ScanRecordTable(self._barcode_table, self._image_frame, self._config, self.on_record_table_clicked)
7582

7683
# Message display
77-
self._message_display = MessageDisplay()
78-
self._message_display.setFixedHeight(64)
84+
self._message_box = MessageBox()
85+
self._message_box.setFixedHeight(64)
7986

8087
# Open options first to make sure the cameras are set up correctly.
8188
# Start live capture of the side as soon as the dialog box is closed
@@ -89,7 +96,7 @@ def _init_ui(self):
8996

9097
img_vbox = QtGui.QVBoxLayout()
9198
img_vbox.addWidget(self._image_frame)
92-
img_vbox.addWidget(self._message_display)
99+
img_vbox.addWidget(self._message_box)
93100
hbox.addLayout(img_vbox)
94101

95102
vbox = QtGui.QVBoxLayout()
@@ -185,20 +192,46 @@ def _camera_capture_alive(self):
185192
return self._camera_scanner is not None and self._camera_switch is not None
186193

187194
def _read_view_queue(self):
188-
if not self._view_queue.empty():
189-
try:
190-
image = self._view_queue.get(False)
191-
self._image_frame.display_puck_image(image)
192-
except queue.Empty:
193-
pass
195+
if self._view_queue.empty():
196+
return
197+
198+
try:
199+
image = self._view_queue.get(False)
200+
except queue.Empty:
201+
return
202+
203+
self._image_frame.display_puck_image(image)
194204

195205
def _read_message_queue(self):
196-
if not self._message_queue.empty():
197-
try:
198-
message = self._message_queue.get(False)
199-
self._message_display.display_message(message)
200-
except queue.Empty:
201-
return
206+
if self._message_queue.empty():
207+
return
208+
209+
try:
210+
scanner_msg = self._message_queue.get(False)
211+
except queue.Empty:
212+
return
213+
214+
if self._camera_switch.is_side() and isinstance(scanner_msg, NoNewBarcodeMessage):
215+
if not self._msg_timer_is_running():
216+
# The result queue is read at a slower rate - use a timer to give it time to process a new barcode
217+
self._start_msg_timer()
218+
elif self._has_msg_timer_timeout():
219+
self._message_box.display(MessageFactory.duplicate_barcode_message())
220+
else:
221+
self._reset_msg_timer()
222+
self._message_box.display(MessageFactory.from_scanner_message(scanner_msg))
223+
224+
def _reset_msg_timer(self):
225+
self._duplicate_msg_timer = None
226+
227+
def _start_msg_timer(self):
228+
self._duplicate_msg_timer = time.time()
229+
230+
def _msg_timer_is_running(self):
231+
return self._duplicate_msg_timer is not None
232+
233+
def _has_msg_timer_timeout(self):
234+
return self._msg_timer_is_running() and time.time() - self._duplicate_msg_timer > 2 * RESULT_TIMER_PERIOD / 1000
202235

203236
def _read_result_queue(self):
204237
""" Called every second; read any new results from the scan results queue, store them and display them.
@@ -222,17 +255,19 @@ def _read_side_scan(self):
222255

223256
# Barcode successfully read
224257
Beeper.beep()
225-
print("MAIN: holder barcode recorded")
258+
print("MAIN: puck barcode recorded")
226259
if self._record_table.unique_side_barcode(plate): # if new side barcode
227260
self.original_plate = plate
228261
self._latest_holder_image = holder_image
229-
self._message_display.display_message(Message(MessageType.INFO, "Plate barcode recorded"))
262+
self._message_box.display(MessageFactory.puck_recorded_message())
230263
self._camera_switch.restart_live_capture_from_top()
264+
else:
265+
self._message_box.display(MessageFactory.duplicate_barcode_message())
231266

232267
def _read_top_scan(self):
233268
if self._result_queue.empty():
234269
if self._camera_switch.is_top_scan_timeout():
235-
self._message_display.display_message(Message(MessageType.INFO, "Scan timeout", lifetime=4))
270+
self._message_box.display(MessageFactory.scan_timeout_message())
236271
print("\n*** Scan timeout ***")
237272
self._camera_switch.restart_live_capture_from_side()
238273
return
@@ -248,6 +283,6 @@ def _read_top_scan(self):
248283
# Barcodes successfully read
249284
Beeper.beep()
250285
print("Scan Completed")
251-
self._message_display.display_message(Message(MessageType.INFO, "Scan completed"))
286+
self._message_box.display(MessageFactory.scan_completed_message())
252287
self._camera_switch.restart_live_capture_from_side()
253288

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
BLACK = "; color: black"
88
BASIC_STYLE_SHEET = "font-size: 14pt"
99

10-
class MessageDisplay(QGroupBox):
10+
class MessageBox(QGroupBox):
1111
"""GUI component. Displays messages for the user."""
1212
def __init__(self):
13-
super(MessageDisplay, self).__init__()
13+
super(MessageBox, self).__init__()
1414

1515
self.setTitle("Information")
1616

@@ -32,7 +32,7 @@ def _init_ui(self):
3232
vbox.addStretch()
3333
self.setLayout(vbox)
3434

35-
def display_message(self, message):
35+
def display(self, message):
3636
self._message = message
3737
self._message_lbl.setText(self._message.content())
3838
self._message_lbl.setStyleSheet(self._style_sheets[self._message.type()])

dls_barcode/gui/message_factory.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from dls_util.message import MessageType, Message
2+
3+
4+
class MessageFactory:
5+
@staticmethod
6+
def duplicate_barcode_message():
7+
return Message(MessageType.WARNING, "Puck barcode already in database", lifetime=3)
8+
9+
@staticmethod
10+
def from_scanner_message(scanner_msg):
11+
return Message(MessageType.WARNING, scanner_msg.content())
12+
13+
@staticmethod
14+
def puck_recorded_message():
15+
return Message(MessageType.INFO, "Puck barcode recorded")
16+
17+
@staticmethod
18+
def scan_timeout_message():
19+
return Message(MessageType.WARNING, "Scan timeout")
20+
21+
@staticmethod
22+
def scan_completed_message():
23+
return Message(MessageType.INFO, "Scan completed")

dls_barcode/plate/plate.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def __init__(self, geometry_name, num_slots=-1):
1818
self.type = geometry_name
1919
self._geometry = None
2020

21-
2221
# Initialize slots
2322
self._slots = [Slot(i) for i in range(1, self.num_slots+1)]
2423

@@ -37,7 +36,6 @@ def barcodes(self):
3736
"""
3837
return [slot.barcode_data() for slot in self._slots]
3938

40-
4139
def geometry(self):
4240
return self._geometry
4341

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class NoBarcodesDetectedError(Exception):
2+
def __init__(self):
3+
super(NoBarcodesDetectedError, self).__init__("No barcode detected")

dls_barcode/scan/open/open_scanner.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22

33
from dls_barcode.plate import Plate
44
from dls_barcode.geometry import Geometry
5+
from ..no_barcodes_detected_error import NoBarcodesDetectedError
56
from .open_scan_result import OpenScanResult
67

78

8-
class NoBarcodesError(Exception):
9-
pass
10-
11-
129
class OpenScanner:
1310
def __init__(self, barcode_sizes):
1411
self.plate_type = Geometry.NO_GEOMETRY
@@ -32,17 +29,17 @@ def scan_next_frame(self, frame_img, is_single_image=False):
3229
try:
3330
barcodes = self._perform_frame_scan()
3431
result.set_barcodes(barcodes)
35-
except NoBarcodesError as ex:
32+
except NoBarcodesDetectedError as ex:
3633
result.set_error(str(ex))
3734

3835
# Create a 'blank' geometry object to store the barcode locations
3936
new_barcodes = result.new_barcodes()
40-
num_barcodes = len(new_barcodes)
37+
num_new_barcodes = len(new_barcodes)
4138
geometry = self._create_geometry(new_barcodes)
4239

4340
# Create the plate
4441
if any(new_barcodes):
45-
plate = Plate(self.plate_type, num_slots=num_barcodes)
42+
plate = Plate(self.plate_type, num_slots=num_new_barcodes)
4643
plate.set_geometry(geometry)
4744
for s, barcode in enumerate(new_barcodes):
4845
plate.slot(s).set_barcode(barcode)
@@ -76,7 +73,7 @@ def _locate_all_barcodes_in_image(self):
7673
barcodes = DataMatrix.locate_all_barcodes_in_image(self._frame_img, self.barcode_sizes)
7774

7875
if len(barcodes) == 0:
79-
raise NoBarcodesError("No Barcodes Detected In Image")
76+
raise NoBarcodesDetectedError()
8077
return barcodes
8178

8279
def _is_barcode_new(self, barcode):

0 commit comments

Comments
 (0)