Skip to content

Commit 722f42a

Browse files
committed
Merge branch 'dev'
2 parents d77a310 + 9858b19 commit 722f42a

10 files changed

+192
-158
lines changed

com/pyboard.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None):
4545
while True:
4646
if data.endswith(ending):
4747
break
48-
elif self.serial.inWaiting() > 0:
48+
elif self.serial.in_waiting > 0:
4949
new_data = self.serial.read(1)
5050
data = data + new_data
5151
if data_consumer:
@@ -61,10 +61,10 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None):
6161
def enter_raw_repl(self):
6262
self.serial.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program
6363
# flush input (without relying on serial.flushInput())
64-
n = self.serial.inWaiting()
64+
n = self.serial.in_waiting
6565
while n > 0:
6666
self.serial.read(n)
67-
n = self.serial.inWaiting()
67+
n = self.serial.in_waiting
6868
self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL
6969
data = self.read_until(1, b'to exit\r\n>')
7070
if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'):

com/pycboard.py

+50-39
Original file line numberDiff line numberDiff line change
@@ -348,53 +348,64 @@ def process_data(self):
348348
pass new_data to data_logger and print_func if specified, return new_data.'''
349349
new_data = []
350350
error_message = None
351-
while self.serial.inWaiting() > 0:
352-
new_byte = self.serial.read(1)
353-
if new_byte == b'A': # Analog data, 13 byte header + variable size content.
354-
data_header = self.serial.read(13)
355-
typecode = data_header[0:1].decode()
356-
if typecode not in ('b','B','h','H','l','L'):
357-
new_data.append(('!','bad typecode A'))
358-
continue
359-
ID = int.from_bytes(data_header[1:3], 'little')
360-
sampling_rate = int.from_bytes(data_header[3:5], 'little')
361-
data_len = int.from_bytes(data_header[5:7], 'little')
362-
timestamp = int.from_bytes(data_header[7:11], 'little')
363-
checksum = int.from_bytes(data_header[11:13], 'little')
364-
data_array = array(typecode, self.serial.read(data_len))
365-
if checksum == (sum(data_header[:-2]) + sum(data_array)) & 0xffff: # Checksum OK.
366-
new_data.append(('A',ID, sampling_rate, timestamp, data_array))
351+
unexpected_input = []
352+
while self.serial.in_waiting > 0:
353+
new_byte = self.serial.read(1)
354+
if new_byte == b'\x07': # Start of pyControl message.
355+
if unexpected_input: # Output any unexpected characters recived prior to message start.
356+
new_data.append(('!','Unexpected input recieved from board: ' +
357+
''.join(unexpected_input)))
358+
unexpected_input = []
359+
type_byte = self.serial.read(1) # Message type identifier.
360+
if type_byte == b'A': # Analog data, 13 byte header + variable size content.
361+
data_header = self.serial.read(13)
362+
typecode = data_header[0:1].decode()
363+
if typecode not in ('b','B','h','H','l','L'):
364+
new_data.append(('!','bad typecode A'))
365+
continue
366+
ID = int.from_bytes(data_header[1:3], 'little')
367+
sampling_rate = int.from_bytes(data_header[3:5], 'little')
368+
data_len = int.from_bytes(data_header[5:7], 'little')
369+
timestamp = int.from_bytes(data_header[7:11], 'little')
370+
checksum = int.from_bytes(data_header[11:13], 'little')
371+
data_array = array(typecode, self.serial.read(data_len))
372+
if checksum == (sum(data_header[:-2]) + sum(data_array)) & 0xffff: # Checksum OK.
373+
new_data.append(('A',ID, sampling_rate, timestamp, data_array))
374+
else:
375+
new_data.append(('!','bad checksum A'))
376+
elif type_byte == b'D': # Event or state entry, 8 byte data header only.
377+
data_header = self.serial.read(8)
378+
timestamp = int.from_bytes(data_header[ :4], 'little')
379+
ID = int.from_bytes(data_header[4:6], 'little')
380+
checksum = int.from_bytes(data_header[6:8], 'little')
381+
if checksum == sum(data_header[:-2]): # Checksum OK.
382+
new_data.append(('D',timestamp, ID))
383+
else:
384+
new_data.append(('!','bad checksum D'))
385+
elif type_byte in (b'P', b'V'): # User print statement or set variable, 8 byte data header + variable size content.
386+
data_header = self.serial.read(8)
387+
data_len = int.from_bytes(data_header[ :2], 'little')
388+
timestamp = int.from_bytes(data_header[2:6], 'little')
389+
checksum = int.from_bytes(data_header[6:8], 'little')
390+
data_bytes = self.serial.read(data_len)
391+
if not checksum == (sum(data_header[:-2]) + sum(data_bytes)) & 0xffff: # Bad checksum.
392+
new_data.append(('!','bad checksum ' + type_byte.decode()))
393+
continue
394+
new_data.append((type_byte.decode(),timestamp, data_bytes.decode()))
395+
if type_byte == b'V': # Store new variable value in sm_info
396+
v_name, v_str = data_bytes.decode().split(' ', 1)
397+
self.sm_info['variables'][v_name] = eval(v_str)
367398
else:
368-
new_data.append(('!','bad checksum A'))
369-
elif new_byte == b'D': # Event or state entry, 8 byte data header only.
370-
data_header = self.serial.read(8)
371-
timestamp = int.from_bytes(data_header[ :4], 'little')
372-
ID = int.from_bytes(data_header[4:6], 'little')
373-
checksum = int.from_bytes(data_header[6:8], 'little')
374-
if checksum == sum(data_header[:-2]): # Checksum OK.
375-
new_data.append(('D',timestamp, ID))
376-
else:
377-
new_data.append(('!','bad checksum D'))
378-
elif new_byte in (b'P', b'V'): # User print statement or set variable, 8 byte data header + variable size content.
379-
data_header = self.serial.read(8)
380-
data_len = int.from_bytes(data_header[ :2], 'little')
381-
timestamp = int.from_bytes(data_header[2:6], 'little')
382-
checksum = int.from_bytes(data_header[6:8], 'little')
383-
data_bytes = self.serial.read(data_len)
384-
if not checksum == (sum(data_header[:-2]) + sum(data_bytes)) & 0xffff: # Bad checksum.
385-
new_data.append(('!','bad checksum ' + new_byte.decode()))
386-
continue
387-
new_data.append((new_byte.decode(),timestamp, data_bytes.decode()))
388-
if new_byte == b'V': # Store new variable value in sm_info
389-
v_name, v_str = data_bytes.decode().split(' ', 1)
390-
self.sm_info['variables'][v_name] = eval(v_str)
399+
unexpected_input.append(type_byte.decode())
391400
elif new_byte == b'\x04': # End of framework run.
392401
self.framework_running = False
393402
data_err = self.read_until(2, b'\x04>', timeout=10)
394403
if len(data_err) > 2:
395404
error_message = data_err[:-3].decode()
396405
new_data.append(('!', error_message))
397406
break
407+
else:
408+
unexpected_input.append(new_byte.decode())
398409
if new_data and self.data_logger:
399410
self.data_logger.process_data(new_data)
400411
if error_message:

config/gui_settings.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
VERSION = '1.6'
44

5-
update_interval = 20 # Interval between calls to the GUIs update function (ms).
5+
update_interval = 10 # Interval between calls to the GUIs update function (ms).
66

77
event_history_len = 200 # Length of event history to plot (# events).
88
state_history_len = 100 # Length of state history to plot (# states).
9-
analog_history_dur = 12 # Duration of analog signal history to plot (seconds).
9+
analog_history_dur = 12 # Duration of analog signal history to plot (seconds).
10+
11+
ui_font_size = 11 # Font size of UI elements (button text, label text etc.).
12+
log_font_size = 9 # Font size of the data log.

gui/GUI_main.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pyqtgraph.Qt import QtGui, QtCore
77

88
from config.paths import dirs
9-
from config.gui_settings import VERSION
9+
from config.gui_settings import VERSION, ui_font_size
1010
from gui.run_task_tab import Run_task_tab
1111
from gui.dialogs import Board_config_dialog, Keyboard_shortcuts_dialog, Paths_dialog
1212
from gui.configure_experiment_tab import Configure_experiment_tab
@@ -188,6 +188,10 @@ def excepthook(self, ex_type, ex_value, ex_traceback):
188188
def launch_GUI():
189189
'''Launch the pyControl GUI.'''
190190
app = QtGui.QApplication(sys.argv)
191+
app.setStyle('Fusion')
192+
font = QtGui.QFont()
193+
font.setPixelSize(ui_font_size)
194+
app.setFont(font)
191195
gui_main = GUI_main()
192196
gui_main.app = app # To allow app functions to be called from GUI.
193197
sys.excepthook = gui_main.excepthook

gui/plotting.py

+13-26
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import numpy as np
33
from datetime import timedelta
44
import pyqtgraph as pg
5-
from pyqtgraph.Qt import QtGui, QtWidgets
5+
from pyqtgraph.Qt import QtGui
66
from PyQt5.QtCore import Qt
77

88
from config.gui_settings import event_history_len, state_history_len, analog_history_dur
@@ -114,34 +114,28 @@ def run_start(self):
114114
self.data = np.zeros([self.data_len*2, 2], int)
115115
for plot in self.plots.values():
116116
plot.clear()
117-
self.cs = self.state_IDs[0]
118-
self.updated_states = []
119117

120118
def process_data(self, new_data):
121119
'''Store new data from board'''
122120
new_states = [nd for nd in new_data if nd[0] == 'D' and nd[2] in self.state_IDs]
123-
self.updated_states = [self.cs]
124121
if new_states:
125122
n_new =len(new_states)
126123
self.data = np.roll(self.data, -2*n_new, axis=0)
127124
for i, ns in enumerate(new_states): # Update data array.
128125
timestamp, ID = ns[1:]
129-
self.updated_states.append(ID)
130126
j = 2*(-n_new+i) # Index of state entry in self.data
131127
self.data[j-1:,0] = timestamp
132128
self.data[j: ,1] = ID
133-
self.cs = ID
134129

135130
def update(self, run_time):
136131
'''Update plots.'''
137-
self.data[-1,0] = 1000*run_time # Update exit time of current state to current time.
138-
for us in self.updated_states: # Set data for updated state plots.
139-
state_data = self.data[self.data[:,1]==us,:]
140-
timestamps, ID = (state_data[:,0]/1000, state_data[:,1])
141-
self.plots[us].setData(x=timestamps, y=ID, connect='pairs')
142-
# Shift all state plots.
143-
for plot in self.plots.values():
144-
plot.setPos(-run_time, 0)
132+
self.data[-1,0] = run_time*1000 # Update exit time of current state to current time.
133+
for ID in self.state_IDs:
134+
state_data = self.data[self.data[:,1]==ID,:]
135+
timestamps, IDs = (state_data[:,0]/1000-run_time, state_data[:,1])
136+
if timestamps.size > 0:
137+
self.plots[ID].setData(x=timestamps, y=IDs, connect='pairs')
138+
145139

146140
# Events_plot--------------------------------------------------------
147141

@@ -187,10 +181,8 @@ def process_data(self, new_data):
187181

188182
def update(self, run_time):
189183
'''Update plots'''
190-
# Should not need to setData but setPos does not cause redraw otherwise.
191184
if not self.event_IDs: return
192-
self.plot.setData(self.data, symbolBrush=[pg.intColor(ID) for ID in self.data[:,1]])
193-
self.plot.setPos(-run_time, 0)
185+
self.plot.setData(x=self.data[:,0]-run_time, y=self.data[:,1], symbolBrush=[pg.intColor(ID) for ID in self.data[:,1]])
194186

195187
# ------------------------------------------------------------------------------------------
196188

@@ -205,15 +197,12 @@ def __init__(self, parent=None, data_dur=10):
205197
self.axis.setMouseEnabled(x=True,y=False)
206198
self.axis.showGrid(x=True,alpha=0.75)
207199
self.axis.setLimits(xMax=0)
208-
self.legend = None
209200

210201
def set_state_machine(self, sm_info):
211202
self.inputs = sm_info['analog_inputs']
212203
if not self.inputs: return # State machine may not have analog inputs.
213-
if self.legend:
214-
self.legend.close()
215-
self.legend = self.axis.addLegend(offset=(10, 10))
216204
self.axis.clear()
205+
self.legend = self.axis.addLegend(offset=(10, 10))
217206
self.plots = {ai['ID']: self.axis.plot(name=name,
218207
pen=pg.mkPen(pg.intColor(ai['ID'],len(self.inputs)))) for name, ai in sorted(self.inputs.items())}
219208
self.axis.getAxis('bottom').setLabel('Time (seconds)')
@@ -232,7 +221,6 @@ def process_data(self, new_data):
232221
'''Store new data from board.'''
233222
if not self.inputs: return # State machine may not have analog inputs.
234223
new_analog = [nd for nd in new_data if nd[0] == 'A']
235-
self.updated_inputs = [na[1] for na in new_analog]
236224
for na in new_analog:
237225
ID, sampling_rate, timestamp, data_array = na[1:]
238226
new_len = len(data_array)
@@ -243,10 +231,9 @@ def process_data(self, new_data):
243231
def update(self, run_time):
244232
'''Update plots.'''
245233
if not self.inputs: return # State machine may not have analog inputs.
246-
for ID in self.updated_inputs:
247-
self.plots[ID].setData(self.data[ID])
248-
for plot in self.plots.values():
249-
plot.setPos(-run_time, 0)
234+
for ai in self.inputs.values():
235+
ID = ai['ID']
236+
self.plots[ID].setData(x=self.data[ID][:,0]-run_time, y=self.data[ID][:,1])
250237

251238
# -----------------------------------------------------
252239

0 commit comments

Comments
 (0)