Skip to content

Commit

Permalink
Merge pull request #130 from aj-ptw/add-exp-python
Browse files Browse the repository at this point in the history
Add exp python
  • Loading branch information
AJ Keller authored Dec 2, 2016
2 parents c2c75fe + 4ce630d commit f96cdd9
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 1 deletion.
117 changes: 117 additions & 0 deletions examples/python/handoff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import json
import sys
import numpy as np
import time
import zmq


class Interface:
def __init__(self, verbose=False):
context = zmq.Context()
self._socket = context.socket(zmq.PAIR)
self._socket.connect("tcp://localhost:3004")

self.verbose = verbose

if self.verbose:
print "Client Ready!"

# Send a quick message to tell node process we are up and running
self.send(json.dumps({
'action': 'started',
'command': 'status',
'message': time.time()*1000.0
}))

def send(self, msg):
"""
Sends a message to TCP server
:param msg: str
A string to send to node TCP server, could be a JSON dumps...
:return: None
"""
if self.verbose:
print '<- out ' + msg
self._socket.send(msg)
return

def recv(self):
"""
Checks the ZeroMQ for data
:return: str
String of data
"""
return self._socket.recv()


class RingBuffer(np.ndarray):
"""A multidimensional ring buffer."""

def __new__(cls, input_array):
obj = np.asarray(input_array).view(cls)
return obj

def __array_finalize__(self, obj):
if obj is None:
return

def __array_wrap__(self, out_arr, context=None):
return np.ndarray.__array_wrap__(self, out_arr, context)

def append(self, x):
"""Adds element x to the ring buffer."""
x = np.asarray(x)
self[:, :-1] = self[:, 1:]
self[:, -1] = x


def main(argv):
nb_chan = 8
verbose = True

# Create a new python interface.
interface = Interface(verbose=verbose)
# Signal buffer
signal = RingBuffer(np.zeros((nb_chan + 1, 2500)))

while True:
msg = interface.recv()
try:
dicty = json.loads(msg)
action = dicty.get('action')
command = dicty.get('command')
message = dicty.get('message')

if command == 'sample':
if action == 'process':
# Do sample processing here
try:
if type(message) is not dict:
print "sample is not a dict", message
raise ValueError
# Get keys of sample
data = np.zeros(9)

data[:-1] = message.get('channelData')
data[-1] = message.get('timeStamp')

# Add data to end of ring buffer
signal.append(data)

print message.get('sampleNumber')
except ValueError as e:
print e
elif command == 'status':
if action == 'active':
interface.send(json.dumps({
'action': 'alive',
'command': 'status',
'message': time.time() * 1000.0
}))

except BaseException as e:
print e


if __name__ == '__main__':
main(sys.argv[1:])
212 changes: 212 additions & 0 deletions examples/python/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/**
* This is an example from the readme.md
* On windows you should run with PowerShell not git bash.
* Install
* [nodejs](https://nodejs.org/en/)
*
* To run:
* change directory to this file `cd examples/debug`
* do `npm install`
* then `npm start`
*/
var OpenBCIBoard = require('openbci').OpenBCIBoard;
var port_pub = 'tcp://127.0.0.1:3004';
var zmq = require('zmq-prebuilt');
var socket = zmq.socket('pair');
var simulate = true; // Sends synthetic data
var debug = false; // Pretty print any bytes in and out... it's amazing...
var verbose = true; // Adds verbosity to functions

var ourBoard = new OpenBCIBoard({
simulate: simulate,
simulatorFirmwareVersion: 'v2',
debug: debug,
verbose: verbose
});

var sampleRate = 250; // Default to 250, ALWAYS verify with a call to `.sampleRate()` after 'ready' event!
var timeSyncPossible = false;
var resyncPeriodMin = 1;
var secondsInMinute = 60;
var resyncPeriod = ourBoard.sampleRate() * resyncPeriodMin * secondsInMinute;

