Commit 310b5d085398ff4fcdc3a34cd1c690fcf5949bf6

Authored by Victor Hugo Souza
Committed by GitHub
2 parents a406f61e eb4c656f
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
invesalius/constants.py
@@ -830,3 +830,7 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit @@ -830,3 +830,7 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit
830 MARKER_FILE_MAGICK_STRING = "INVESALIUS3_MARKER_FILE_" 830 MARKER_FILE_MAGICK_STRING = "INVESALIUS3_MARKER_FILE_"
831 CURRENT_MARKER_FILE_VERSION = 0 831 CURRENT_MARKER_FILE_VERSION = 0
832 WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss") 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,7 +28,7 @@ from invesalius.pubsub import pub as Publisher
28 class SerialPortConnection(threading.Thread): 28 class SerialPortConnection(threading.Thread):
29 BINARY_PULSE = b'\x01' 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 Thread created to communicate using the serial port to interact with software during neuronavigation. 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,28 +37,29 @@ class SerialPortConnection(threading.Thread):
37 self.connection = None 37 self.connection = None
38 self.stylusplh = False 38 self.stylusplh = False
39 39
40 - self.port = port 40 + self.com_port = com_port
  41 + self.baud_rate = baud_rate
41 self.serial_port_queue = serial_port_queue 42 self.serial_port_queue = serial_port_queue
42 self.event = event 43 self.event = event
43 self.sleep_nav = sleep_nav 44 self.sleep_nav = sleep_nav
44 45
45 def Connect(self): 46 def Connect(self):
46 - if self.port is None: 47 + if self.com_port is None:
47 print("Serial port init error: COM port is unset.") 48 print("Serial port init error: COM port is unset.")
48 return 49 return
49 try: 50 try:
50 import serial 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 Publisher.sendMessage('Serial port connection', state=True) 55 Publisher.sendMessage('Serial port connection', state=True)
55 except: 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 def Disconnect(self): 59 def Disconnect(self):
59 if self.connection: 60 if self.connection:
60 self.connection.close() 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 Publisher.sendMessage('Serial port connection', state=False) 64 Publisher.sendMessage('Serial port connection', state=False)
64 65
@@ -74,12 +75,11 @@ class SerialPortConnection(threading.Thread): @@ -74,12 +75,11 @@ class SerialPortConnection(threading.Thread):
74 trigger_on = False 75 trigger_on = False
75 try: 76 try:
76 lines = self.connection.readlines() 77 lines = self.connection.readlines()
  78 + if lines:
  79 + trigger_on = True
77 except: 80 except:
78 print("Error: Serial port could not be read.") 81 print("Error: Serial port could not be read.")
79 82
80 - if lines:  
81 - trigger_on = True  
82 -  
83 if self.stylusplh: 83 if self.stylusplh:
84 trigger_on = True 84 trigger_on = True
85 self.stylusplh = False 85 self.stylusplh = False
invesalius/data/trackers.py
@@ -266,7 +266,7 @@ def PlhSerialConnection(tracker_id): @@ -266,7 +266,7 @@ def PlhSerialConnection(tracker_id):
266 import serial 266 import serial
267 from wx import ID_OK 267 from wx import ID_OK
268 trck_init = None 268 trck_init = None
269 - dlg_port = dlg.SetCOMport() 269 + dlg_port = dlg.SetCOMPort(select_baud_rate=False)
270 if dlg_port.ShowModal() == ID_OK: 270 if dlg_port.ShowModal() == ID_OK:
271 com_port = dlg_port.GetValue() 271 com_port = dlg_port.GetValue()
272 try: 272 try:
invesalius/gui/dialogs.py
@@ -4322,7 +4322,8 @@ class SetNDIconfigs(wx.Dialog): @@ -4322,7 +4322,8 @@ class SetNDIconfigs(wx.Dialog):
4322 self._init_gui() 4322 self._init_gui()
4323 4323
4324 def serial_ports(self): 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 import serial.tools.list_ports 4328 import serial.tools.list_ports
4328 4329
@@ -4430,13 +4431,16 @@ class SetNDIconfigs(wx.Dialog): @@ -4430,13 +4431,16 @@ class SetNDIconfigs(wx.Dialog):
4430 return self.com_ports.GetString(self.com_ports.GetSelection()).encode(const.FS_ENCODE), fn_probe, fn_ref, fn_obj 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 self._init_gui() 4439 self._init_gui()
4437 4440
4438 def serial_ports(self): 4441 def serial_ports(self):
4439 - """ Lists serial port names 4442 + """
  4443 + Lists serial port names
