thesis
Esse commit está contido em:
Arquivo binário não exibido.
@@ -0,0 +1,579 @@
|
||||
"""
|
||||
Core OpenBCI object for handling connections and samples from the board.
|
||||
|
||||
EXAMPLE USE:
|
||||
|
||||
def handle_sample(sample):
|
||||
print(sample.channels)
|
||||
|
||||
board = OpenBCIBoard()
|
||||
board.print_register_settings()
|
||||
board.start(handle_sample)
|
||||
|
||||
NOTE: If daisy modules is enabled, the callback will occur every two samples, hence "packet_id" will only contain even numbers. As a side effect, the sampling rate will be divided by 2.
|
||||
|
||||
FIXME: at the moment we can just force daisy mode, do not check that the module is detected.
|
||||
|
||||
|
||||
"""
|
||||
import serial
|
||||
import struct
|
||||
import numpy as np
|
||||
import time
|
||||
import timeit
|
||||
import atexit
|
||||
import logging
|
||||
import threading
|
||||
import sys
|
||||
import pdb
|
||||
import glob
|
||||
|
||||
SAMPLE_RATE = 250.0 # Hz
|
||||
START_BYTE = 0xA0 # start of data packet
|
||||
END_BYTE = 0xC0 # end of data packet
|
||||
ADS1299_Vref = 4.5 #reference voltage for ADC in ADS1299. set by its hardware
|
||||
ADS1299_gain = 24.0 #assumed gain setting for ADS1299. set by its Arduino code
|
||||
scale_fac_uVolts_per_count = ADS1299_Vref/float((pow(2,23)-1))/ADS1299_gain*1000000.
|
||||
scale_fac_accel_G_per_count = 0.002 /(pow(2,4)) #assume set to +/4G, so 2 mG
|
||||
'''
|
||||
#Commands for in SDK http://docs.openbci.com/software/01-Open BCI_SDK:
|
||||
|
||||
command_stop = "s";
|
||||
command_startText = "x";
|
||||
command_startBinary = "b";
|
||||
command_startBinary_wAux = "n";
|
||||
command_startBinary_4chan = "v";
|
||||
command_activateFilters = "F";
|
||||
command_deactivateFilters = "g";
|
||||
command_deactivate_channel = {"1", "2", "3", "4", "5", "6", "7", "8"};
|
||||
command_activate_channel = {"q", "w", "e", "r", "t", "y", "u", "i"};
|
||||
command_activate_leadoffP_channel = {"!", "@", "#", "$", "%", "^", "&", "*"}; //shift + 1-8
|
||||
command_deactivate_leadoffP_channel = {"Q", "W", "E", "R", "T", "Y", "U", "I"}; //letters (plus shift) right below 1-8
|
||||
command_activate_leadoffN_channel = {"A", "S", "D", "F", "G", "H", "J", "K"}; //letters (plus shift) below the letters below 1-8
|
||||
command_deactivate_leadoffN_channel = {"Z", "X", "C", "V", "B", "N", "M", "<"}; //letters (plus shift) below the letters below the letters below 1-8
|
||||
command_biasAuto = "`";
|
||||
command_biasFixed = "~";
|
||||
'''
|
||||
|
||||
class OpenBCIBoard(object):
|
||||
"""
|
||||
|
||||
Handle a connection to an OpenBCI board.
|
||||
|
||||
Args:
|
||||
port: The port to connect to.
|
||||
baud: The baud of the serial connection.
|
||||
daisy: Enable or disable daisy module and 16 chans readings
|
||||
"""
|
||||
|
||||
def __init__(self, port=None, baud=115200, filter_data=True,
|
||||
scaled_output=True, daisy=False, log=True, timeout=None):
|
||||
self.log = log # print_incoming_text needs log
|
||||
self.streaming = False
|
||||
self.baudrate = baud
|
||||
self.timeout = timeout
|
||||
if not port:
|
||||
port = self.find_port()
|
||||
self.port = port
|
||||
print("Connecting to V3 at port %s" %(port))
|
||||
self.ser = serial.Serial(port= port, baudrate = baud, timeout=timeout)
|
||||
|
||||
print("Serial established...")
|
||||
|
||||
time.sleep(2)
|
||||
#Initialize 32-bit board, doesn't affect 8bit board
|
||||
self.ser.write(b'v');
|
||||
|
||||
|
||||
#wait for device to be ready
|
||||
time.sleep(1)
|
||||
self.print_incoming_text()
|
||||
|
||||
self.streaming = False
|
||||
self.filtering_data = filter_data
|
||||
self.scaling_output = scaled_output
|
||||
self.eeg_channels_per_sample = 8 # number of EEG channels per sample *from the board*
|
||||
self.aux_channels_per_sample = 3 # number of AUX channels per sample *from the board*
|
||||
self.read_state = 0
|
||||
self.daisy = daisy
|
||||
self.last_odd_sample = OpenBCISample(-1, [], []) # used for daisy
|
||||
self.log_packet_count = 0
|
||||
self.attempt_reconnect = False
|
||||
self.last_reconnect = 0
|
||||
self.reconnect_freq = 5
|
||||
self.packets_dropped = 0
|
||||
|
||||
#Disconnects from board when terminated
|
||||
atexit.register(self.disconnect)
|
||||
|
||||
def getSampleRate(self):
|
||||
if self.daisy:
|
||||
return SAMPLE_RATE/2
|
||||
else:
|
||||
return SAMPLE_RATE
|
||||
|
||||
def getNbEEGChannels(self):
|
||||
if self.daisy:
|
||||
return self.eeg_channels_per_sample*2
|
||||
else:
|
||||
return self.eeg_channels_per_sample
|
||||
|
||||
def getNbAUXChannels(self):
|
||||
return self.aux_channels_per_sample
|
||||
|
||||
def start_streaming(self, callback, lapse=-1):
|
||||
"""
|
||||
Start handling streaming data from the board. Call a provided callback
|
||||
for every single sample that is processed (every two samples with daisy module).
|
||||
|
||||
Args:
|
||||
callback: A callback function -- or a list of functions -- that will receive a single argument of the
|
||||
OpenBCISample object captured.
|
||||
"""
|
||||
if not self.streaming:
|
||||
self.ser.write(b'b')
|
||||
self.streaming = True
|
||||
|
||||
start_time = timeit.default_timer()
|
||||
|
||||
# Enclose callback funtion in a list if it comes alone
|
||||
if not isinstance(callback, list):
|
||||
callback = [callback]
|
||||
|
||||
|
||||
#Initialize check connection
|
||||
self.check_connection()
|
||||
|
||||
while self.streaming:
|
||||
|
||||
# read current sample
|
||||
sample = self._read_serial_binary()
|
||||
# if a daisy module is attached, wait to concatenate two samples (main board + daisy) before passing it to callback
|
||||
if self.daisy:
|
||||
# odd sample: daisy sample, save for later
|
||||
if ~sample.id % 2:
|
||||
self.last_odd_sample = sample
|
||||
# even sample: concatenate and send if last sample was the fist part, otherwise drop the packet
|
||||
elif sample.id - 1 == self.last_odd_sample.id:
|
||||
# the aux data will be the average between the two samples, as the channel samples themselves have been averaged by the board
|
||||
avg_aux_data = list((np.array(sample.aux_data) + np.array(self.last_odd_sample.aux_data))/2)
|
||||
whole_sample = OpenBCISample(sample.id, sample.channel_data + self.last_odd_sample.channel_data, avg_aux_data)
|
||||
for call in callback:
|
||||
call(whole_sample)
|
||||
else:
|
||||
for call in callback:
|
||||
call(sample)
|
||||
|
||||
if(lapse > 0 and timeit.default_timer() - start_time > lapse):
|
||||
self.stop();
|
||||
if self.log:
|
||||
self.log_packet_count = self.log_packet_count + 1;
|
||||
|
||||
|
||||
"""
|
||||
PARSER:
|
||||
Parses incoming data packet into OpenBCISample.
|
||||
Incoming Packet Structure:
|
||||
Start Byte(1)|Sample ID(1)|Channel Data(24)|Aux Data(6)|End Byte(1)
|
||||
0xA0|0-255|8, 3-byte signed ints|3 2-byte signed ints|0xC0
|
||||
|
||||
"""
|
||||
def _read_serial_binary(self, max_bytes_to_skip=3000):
|
||||
def read(n):
|
||||
b = self.ser.read(n)
|
||||
if not b:
|
||||
self.warn('Device appears to be stalled. Quitting...')
|
||||
sys.exit()
|
||||
raise Exception('Device Stalled')
|
||||
sys.exit()
|
||||
return '\xFF'
|
||||
else:
|
||||
return b
|
||||
|
||||
for rep in range(max_bytes_to_skip):
|
||||
|
||||
#---------Start Byte & ID---------
|
||||
if self.read_state == 0:
|
||||
|
||||
b = read(1)
|
||||
|
||||
if struct.unpack('B', b)[0] == START_BYTE:
|
||||
if(rep != 0):
|
||||
self.warn('Skipped %d bytes before start found' %(rep))
|
||||
rep = 0;
|
||||
packet_id = struct.unpack('B', read(1))[0] #packet id goes from 0-255
|
||||
log_bytes_in = str(packet_id);
|
||||
|
||||
self.read_state = 1
|
||||
|
||||
#---------Channel Data---------
|
||||
elif self.read_state == 1:
|
||||
channel_data = []
|
||||
for c in range(self.eeg_channels_per_sample):
|
||||
|
||||
#3 byte ints
|
||||
literal_read = read(3)
|
||||
|
||||
unpacked = struct.unpack('3B', literal_read)
|
||||
log_bytes_in = log_bytes_in + '|' + str(literal_read);
|
||||
|
||||
#3byte int in 2s compliment
|
||||
if (unpacked[0] >= 127):
|
||||
pre_fix = bytes(bytearray.fromhex('FF'))
|
||||
else:
|
||||
pre_fix = bytes(bytearray.fromhex('00'))
|
||||
|
||||
|
||||
literal_read = pre_fix + literal_read;
|
||||
|
||||
#unpack little endian(>) signed integer(i) (makes unpacking platform independent)
|
||||
myInt = struct.unpack('>i', literal_read)[0]
|
||||
|
||||
if self.scaling_output:
|
||||
channel_data.append(myInt*scale_fac_uVolts_per_count)
|
||||
else:
|
||||
channel_data.append(myInt)
|
||||
|
||||
self.read_state = 2;
|
||||
|
||||
#---------Accelerometer Data---------
|
||||
elif self.read_state == 2:
|
||||
aux_data = []
|
||||
for a in range(self.aux_channels_per_sample):
|
||||
|
||||
#short = h
|
||||
acc = struct.unpack('>h', read(2))[0]
|
||||
log_bytes_in = log_bytes_in + '|' + str(acc);
|
||||
|
||||
if self.scaling_output:
|
||||
aux_data.append(acc*scale_fac_accel_G_per_count)
|
||||
else:
|
||||
aux_data.append(acc)
|
||||
|
||||
self.read_state = 3;
|
||||
#---------End Byte---------
|
||||
elif self.read_state == 3:
|
||||
val = struct.unpack('B', read(1))[0]
|
||||
log_bytes_in = log_bytes_in + '|' + str(val);
|
||||
self.read_state = 0 #read next packet
|
||||
if (val == END_BYTE):
|
||||
sample = OpenBCISample(packet_id, channel_data, aux_data)
|
||||
self.packets_dropped = 0
|
||||
return sample
|
||||
else:
|
||||
self.warn("ID:<%d> <Unexpected END_BYTE found <%s> instead of <%s>"
|
||||
%(packet_id, val, END_BYTE))
|
||||
logging.debug(log_bytes_in);
|
||||
self.packets_dropped = self.packets_dropped + 1
|
||||
|
||||
"""
|
||||
|
||||
Clean Up (atexit)
|
||||
|
||||
"""
|
||||
def stop(self):
|
||||
print("Stopping streaming...\nWait for buffer to flush...")
|
||||
self.streaming = False
|
||||
self.ser.write(b's')
|
||||
if self.log:
|
||||
logging.warning('sent <s>: stopped streaming')
|
||||
|
||||
def disconnect(self):
|
||||
if(self.streaming == True):
|
||||
self.stop()
|
||||
if (self.ser.isOpen()):
|
||||
print("Closing Serial...")
|
||||
self.ser.close()
|
||||
logging.warning('serial closed')
|
||||
|
||||
|
||||
"""
|
||||
|
||||
SETTINGS AND HELPERS
|
||||
|
||||
"""
|
||||
def warn(self, text):
|
||||
if self.log:
|
||||
#log how many packets where sent succesfully in between warnings
|
||||
if self.log_packet_count:
|
||||
logging.info('Data packets received:'+str(self.log_packet_count))
|
||||
self.log_packet_count = 0;
|
||||
logging.warning(text)
|
||||
print("Warning: %s" % text)
|
||||
|
||||
|
||||
def print_incoming_text(self):
|
||||
"""
|
||||
|
||||
When starting the connection, print all the debug data until
|
||||
we get to a line with the end sequence '$$$'.
|
||||
|
||||
"""
|
||||
line = ''
|
||||
#Wait for device to send data
|
||||
time.sleep(1)
|
||||
|
||||
if self.ser.inWaiting():
|
||||
line = ''
|
||||
c = ''
|
||||
#Look for end sequence $$$
|
||||
while '$$$' not in line:
|
||||
c = self.ser.read().decode('utf-8')
|
||||
line += c
|
||||
print(line);
|
||||
else:
|
||||
self.warn("No Message")
|
||||
|
||||
def openbci_id(self, serial):
|
||||
"""
|
||||
|
||||
When automatically detecting port, parse the serial return for the "OpenBCI" ID.
|
||||
|
||||
"""
|
||||
line = ''
|
||||
#Wait for device to send data
|
||||
time.sleep(2)
|
||||
|
||||
if serial.inWaiting():
|
||||
line = ''
|
||||
c = ''
|
||||
#Look for end sequence $$$
|
||||
while '$$$' not in line:
|
||||
c = serial.read().decode('utf-8')
|
||||
line += c
|
||||
if "OpenBCI" in line:
|
||||
return True
|
||||
return False
|
||||
|
||||
def print_register_settings(self):
|
||||
self.ser.write(b'?')
|
||||
time.sleep(0.5)
|
||||
print_incoming_text();
|
||||
#DEBBUGING: Prints individual incoming bytes
|
||||
def print_bytes_in(self):
|
||||
if not self.streaming:
|
||||
self.ser.write(b'b')
|
||||
self.streaming = True
|
||||
while self.streaming:
|
||||
print(struct.unpack('B',self.ser.read())[0]);
|
||||
|
||||
'''Incoming Packet Structure:
|
||||
Start Byte(1)|Sample ID(1)|Channel Data(24)|Aux Data(6)|End Byte(1)
|
||||
0xA0|0-255|8, 3-byte signed ints|3 2-byte signed ints|0xC0'''
|
||||
|
||||
def print_packets_in(self):
|
||||
while self.streaming:
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
|
||||
if b == START_BYTE:
|
||||
self.attempt_reconnect = False
|
||||
if skipped_str:
|
||||
logging.debug('SKIPPED\n' + skipped_str + '\nSKIPPED')
|
||||
skipped_str = ''
|
||||
|
||||
packet_str = "%03d"%(b) + '|';
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
packet_str = packet_str + "%03d"%(b) + '|';
|
||||
|
||||
#data channels
|
||||
for i in range(24-1):
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
packet_str = packet_str + '.' + "%03d"%(b);
|
||||
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
packet_str = packet_str + '.' + "%03d"%(b) + '|';
|
||||
|
||||
#aux channels
|
||||
for i in range(6-1):
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
packet_str = packet_str + '.' + "%03d"%(b);
|
||||
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
packet_str = packet_str + '.' + "%03d"%(b) + '|';
|
||||
|
||||
#end byte
|
||||
b = struct.unpack('B', self.ser.read())[0];
|
||||
|
||||
#Valid Packet
|
||||
if b == END_BYTE:
|
||||
packet_str = packet_str + '.' + "%03d"%(b) + '|VAL';
|
||||
print(packet_str)
|
||||
#logging.debug(packet_str)
|
||||
|
||||
#Invalid Packet
|
||||
else:
|
||||
packet_str = packet_str + '.' + "%03d"%(b) + '|INV';
|
||||
#Reset
|
||||
self.attempt_reconnect = True
|
||||
|
||||
|
||||
else:
|
||||
print(b)
|
||||
if b == END_BYTE:
|
||||
skipped_str = skipped_str + '|END|'
|
||||
else:
|
||||
skipped_str = skipped_str + "%03d"%(b) + '.'
|
||||
|
||||
if self.attempt_reconnect and (timeit.default_timer()-self.last_reconnect) > self.reconnect_freq:
|
||||
self.last_reconnect = timeit.default_timer()
|
||||
self.warn('Reconnecting')
|
||||
self.reconnect()
|
||||
|
||||
|
||||
|
||||
def check_connection(self, interval = 2, max_packets_to_skip=10):
|
||||
#check number of dropped packages and establish connection problem if too large
|
||||
if self.packets_dropped > max_packets_to_skip:
|
||||
#if error, attempt to reconect
|
||||
self.reconnect()
|
||||
# check again again in 2 seconds
|
||||
threading.Timer(interval, self.check_connection).start()
|
||||
|
||||
def reconnect(self):
|
||||
self.packets_dropped = 0
|
||||
self.warn('Reconnecting')
|
||||
self.stop()
|
||||
time.sleep(0.5)
|
||||
self.ser.write(b'v')
|
||||
time.sleep(0.5)
|
||||
self.ser.write(b'b')
|
||||
time.sleep(0.5)
|
||||
self.streaming = True
|
||||
#self.attempt_reconnect = False
|
||||
|
||||
|
||||
#Adds a filter at 60hz to cancel out ambient electrical noise
|
||||
def enable_filters(self):
|
||||
self.ser.write(b'f')
|
||||
self.filtering_data = True;
|
||||
|
||||
def disable_filters(self):
|
||||
self.ser.write(b'g')
|
||||
self.filtering_data = False;
|
||||
|
||||
def test_signal(self, signal):
|
||||
if signal == 0:
|
||||
self.ser.write(b'0')
|
||||
self.warn("Connecting all pins to ground")
|
||||
elif signal == 1:
|
||||
self.ser.write(b'p')
|
||||
self.warn("Connecting all pins to Vcc")
|
||||
elif signal == 2:
|
||||
self.ser.write(b'-')
|
||||
self.warn("Connecting pins to low frequency 1x amp signal")
|
||||
elif signal == 3:
|
||||
self.ser.write(b'=')
|
||||
self.warn("Connecting pins to high frequency 1x amp signal")
|
||||
elif signal == 4:
|
||||
self.ser.write(b'[')
|
||||
self.warn("Connecting pins to low frequency 2x amp signal")
|
||||
elif signal == 5:
|
||||
self.ser.write(b']')
|
||||
self.warn("Connecting pins to high frequency 2x amp signal")
|
||||
else:
|
||||
self.warn("%s is not a known test signal. Valid signals go from 0-5" %(signal))
|
||||
|
||||
def set_channel(self, channel, toggle_position):
|
||||
#Commands to set toggle to on position
|
||||
if toggle_position == 1:
|
||||
if channel is 1:
|
||||
self.ser.write(b'!')
|
||||
if channel is 2:
|
||||
self.ser.write(b'@')
|
||||
if channel is 3:
|
||||
self.ser.write(b'#')
|
||||
if channel is 4:
|
||||
self.ser.write(b'$')
|
||||
if channel is 5:
|
||||
self.ser.write(b'%')
|
||||
if channel is 6:
|
||||
self.ser.write(b'^')
|
||||
if channel is 7:
|
||||
self.ser.write(b'&')
|
||||
if channel is 8:
|
||||
self.ser.write(b'*')
|
||||
if channel is 9 and self.daisy:
|
||||
self.ser.write(b'Q')
|
||||
if channel is 10 and self.daisy:
|
||||
self.ser.write(b'W')
|
||||
if channel is 11 and self.daisy:
|
||||
self.ser.write(b'E')
|
||||
if channel is 12 and self.daisy:
|
||||
self.ser.write(b'R')
|
||||
if channel is 13 and self.daisy:
|
||||
self.ser.write(b'T')
|
||||
if channel is 14 and self.daisy:
|
||||
self.ser.write(b'Y')
|
||||
if channel is 15 and self.daisy:
|
||||
self.ser.write(b'U')
|
||||
if channel is 16 and self.daisy:
|
||||
self.ser.write(b'I')
|
||||
#Commands to set toggle to off position
|
||||
elif toggle_position == 0:
|
||||
if channel is 1:
|
||||
self.ser.write(b'1')
|
||||
if channel is 2:
|
||||
self.ser.write(b'2')
|
||||
if channel is 3:
|
||||
self.ser.write(b'3')
|
||||
if channel is 4:
|
||||
self.ser.write(b'4')
|
||||
if channel is 5:
|
||||
self.ser.write(b'5')
|
||||
if channel is 6:
|
||||
self.ser.write(b'6')
|
||||
if channel is 7:
|
||||
self.ser.write(b'7')
|
||||
if channel is 8:
|
||||
self.ser.write(b'8')
|
||||
if channel is 9 and self.daisy:
|
||||
self.ser.write(b'q')
|
||||
if channel is 10 and self.daisy:
|
||||
self.ser.write(b'w')
|
||||
if channel is 11 and self.daisy:
|
||||
self.ser.write(b'e')
|
||||
if channel is 12 and self.daisy:
|
||||
self.ser.write(b'r')
|
||||
if channel is 13 and self.daisy:
|
||||
self.ser.write(b't')
|
||||
if channel is 14 and self.daisy:
|
||||
self.ser.write(b'y')
|
||||
if channel is 15 and self.daisy:
|
||||
self.ser.write(b'u')
|
||||
if channel is 16 and self.daisy:
|
||||
self.ser.write(b'i')
|
||||
|
||||
def find_port(self):
|
||||
# Finds the serial port names
|
||||
if sys.platform.startswith('win'):
|
||||
ports = ['COM%s' % (i+1) for i in range(256)]
|
||||
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
|
||||
ports = glob.glob('/dev/ttyUSB*')
|
||||
elif sys.platform.startswith('darwin'):
|
||||
ports = glob.glob('/dev/tty.usbserial*')
|
||||
else:
|
||||
raise EnvironmentError('Error finding ports on your operating system')
|
||||
openbci_port = ''
|
||||
for port in ports:
|
||||
try:
|
||||
s = serial.Serial(port= port, baudrate = self.baudrate, timeout=self.timeout)
|
||||
s.write(b'v')
|
||||
openbci_serial = self.openbci_id(s)
|
||||
s.close()
|
||||
if openbci_serial:
|
||||
openbci_port = port;
|
||||
except (OSError, serial.SerialException):
|
||||
pass
|
||||
if openbci_port == '':
|
||||
raise OSError('Cannot find OpenBCI port')
|
||||
else:
|
||||
return openbci_port
|
||||
|
||||
class OpenBCISample(object):
|
||||
"""Object encapulsating a single sample from the OpenBCI board."""
|
||||
def __init__(self, packet_id, channel_data, aux_data):
|
||||
self.id = packet_id;
|
||||
self.channel_data = channel_data;
|
||||
self.aux_data = aux_data;
|
||||
|
||||
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sun Aug 07 00:03:34 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import open_bci_v3 as bci
|
||||
import json
|
||||
from Queue import Queue
|
||||
from twisted.internet.defer import inlineCallbacks, CancelledError
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
from twisted.python import log
|
||||
from twisted.internet import reactor, threads
|
||||
from autobahn.twisted.websocket import WebSocketClientProtocol, \
|
||||
WebSocketClientFactory
|
||||
|
||||
|
||||
queue = Queue()
|
||||
|
||||
order_number = 0
|
||||
last_packet_id = None
|
||||
|
||||
|
||||
def read_samples():
|
||||
# USB dongle settings
|
||||
port = "COM3"
|
||||
baud = 115200
|
||||
# connect to board
|
||||
board = bci.OpenBCIBoard(port=port, baud=baud, filter_data=False)
|
||||
board.start_streaming(process_sample)
|
||||
|
||||
|
||||
def process_sample(sample):
|
||||
""" format sample as json string and add it to queue """
|
||||
global last_packet_id
|
||||
global order_number
|
||||
if last_packet_id is not None:
|
||||
# it is assumed that no more than 255 packets are skipped
|
||||
difference = (sample.id - last_packet_id) % 256
|
||||
order_number = order_number + difference
|
||||
last_packet_id = sample.id
|
||||
dict_sample = {
|
||||
"channel_data": sample.channel_data,
|
||||
"order_number": order_number
|
||||
}
|
||||
json_sample = json.dumps(dict_sample)
|
||||
queue.put(json_sample)
|
||||
|
||||
|
||||
def wait_for_sample():
|
||||
""" defer queue reading """
|
||||
d = threads.deferToThread(read_queue)
|
||||
#reactor.callLater(0, read_queue, d.callback)
|
||||
timeout = reactor.callLater(5, d.cancel)
|
||||
def cancel_timeout(result):
|
||||
if timeout.active():
|
||||
timeout.cancel()
|
||||
return result
|
||||
d.addBoth(cancel_timeout)
|
||||
return d
|
||||
|
||||
|
||||
def read_queue():
|
||||
### get sample values stored in queue and give results to Deferred ###
|
||||
sample = queue.get()
|
||||
return sample
|
||||
|
||||
|
||||
class MyClientProtocol(WebSocketClientProtocol):
|
||||
|
||||
def onConnect(self, response):
|
||||
print("Server connected: {0}".format(response.peer))
|
||||
|
||||
@inlineCallbacks
|
||||
def onOpen(self):
|
||||
print("WebSocket connection open.")
|
||||
while True:
|
||||
try:
|
||||
sample = yield wait_for_sample()
|
||||
self.sendMessage(sample)
|
||||
except CancelledError:
|
||||
self.sendClose()
|
||||
|
||||
def onClose(self, wasClean, code, reason):
|
||||
print("WebSocket connection closed: {0}".format(reason))
|
||||
os._exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
log.startLogging(sys.stdout)
|
||||
|
||||
factory = WebSocketClientFactory("ws://127.0.0.1:1912/api/samples")
|
||||
factory.protocol = MyClientProtocol
|
||||
|
||||
reactor.connectTCP("127.0.0.1", 1912, factory)
|
||||
reactor.callInThread(read_samples)
|
||||
reactor.run()
|
||||
|
||||
"""
|
||||
import socket
|
||||
import open_bci_v3 as bci
|
||||
import json
|
||||
|
||||
sock = None
|
||||
|
||||
|
||||
# configure board and server connection
|
||||
def start_streaming():
|
||||
# USB dongle settings
|
||||
port = "COM3"
|
||||
baud = 115200
|
||||
# server target
|
||||
tcp_ip = "127.0.0.1"
|
||||
tcp_port = 1912
|
||||
# open connection to server
|
||||
global sock
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((tcp_ip, tcp_port))
|
||||
# connect to board
|
||||
board = bci.OpenBCIBoard(port=port, baud=baud, filter_data=False)
|
||||
board.start_streaming(process_sample)
|
||||
|
||||
|
||||
# format sample as json string
|
||||
def process_sample(sample):
|
||||
dict_sample = {
|
||||
"id": sample.id,
|
||||
"channel_data": sample.channel_data
|
||||
}
|
||||
|
||||
json_sample = json.dumps(dict_sample)
|
||||
send_sample(json_sample)
|
||||
|
||||
|
||||
# send sample data to server
|
||||
def send_sample(json_sample):
|
||||
|
||||
sock.send(json_sample)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_streaming()
|
||||
|
||||
"""
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 09 23:21:01 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
from .sample_receiver import SampleReceiver
|
||||
from .database import Database
|
||||
from .session_list_handler import SessionListHandler
|
||||
from .sample_list_handler import SampleListHandler
|
||||
from .course_list_handler import CourseListHandler
|
||||
from .instructor_list_handler import InstructorListHandler
|
||||
from .patient_list_handler import PatientListHandler
|
||||
from .patient_handler import PatientHandler
|
||||
from .instructor_handler import InstructorHandler
|
||||
from .course_handler import CourseHandler
|
||||
|
||||
|
||||
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sun Aug 07 01:16:27 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import os
|
||||
|
||||
import samplesserver
|
||||
|
||||
|
||||
def make_app():
|
||||
settings = {
|
||||
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
||||
"debug": True
|
||||
}
|
||||
|
||||
app = tornado.web.Application([
|
||||
(r"/api/samples", samplesserver.SampleReceiver),
|
||||
(r"/api/sessions", samplesserver.SessionListHandler),
|
||||
(r"/api/sessions/(\d+)/samples", samplesserver.SampleListHandler),
|
||||
(r"/api/courses", samplesserver.CourseListHandler),
|
||||
(r"/api/instructors", samplesserver.InstructorListHandler),
|
||||
(r"/api/patients", samplesserver.PatientListHandler),
|
||||
(r"/api/patients/(\d+)", samplesserver.PatientHandler),
|
||||
(r"/api/instructors/(\d+)", samplesserver.InstructorHandler),
|
||||
(r"/api/courses/(\d+)", samplesserver.CourseHandler),
|
||||
(
|
||||
r"/(.*)", tornado.web.StaticFileHandler,
|
||||
{
|
||||
"path": settings['static_path'],
|
||||
"default_filename": "index.html"
|
||||
}
|
||||
),
|
||||
], **settings)
|
||||
app.db = samplesserver.Database()
|
||||
return app
|
||||
|
||||
|
||||
app = make_app()
|
||||
app.listen(1912, "127.0.0.1")
|
||||
tornado.ioloop.IOLoop.current().start()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from autobahn.twisted.websocket import WebSocketServerProtocol, \
|
||||
WebSocketServerFactory
|
||||
|
||||
|
||||
class MyServerProtocol(WebSocketServerProtocol):
|
||||
|
||||
def onConnect(self, request):
|
||||
print("Client connecting: {0}".format(request.peer))
|
||||
|
||||
def onOpen(self):
|
||||
print("WebSocket connection open.")
|
||||
|
||||
def onMessage(self, payload, isBinary):
|
||||
if isBinary:
|
||||
print("Binary message received: {0} bytes".format(len(payload)))
|
||||
else:
|
||||
print("Text message received: {0}".format(payload.decode('utf8')))
|
||||
|
||||
def onClose(self, wasClean, code, reason):
|
||||
print("WebSocket connection closed: {0}".format(reason))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
import sys
|
||||
|
||||
from twisted.python import log
|
||||
from twisted.internet import reactor
|
||||
|
||||
log.startLogging(sys.stdout)
|
||||
|
||||
factory = WebSocketServerFactory("ws://127.0.0.1:1912")
|
||||
factory.protocol = MyServerProtocol
|
||||
|
||||
reactor.listenTCP(1912, factory)
|
||||
reactor.run()
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
import socket
|
||||
|
||||
|
||||
tcp_ip = "127.0.0.1"
|
||||
tcp_port = 1912
|
||||
buffer_size = 250
|
||||
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.bind((tcp_ip, tcp_port))
|
||||
sock.listen(1)
|
||||
|
||||
conn, addr = sock.accept()
|
||||
print "Connection address:", addr
|
||||
while 1:
|
||||
data = conn.recv(buffer_size)
|
||||
if not data: break
|
||||
print "received data:", data
|
||||
conn.send(data)
|
||||
conn.close()
|
||||
"""
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 16 20:37:29 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class CourseHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self, course_id):
|
||||
course = self.application.db.fetch_course(course_id)
|
||||
if course:
|
||||
self.write({
|
||||
"id": course[0],
|
||||
"name": course[2],
|
||||
"code": course[3],
|
||||
"credit_value": course[4]
|
||||
})
|
||||
|
||||
def post(self, course_id):
|
||||
instructor_name = self.get_body_argument("instructor_name")
|
||||
name = self.get_body_argument("name")
|
||||
code = self.get_body_argument("code")
|
||||
credit_value = self.get_body_argument("credit_value")
|
||||
self.application.db.edit_patient(course_id, instructor_name, name, code, credit_value)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Aug 15 16:14:11 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class CourseListHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
courses_result = self.application.db.fetch_courses()
|
||||
courses_list = []
|
||||
if courses_result:
|
||||
for course in courses_result:
|
||||
courses_list.append({
|
||||
"id": course[0],
|
||||
"instructor_id": course[1],
|
||||
"name": course[2],
|
||||
"code": course[3],
|
||||
"credit_value": course[4]
|
||||
})
|
||||
self.write({"courses": courses_list})
|
||||
|
||||
def post(self):
|
||||
instructor_id = self.get_body_argument("instructor_id")
|
||||
name = self.get_body_argument("name")
|
||||
code = self.get_body_argument("code")
|
||||
credit_value = self.get_body_argument("credits")
|
||||
self.application.db.add_course(instructor_id, name, code, credit_value)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,252 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 09 23:42:12 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import datetime
|
||||
|
||||
class Database(object):
|
||||
|
||||
def __init__(self):
|
||||
self.conn = sqlite3.connect("samples_server.db",
|
||||
detect_types=sqlite3.PARSE_DECLTYPES)
|
||||
self._create_tables()
|
||||
|
||||
def _create_tables(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
start_time DATETIME NOT NULL,
|
||||
end_time DATETIME,
|
||||
course_id INTEGER,
|
||||
instructor_id INTEGER,
|
||||
patient_id INTEGER,
|
||||
sample_rate REAL,
|
||||
resolution REAL,
|
||||
FOREIGN KEY(course_id) REFERENCES courses(id),
|
||||
FOREIGN KEY(instructor_id) REFERENCES instructors(id),
|
||||
FOREIGN KEY(patient_id) REFERENCES patients(id)
|
||||
)
|
||||
""")
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS samples (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
channel1 REAL NOT NULL,
|
||||
channel2 REAL NOT NULL,
|
||||
channel3 REAL NOT NULL,
|
||||
channel4 REAL NOT NULL,
|
||||
channel5 REAL NOT NULL,
|
||||
channel6 REAL NOT NULL,
|
||||
channel7 REAL NOT NULL,
|
||||
channel8 REAL NOT NULL,
|
||||
order_number INTEGER NOT NULL,
|
||||
session_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
||||
)
|
||||
""")
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS instructors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT
|
||||
)
|
||||
""")
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS courses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
instructor_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
code TEXT NOT NULL,
|
||||
credit_value INT,
|
||||
FOREIGN KEY(instructor_id) REFERENCES instructor(id)
|
||||
)
|
||||
""")
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS patients (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT
|
||||
)
|
||||
""")
|
||||
self.conn.commit()
|
||||
|
||||
|
||||
def create_session(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO sessions(start_time, sample_rate) VALUES (?,?)
|
||||
""", (datetime.datetime.now(),250))
|
||||
self.conn.commit()
|
||||
session_id = cursor.lastrowid
|
||||
return session_id
|
||||
|
||||
def create_sample(self, session_id, sample):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO samples(
|
||||
channel1,
|
||||
channel2,
|
||||
channel3,
|
||||
channel4,
|
||||
channel5,
|
||||
channel6,
|
||||
channel7,
|
||||
channel8,
|
||||
order_number,
|
||||
session_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
""", (
|
||||
sample["channel_data"][0],
|
||||
sample["channel_data"][1],
|
||||
sample["channel_data"][2],
|
||||
sample["channel_data"][3],
|
||||
sample["channel_data"][4],
|
||||
sample["channel_data"][5],
|
||||
sample["channel_data"][6],
|
||||
sample["channel_data"][7],
|
||||
sample["order_number"],
|
||||
session_id
|
||||
)
|
||||
)
|
||||
self.conn.commit()
|
||||
sample_id = cursor.lastrowid
|
||||
return sample_id
|
||||
|
||||
def finalise_session(self, session_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE sessions
|
||||
SET end_time = ?
|
||||
WHERE id = ?
|
||||
""", (datetime.datetime.now(), session_id))
|
||||
self.conn.commit()
|
||||
|
||||
def fetch_sessions(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM sessions
|
||||
""")
|
||||
sessions = cursor.fetchall()
|
||||
return sessions
|
||||
|
||||
def fetch_samples(self, session_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM samples
|
||||
WHERE session_id = ?
|
||||
""", session_id)
|
||||
samples = cursor.fetchall()
|
||||
return samples
|
||||
|
||||
def fetch_courses(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM courses
|
||||
""")
|
||||
courses = cursor.fetchall()
|
||||
return courses
|
||||
|
||||
def fetch_course(self, course_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM courses
|
||||
WHERE id = ?
|
||||
""", course_id)
|
||||
course = cursor.fetchone()
|
||||
return course
|
||||
|
||||
def add_course(self, instructor_id, name, code, credit_value):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO courses (instructor_id, name, code, credit_value)
|
||||
VALUES (?,?,?,?)
|
||||
""", (instructor_id, name, code, credit_value))
|
||||
self.conn.commit()
|
||||
|
||||
def edit_course(self, course_id, instructor_id, name, code, credit_value):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE courses
|
||||
SET instructor_id = ?, name = ?, code = ?, credit_value = ?
|
||||
WHERE id = ?
|
||||
""", (instructor_id, name, code, credit_value, course_id))
|
||||
self.conn.commit()
|
||||
|
||||
def fetch_instructors(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM instructors
|
||||
""")
|
||||
instructors = cursor.fetchall()
|
||||
return instructors
|
||||
|
||||
def add_instructor(self, name, email):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO instructors (name, email)
|
||||
VALUES (?,?)
|
||||
""", (name, email))
|
||||
self.conn.commit()
|
||||
|
||||
def fetch_instructor(self, instructor_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM instructors
|
||||
WHERE id = ?
|
||||
""", instructor_id)
|
||||
instructor = cursor.fetchone()
|
||||
return instructor
|
||||
|
||||
def edit_instructor(self, instructor_id, name, email):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE instructors
|
||||
SET name = ?, email = ?
|
||||
WHERE id = ?
|
||||
""", (name, email, instructor_id))
|
||||
self.conn.commit()
|
||||
|
||||
def add_patient(self, code):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO patients (code)
|
||||
VALUES (?)
|
||||
""", (code,))
|
||||
self.conn.commit()
|
||||
|
||||
def edit_patient(self, code, patient_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE patients
|
||||
SET code = ?
|
||||
WHERE id = ?
|
||||
""", (code, patient_id))
|
||||
self.conn.commit()
|
||||
|
||||
def fetch_patients(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM patients
|
||||
""")
|
||||
patients = cursor.fetchall()
|
||||
return patients
|
||||
|
||||
def fetch_patient(self, patient_id):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT *
|
||||
FROM patients
|
||||
WHERE id = ?
|
||||
""", patient_id)
|
||||
patient = cursor.fetchone()
|
||||
return patient
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 16 20:22:16 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class InstructorHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self, instructor_id):
|
||||
instructor = self.application.db.fetch_instructor(instructor_id)
|
||||
if instructor:
|
||||
self.write({
|
||||
"id": instructor[0],
|
||||
"name": instructor[1],
|
||||
"email": instructor[2]
|
||||
})
|
||||
|
||||
def post(self, instructor_id):
|
||||
name = self.get_body_argument("name")
|
||||
email = self.get_body_argument("email")
|
||||
self.application.db.edit_instructor(instructor_id, name, email)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Aug 15 16:26:31 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class InstructorListHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
instructors_result = self.application.db.fetch_instructors()
|
||||
instructors_list = []
|
||||
if instructors_result:
|
||||
for instructor in instructors_result:
|
||||
instructors_list.append({
|
||||
"id": instructor[0],
|
||||
"name": instructor[1],
|
||||
"email": instructor[2]
|
||||
})
|
||||
self.write({"instructors": instructors_list})
|
||||
|
||||
def post(self):
|
||||
name = self.get_body_argument("name")
|
||||
email = self.get_body_argument("email")
|
||||
self.application.db.add_instructor(name, email)
|
||||
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 16 19:09:45 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class PatientHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self, patient_id):
|
||||
patient = self.application.db.fetch_patient(patient_id)
|
||||
if patient:
|
||||
self.write({"id": patient[0], "code": patient[1]})
|
||||
|
||||
def post(self, patient_id):
|
||||
code = self.get_body_argument("code")
|
||||
self.application.db.edit_patient(code, patient_id)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Aug 15 16:37:22 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class PatientListHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
patients_result = self.application.db.fetch_patients()
|
||||
patients_list = []
|
||||
if patients_result:
|
||||
for patient in patients_result:
|
||||
patients_list.append({
|
||||
"id": patient[0],
|
||||
"code": patient[1]
|
||||
})
|
||||
self.write({"patients": patients_list})
|
||||
|
||||
def post(self):
|
||||
code = self.get_body_argument("code")
|
||||
self.application.db.add_patient(code)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Aug 12 14:44:21 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class SampleListHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self, session_id):
|
||||
samples_result = self.application.db.fetch_samples(session_id)
|
||||
samples_list = []
|
||||
if samples_result:
|
||||
for sample in samples_result:
|
||||
samples_list.append({
|
||||
"id": sample[0],
|
||||
"order_number": sample[9],
|
||||
"session_id": sample[10],
|
||||
"channel_data": sample[1:9]
|
||||
})
|
||||
self.write({"samples": samples_list})
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 09 23:30:24 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.websocket
|
||||
import tornado.escape
|
||||
|
||||
|
||||
class SampleReceiver(tornado.websocket.WebSocketHandler):
|
||||
|
||||
def open(self):
|
||||
session_id = self.application.db.create_session()
|
||||
self.session_id = session_id
|
||||
|
||||
def on_message(self, json_sample):
|
||||
dict_sample = tornado.escape.json_decode(json_sample)
|
||||
self.application.db.create_sample(self.session_id, dict_sample)
|
||||
|
||||
def on_close(self):
|
||||
self.application.db.finalise_session(self.session_id)
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu Aug 11 23:37:11 2016
|
||||
|
||||
@author: Badger
|
||||
"""
|
||||
|
||||
import tornado.web
|
||||
|
||||
|
||||
class SessionListHandler(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
sessions_result = self.application.db.fetch_sessions()
|
||||
sessions_list = []
|
||||
if sessions_result:
|
||||
for session in sessions_result:
|
||||
sessions_list.append({
|
||||
"id": session[0],
|
||||
"start_time": session[1],
|
||||
"end_time": session[2],
|
||||
"course_id":session[3],
|
||||
"instructor_id":session[4],
|
||||
"patient_id":session[5],
|
||||
"sample_rate":session[6],
|
||||
"resolution":session[7]
|
||||
})
|
||||
self.write({"sessions": sessions_list})
|
||||
Arquivo binário não exibido.
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>Wireless Biopotential Recorder</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-container" class="container">
|
||||
<div class="page-header">
|
||||
<h1>Wireless Biopotential Recorder</h1>
|
||||
</div>
|
||||
<ul class="nav nav-pills">
|
||||
|
||||
</ul>
|
||||
<div id="page-specific">
|
||||
</div>
|
||||
</div>
|
||||
<script id="course-form-template" type="text/x-handlebars-template">
|
||||
<form id="course-form">
|
||||
<div class="form-group">
|
||||
<label for="courseName">Course name</label>
|
||||
<input type="text" class="form-control" id="courseName" placeholder="Course name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="courseCode">Course code</label>
|
||||
<input type="text" class="form-control" id="courseCode" placeholder="Course code">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="courseInstructor">Course instructor</label>
|
||||
<select class="form-control" id="courseInstructor">
|
||||
{{#each instructors}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="courseCredits">Course credits</label>
|
||||
<input type="number" min="0" class="form-control" id="courseCredits" placeholder="Credits">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Submit</button>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<script id="instructor-form-template" type="text/x-handlebars-template">
|
||||
<form id="instructor-form">
|
||||
<div class="form-group">
|
||||
<label for="instructorName">Instructor name</label>
|
||||
<input type="text" class="form-control" id="instructorName" placeholder="Instructor name" value="{{name}}" default="{{name}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="instructorEmail">Instructor email</label>
|
||||
<input type="text" class="form-control" id="instructorEmail" placeholder="Email" value="{{email}}" default ="{{email}}">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-default" id="instructor-submit">Submit</button>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<script id="patient-form-template" type="text/x-handlebars-template">
|
||||
<form id="patient-form">
|
||||
<div class="form-group">
|
||||
<label for="patientCode">Patient code</label>
|
||||
<input type="text" class="form-control" id="patientCode" placeholder="Patient code" value="{{code}}" default="{{code}}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default" id="patient-submit">Submit</button>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.rawgit.com/visionmedia/page.js/master/page.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.1/Chart.bundle.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
|
||||
<script src="/static/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,446 @@
|
||||
function showNavLinks() {
|
||||
$(".nav").empty();
|
||||
$(".nav").append($("<li><button class='btn'>Sessions</button></li>")
|
||||
.attr("role", "presentation")
|
||||
.attr("id", "sess-link"));
|
||||
$(".nav").append($("<li><button class='btn'>Courses</button></li>")
|
||||
.attr("role", "presentation")
|
||||
.attr("id", "crs-link"));
|
||||
$(".nav").append($("<li><button class='btn'>Patients</button></li>")
|
||||
.attr("role", "presentation")
|
||||
.attr("id", "pat-link"));
|
||||
$(".nav").append($("<li><button class='btn'>Instructors</button></li>")
|
||||
.attr("role", "presentation")
|
||||
.attr("id", "instr-link"));
|
||||
$("#sess-link").click(function() {
|
||||
page("/");
|
||||
});
|
||||
$("#crs-link").click(function() {
|
||||
page('/courses');
|
||||
});
|
||||
$("#instr-link").click(function() {
|
||||
page("/instructors");
|
||||
});
|
||||
$("#pat-link").click(function() {
|
||||
page("/patients");
|
||||
});
|
||||
}
|
||||
|
||||
function showPatients() {
|
||||
$.get("/api/patients")
|
||||
.done(function(data) {
|
||||
populatePatientsTable(data.patients)
|
||||
});
|
||||
}
|
||||
|
||||
function populatePatientsTable(patients) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Patients</h2>");
|
||||
$("#page-specific")
|
||||
.append($(" <button>Add patient</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("type", "button")
|
||||
.attr("id", "add-patient"));
|
||||
$("#add-patient").click(function() {
|
||||
addPatientForm();
|
||||
});
|
||||
if (patients[0]) {
|
||||
var table = $("<table>")
|
||||
.addClass("table table-bordered").attr("id", "patients-table");
|
||||
var thead = $("<thead>");
|
||||
var tr = $("<tr>");
|
||||
tr.append($("<th>Patient ID</th>"));
|
||||
tr.append($("<th>Patient code</th>"));
|
||||
tr.append($("<th>Options</th>"));
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
table.append($("<tbody>"));
|
||||
$("#page-specific").append(table);
|
||||
$.each(patients, function(index, patient) {
|
||||
var row = $("<tr>");
|
||||
row.append($("<td>").text(patient.id));
|
||||
row.append($("<td>").text(patient.code));
|
||||
var editButton = $("<button>Edit</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("id","edit" + patient.id);
|
||||
row.append(editButton);
|
||||
editButton.click(function() {
|
||||
editPatientForm(patient.id);
|
||||
});
|
||||
$("#patients-table tbody").append(row);
|
||||
});
|
||||
} else {
|
||||
$("#page-specific").append("<h3>No patients available</h3>");
|
||||
}
|
||||
}
|
||||
|
||||
function addPatientForm() {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Add patient</h2>");
|
||||
var source = $("#patient-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
$("#page-specific").append(template());
|
||||
$("#patient-form").submit(function() {
|
||||
$("form button").prop("disabled", true);
|
||||
var code = $("#patientCode").val();
|
||||
$.post("/api/patients", {code: code});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function editPatientForm(id) {
|
||||
$.get("/api/patients/" + id)
|
||||
.done(function(data) {
|
||||
patient = data;
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Edit patient</h2>");
|
||||
var source = $("#patient-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
$("#page-specific").append(template(patient));
|
||||
$("#patient-form").submit(function() {
|
||||
$("form button").prop("disabled", true);
|
||||
var code = $("#patientCode").val();
|
||||
$.post("/api/patients/" + id, {code: code});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showInstructors() {
|
||||
$.get("/api/instructors")
|
||||
.done(function(data) {
|
||||
populateInstructorsTable(data.instructors);
|
||||
});
|
||||
}
|
||||
|
||||
function populateInstructorsTable(instructors) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Instructors</h2>");
|
||||
$("#page-specific")
|
||||
.append($(" <button>Add instructor</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("type", "button")
|
||||
.attr("id", "add-instructor"));
|
||||
$("#add-instructor").click(function() {
|
||||
addInstructorForm();
|
||||
});
|
||||
if (instructors[0]) {
|
||||
var table = $("<table>")
|
||||
.addClass("table table-bordered").attr("id", "instructors-table");
|
||||
var thead = $("<thead>");
|
||||
var tr = $("<tr>");
|
||||
tr.append($("<th>Instructor ID</th>"));
|
||||
tr.append($("<th>Name</th>"));
|
||||
tr.append($("<th>Email</th>"));
|
||||
tr.append($("<th>Options</th>"));
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
table.append($("<tbody>"));
|
||||
$("#page-specific").append(table);
|
||||
$.each(instructors, function(index, instructor) {
|
||||
var row = $("<tr>");
|
||||
row.append($("<td>").text(instructor.id));
|
||||
row.append($("<td>").text(instructor.name));
|
||||
row.append($("<td>").text(instructor.email));
|
||||
var editButton = $("<button>Edit</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("id","edit" + instructor.id);
|
||||
row.append(editButton);
|
||||
editButton.click(function() {
|
||||
editInstructorForm(instructor.id);
|
||||
});
|
||||
$("#instructors-table tbody").append(row);
|
||||
});
|
||||
} else {
|
||||
$("#page-specific").append("<h3>No instructors available</h3>");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function addInstructorForm() {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Add instructor</h2>");
|
||||
var source = $("#instructor-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
$("#page-specific").append(template());
|
||||
$("#instructor-form").submit(function() {
|
||||
$("form button").prop("disabled", true);
|
||||
var name = $("#instructorName").val();
|
||||
var email = $("#instructorEmail").val();
|
||||
$.post("/api/instructors", {name: name, email: email});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function editInstructorForm(id) {
|
||||
$.get("/api/instructors/" + id)
|
||||
.done(function(data) {
|
||||
instructor = data;
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Edit instructor</h2>");
|
||||
var source = $("#instructor-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
$("#page-specific").append(template(instructor));
|
||||
$("#instructor-form").submit(function() {
|
||||
$("form button").prop("disabled", true);
|
||||
var name = $("#instructorName").val();
|
||||
var email = $("#instructorEmail").val();
|
||||
$.post("/api/instructors/" + id, {name: name, email: email});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showCourses() {
|
||||
$.get("/api/courses")
|
||||
.done(function(data) {
|
||||
populateCoursesTable(data.courses);
|
||||
});
|
||||
}
|
||||
|
||||
function populateCoursesTable(courses) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Courses</h2>");
|
||||
$("#page-specific")
|
||||
.append($(" <button>Add course</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("type", "button")
|
||||
.attr("id", "add-course"));
|
||||
$("#add-course").click(function() {
|
||||
addCourseForm();
|
||||
});
|
||||
if (courses[0]) {
|
||||
var table = $("<table>")
|
||||
.addClass("table table-bordered").attr("id", "courses-table");
|
||||
var thead = $("<thead>");
|
||||
var tr = $("<tr>");
|
||||
tr.append($("<th>Course ID</th>"));
|
||||
tr.append($("<th>Instructor ID</th>"));
|
||||
tr.append($("<th>Name</th>"));
|
||||
tr.append($("<th>Code</th>"));
|
||||
tr.append($("<th>Credit Value</th>"));
|
||||
tr.append($("<th>Options</th>"));
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
table.append($("<tbody>"));
|
||||
$("#page-specific").append(table);
|
||||
$.each(courses, function(index, course) {
|
||||
var row = $("<tr>");
|
||||
row.append($("<td>").text(course.id));
|
||||
row.append($("<td>").text(course.instructor_id));
|
||||
row.append($("<td>").text(course.name));
|
||||
row.append($("<td>").text(course.code));
|
||||
row.append($("<td>").text(course.credit_value));
|
||||
var editButton = $("<button>Edit</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("id","edit" + course.id);
|
||||
row.append(editButton);
|
||||
editButton.click(function() {
|
||||
editCourseForm(course.id);
|
||||
});
|
||||
$("#courses-table tbody").append(row);
|
||||
});
|
||||
} else {
|
||||
$("#page-specific").append("<h3>No courses available</h3>");
|
||||
}
|
||||
}
|
||||
|
||||
function addCourseForm() {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Add course</h2>");
|
||||
$.get("/api/instructors")
|
||||
.done(function(data) {
|
||||
var source = $("#course-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
$("#page-specific").append(template(data));
|
||||
$("#course-form").submit(function() {
|
||||
$("form button").prop("disabled", true);
|
||||
var name = $("#courseName").val();
|
||||
var instructor_id = $("#courseInstructor").val();
|
||||
var code = $("#courseCode").val();
|
||||
var credits = $("#courseCredits").val();
|
||||
$.post("/api/courses", {
|
||||
name: name,
|
||||
instructor_id: instructor_id,
|
||||
code: code,
|
||||
credits: credits
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editCourseForm(id) {
|
||||
$.get("/api/courses/" + id)
|
||||
.done(function(course) {
|
||||
$.get("/api/instructors")
|
||||
.done(function(instructors) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Edit course</h2>");
|
||||
var source = $("#course-form-template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
data = {course: course, instructors: instructors};
|
||||
$("#page-specific").append(template(data));
|
||||
$("#course-form").submit(function() {
|
||||
var name = $("#courseName").val();
|
||||
var instructor_id = $("#courseInstructor").val();
|
||||
var code = $("#courseCode").val();
|
||||
var credits = $("#courseCredits").val();
|
||||
$.post("/api/courses", {
|
||||
name: name,
|
||||
instructor_id: instructor_id,
|
||||
code: code,
|
||||
credits: credits
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showSessions() {
|
||||
$.get("/api/sessions")
|
||||
.done(function(data) {
|
||||
populateSessionsTable(data.sessions);
|
||||
});
|
||||
}
|
||||
|
||||
function populateSessionsTable(sessions) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Recorded Sessions</h2>");
|
||||
var table = $("<table>")
|
||||
.addClass("table table-bordered").attr("id", "sessions-table");
|
||||
var thead = $("<thead>");
|
||||
var tr = $("<tr>");
|
||||
tr.append($("<th>Session ID</th>"));
|
||||
tr.append($("<th>Start time</th>"));
|
||||
tr.append($("<th>End time</th>"));
|
||||
tr.append($("<th>Course ID</th>"));
|
||||
tr.append($("<th>Patient ID</th>"));
|
||||
tr.append($("<th>Sample rate</th>"));
|
||||
tr.append($("<th>Measurement resolution</th>"));
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
table.append($("<tbody>"));
|
||||
$("#page-specific").append(table);
|
||||
$.each(sessions, function(index, session) {
|
||||
var row = $("<tr>");
|
||||
row.append($("<td>").text(session.id));
|
||||
row.append($("<td>").text(session.start_time));
|
||||
row.append($("<td>").text(session.end_time));
|
||||
row.append($("<td>").text(session.course_id));
|
||||
row.append($("<td>").text(session.patient_id));
|
||||
row.append($("<td>").text(session.sample_rate + " Hz"));
|
||||
row.append($("<td>").text(session.resolution));
|
||||
row.click(function() {
|
||||
page("/sessions/" + session.id);
|
||||
});
|
||||
$("#sessions-table tbody").append(row);
|
||||
});
|
||||
}
|
||||
|
||||
function showSamples(ctx) {
|
||||
var id = ctx.params.id;
|
||||
$.get("/api/sessions/" + id + "/samples")
|
||||
.done(function(data) {
|
||||
populateSamplesTable(data.samples);
|
||||
});
|
||||
}
|
||||
|
||||
function populateSamplesTable(samples) {
|
||||
$("#page-specific").empty();
|
||||
showNavLinks();
|
||||
$("#page-specific").append("<h2>Recorded Samples</h2>");
|
||||
$("#page-specific")
|
||||
.append($(" <button>Back to Sessions list</button>")
|
||||
.addClass("btn btn-default")
|
||||
.attr("type", "button")
|
||||
.attr("id", "back"));
|
||||
$("#back").click(function() {
|
||||
page("/");
|
||||
});
|
||||
createSampleChart(samples, 0);
|
||||
createSampleChart(samples, 1);
|
||||
createSampleChart(samples, 2);
|
||||
createSampleChart(samples, 3);
|
||||
createSampleChart(samples, 4);
|
||||
createSampleChart(samples, 5);
|
||||
createSampleChart(samples, 6);
|
||||
createSampleChart(samples, 7);
|
||||
}
|
||||
|
||||
function createSampleChart(samples, channel) {
|
||||
$("#page-specific").append($("<canvas>")
|
||||
.attr("id","sample-chart" + channel));
|
||||
var canvas = $("#sample-chart" + channel);
|
||||
var sampleArray = []
|
||||
$.each(samples, function(index, sample) {
|
||||
sampleTime = sample.order_number * 0.004;
|
||||
sampleVoltage = sample.channel_data[channel];
|
||||
sampleArray[index] = {
|
||||
x: sampleTime,
|
||||
y: sampleVoltage
|
||||
};
|
||||
});
|
||||
var chart = new Chart(canvas, {
|
||||
type: "line",
|
||||
data: {
|
||||
datasets:[{
|
||||
fill: false,
|
||||
data: sampleArray,
|
||||
radius: 0,
|
||||
tension: 0,
|
||||
borderColor: "rgba(0,0,0,1)",
|
||||
borderWidth: 1,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: "Channel " + (channel + 1)
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'linear',
|
||||
position: 'bottom',
|
||||
scaleLabel: {
|
||||
display:true,
|
||||
labelString: "Time (seconds)"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
type: "linear",
|
||||
scaleLabel: {
|
||||
display:true,
|
||||
labelString: "Voltage (microvolts)"
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
page('/', showSessions);
|
||||
page('/sessions/:id', showSamples);
|
||||
page('/courses', showCourses);
|
||||
page('/instructors', showInstructors);
|
||||
page('/patients', showPatients);
|
||||
|
||||
page({"hashbang": true});
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário