gtr/wavey-wind/server.js

287 lines
6.6 KiB
JavaScript

const express = require('express')
const http = require('http')
const WebSocket = require('ws')
const osc = require('osc')
const readline = require('readline')
const fs = require('fs')
const port = 6676
// Vzemi iz argumenta
const tty = process.argv[2]
let eulerRotation = [0, 0, 0]
let baudrate = parseInt(process.argv[3])
if (!baudrate) {
baudrate = 115200
}
const include_files = [
'/anim.js',
'/control.js',
'/osctl.js',
'/test.js',
'/node_modules/three/build/three.min.js',
'/node_modules/nouislider/distribute/nouislider.min.js',
'/node_modules/nouislider/distribute/nouislider.min.css',
'/node_modules/osc/dist/osc-browser.js'
];
const app = express();
const server = http.Server(app);
const DEBUG = true
// Odprti serijski OSC link
let scon = null
let buttonHeld = [false, false, false, false]
let buttonValue = [false, false, false, false]
let switchHeld = [false, false, false, false]
let switchValue = [false, false, false, false]
function openSerial() {
console.log('opening ', tty, baudrate)
scon = new osc.SerialPort({
devicePath: tty,
bitrate: baudrate,
autoOpen: true,
useSLIP: true
})
scon.open()
scon.on('open', e => {
console.log('serial connection opened')
//console.log(scon)
})
scon.on('error', e => {
console.error('tty error', e)
//scon.close()
})
scon.on('close', e => {
console.warn('serial connection closed, restarting in 1 second')
})
// Arduino OSC gre v web
scon.on('message', msg => {
// Debug incoming osc
if (DEBUG) {
console.log('osc msg', msg)
}
sendAll(msg, null, null, osclients)
})
if (scon._closeCode) {
scon = null
console.log('restarting serial connection')
setTimeout(openSerial, 1000)
}
}
if (tty) {
openSerial(baudrate)
}
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/ctl', (req, res) => {
res.sendFile(__dirname + '/control.html');
});
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/test.html');
});
let settings = {};
app.get('/settings', function(req, res) {
res.send(settings);
});
include_files.map(function(file) {
app.get(file, function(req, res){
res.sendFile(__dirname + file);
});
});
server.listen(port, () => console.log('listening on *:' + port))
// Websocket init
const wss = new WebSocket.Server({ server })
// Relay multicast to websockets
// @TODO still sends to supercollider? Do we need two sockets?
//var dgram = require('dgram');
//var sss = dgram.createSocket('udp4');
//sss.on('listening', () => {
// sss.addMembership('224.0.1.9');
//})
//sss.bind(57120);
const scudp = new osc.UDPPort({
//socket: sss
remotePort: 57121,
localPort: 57100
})
scudp.on('open', () => {
console.log("UDP to OSC open")
})
scudp.on('message', (msg) => {
console.log('got UDP msg', msg);
osclients.forEach( client => {
if (client) {
//console.log("sending", msg, info)
client.send(msg)
}
})
})
scudp.on('error', (e) => {
console.log('UDP OSC error!', e)
})
scudp.open()
function eulerFromQuaternion(quaternion) {
// Quaternion to matrix.
const w = quaternion[0], x = quaternion[1], y = quaternion[2], z = quaternion[3];
const x2 = x + x, y2 = y + y, z2 = z + z;
const xx = x * x2, xy = x * y2, xz = x * z2;
const yy = y * y2, yz = y * z2, zz = z * z2;
const wx = w * x2, wy = w * y2, wz = w * z2;
const matrix = [
1 - ( yy + zz ), xy + wz, xz - wy, 0,
xy - wz, 1 - ( xx + zz ), yz + wx, 0,
xz + wy, yz - wx, 1 - ( xx + yy ), 0,
0, 0, 0, 1
];
// Matrix to euler
function clamp( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
}
const m11 = matrix[ 0 ], m12 = matrix[ 4 ], m13 = matrix[ 8 ];
const m21 = matrix[ 1 ], m22 = matrix[ 5 ], m23 = matrix[ 9 ];
const m31 = matrix[ 2 ], m32 = matrix[ 6 ], m33 = matrix[ 10 ];
var euler = [ 0, 0, 0 ];
euler[1] = Math.asin( clamp( m13, - 1, 1 ) );
if ( Math.abs( m13 ) < 0.9999999 ) {
euler[0] = Math.atan2( - m23, m33 );
euler[2] = Math.atan2( - m12, m11 );
} else {
euler[0] = Math.atan2( m32, m22 );
euler[2] = 0;
}
return euler;
}
var threshold = 20;
const sendAll = (msg, info, oscWS, osclients) => {
if (msg.address == '/keys') {
// keys 0 + 3 = euler reset
if (msg.args[0] <= threshold && msg.args[3] <= threshold ) {
eulerRotation = [0, 0, 0];
console.log("EULER ROTATION RESET");
}
}
// LOGIC FOR MOMENTARY BUTTONS (0 - 3)
for (let i = 0; i < 4; i++) {
// toggle button 2 state between 0 and 1
if (msg.address == '/keys') {
if (msg.args[i] <= threshold ) {
buttonValue[i] = 1;
} else {
buttonValue[i] = 0;
}
msg.args.push(buttonValue[i] ? 1 : 0)
}
}
// LOGIC FOR TOGGLE SWITCHES (4-7)
for (let i = 4; i < 8; i++) {
// toggle button 2 state between 0 and 1
if (msg.address == '/keys') {
if (msg.args[i] <= threshold ) {
if (switchHeld[i-4] == false) {
switchValue[i-4] = !switchValue[i-4]
switchHeld[i-4] = true
}
} else {
if (switchHeld[i-4]) {
switchHeld[i-4] = false
}
}
msg.args.push(switchValue[i-4] ? 1 : 0)
}
}
//------------------------------------------------
// Convert quaternion diff to euler angle diff
if (msg.address == '/quaternionDiff') {
const euler = eulerFromQuaternion(msg.args, 'XYZ');
sendAll({
address: '/eulerDiff',
args: euler
}, info, oscWS, osclients)
eulerRotation[0] += euler[0]
eulerRotation[1] += euler[1]
eulerRotation[2] += euler[2]
sendAll({
address: '/euler',
args: eulerRotation
}, info, oscWS, osclients)
}
osclients.forEach( client => {
if (client && oscWS != client) {
// console.log("sending", msg, info)
client.send(msg)
}
})
if (scudp) {
console.log("SENDING UDP", msg)
scudp.send(msg)
}
}
const osclients = []
wss.on('connection', function (ws) {
console.log('new client connection')
const oscWS = new osc.WebSocketPort({
socket: ws,
metadata: false
});
// Vsi OSC sem grejo naprej na kliente OSC
oscWS.on('packet', (packet, info) => {
// Broadcast adjust msg
const [address, args] = packet
sendAll({ address, args}, info, oscWS, osclients)
})
oscWS.on('error', error => {
console.warn('Ignoring invalid OSC')
console.warn(error)
})
osclients.push(oscWS)
})