4440 """ 4444 """
4441 import serial.tools.list_ports 4445 import serial.tools.list_ports
4442 if sys.platform.startswith('win'): 4446 if sys.platform.startswith('win'):
@@ -4446,12 +4450,26 @@ class SetCOMport(wx.Dialog): @@ -4446,12 +4450,26 @@ class SetCOMport(wx.Dialog):
4446 return ports 4450 return ports
4447 4451
4448 def _init_gui(self): 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 ports = self.serial_ports() 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 btn_ok = wx.Button(self, wx.ID_OK) 4473 btn_ok = wx.Button(self, wx.ID_OK)
4456 btn_ok.SetHelpText("") 4474 btn_ok.SetHelpText("")
4457 btn_ok.SetDefault() 4475 btn_ok.SetDefault()
@@ -4464,10 +4482,16 @@ class SetCOMport(wx.Dialog): @@ -4464,10 +4482,16 @@ class SetCOMport(wx.Dialog):
4464 btnsizer.AddButton(btn_cancel) 4482 btnsizer.AddButton(btn_cancel)
4465 btnsizer.Realize() 4483 btnsizer.Realize()
4466 4484
  4485 + # Set up the main sizer
4467 main_sizer = wx.BoxSizer(wx.VERTICAL) 4486 main_sizer = wx.BoxSizer(wx.VERTICAL)
4468 4487
4469 main_sizer.Add((5, 5)) 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 main_sizer.Add((5, 5)) 4495 main_sizer.Add((5, 5))
4472 main_sizer.Add(btnsizer, 0, wx.EXPAND) 4496 main_sizer.Add(btnsizer, 0, wx.EXPAND)
4473 main_sizer.Add((5, 5)) 4497 main_sizer.Add((5, 5))
@@ -4478,7 +4502,14 @@ class SetCOMport(wx.Dialog): @@ -4478,7 +4502,14 @@ class SetCOMport(wx.Dialog):
4478 self.CenterOnParent() 4502 self.CenterOnParent()
4479 4503
4480 def GetValue(self): 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 class ManualWWWLDialog(wx.Dialog): 4515 class ManualWWWLDialog(wx.Dialog):
invesalius/gui/task_navigator.py
@@ -222,8 +222,8 @@ class InnerFoldPanel(wx.Panel): @@ -222,8 +222,8 @@ class InnerFoldPanel(wx.Panel):
222 checkcamera.Bind(wx.EVT_CHECKBOX, self.OnVolumeCamera) 222 checkcamera.Bind(wx.EVT_CHECKBOX, self.OnVolumeCamera)
223 self.checkcamera = checkcamera 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 checkbox_serial_port = wx.CheckBox(self, -1, _('Serial port')) 227 checkbox_serial_port = wx.CheckBox(self, -1, _('Serial port'))
228 checkbox_serial_port.SetToolTip(tooltip) 228 checkbox_serial_port.SetToolTip(tooltip)
229 checkbox_serial_port.SetValue(False) 229 checkbox_serial_port.SetValue(False)
@@ -285,14 +285,20 @@ class InnerFoldPanel(wx.Panel): @@ -285,14 +285,20 @@ class InnerFoldPanel(wx.Panel):
285 self.checkobj.Enable(True) 285 self.checkobj.Enable(True)
286 286
287 def OnEnableSerialPort(self, evt, ctrl): 287 def OnEnableSerialPort(self, evt, ctrl):
288 - com_port = None  
289 if ctrl.GetValue(): 288 if ctrl.GetValue():
290 from wx import ID_OK 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 def OnShowObject(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True): 303 def OnShowObject(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True):
298 if not evt: 304 if not evt:
@@ -490,7 +496,6 @@ class NeuronavigationPanel(wx.Panel): @@ -490,7 +496,6 @@ class NeuronavigationPanel(wx.Panel):
490 Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials') 496 Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials')
491 Publisher.subscribe(self.SetImageFiducial, 'Set image fiducial') 497 Publisher.subscribe(self.SetImageFiducial, 'Set image fiducial')
492 Publisher.subscribe(self.SetTrackerFiducial, 'Set tracker fiducial') 498 Publisher.subscribe(self.SetTrackerFiducial, 'Set tracker fiducial')
493 - Publisher.subscribe(self.UpdateSerialPort, 'Update serial port')  
494 Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state') 499 Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state')
495 Publisher.subscribe(self.UpdateImageCoordinates, 'Set cross focal point') 500 Publisher.subscribe(self.UpdateImageCoordinates, 'Set cross focal point')
496 Publisher.subscribe(self.OnDisconnectTracker, 'Disconnect tracker') 501 Publisher.subscribe(self.OnDisconnectTracker, 'Disconnect tracker')
@@ -614,9 +619,6 @@ class NeuronavigationPanel(wx.Panel): @@ -614,9 +619,6 @@ class NeuronavigationPanel(wx.Panel):
614 def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True): 619 def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None, use_default_object=True):
615 self.navigation.track_obj = flag 620 self.navigation.track_obj = flag
616 621
617 - def UpdateSerialPort(self, serial_port):  
618 - self.navigation.serial_port = serial_port  
619 -  
620 def ResetICP(self): 622 def ResetICP(self):
621 self.icp.ResetICP() 623 self.icp.ResetICP()
622 self.checkbox_icp.Enable(False) 624 self.checkbox_icp.Enable(False)
invesalius/navigation/navigation.py
@@ -168,7 +168,9 @@ class Navigation(): @@ -168,7 +168,9 @@ class Navigation():
168 self.sleep_nav = const.SLEEP_NAVIGATION 168 self.sleep_nav = const.SLEEP_NAVIGATION
169 169
170 # Serial port 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 self.serial_port_connection = None 174 self.serial_port_connection = None
173 175
174 # During navigation 176 # During navigation
@@ -178,6 +180,7 @@ class Navigation(): @@ -178,6 +180,7 @@ class Navigation():
178 180
179 def __bind_events(self): 181 def __bind_events(self):
180 Publisher.subscribe(self.CoilAtTarget, 'Coil at target') 182 Publisher.subscribe(self.CoilAtTarget, 'Coil at target')
  183 + Publisher.subscribe(self.UpdateSerialPort, 'Update serial port')
