Commit 310b5d085398ff4fcdc3a34cd1c690fcf5949bf6
Committed by
GitHub
Exists in
master
Merge pull request #375 from okahilak/allow-selecting-baud-rate-for-serial-port
MOD: Allow selecting baud rate for serial port communication
Showing
6 changed files
with
87 additions
and
44 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -830,3 +830,7 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit |
830 | 830 | MARKER_FILE_MAGICK_STRING = "INVESALIUS3_MARKER_FILE_" |
831 | 831 | CURRENT_MARKER_FILE_VERSION = 0 |
832 | 832 | WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss") |
833 | + | |
834 | +# Serial port | |
835 | +BAUD_RATES = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200] | |
836 | +BAUD_RATE_DEFAULT_SELECTION = 4 | ... | ... |
invesalius/data/serial_port_connection.py
... | ... | @@ -28,7 +28,7 @@ from invesalius.pubsub import pub as Publisher |
28 | 28 | class SerialPortConnection(threading.Thread): |
29 | 29 | BINARY_PULSE = b'\x01' |
30 | 30 | |
31 | - def __init__(self, port, serial_port_queue, event, sleep_nav): | |
31 | + def __init__(self, com_port, baud_rate, serial_port_queue, event, sleep_nav): | |
32 | 32 | """ |
33 | 33 | Thread created to communicate using the serial port to interact with software during neuronavigation. |
34 | 34 | """ |
... | ... | @@ -37,28 +37,29 @@ class SerialPortConnection(threading.Thread): |
37 | 37 | self.connection = None |
38 | 38 | self.stylusplh = False |
39 | 39 | |
40 | - self.port = port | |
40 | + self.com_port = com_port | |
41 | + self.baud_rate = baud_rate | |
41 | 42 | self.serial_port_queue = serial_port_queue |
42 | 43 | self.event = event |
43 | 44 | self.sleep_nav = sleep_nav |
44 | 45 | |
45 | 46 | def Connect(self): |
46 | - if self.port is None: | |
47 | + if self.com_port is None: | |
47 | 48 | print("Serial port init error: COM port is unset.") |
48 | 49 | return |
49 | 50 | try: |
50 | 51 | import serial |
51 | - self.connection = serial.Serial(self.port, baudrate=115200, timeout=0) | |
52 | - print("Connection to port {} opened.".format(self.port)) | |
52 | + self.connection = serial.Serial(self.com_port, baudrate=self.baud_rate, timeout=0) | |
53 | + print("Connection to port {} opened.".format(self.com_port)) | |
53 | 54 | |
54 | 55 | Publisher.sendMessage('Serial port connection', state=True) |
55 | 56 | except: |
56 | - print("Serial port init error: Connecting to port {} failed.".format(self.port)) | |
57 | + print("Serial port init error: Connecting to port {} failed.".format(self.com_port)) | |
57 | 58 | |
58 | 59 | def Disconnect(self): |
59 | 60 | if self.connection: |
60 | 61 | self.connection.close() |
61 | - print("Connection to port {} closed.".format(self.port)) | |
62 | + print("Connection to port {} closed.".format(self.com_port)) | |
62 | 63 | |
63 | 64 | Publisher.sendMessage('Serial port connection', state=False) |
64 | 65 | |
... | ... | @@ -74,12 +75,11 @@ class SerialPortConnection(threading.Thread): |
74 | 75 | trigger_on = False |
75 | 76 | try: |
76 | 77 | lines = self.connection.readlines() |
78 | + if lines: | |
79 | + trigger_on = True | |
77 | 80 | except: |
78 | 81 | print("Error: Serial port could not be read.") |
79 | 82 | |
80 | - if lines: | |
81 | - trigger_on = True | |
82 | - | |
83 | 83 | if self.stylusplh: |
84 | 84 | trigger_on = True |
85 | 85 | self.stylusplh = False | ... | ... |
invesalius/data/trackers.py
... | ... | @@ -266,7 +266,7 @@ def PlhSerialConnection(tracker_id): |
266 | 266 | import serial |
267 | 267 | from wx import ID_OK |
268 | 268 | trck_init = None |
269 | - dlg_port = dlg.SetCOMport() | |
269 | + dlg_port = dlg.SetCOMPort(select_baud_rate=False) | |
270 | 270 | if dlg_port.ShowModal() == ID_OK: |
271 | 271 | com_port = dlg_port.GetValue() |
272 | 272 | try: | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -4322,7 +4322,8 @@ class SetNDIconfigs(wx.Dialog): |
4322 | 4322 | self._init_gui() |
4323 | 4323 | |
4324 | 4324 | def serial_ports(self): |
4325 | - """ Lists serial port names and pre-select the description containing NDI | |
4325 | + """ | |
4326 | + Lists serial port names and pre-select the description containing NDI | |
4326 | 4327 | """ |
4327 | 4328 | import serial.tools.list_ports |
4328 | 4329 | |
... | ... | @@ -4430,13 +4431,16 @@ class SetNDIconfigs(wx.Dialog): |
4430 | 4431 | return self.com_ports.GetString(self.com_ports.GetSelection()).encode(const.FS_ENCODE), fn_probe, fn_ref, fn_obj |
4431 | 4432 | |
4432 | 4433 | |
4433 | -class SetCOMport(wx.Dialog): | |
4434 | - def __init__(self, title=_("Select COM port")): | |
4435 | - wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) | |
4434 | +class SetCOMPort(wx.Dialog): | |
4435 | + def __init__(self, select_baud_rate, title=_("Select COM port")): | |
4436 | + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.STAY_ON_TOP) | |
4437 | + | |
4438 | + self.select_baud_rate = select_baud_rate | |
4436 | 4439 | self._init_gui() |
4437 | 4440 | |
4438 | 4441 | def serial_ports(self): |
4439 | - """ Lists serial port names | |
4442 | + """ | |
4443 | + Lists serial port names | |
4440 | 4444 | """ |
4441 | 4445 | import serial.tools.list_ports |
4442 | 4446 | if sys.platform.startswith('win'): |
... | ... | @@ -4446,12 +4450,26 @@ class SetCOMport(wx.Dialog): |
4446 | 4450 | return ports |
4447 | 4451 | |
4448 | 4452 | def _init_gui(self): |
4449 | - self.com_ports = wx.ComboBox(self, -1, style=wx.CB_DROPDOWN|wx.CB_READONLY) | |
4453 | + # COM port selection | |
4450 | 4454 | ports = self.serial_ports() |
4451 | - self.com_ports.Append(ports) | |
4455 | + self.com_port_dropdown = wx.ComboBox(self, -1, choices=ports, style=wx.CB_DROPDOWN | wx.CB_READONLY) | |
4456 | + self.com_port_dropdown.SetSelection(0) | |
4457 | + | |
4458 | + com_port_text_and_dropdown = wx.BoxSizer(wx.VERTICAL) | |
4459 | + com_port_text_and_dropdown.Add(wx.StaticText(self, wx.ID_ANY, "COM port"), 0, wx.TOP | wx.RIGHT,5) | |
4460 | + com_port_text_and_dropdown.Add(self.com_port_dropdown, 0, wx.EXPAND) | |
4452 | 4461 | |
4453 | - # self.goto_orientation.SetSelection(cb_init) | |
4462 | + # Baud rate selection | |
4463 | + if self.select_baud_rate: | |
4464 | + baud_rates_as_strings = [str(baud_rate) for baud_rate in const.BAUD_RATES] | |
4465 | + self.baud_rate_dropdown = wx.ComboBox(self, -1, choices=baud_rates_as_strings, style=wx.CB_DROPDOWN | wx.CB_READONLY) | |
4466 | + self.baud_rate_dropdown.SetSelection(const.BAUD_RATE_DEFAULT_SELECTION) | |
4454 | 4467 | |
4468 | + baud_rate_text_and_dropdown = wx.BoxSizer(wx.VERTICAL) | |
4469 | + baud_rate_text_and_dropdown.Add(wx.StaticText(self, wx.ID_ANY, "Baud rate"), 0, wx.TOP | wx.RIGHT,5) | |
4470 | + baud_rate_text_and_dropdown.Add(self.baud_rate_dropdown, 0, wx.EXPAND) | |
4471 | + | |
4472 | + # OK and Cancel buttons | |
4455 | 4473 | btn_ok = wx.Button(self, wx.ID_OK) |
4456 | 4474 | btn_ok.SetHelpText("") |
4457 | 4475 | btn_ok.SetDefault() |
... | ... | @@ -4464,10 +4482,16 @@ class SetCOMport(wx.Dialog): |
4464 | 4482 | btnsizer.AddButton(btn_cancel) |
4465 | 4483 | btnsizer.Realize() |
4466 | 4484 | |
4485 | + # Set up the main sizer | |
4467 | 4486 | main_sizer = wx.BoxSizer(wx.VERTICAL) |
4468 | 4487 | |
4469 | 4488 | main_sizer.Add((5, 5)) |
4470 | - main_sizer.Add(self.com_ports, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) | |
4489 | + main_sizer.Add(com_port_text_and_dropdown, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) | |
4490 | + | |
4491 | + if self.select_baud_rate: | |
4492 | + main_sizer.Add((5, 5)) | |
4493 | + main_sizer.Add(baud_rate_text_and_dropdown, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) | |
4494 | + | |
4471 | 4495 | main_sizer.Add((5, 5)) |
4472 | 4496 | main_sizer.Add(btnsizer, 0, wx.EXPAND) |
4473 | 4497 | main_sizer.Add((5, 5)) |
... | ... | @@ -4478,7 +4502,14 @@ class SetCOMport(wx.Dialog): |
4478 | 4502 | self.CenterOnParent() |
4479 | 4503 | |
4480 | 4504 | def GetValue(self): |
4481 | - return self.com_ports.GetString(self.com_ports.GetSelection()) | |
4505 | + com_port = self.com_port_dropdown.GetString(self.com_port_dropdown.GetSelection()) | |
4506 | + | |
4507 | + if self.select_baud_rate: | |
4508 | + baud_rate = self.baud_rate_dropdown.GetString(self.baud_rate_dropdown.GetSelection()) | |
4509 | + else: | |
4510 | + baud_rate = None | |
4511 | + | |
4512 | + return com_port, baud_rate | |
4482 | 4513 | |
4483 | 4514 | |
4484 | 4515 | class ManualWWWLDialog(wx.Dialog): | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -222,8 +222,8 @@ class InnerFoldPanel(wx.Panel): |
222 | 222 | checkcamera.Bind(wx.EVT_CHECKBOX, self.OnVolumeCamera) |
223 | 223 | self.checkcamera = checkcamera |
224 | 224 | |
225 | - # Check box to create markers from serial port | |
226 | - tooltip = wx.ToolTip(_("Enable serial port communication for creating markers")) | |
225 | + # Check box to use serial port to trigger pulse signal and create markers | |
226 | + tooltip = wx.ToolTip(_("Enable serial port communication to trigger pulse and create markers")) | |
227 | 227 | checkbox_serial_port = wx.CheckBox(self, -1, _('Serial port')) |
228 | 228 | checkbox_serial_port.SetToolTip(tooltip) |
229 | 229 | checkbox_serial_port.SetValue(False) |
... | ... | @@ -285,14 +285,20 @@ class InnerFoldPanel(wx.Panel): |
285 | 285 | self.checkobj.Enable(True) |
286 | 286 | |
287 | 287 | def OnEnableSerialPort(self, evt, ctrl): |
288 | - com_port = None | |
289 | 288 | if ctrl.GetValue(): |
290 | 289 | from wx import ID_OK |
291 | - dlg_port = dlg.SetCOMport() | |
292 | - if dlg_port.ShowModal() == ID_OK: | |
293 | - com_port = dlg_port.GetValue() | |
290 | + dlg_port = dlg.SetCOMPort(select_baud_rate=False) | |
294 | 291 | |
295 | - Publisher.sendMessage('Update serial port', serial_port=com_port) | |
292 | + if dlg_port.ShowModal() != ID_OK: | |
293 | + ctrl.SetValue(False) | |
294 | + return | |
295 | + | |
296 | + com_port = dlg_port.GetValue() | |
297 | + baud_rate = 115200 | |
298 | + | |
299 | + Publisher.sendMessage('Update serial port', serial_port_in_use=True, com_port=com_port, baud_rate=baud_rate) | |
300 | + else: | |
301 | + Publisher.sendMessage('Update serial port', serial_port_in_use=False) | |
296 | 302 | |
297 | 303 | def OnShowObject(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True): |
298 | 304 | if not evt: |
... | ... | @@ -490,7 +496,6 @@ class NeuronavigationPanel(wx.Panel): |
490 | 496 | Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials') |
491 | 497 | Publisher.subscribe(self.SetImageFiducial, 'Set image fiducial') |
492 | 498 | Publisher.subscribe(self.SetTrackerFiducial, 'Set tracker fiducial') |
493 | - Publisher.subscribe(self.UpdateSerialPort, 'Update serial port') | |
494 | 499 | Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state') |
495 | 500 | Publisher.subscribe(self.UpdateImageCoordinates, 'Set cross focal point') |
496 | 501 | Publisher.subscribe(self.OnDisconnectTracker, 'Disconnect tracker') |
... | ... | @@ -614,9 +619,6 @@ class NeuronavigationPanel(wx.Panel): |
614 | 619 | def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True): |
615 | 620 | self.navigation.track_obj = flag |
616 | 621 | |
617 | - def UpdateSerialPort(self, serial_port): | |
618 | - self.navigation.serial_port = serial_port | |
619 | - | |
620 | 622 | def ResetICP(self): |
621 | 623 | self.icp.ResetICP() |
622 | 624 | self.checkbox_icp.Enable(False) | ... | ... |
invesalius/navigation/navigation.py
... | ... | @@ -168,7 +168,9 @@ class Navigation(): |
168 | 168 | self.sleep_nav = const.SLEEP_NAVIGATION |
169 | 169 | |
170 | 170 | # Serial port |
171 | - self.serial_port = None | |
171 | + self.serial_port_in_use = False | |
172 | + self.com_port = None | |
173 | + self.baud_rate = None | |
172 | 174 | self.serial_port_connection = None |
173 | 175 | |
174 | 176 | # During navigation |
... | ... | @@ -178,6 +180,7 @@ class Navigation(): |
178 | 180 | |
179 | 181 | def __bind_events(self): |
180 | 182 | Publisher.subscribe(self.CoilAtTarget, 'Coil at target') |
183 | + Publisher.subscribe(self.UpdateSerialPort, 'Update serial port') | |
181 | 184 | |
182 | 185 | def CoilAtTarget(self, state): |
183 | 186 | self.coil_at_target = state |
... | ... | @@ -186,8 +189,10 @@ class Navigation(): |
186 | 189 | self.sleep_nav = sleep |
187 | 190 | self.serial_port_connection.sleep_nav = sleep |
188 | 191 | |
189 | - def SerialPortEnabled(self): | |
190 | - return self.serial_port is not None | |
192 | + def UpdateSerialPort(self, serial_port_in_use, com_port=None, baud_rate=None): | |
193 | + self.serial_port_in_use = serial_port_in_use | |
194 | + self.com_port = com_port | |
195 | + self.baud_rate = baud_rate | |
191 | 196 | |
192 | 197 | def SetReferenceMode(self, value): |
193 | 198 | self.ref_mode_id = value |
... | ... | @@ -215,7 +220,7 @@ class Navigation(): |
215 | 220 | return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD |
216 | 221 | |
217 | 222 | def PedalStateChanged(self, state): |
218 | - if state is True and self.coil_at_target and self.SerialPortEnabled(): | |
223 | + if state is True and self.coil_at_target and self.serial_port_in_use: | |
219 | 224 | self.serial_port_connection.SendPulse() |
220 | 225 | |
221 | 226 | def StartNavigation(self, tracker): |
... | ... | @@ -227,7 +232,7 @@ class Navigation(): |
227 | 232 | if self.event.is_set(): |
228 | 233 | self.event.clear() |
229 | 234 | |
230 | - vis_components = [self.SerialPortEnabled(), self.view_tracts, self.peel_loaded] | |
235 | + vis_components = [self.serial_port_in_use, self.view_tracts, self.peel_loaded] | |
231 | 236 | vis_queues = [self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue] |
232 | 237 | |
233 | 238 | Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) |
... | ... | @@ -276,12 +281,13 @@ class Navigation(): |
276 | 281 | |
277 | 282 | if not errors: |
278 | 283 | #TODO: Test the serial port thread |
279 | - if self.SerialPortEnabled(): | |
284 | + if self.serial_port_in_use: | |
280 | 285 | self.serial_port_connection = spc.SerialPortConnection( |
281 | - self.serial_port, | |
282 | - self.serial_port_queue, | |
283 | - self.event, | |
284 | - self.sleep_nav, | |
286 | + com_port=self.com_port, | |
287 | + baud_rate=self.baud_rate, | |
288 | + serial_port_queue=self.serial_port_queue, | |
289 | + event=self.event, | |
290 | + sleep_nav=self.sleep_nav, | |
285 | 291 | ) |
286 | 292 | self.serial_port_connection.Connect() |
287 | 293 | jobs_list.append(self.serial_port_connection) |
... | ... | @@ -327,7 +333,7 @@ class Navigation(): |
327 | 333 | if self.serial_port_connection is not None: |
328 | 334 | self.serial_port_connection.join() |
329 | 335 | |
330 | - if self.SerialPortEnabled(): | |
336 | + if self.serial_port_in_use: | |
331 | 337 | self.serial_port_queue.clear() |
332 | 338 | self.serial_port_queue.join() |
333 | 339 | |
... | ... | @@ -338,5 +344,5 @@ class Navigation(): |
338 | 344 | self.tracts_queue.clear() |
339 | 345 | self.tracts_queue.join() |
340 | 346 | |
341 | - vis_components = [self.SerialPortEnabled(), self.view_tracts, self.peel_loaded] | |
347 | + vis_components = [self.serial_port_in_use, self.view_tracts, self.peel_loaded] | |
342 | 348 | Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) | ... | ... |