Skip to content

Commit

Permalink
app finished
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelpalumbo committed May 5, 2020
1 parent 2312262 commit ad0ab79
Show file tree
Hide file tree
Showing 355 changed files with 55,629 additions and 121 deletions.
Binary file added .DS_Store
Binary file not shown.
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,61 @@
# wspd
## wspd (websocket 4 puredata)

**I highly recommend reading this document at https://github.com/michaelpalumbo/wspd/blob/master/README.md or opening [README.pdf](readme.pdf)**

This app acts as a bridge between two computers over the web, enables very fast transmission of controller data between, for example, two puredata patches.

Since first starting the code, it's become a lot more general than only being used for puredata, but I didn't bother changing the name of the repository!


### Instructions
1. install nodejs
2. clone/download the wspd repository at https://github.com/michaelpalumbo/wspd
3. Open terminal, cd into the folder 'wspd' (the rest of this readme assumes that you're working from this directory)
4. verify nodejs is installed:

```shell
node -v
```
5.. might need to install dependencies:

```shell
npm install
```
### Test locally
1. start the server

```shell
npm start server
```

2.. open a 2nd terminal window, cd into wspd

3.. start the client

```shell
npm start client localhost
```

4.. open the tester.maxpat (or tester.pd)

Click either of the toggles to confirm data is being sent from pd>nodejs>pd. You should be able to have both toggles clicked, this verifies bi-directionality.

### Telematic setup
1. whomever can configure their router with DMZ and port-forwarding should run the app in server mode. The diagram below assumes that Jen would be running the server (the code doesn't need to be modified if you choose to have Erin run the server instead)

![](wspd_schema_v2.png)

### OSC Syntax
The nodejs app assumes that all incoming data on UDP follows the OSC format of an Address Pattern followed by Type Tag String. Examples:

- /index 3
- /position 0.3 0.6 0.8
- /msg hello world

If you do not supply an address pattern using the '/', the app will ignore the message.

#### Other messages
As of right now, the app accepts OSC over UDP, but it would be reasonable to update the script so that it could accept other data from other sources, such from serial ports, MIDI input, etc.

#### Other remotes/web app
The server can also host a web app, i.e. could be used to host a couple of controls on a web page for anyone to remotely control a patch on either of your machines, etc. The preliminary code is there, just figured I'd mention it if it was of interest at a later time.
Binary file added README.pdf
Binary file not shown.
235 changes: 235 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// this script runs at either side
// it's function as either a server or client is determined by a cli arg
console.log(process.argv[2] + ' mode')

const mode = process.argv[2]
if (mode === 'client' && !process.argv[3]){
console.log('error: client mode requires a 2nd argument to specify server IP address\nexample:\n\nnpm start client localhost')
process.exit()
}
// ***** Local UDP Send & Receive Config ******* //
let localReceivePort;
let localSendPort;
if(mode === 'server'){
localReceivePort = 7402
localSendPort = 7401
} else if (mode === 'client'){
localReceivePort = 7404
localSendPort = 7403
}

// ***** Local UDP-Sender ******* //
// this is used by either mode!
function init(){

}



// both modes require these libs
const { Client, Server } = require('node-osc');
const WebSocket = require('ws');
let ws; // keep this here

if (mode === "server"){

// run the serverconst WebSocket = require('ws');
const app = require('express')()
const http = require('http').createServer(app);;

let listenPort = (process.env.PORT || 8081)
const wss = new WebSocket.Server({ 'server': http, clientTracking: true });
http.listen(listenPort, function(){
})
wss.on('connection', function connection(ws, req, client) {

console.log('new connection established ')
const localSend = new Client('127.0.0.1', localSendPort);
console.log('Configure your local pd patch(es) to listen on UDP Port ' + localSendPort)

const localReceive = new Server(localReceivePort, '0.0.0.0');
// once running, inform user
localReceive.on('listening', () => {
console.log('Configure your local pd patch(es) to send on UDP Port ' + localReceivePort)
})
// handle message:
localReceive.on('message', (msg) => {
// validate correct OSC address pattern syntax
// console.log(msg)
if(msg[0].charAt(0) === '/'){
// get the address pattern
ap = msg[0]
// trim the address pattern
msg.shift()
// construct object to send over websocket
message = {
// cmd allows us to send other types of messages, ask Michael for more info if curious!
cmd: 'OSC',
date: new Date().toUTCString(),
addressPattern: ap,
// this is the data!
typeTagString: msg
}
// inform user
// console.log('sending to remote:\n', message)
// package data for the web, send it!
// if(ws){
ws.send(JSON.stringify(message))
// }

} else {
// if incoming OSC message does not have an address pattern, refuse to handle it
console.log('error, OSC Message must lead with an addressPattern\n\ni.e. /bioData')
}
});


ws.on('message', function incoming(message) {
msg = JSON.parse(message)
// console.log(msg)

switch (msg.cmd){
// in case you want to receive other data and route it elsewhere
case 'OSC':
localSend.send(msg.addressPattern, msg.typeTagString, (err) => {
if (err) console.error(err);
});

break;


default:
console.log('client sent message with unknown cmd: ' + msg)
// ws.send('server received message but did not understand: ' + msg)
break;



}
});

ws.on('close', function(code, reason) {

})
});
// we can use this if we want to send to multiple clients!
function broadcast(msg){
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(msg);
}
});
}





} else if (mode === 'client'){
const localSend = new Client('127.0.0.1', localSendPort);
console.log('Configure your local pd patch(es) to listen on UDP Port ' + localSendPort)
// run the app in client mode
// ***** Websocket ******* //
// WebSocket that will automatically attempt to reconnect if the connection is closed, or if the remote server goes down
const ReconnectingWebSocket = require('reconnecting-websocket');
const serverIP = process.argv[3]
const serverPort = '8081';
const serverWSAddress = `ws://${serverIP}:${serverPort}`;
// options for the reconnecting websocket
const rwsOptions = {
// make rws use the webSocket module implementation
WebSocket: WebSocket,
// ms to try reconnecting:
connectionTimeout: 1000,
//debug:true,
}

// create a websocket
// console.log(`attempting to connect to server at ${serverWSAddress}`)
ws = new ReconnectingWebSocket(serverWSAddress, [], rwsOptions);

// if the server responds with an error
ws.addEventListener('error', () => {
console.log(`connection error: ${serverIP}`);
});
// on successful connection to server:
ws.addEventListener('open', () => {
console.log (`connected to server at ${serverWSAddress}`)
});
// on close:
ws.addEventListener('close', () => {
console.log("server connection closed");
});
// handle messages
ws.addEventListener('message', (data) => {
let msg = JSON.parse(data.data);
// console.log(msg)
switch (msg.cmd){
// in case you want to receive other data and route it elsewhere
case 'OSC':
// send formatted OSC message locally (i.e. a pd patch)
localSend.send(msg.addressPattern, msg.typeTagString, (err) => {
if (err) console.error(err);
});
break;

default:
// inform user that unknown message commang used
console.log('client sent message with unknown cmd: ' + msg)
break;
}
});

// ***** Local UDP-Receiver ******* //
// this is used by either mode!
let localReceivePort;
if(mode === 'server'){
localReceivePort = 7402
} else if (mode === 'client'){
localReceivePort = 7404
}
const localReceive = new Server(localReceivePort, '0.0.0.0');
// once running, inform user
localReceive.on('listening', () => {
console.log('Configure your local pd patch(es) to send on UDP Port ' + localReceivePort)
})
// handle message:
localReceive.on('message', (msg) => {
// validate correct OSC address pattern syntax
// console.log(msg)
if(msg[0].charAt(0) === '/'){
// get the address pattern
ap = msg[0]
// trim the address pattern
msg.shift()
// construct object to send over websocket
message = {
// cmd allows us to send other types of messages, ask Michael for more info if curious!
cmd: 'OSC',
date: new Date().toUTCString(),
addressPattern: ap,
// this is the data!
typeTagString: msg
}
// inform user
// console.log('sending to remote:\n', message)
// package data for the web, send it!
// if(ws){
ws.send(JSON.stringify(message))
// }

} else {
// if incoming OSC message does not have an address pattern, refuse to handle it
console.log('error, OSC Message must lead with an addressPattern\n\ni.e. /bioData')
}
});

} else {
// app needs to know what mode to run in
console.log('error! first argument must be either \'client\' or \'server\'')
// stop node process
process.exit()
}



