Newer
Older
//
// server.js
//
// Jake Read at the Center for Bits and Atoms
// (c) Massachusetts Institute of Technology 2018
//
// This work may be reproduced, modified, distributed, performed, and
// displayed for any purpose, but must acknowledge the mods
// project. Copyright is retained and must be preserved. The work is
// provided as is; no warranty is provided, and users accept all
// liability.
RULES:
modules have Ouputs() and Inputs() that connect to other software modules running on the same CPU. It's a directed graph.
modules can have ATKPort()s that use a Link() to get to remote hardware, per message-passing network. It's a physical graph of duplex connections.
this is nice because we might have two Link()s to double up on bandwidth.
alternately, Link()s could be TCP connections to other pieces of hardware, including something like us !
*/
/*
// file system to load / look at our available modules / edit them
const fs = require('fs')
// express serves files, http makes the connection
const app = require('express')()
const http = require('http').Server(app)
// websocket does websocket
const WebSocket = require('ws')
// serving this handful of static files
app.get('/', (req, res) => {
console.log('client req /')
res.sendFile(__dirname + '/client/index.html')
})
app.get('/:file', (req, res) => {
console.log('client req', req.params.file)
res.sendFile(__dirname + '/client/' + req.params.file)
})
// and listening for requests here
const wss = new WebSocket.Server({ port: 8081 })
wss.on('connection', (ws) => {
sckt = ws
// say hello
console.log('socket open on 8081')
ws.on('message', (evt) => {
socketRecv(evt)
})
})
// through this window
http.listen(8080, () => {
console.log('listening on 8080 for static files')
})
var sckt = null
if (sckt) {
var msg = {
type: type,
data: data
}
sckt.send(JSON.stringify(msg))
var recv = JSON.parse(evt)
var type = recv.type
var data = recv.data
console.log('RECV CONSOLE:', data)
break
case 'get menu':
var tree = buildMenu()
socketSend('put menu', tree)
break
case 'add module':
addModule(data)
break
putLink(data)
case 'put ui':
changeUi(data)
break
console.log('ERR server recv with non recognized type', recv)
------------------------------------------------------
------------------------------------------------------
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// a program is a list of modules,
// modules have internal lists of modules that they call
var prgmem = {
// we want to describe all of the nodes first,
// then we can add connections once we have hooks
// to each of them
modules: {
gate: {
path: './src/util/gate.js',
conn: {
}
ui:{
pos: [100, 100]
}
},
delay: {
path: './src/util/delay.js',
state: {
ms: 500
}
},
log: {
path: './src/util.log.js'
}
},
connections: ['gate.outputs.out', 'delay.inputs.thru']
}
function loadPrgmem(desc) {
console.log(desc)
}
/*
------------------------------------------------------
LOST FUNCTION
------------------------------------------------------
*/
function setUiPos(module, left, top) {
if (module.ui == null) {
module.ui = {}
}
module.ui.left = left
module.ui.top = top
}
/*
------------------------------------------------------
PROGRAM WRITING
------------------------------------------------------
function addModule(path) {
// get and add to server system
if (fs.existsSync(path)) {
var src = require(path) // get and return
var mod = new src() // make a new one
modules.push(mod) // add to the system
// assign and id and remember from whence it came
mod.path = path
// we need to add some top-level info to the inputs so that we can draw them
mod.inputs[item].parentId = mod.id
mod.inputs[item].key = item
}
for (item in mod.state) {
if (item == 'onChange' | item == 'emitChange' | item == 'emitters') {
//console.log('rolling past change fn')
} else {
mod.state['_' + item] = mod.state[item]
mod.state[item] = {}
console.log('ADDING MODULE', mod.description.name)
// first to UI
// also to fn call, in case writing program ?
return mod
console.log('ERR no module found at', path)
function writeStateObject(mod, item) {
Object.defineProperty(mod.state, item, {
set: function(x) {
this['_' + item] = x
this.emitChange(item)
// update server (or in some cases, a confirmation)
putState(mod)
get: function() {
return this['_' + item]
}
})
}
/*
PROGRAM UPDATING
*/
//console.log('modules at prgmem start', modules)
function putReps() {
reps.push(makeRep(modules[mod]))
// scrape for only things we'll want to represent
var rep = {
id: mod.id,
description: mod.description,
inputs: mod.inputs,
outputs: mod.outputs,
state: {}
}
// holds UI information - to load mods at location
for (var key in mod.state) {
if (isStateKey(key)) {
return rep
}
function putRep(mod) {
socketSend('add rep', makeRep(mod))
}
socketSend('change rep', makeRep(mod))
}
// update state from UI to server
function changeState(data) {
// of one module
// should just recv all state, walk tree for differences
var oldState = modules[data.id].state
var newState = data.state
// rep only holds proper key-values w/o emitters, _values etc
if (oldState[key].isButton) {
if (oldState[key].isPressed != newState[key].isPressed) {
console.log('CHANGE BUTTON STATE', key, 'to', newState[key], 'in', data.id)
// this will trigger some 'onChange' f'ns
// that might change some state
// to prevent quickly changing it back, we'll exit now (one state change from UI per trx)
} else if (oldState[key].isMultiLine) {
if (oldState[key].value != newState[key].value) {
console.log('CHANGE MULTILINE STATE', key, 'to', newState[key].value, 'in', data.id)
oldState[key].value = newState[key].value
return true
console.log('CHANGE STATE', key, 'to', newState[key], 'in', data.id)
oldState[key] = newState[key]
return true
console.log("NO UI")
mod.ui = {}
}
mod.ui = data.ui
console.log('CHANGE UI', mod.id, mod.ui)
}
function isStateKey(key) {
if (key.indexOf('_') == 0 || key == 'emitters' || key == 'onChange' || key == 'emitChange') {
return false
} else {
return true
}
}
// push new state from server to UI
function putState(mod) {
// push just the state to the individual mod
for (var key in mod.state) {
if (isStateKey(key)) {
socketSend('change state', data)
function putLink(data) {
var fromModule = modules.find((module) => {
return module.id === data.from.id
})
var toModule = modules.find((module) => {
return module.id === data.to.id
})
if (fromModule.outputs[data.from.output].isLinked(toModule.inputs[data.to.input])) {
console.log("HOOKDOWN")
fromModule.outputs[data.from.output].remove(toModule.inputs[data.to.input])
fromModule.outputs[data.from.output].attach(toModule.inputs[data.to.input])
// replace it
changeRep(fromModule)
tree[dir[i]] = {}
var subdir = fs.readdirSync('./src/' + dir[i])
var obj = {}
obj.path = './src/' + dir[i] + '/' + subdir[j]
tree[dir[i]][subdir[j].slice(0, -3)] = obj
}
}
}