Skip to content

Commit 9858b19

Browse files
committed
- Messages sent from pyboard to computer during framework run now all start with a special character (b'\x07') to indicate the start of the message. Any input that does not match the expected format is stored in an unexpected_input buffer and printed as a warning message in the log when the next valid message is recieved. This should make data transfer from the board more robust to any unexpected messages, and ensure that warnings on the board that do not crash the task (e.g. errors in interrupt service routines) do not fail silently.
1 parent 67eea59 commit 9858b19

File tree

3 files changed

+59
-48
lines changed

3 files changed

+59
-48
lines changed

com/pycboard.py

+49-38
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+
unexpected_input = []
351352
while self.serial.in_waiting > 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))
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:

pyControl/framework.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,13 @@ def output_data(event):
192192
timestamp = event[0].to_bytes(4, 'little')
193193
ID = event[2].to_bytes(2, 'little')
194194
checksum = sum(timestamp + ID).to_bytes(2, 'little')
195-
usb_serial.send(b'D' + timestamp + ID + checksum)
195+
usb_serial.send(b'\x07D' + timestamp + ID + checksum)
196196
elif event[1] in (print_typ, varbl_typ): # send user generated output string.
197197
if event[1] == print_typ: # send user generated output string.
198-
start_byte = b'P'
198+
start_byte = b'\x07P'
199199
data_bytes = event[2].encode()
200200
elif event[1] == varbl_typ: # Variable changed.
201-
start_byte = b'V'
201+
start_byte = b'\x07V'
202202
data_bytes = event[2][0].encode() + b' ' + event[2][1].encode()
203203
data_len = len(data_bytes).to_bytes(2, 'little')
204204
timestamp = event[0].to_bytes(4, 'little')

pyControl/hardware.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ class Analog_input(IO_object):
236236
# stream data to continously to computer as well as generate framework events when
237237
# voltage goes above / below specified value. The Analog_input class is subclassed
238238
# by other hardware devices that generate continous data such as the Rotory_encoder.
239-
# Serial data format for sending data to computer: 'A c i r l t k D' where:
240-
# A character indicating start of analog data chunk (1 byte)
239+
# Serial data format for sending data to computer: '\x07A c i r l t k D' where:
240+
# \x07A Message start byte and A character indicating start of analog data chunk (2 bytes)
241241
# c data array typecode (1 byte)
242242
# i ID of analog input (2 byte)
243243
# r sampling rate (Hz) (2 bytes)
@@ -269,7 +269,7 @@ def __init__(self, pin, name, sampling_rate, threshold=None, rising_event=None,
269269
self.buffers = (array(data_type, [0]*self.buffer_size),array(data_type, [0]*self.buffer_size))
270270
self.buffers_mv = (memoryview(self.buffers[0]), memoryview(self.buffers[1]))
271271
self.buffer_start_times = array('i', [0,0])
272-
self.data_header = array('B', b'A' + data_type.encode() +
272+
self.data_header = array('B', b'\x07A' + data_type.encode() +
273273
self.ID.to_bytes(2,'little') + sampling_rate.to_bytes(2,'little') + b'\x00'*8)
274274
# Event generation variables
275275
self.threshold = threshold
@@ -359,11 +359,11 @@ def _process_streaming(self):
359359
def _send_buffer(self, buffer_n, n_samples=False):
360360
# Send specified buffer to host computer.
361361
n_bytes = self.bytes_per_sample*n_samples if n_samples else self.bytes_per_sample*self.buffer_size
362-
self.data_header[6:8] = n_bytes.to_bytes(2,'little')
363-
self.data_header[8:12] = self.buffer_start_times[buffer_n].to_bytes(4,'little')
362+
self.data_header[7:9] = n_bytes.to_bytes(2,'little')
363+
self.data_header[9:13] = self.buffer_start_times[buffer_n].to_bytes(4,'little')
364364
checksum = sum(self.buffers_mv[buffer_n][:n_samples] if n_samples else self.buffers[buffer_n])
365-
checksum += sum(self.data_header[1:12])
366-
self.data_header[12:14] = checksum.to_bytes(2,'little')
365+
checksum += sum(self.data_header[2:13])
366+
self.data_header[13:15] = checksum.to_bytes(2,'little')
367367
fw.usb_serial.write(self.data_header)
368368
if n_samples: # Send first n_samples from buffer.
369369
fw.usb_serial.send(self.buffers_mv[buffer_n][:n_samples])

0 commit comments

Comments
 (0)