Binary file added archive/.DS_Store
Binary file not shown.
55 changes: 55 additions & 0 deletions archive/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const WebSocket = require('ws');
const { Client, Server } = require('node-osc');
let ip = process.argv[2]

if(!ip){
console.log('error! Server IP not provided as first argument\n\ni.e. node client.js localhost')
process.exit()
}
const ws = new WebSocket('ws://' + ip + ':8081');

ws.on('open', function open() {
// ws.send(['something']);
});

ws.on('message', function incoming(data) {
console.log(data);
});

ws.on('error', (err) => {
console.log(err.message)
});



/// UDP
// TODO add a udpSend (from the client.js) on port 7400
// NOTE that it shouldn't run if 'local' cli arg given
// const udpSend = new Client('127.0.0.1', 3333);
const udpReceive = new Server(7400, '0.0.0.0');

udpReceive.on('listening', () => {
console.log('OSC Server is listening.')
})

let data = {
index: null,
heartSignal: null,
bpm: null,
bpmChange: null,
bvpa: null,
gsr: null,
gsrLoPass: null,
}
udpReceive.on('message', (msg) => {
msg.shift()
data.index = msg[0]
data.heartSignal = msg[1]
let output = JSON.stringify(data)
console.log('sending to remote: ', data)
ws.send(output)
console.log('data from 7400', msg[0], msg[5])
let array = [ msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6] ]
console.log(array)
// ws.send(array)
});
Loading

0 comments on commit ad0ab79

Please sign in to comment.