Commit 0f775c4e8b37f4de8d9b9acc4e62e3af298d20f7
Committed by
GitHub
1 parent
d133ced3
Exists in
master
ADD: Send pulse via serial port when pedal triggered and coil at target (#326)
- Do more specific error handling in SerialPortConnection class, distinguishing between the error caused by the queue becoming full from the error caused by reading from or writing into the serial port. - Also, add a comment explaining why additional sleeping is needed inside the thread in SerialPortConnection class.
Showing
2 changed files
with
60 additions
and
11 deletions
Show diff stats
invesalius/data/serial_port_connection.py
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | # detalhes. |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | +import queue | |
20 | 21 | import threading |
21 | 22 | import time |
22 | 23 | |
... | ... | @@ -25,6 +26,7 @@ from invesalius.pubsub import pub as Publisher |
25 | 26 | |
26 | 27 | |
27 | 28 | class SerialPortConnection(threading.Thread): |
29 | + BINARY_PULSE = b'\x01' | |
28 | 30 | |
29 | 31 | def __init__(self, port, serial_port_queue, event, sleep_nav): |
30 | 32 | """ |
... | ... | @@ -54,25 +56,49 @@ class SerialPortConnection(threading.Thread): |
54 | 56 | except: |
55 | 57 | print("Serial port init error: Connecting to port {} failed.".format(self.port)) |
56 | 58 | |
59 | + def SendPulse(self): | |
60 | + success = False | |
61 | + try: | |
62 | + self.connection.write(self.BINARY_PULSE) | |
63 | + success = True | |
64 | + except: | |
65 | + print("Error: Serial port could not be written into.") | |
66 | + | |
67 | + return success | |
68 | + | |
57 | 69 | def run(self): |
58 | 70 | while not self.event.is_set(): |
59 | 71 | trigger_on = False |
60 | 72 | try: |
61 | - self.connection.write(b'0') | |
62 | - time.sleep(0.3) | |
63 | - | |
64 | 73 | lines = self.connection.readlines() |
65 | - if lines: | |
66 | - trigger_on = True | |
74 | + except: | |
75 | + print("Error: Serial port could not be read.") | |
67 | 76 | |
68 | - if self.stylusplh: | |
69 | - trigger_on = True | |
70 | - self.stylusplh = False | |
77 | + if lines: | |
78 | + trigger_on = True | |
71 | 79 | |
80 | + if self.stylusplh: | |
81 | + trigger_on = True | |
82 | + self.stylusplh = False | |
83 | + | |
84 | + try: | |
72 | 85 | self.serial_port_queue.put_nowait(trigger_on) |
73 | - time.sleep(self.sleep_nav) | |
74 | - except: | |
75 | - print("Trigger not read, error") | |
86 | + except queue.Full: | |
87 | + print("Error: Serial port queue full.") | |
88 | + | |
89 | + time.sleep(self.sleep_nav) | |
90 | + | |
91 | + # XXX: This is needed here because the serial port queue has to be read | |
92 | + # at least as fast as it is written into, otherwise it will eventually | |
93 | + # become full. Reading is done in another thread, which has the same | |
94 | + # sleeping parameter sleep_nav between consecutive runs as this thread. | |
95 | + # However, a single run of that thread takes longer to finish than a | |
96 | + # single run of this thread, causing that thread to lag behind. Hence, | |
97 | + # the additional sleeping here to ensure that this thread lags behind the | |
98 | + # other thread and not the other way around. However, it would be nice to | |
99 | + # handle the timing dependencies between the threads in a more robust way. | |
100 | + # | |
101 | + time.sleep(0.3) | |
76 | 102 | else: |
77 | 103 | if self.connection: |
78 | 104 | self.connection.close() | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -310,6 +310,8 @@ class InnerFoldPanel(wx.Panel): |
310 | 310 | |
311 | 311 | class Navigation(): |
312 | 312 | def __init__(self): |
313 | + self.pedal_connection = PedalConnection() | |
314 | + | |
313 | 315 | self.image_fiducials = np.full([3, 3], np.nan) |
314 | 316 | self.correg = None |
315 | 317 | self.current_coord = 0, 0, 0 |
... | ... | @@ -344,6 +346,17 @@ class Navigation(): |
344 | 346 | self.serial_port = None |
345 | 347 | self.serial_port_connection = None |
346 | 348 | |
349 | + # During navigation | |
350 | + self.coil_at_target = False | |
351 | + | |
352 | + self.__bind_events() | |
353 | + | |
354 | + def __bind_events(self): | |
355 | + Publisher.subscribe(self.CoilAtTarget, 'Coil at target') | |
356 | + | |
357 | + def CoilAtTarget(self, state): | |
358 | + self.coil_at_target = state | |
359 | + | |
347 | 360 | def UpdateSleep(self, sleep): |
348 | 361 | self.sleep_nav = sleep |
349 | 362 | self.serial_port_connection.sleep_nav = sleep |
... | ... | @@ -371,6 +384,12 @@ class Navigation(): |
371 | 384 | fre = icp.icp_fre if icp.use_icp else self.fre |
372 | 385 | return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD |
373 | 386 | |
387 | + def PedalStateChanged(self, state): | |
388 | + if state is True and self.coil_at_target and self.SerialPortEnabled(): | |
389 | + success = self.serial_port_connection.SendPulse() | |
390 | + if success: | |
391 | + Publisher.sendMessage('Pulse triggered', state=True) | |
392 | + | |
374 | 393 | def StartNavigation(self, tracker): |
375 | 394 | tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials() |
376 | 395 | ref_mode_id = tracker.GetReferenceMode() |
... | ... | @@ -466,9 +485,13 @@ class Navigation(): |
466 | 485 | jobs.start() |
467 | 486 | # del jobs |
468 | 487 | |
488 | + self.pedal_connection.add_callback('navigation', self.PedalStateChanged) | |
489 | + | |
469 | 490 | def StopNavigation(self): |
470 | 491 | self.event.set() |
471 | 492 | |
493 | + self.pedal_connection.remove_callback('navigation') | |
494 | + | |
472 | 495 | self.coord_queue.clear() |
473 | 496 | self.coord_queue.join() |
474 | 497 | ... | ... |