Esse commit está contido em:
squonkbadger
2016-08-16 21:14:57 +03:00
commit 6f53a6fb7a
30 arquivos alterados com 1881 adições e 0 exclusões
Arquivo binário não exibido.
+579
Ver Arquivo
@@ -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.
+148
Ver Arquivo
@@ -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.
+20
Ver Arquivo
@@ -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.
+110
Ver Arquivo
@@ -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.
+252
Ver Arquivo
@@ -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>
+446
Ver Arquivo
@@ -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});