ourBoard.autoFindOpenBCIBoard().then(portName => {
if (portName) {
/**
* Connect to the board with portName
* i.e. ourBoard.connect(portName).....
*/
// Call to connect
ourBoard.connect(portName)
.then(() => {
ourBoard.on('ready', () => {

// Get the sample rate after 'ready'
sampleRate = ourBoard.sampleRate();
// Find out if you can even time sync, you must be using v2 and this is only accurate after a `.softReset()` call which is called internally on `.connect()`. We parse the `.softReset()` response for the presence of firmware version 2 properties.
timeSyncPossible = ourBoard.usingVersionTwoFirmware();

if (timeSyncPossible) {
ourBoard.streamStart()
.catch(err => {
console.log(`stream start: ${err}`);
});
} else {
console.log('not able to time sync');
}
})
})
.catch(err => {
console.log(`connect: ${err}`);
});
} else {
/** Unable to auto find OpenBCI board */
console.log('Unable to auto find OpenBCI board');
}
});

var sampleFunc = sample => {
if (sample._count % resyncPeriod === 0) {
ourBoard.syncClocksFull()
.then(syncObj => {
// Sync was successful
if (syncObj.valid) {
// Log the object to check it out!
console.log(`timeOffset`, syncObj.timeOffsetMaster);
} else {
// Retry it
console.log(`Was not able to sync... retry!`);
}
});
}

if (sample.timeStamp) { // true after the first successful sync
if (sample.timeStamp < 10 * 60 * 60 * 1000) { // Less than 10 hours
console.log(`Bad time sync ${sample.timeStamp}`);
} else {
sendToPython({
action: 'process',
command: 'sample',
message: sample
});
}
}
};

// Subscribe to your functions
ourBoard.on('sample', sampleFunc);

// ZMQ fun

socket.bind(port_pub, function (err) {
if (err) throw err;
console.log(`bound to ${port_pub}`);
});

/**
* Used to send a message to the Python process.
* @param {Object} interProcessObject The standard inter-process object.
* @return {None}
*/
var sendToPython = (interProcessObject, verbose) => {
if (verbose) {
console.log(`<- out ${JSON.stringify(interProcessObject)}`);
}
if (socket) {
socket.send(JSON.stringify(interProcessObject));
}
};

var receiveFromPython = (raw_data) => {
try {
let body = JSON.parse(raw_data); // five because `resp `
processInterfaceObject(body);
} catch (err) {
console.log('in -> ' + 'bad json');
}
};

socket.on('message', receiveFromPython);

var sendStatus = () => {
sendToPython({'action': 'active', 'message': 'ready', 'command': 'status'}, true);
};

sendStatus();

/**
* Process an incoming message
* @param {String} body A stringify JSON object that shall be parsed.
* @return {None}
*/
var processInterfaceObject = (body) => {
switch (body.command) {
case 'status':
processStatus(body);
break;
default:
unrecognizedCommand(body);
break;
}
};

/**
* Used to process a status related command from TCP IPC.
* @param {Object} body
* @return {None}
*/
var processStatus = (body) => {
switch (body.action) {
case 'started':
console.log(`python started @ ${body.message}ms`);
break;
case 'alive':
console.log(`python duplex communication test completed @ ${body.message}ms`);
break;
default:
unrecognizedCommand(body);
break;
}
};

function unrecognizedCommand (body) {
console.log(`unrecognizedCommand ${body}`);
}

function exitHandler (options, err) {
if (options.cleanup) {
if (verbose) console.log('clean');
/** Do additional clean up here */
}
if (err) console.log(err.stack);
if (options.exit) {
if (verbose) console.log('exit');
ourBoard.disconnect().catch(console.log);
}
}

if (process.platform === "win32") {
const rl = require("readline").createInterface({
input: process.stdin,
output: process.stdout
});

rl.on("SIGINT", function () {
process.emit("SIGINT");
});
}

// do something when app is closing
process.on('exit', exitHandler.bind(null, {
cleanup: true
}));

// catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {
exit: true
}));

// catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {
exit: true
}));
30 changes: 30 additions & 0 deletions examples/python/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "python",
"version": "1.0.0",
"description": "node to python example",
"main": "index.js",
"scripts": {
"start": "concurrently --kill-others \"python handoff.py\" \"node index.js\"",
"start-node": "node index.js",
"start-verbose": "concurrently --kill-others \"python handoff.py -v\" \"node index.js\"",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"python",
"openbci",
"node"
],
"author": "AJ Keller",
"license": "MIT",
"dependencies": {
"openbci": "^1.4.2",
"zmq-prebuilt": "^2.1.0"
},
"devEngines": {
"node": "<=6.x",
"npm": ">=3.x"
},
"devDependencies": {
"concurrently": "^3.1.0"
}
}
Loading

0 comments on commit f96cdd9

Please sign in to comment.