181 184
182 def CoilAtTarget(self, state): 185 def CoilAtTarget(self, state):
183 self.coil_at_target = state 186 self.coil_at_target = state
@@ -186,8 +189,10 @@ class Navigation(): @@ -186,8 +189,10 @@ class Navigation():
186 self.sleep_nav = sleep 189 self.sleep_nav = sleep
187 self.serial_port_connection.sleep_nav = sleep 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 def SetReferenceMode(self, value): 197 def SetReferenceMode(self, value):
193 self.ref_mode_id = value 198 self.ref_mode_id = value
@@ -215,7 +220,7 @@ class Navigation(): @@ -215,7 +220,7 @@ class Navigation():
215 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD 220 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD
216 221
217 def PedalStateChanged(self, state): 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 self.serial_port_connection.SendPulse() 224 self.serial_port_connection.SendPulse()
220 225
221 def StartNavigation(self, tracker): 226 def StartNavigation(self, tracker):
@@ -227,7 +232,7 @@ class Navigation(): @@ -227,7 +232,7 @@ class Navigation():
227 if self.event.is_set(): 232 if self.event.is_set():
228 self.event.clear() 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 vis_queues = [self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue] 236 vis_queues = [self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue]
232 237
233 Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) 238 Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)
@@ -276,12 +281,13 @@ class Navigation(): @@ -276,12 +281,13 @@ class Navigation():
276 281
277 if not errors: 282 if not errors:
278 #TODO: Test the serial port thread 283 #TODO: Test the serial port thread
279 - if self.SerialPortEnabled(): 284 + if self.serial_port_in_use:
280 self.serial_port_connection = spc.SerialPortConnection( 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 self.serial_port_connection.Connect() 292 self.serial_port_connection.Connect()
287 jobs_list.append(self.serial_port_connection) 293 jobs_list.append(self.serial_port_connection)
@@ -327,7 +333,7 @@ class Navigation(): @@ -327,7 +333,7 @@ class Navigation():
327 if self.serial_port_connection is not None: 333 if self.serial_port_connection is not None:
328 self.serial_port_connection.join() 334 self.serial_port_connection.join()
329 335
330 - if self.SerialPortEnabled(): 336 + if self.serial_port_in_use:
331 self.serial_port_queue.clear() 337 self.serial_port_queue.clear()
332 self.serial_port_queue.join() 338 self.serial_port_queue.join()
333 339
@@ -338,5 +344,5 @@ class Navigation(): @@ -338,5 +344,5 @@ class Navigation():
338 self.tracts_queue.clear() 344 self.tracts_queue.clear()
339 self.tracts_queue.join() 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 Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) 348 Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)