Commit 781518c5 authored by Bruno Laurencich's avatar Bruno Laurencich

add license

parent 52d438a1
[submodule "remote_console/simple-websocket-server"]
path = remote_console/simple-websocket-server
url = https://github.com/dpallot/simple-websocket-server.git
[submodule "remote_console/pexpect"]
path = remote_console/pexpect
url = https://github.com/pexpect/pexpect.git
<!-- Chordata.xml -->
<Chordata version="1.0">
<Chordata version="0.1.0">
<Configuration>
<Communication>
<Adapter>
......
<xs:schema
<!--
* Chordata.xsd
* Schema for the configuration, and armature description file (Chordata.xml) of the notochord program.
*
* http://chordata.cc
* contact@chordata.cc
*
*
* Copyright 2018 Bruno Laurencich
*
* This file is part of Notochord.
*
* Notochord is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Notochord is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Notochord. If not, see <https://www.gnu.org/licenses/>.
*
* This program uses code from various sources, the default license is GNU GPLv3
* for all code, the dependencies where originally distributed as follows:
* -LSM9DS# libraries: Copyright 2014,2015 Jim Lindblom @ SparkFun Electronics, as beerware.
* -fmt library: Copyright (c) 2012 - present, Victor Zverovich, Under BSD 2-clause License.
* -spdlog library: Copyright(c) 2015 Gabi Melman, under MIT license.
* -args library: Copyright (c) 2016 Taylor C. Richberger, under MIT license.
* -bcm2835 library: Copyright 2011-2013 Mike McCaule, Under GNU-GPLv2 License.
* -oscpack library: Copyright (c) 2004-2013 Ross Bencina, under MIT license.
* -Sensor fusion algorithms: Copyright 2011 SOH Madgwick, Under GNU-GPLv3 License.
* -tinyxml2 library: Copyright <unknowk year> Lee Thomason, under zlib License.
* -pstreams library: Copyright (C) 2001 - 2017 Jonathan Wakely, under Boost Software License
* -catch library: Copyright (c) 2012 Two Blue Cubes Ltd, under the Boost Software License, Version 1.0
* -trompeloeil mocking framework: Copyright Björn Fahller 2014-2017 under the Boost Software License, Version 1.0
* -->
<xs:schema
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
......
This diff is collapsed.
......@@ -3,34 +3,55 @@ _Hub software of the Chordata motion capture system_
### Hardware
Tested under a Raspberry Pi3 with Raspbian 8 "Jessie" Lite
Tested under a Raspberry Pi3 with Raspbian "Stretch" Lite
with one Chordata IMU-Proto V1.1 as a multiplexer
with one Chordata Hub R1 as a multiplexer
and three Chordata IMU-Proto V1.1 as sensors
and three Chordata K-Ceptor R2 as sensors
### WIRING
[RPI 1 (3.3v)] -> (All the IMU-PROTO's 3.3v)
[RPI 6 (GND)] -> (All the IMU-PROTO's GND)
[RPI 3 (SDA)] -> [MUX SDA-IN]
[RPI 5 (SCL)] -> [MUX SCL-IN]
[MUX SDA-OUT] -> [Mano SDA-IN]
[MUX SCL-OUT] -> [Mano SCL-IN]
[MUX SD2] -> [Antebrazo SDA-IN]
[MUX SC2] -> [Antebrazo SCL-IN]
[MUX SD3] -> [Brazo SDA-IN]
[MUX SC3] -> [Brazo SCL-IN]
_(The program will read the `Chordata.xml` and replicate this structure)_
### Usage
- [RPI 1 (3.3v)] -> [Chordata Hub 1 (3.3v)]
- [RPI 6 (GND)] -> [Chordata Hub 2 (GND)]
- [RPI 3 (SDA)] -> [Chordata Hub 4 (SDA)]
- [RPI 5 (SCL)] -> [Chordata Hub 5 (SCL)]
- [RPI 7 (GPIO)] -> [Chordata Hub 3 (ENABLE)]
_The following connections are made with a 6 cores cable, ~26 AWG, with crimped RJ12 connectors._
- [MUX JP1] -> [First KCeptor IN] <ID_module #1>
- [First KCeptor OUT] -> [Second KCeptor IN] <ID_module #4>
- [Second KCeptor OUT]-> [Third KCeptor IN] <ID_module #6>
For this to work the `Chordata.xml` file has to have the following structure):
```xml
<Chordata version="0.1.0">
<Configuration>
(... config nodes ...)
</Configuration>
<Armature>
<Mux Name="main_mux" id="0">
0x73
<Branch Name="main_branch" id="1">
CH_1
<K_Ceptor Name="first" id="2">
1
<K_Ceptor Name="second" id="3">
4
<K_Ceptor Name="third" id="4">
6
</K_Ceptor>
</K_Ceptor>
</K_Ceptor>
</Branch>
</Mux>
</Armature>
</Chordata>
```
Note that any other arbitrary hierarchy can be used, it only has to reflect what is connected in reality.
### INSTALLATION
**First of all you have to activate the i2c adaptor on your rPi**
......@@ -103,4 +124,12 @@ cd bin
**TODO document remote console usage!!**
The proccesing of the raw data is performed by a matlab script created by _Ozan AKTAÞ_: `calib/getMagCalib.m`
\ No newline at end of file
The proccesing of the raw data is performed by a matlab script created by _Ozan AKTAÞ_: `calib/getMagCalib.m`
### LICENSE
This program is free software and is distributed under the GNU General Public License, version 3.
You are free to run, study, share and modify this software.
Derivative work can only be distributed under the same license terms, and replicating this program copyright notice.
More info about the terms of the license [here](https://www.gnu.org/licenses/gpl-faq.en.html).
\ No newline at end of file
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
[packages]
SimpleWebSocketServer = {git = "https://github.com/dpallot/simple-websocket-server.git"}
pexpect = "*"
[requires]
python_version = "2.7"
{
"_meta": {
"hash": {
"sha256": "a37a7d1d2b1b0d153ea9792a3f2fc5e42b25d806c123ebed620177f384e1b622"
},
"pipfile-spec": 6,
"requires": {
"python_version": "2.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"pexpect": {
"hashes": [
"sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
"sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
],
"index": "pypi",
"version": "==4.6.0"
},
"ptyprocess": {
"hashes": [
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
],
"version": "==0.6.0"
},
"simplewebsocketserver": {
"git": "https://github.com/dpallot/simple-websocket-server.git"
}
},
"develop": {}
}
# Remote notochord console
## Usage:
if your pc has a zeroconf mDNS resolving service, like `avahi` in ubuntu or `bounjour` on mac
- Open your browser and go to `http://notochord.local` (don't forget the _http://_ part, or the browser will try to search it on google)
- Click **[Connect]**
- type `help` on the console to see the available commands
if your pc can't resolve mDNS, (probably windows can't) see [this article](https://learn.adafruit.com/bonjour-zeroconf-networking-for-windows-and-linux/overview) to install it
## Installation:
- install `pipenv` and add the local bin repository to the `$PATH`
```bash
pip install --user pipenv
echo "PATH=$PATH:$HOME/.local/bin"
```
- TODO: find a good way to install the dependencies systemwide, or run the server in a virtualenv
at the moment We are doing:
```
cd remote_client
pipenv lock -r > requierments.txt
pip install --user -r requirements.txt
sudo cp -r ~/.local/lib/python2.7/site_packages/* /usr/local/lib/python2.7/dist-packages/
```
- Change the rpi hostname to _notochord_ in:
- /etc/hostname
- /etc/hosts
- Add the servers to systemd boot:
- Put the `notochord_ws_server.service` files in: `/etc/systemd/system`
- Change the _WorkingDirectory_ and _ExecStart_ if necesary
- Enable it with
- ```sudo systemctl enable notochord_ws_server.service```
- reboot, and you shold see them running if `systemctl status notochord_ws_server`
/home/daylan/X/repos/jquery.terminal/css/jquery.terminal-1.15.0.css
\ No newline at end of file
#container {
max-width: 800px;
}
#img-output{
display: grid;
grid-template-columns: repeat(4, 24%);
grid-gap: 3%;
}
#output img {
max-width: 400px;
background-color: grey;
}
html, body {
}
#term-container{
height: 300px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="container">
<img src="../chordata_logo.png">
<div id="term-container"></div>
<form name="mainform">
<p>
<input type="button" name=disconnectButton value="Disconnect" >
<input type="button" name=connectButton value="Connect" >
</p>
<p>
<textarea name="url" cols="50"></textarea>
</p>
</form>
<div id="img-output">
<!-- <a href="https://source.unsplash.com/juHayWuaaoQ/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/juHayWuaaoQ/240x160" />
</a>
<a href="https://source.unsplash.com/eWFdaPRFjwE/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/eWFdaPRFjwE/240x160" />
</a>
<a href="https://source.unsplash.com/i2KibvLYjqk/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/i2KibvLYjqk/240x160" />
</a>
<a href="https://source.unsplash.com/RFgO9B_OR4g/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/RFgO9B_OR4g/240x160" />
</a>
<a href="https://source.unsplash.com/7bwQXzbF6KE/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/7bwQXzbF6KE/240x160" />
</a>
<a href="https://source.unsplash.com/NhU0nUR7920/1500x1000" data-fancybox="images">
<img src="https://source.unsplash.com/NhU0nUR7920/240x160" />
</a> -->
</div>
</div>
</body>
</html>
{
"dependencies": {
"@fancyapps/fancybox": "^3.3.5",
"jquery": "^3.3.1",
"jquery.terminal": "^1.16.1"
},
"name": "ws_notochord_server",
"version": "0.1.0",
"description": "A websocket server to communicate remotly with the notochord program",
"license": "GPLv3",
"private": true,
"devDependencies": {
"css-loader": "^0.28.11",
"html-webpack-plugin": "^3.2.0",
"imports-loader": "^0.8.0",
"style-loader": "^0.21.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.0.2"
},
"scripts": {
"start": "webpack && open index.html"
}
}
import $ from 'jquery'
import 'jquery.terminal';
import '@fancyapps/fancybox'
import * as notochord from 'notochord.terminal.js'
import "terminal_css"
import "css/style.css"
import "fancybox_css"
console.log($)
$(function() {
$('#term-container').terminal(function(command) {
if (command !== '') {
try {
notochord.send(command);
} catch(e) {
this.error(new String(e));
}
} else {
this.echo('');
}
}, {
greetings: 'Welcome to the notochord terminal\n Press connect to begin..',
name: 'notochord_terminal',
height: 400,
prompt: notochord.default_prompt
});
document.mainform.disconnectButton.onclick = notochord.disconnect;
document.mainform.connectButton.onclick = notochord.connect;
})
import $ from 'jquery'
import 'jquery.terminal';
import '@fancyapps/fancybox';
var websocket;
export var default_prompt = '~> ';
export var remote_state = {
running: false,
mode: "unknown"
}
export function init()
{
console.log("WS chordata client.")
document.mainform.url.value = "ws://notochord.local:8000/"
document.mainform.disconnectButton.disabled = true;
}
export function connect()
{
websocket = new WebSocket(document.mainform.url.value);
// console.log(websocket);
websocket.binaryType = "arraybuffer";
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
export function onOpen(evt)
{
writeToScreen("Client: connected");
document.mainform.connectButton.disabled = true;
document.mainform.disconnectButton.disabled = false;
}
export function onClose(evt)
{
if (evt.code == 1000){
writeToScreen("Client: Disconnected");
} else {
writeErrorToScreen("Client: Disconnected with error " + evt.code );
console.log(evt)
}
document.mainform.connectButton.disabled = false;
document.mainform.disconnectButton.disabled = true;
}
export function onMessage(evt)
{
var msg;
if (evt.data instanceof ArrayBuffer){
if (receiveImage(evt.data)){
msg = "Received image size: " + formatBytes(evt.data.byteLength)
writeToScreen(msg);
} else {
msg = "The received stream was not of image type"
writeErrorToScreen(msg);
}
console.log(msg);
} else {
try{
var com = JSON.parse(evt.data);
remote_state.mode = com.mode;
remote_state.running = com.running
console.log(com);
if (com.break == 1){
setWaitingState();
writeToScreen(com.msg);
} else if (com.break == 3) {
setWaitingState();
console.log(com.msg)
} else {
writeToScreen(com.msg);
}
} catch (e) {
if (e instanceof SyntaxError){
console.log("received malformed response")
console.log(evt.data)
writeErrorToScreen("*** Received malformed response ***");
}
}
}
}
function getMimetype(bytes){
var pattern = []
bytes.slice(0, 4).forEach(function(byte){
pattern.push(byte.toString(16))
})
pattern = pattern.join('').toUpperCase()
switch (pattern) {
case '89504E47':
return 'image/png'
case '47494638':
return 'image/gif'
case '25504446':
return false //'application/pdf'
case 'FFD8FFDB':
case 'FFD8FFE0':
case 'FFD8FFE1':
return 'image/jpeg'
case '504B0304':
return false //'application/zip'
default:
return false //'Unknown filetype'
}
}
function getGalleryItem(mime, data){
var fancyanchor = document.createElement("a");
fancyanchor.setAttribute("class", "gallery-item");
fancyanchor.setAttribute("data-fancybox", "gallery");
fancyanchor.setAttribute("data-caption", "Image from notochord");
fancyanchor.href = "data:" + mime + ";base64," + data;
var img = document.createElement('img');
img.src = "data:" + mime + ";base64," + data;
fancyanchor.appendChild(img);
return fancyanchor;
}
export function receiveImage(data){
var bytes = new Uint8Array(data);
var base64 = btoa(String.fromCharCode.apply(null, bytes));
var mime = getMimetype(bytes)
if (!mime) return false;
var destDiv = document.getElementById("img-output");
destDiv.appendChild( getGalleryItem(mime, base64) );
destDiv.innerHTML = destDiv.innerHTML;
$().fancybox({
selector : '[data-fancybox="gallery"]',
loop : true
});
return true;
}
export function formatBytes(a,b){if(0==a)return"0 Bytes";var c=1024,d=b||2,e=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],f=Math.floor(Math.log(a)/Math.log(c));return parseFloat((a/Math.pow(c,f)).toFixed(d))+" "+e[f]}
export function onError(evt)
{
var msg = (evt.data)? evt.data : "<no data attached>";
writeErrorToScreen('Error: ' + msg);
console.log(evt);
websocket.close();
document.mainform.connectButton.disabled = false;
document.mainform.disconnectButton.disabled = true;
}
export function send(message)
{
if (message){
websocket.send(message);
return;
}
}
var fliflo = true
export function setWaitingState(){
fliflo != fliflo;
if (fliflo){
$.terminal.active().set_prompt("~# ")
} else {
$.terminal.active().set_prompt("~% ")
}
}
export function writeToScreen(message)
{
$.terminal.active().set_prompt(default_prompt)
$.terminal.active().echo(message);
}
export function writeErrorToScreen(message)
{
$.terminal.active().set_prompt(default_prompt)
$.terminal.active().error(message);
}
window.addEventListener("load", init, false);
export function sendText() {
send( document.mainform.inputtext.value );
}
export function clearText() {
document.mainform.outputtext.value = "";
}
export function disconnect() {
websocket.close();
}
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const raspIp = "192.168.1.237"
const myconfig = {
title : "Notochord remote console",
host : raspIp
}
module.exports = {
entry: 'main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'source-map',
mode: 'development',
module: {
rules: [
{
test: /\/node_modules\/.+(jquery\.terminal|fancybox).+(jsx|js)$/,
loader: 'imports-loader?jQuery=jquery,$=jquery,this=>window'
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: myconfig.title,
template: 'index.html'
})
],
resolve: {
modules: [path.resolve(__dirname, "src"), "node_modules"],
alias: {
css: path.resolve(__dirname, "./css"),
terminal_css : "jquery.terminal/css/jquery.terminal.css",
fancybox_css : "@fancyapps/fancybox/dist/jquery.fancybox.css"
}
}
};
This source diff could not be displayed because it is too large. You can view the blob instead.
import time
from http.server import SimpleHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
from ws_server import *
HOST_NAME = 'localhost'
PORT_NUMBER = 80
STATIC_DIR = "/client/dist"
class MyHandler(SimpleHTTPRequestHandler):
redirs = {
'/watchdog': {'status': 200, "msg" : "Wow"}
}
def do_GET(self):
if self.path in self.redirs:
self.respond(get_comm_json(self.redirs[self.path]["msg"]),\
self.redirs[self.path]["status"])
elif self.path.startswith("/command"):
self.notochord_command()
else:
self.path = STATIC_DIR + self.path
print(SimpleHTTPRequestHandler.do_GET(self))
#STATIC SERVER
def notochord_command(self):
q = urlparse(self.path)
cmd = "do_" + q.path.lstrip("/command").upper()
print(q, cmd)
q = parse_qs(q.query)
#TODO avoid some commands like do_IMAGE
#TODO: change lstrp per regex
if "p" not in q.keys():
p = []
elif not isinstance(q["p"], list):