diff --git a/client/client.js b/client/client.js index 59260d3d9d53ad66ff241b6ac33c2b14108d68af..27bc8ed90f9ea64dceb3cd13ab3c2b74c6acbcc6 100644 --- a/client/client.js +++ b/client/client.js @@ -58,35 +58,28 @@ function loadModule(rep) { return rep } -// rn: get and load a module, complete that cycle w/ json tests? - -function getModule(location) { - console.log('getting ', location) - var req = new XMLHttpRequest() - req.open('GET', location) - req.send() - req.onreadystatechange = function(res) { - if(this.readyState == 4 && this.status == 200){ - console.log('req recv') - } - } -} // return ul element with name and alt and link? -function buildMenu(elem, evt) { +function buildMenu(tree, screenTarget) { var menuDom = document.createElement('div') menuDom.id = 'menu' - menuDom.style.left = evt.pageX + 'px' - menuDom.style.top = evt.pageY + 'px' - for (key in elem) { + menuDom.style.left = screenTarget.X + 'px' + menuDom.style.top = screenTarget.Y + 'px' + for (key in tree) { var ul = document.createElement('ul') ul.innerHTML = key.toString() - for (subkey in elem[key]) { + for (subkey in tree[key]) { var a = document.createElement('a') a.innerHTML = subkey.toString() a.addEventListener('click', function(evt) { - var location = 'modules/' + key + '/' + subkey - getModule(location) + var path = tree[key][subkey].path + console.log('sending req for module', path) + var data = { + target: 'new module', + path: path, + screenTarget: screenTarget + } + socketSend('get', data) document.body.removeChild(document.getElementById('menu')) }) var li = document.createElement('li') @@ -99,7 +92,7 @@ function buildMenu(elem, evt) { function rmListener(evt) { var findMenu = document.getElementById('menu') - if(findMenu !== null && findMenu.id == 'menu'){ + if (findMenu !== null && findMenu.id == 'menu') { document.body.removeChild(findMenu) } } @@ -110,25 +103,116 @@ function buildMenu(elem, evt) { // get json menu item and render // and ask for module at /obj/key oncontextmenu = function(evt) { - var req = new XMLHttpRequest() - req.open('GET', '/modules/list') - req.send() - req.onreadystatechange = function(res) { - if (this.readyState == 4 && this.status == 200) { - buildMenu(JSON.parse(this.response), evt) + var req = { + type: 'get menu', + data: { + screenTarget: { + X: evt.pageX, + Y: evt.pageY + } } } + sckt.send(JSON.stringify(req)) // prevents propogation to the os return false } + +// client globals + var modules = new Array() +var sckt = {} + +// client fns + +function socketSend(type, data) { + var msg = { + type: type, + data: data + } + sckt.send(JSON.stringify(msg)) +} + +function socketRecv(evt) { + var recv = JSON.parse(evt.data) + var type = recv.type + var data = recv.data + // tree banger + switch (type) { + case 'console': + console.log('recv console:', data) + break + case 'get': + get(data) + break + case 'put': + put(data) + break + case 'put menu': + console.log(data) + buildMenu(data.tree, data.screenTarget) + break + default: + console.log('recv with non recognized type', recv) + break + } +} + +// init & hookup + window.onload = function() { + const socket = new WebSocket('ws://localhost:8081') + + socket.onopen = function(evt) { + // pass to global ref + sckt = this + // say hello + socketSend('console', 'hello server') + console.log('socket open') + // main socket entry point + this.onmessage = (evt) => { + socketRecv(evt) + } + // others + this.onerror = (err) => { + console.log('socket error', err) + } + this.onclose = (evt) => { + console.log('socket closed', evt) + sckt = null + } + } + modules.push(loadModule(gcodeDummy)) modules.push(loadModule(terminalDummy)) for (index in modules) { document.body.append(modules[index].ui.domElem) } -} \ No newline at end of file +} + +/* + +to prototype +write example, has one state, a text box. load, connect, on event goes down thru and back up + +*/ + +/* a few things + + / using + - get a list of available modules + - request a new module to be added to the program + - push state / get state (of individual module) + - get state (on load, of existing modules) + - save state to json + - load state from json + + / dev + - rewrite module file at / and reload all instances + - (how to catch errors in runtime?) + - reload all instances of module at / + - write new defaults into / (same as edit file, don't ambiguate) + +*/ \ No newline at end of file diff --git a/package.json b/package.json index 69cfc641caf045517fc6afd02ebc69da75ce6a6f..2fb0e633c3b106b4c28e65247b879618a0f6e6e4 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,10 @@ }, "dependencies": { "express": "^4.16.3", + "http": "0.0.0", "readline": "^1.3.0", - "serialport": "^6.2.2" + "serialport": "^6.2.2", + "ws": "^6.0.0" }, "devDependencies": {}, "scripts": { diff --git a/run.js b/run.js index 403dc7cedfe306c5827b9ad74b082982e8a4ec09..79ea9e306cb76af942a15c3735f09b24b2afbabb 100644 --- a/run.js +++ b/run.js @@ -1,5 +1,7 @@ -const Terminal = require('./lib/terminal.js') -const Gcode = require('./lib/gcode.js') + + +const Terminal = require('./src/ui/terminal.js') +const Gcode = require('./src/parsing/gcode.js') var terminal = new Terminal() var gcode = new Gcode() diff --git a/server.js b/server.js index d66a2f0dd44b859dfe69c6a1fe591e424ac332ca..56100868dd78e3078b4d6b24c4b2e59c438228b7 100644 --- a/server.js +++ b/server.js @@ -1,74 +1,129 @@ -// require express and do things, serve static and then dish a json +// file system to load / look at our available modules / edit them +const fs = require('fs') -const express = require('express') -const exp = express() +// express serves files, http makes the connection +const app = require('express')() +const http = require('http').Server(app) -// express will serve static files from the client folder -exp.use(express.static('client')) +// websocket does websocket +const WebSocket = require('ws') -var jd = { - parsing: { - gcode: { - description: 'gcode parser', - hasUi: false - } - }, - motion: { - planner: { - description: 'acceleration lookahead', - hasUi: true, - uiPath: 'src/motion/planner/' - }, - kinematics: { - description: 'motion to actuator spaces', - hasUi: false - }, - controller: { - description: 'machine state machine', - hasUi: true, - uiPath: 'src/motion/controller/' - } - }, - motors: { - stepper: { - description: 'stepper motor rep', - hasUi: false - }, - bldc: { - description: 'bldc motor rep', - hasUi: false - } +// 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) +}) + +// server globals + +var sckt = {} + +function socketSend(type, data){ + var msg = { + type: type, + data: data + } + sckt.send(JSON.stringify(msg)) +} + +function socketRecv(evt){ + var recv = JSON.parse(evt) + var type = recv.type + var data = recv.data + // bang thru + switch(type){ + case 'console': + console.log('recv console:', data) + break + case 'get': + get(data) + break + case 'put': + put(data) + break + case 'get menu': + var tree = buildMenu() + var msg = { + tree: tree, + screenTarget: data.screenTarget + } + socketSend('put menu', msg) + break + default: + console.log('server recv with non recognized type', recv) + break } } -// can we express this in just a few f'ns? -// 1st do a req module and place ... link thru run.js - how to place? eval - eval is evil, says most dev.. uses interpreter, not compile +function get(data){ + switch(data.target){ + case 'new module': + console.log('getting module at path', data.path) + if(fs.existsSync(data.path)){ + console.log('module found at', data.path) + var mod = require(data.path) + console.log(mod) + } else { + console.log('no module found at', data.path) + } + break + default: + console.log('get req with no recognized type', data) + } +} -// can probably think through how to just place, hookup, and change state: altho: how to put async? has to be tied to existing link? -// arch. now for managing multiple connections? module that does top-level linking, i.e. gets from another machine? -// can even imagine how to build c and flash -// but how to run code? put new file and reload with? kritical +function put(data){ + switch(data.target){ + case 'state': + // match data.id + // check match data.rep.state on internal + break + default: + console.log('put req with no recognized type', data) + } +} -exp.get('/modules/list', (req, res) => { - res.send(jd) - console.log('get /modules') -}) +// and listening for requests here +const wss = new WebSocket.Server({port: 8081}) -// request for new module -exp.get('/modules/:moduleHeader/:module', (req, res) => { - console.log('req module', req.params) +wss.on('connection', (ws) => { + sckt = ws + socketSend('console', 'hello client') + console.log('socket open') + ws.on('message', (evt) => { + socketRecv(evt) + }) }) -// put state -exp.put('/put/:moduleId', (req, res) => { - +// through this window +http.listen(8080, () => { + console.log('listening on 8080') }) -// link or unlink modules -exp.put('/:link/:moduleId/:output/:moduleId/:input', (req, res) => { -}) -exp.listen(8080, () => console.log("listening on 8080")) +function buildMenu (){ + var tree = {} + var dir = fs.readdirSync('./src') + for(i in dir){ + tree[dir[i]] = {} + var subdir = fs.readdirSync('./src/' + dir[i]) + for(j in subdir){ + // find js files + if(subdir[j].slice(-3) === '.js'){ + var obj = {} + obj.path = './src/' + dir[i] + '/' + subdir[j] + tree[dir[i]][subdir[j].slice(0, -3)] = obj + } + } + } + + return tree +} // put \ No newline at end of file diff --git a/src/motion/planner.js b/src/motion/planner.js new file mode 100644 index 0000000000000000000000000000000000000000..ecf537333ed4ba701cc2e09fe57156754130c77c --- /dev/null +++ b/src/motion/planner.js @@ -0,0 +1,88 @@ +// boilerplate atkapi header +const InOut = require('./lib/inout.js') +let Input = InOut.Input +let Output = InOut.Output + +function Gcode() { + + // state + this.state = { + mode: 'G0', + speeds: { + G0: 1200, + G1: 400 + } + } + + // local functions + var getKeyValues = (str) => { + var kv = {} + for (var i = 0; i < str.length; i++) { + if (str[i].match('[A-Za-z]')) { // regex to match upper case letters + var lastIndex = str.indexOf(' ', i) + if (lastIndex < 0) { + lastIndex = str.length + } + var key = str[i].toUpperCase() + kv[key] = parseFloat(str.slice(i + 1, lastIndex)) + } + } + return kv + } + + var parseGcode = (str) => { + var instruction = { + position: {}, + hasMove: false, + speed: 0 + } + + kv = getKeyValues(str) + // track modality + if(kv.G == 0 | kv.G == 1){ + this.state.mode = 'G' + kv.G.toString() + } else if (kv.G != null) { + // no arcs pls + console.log('unfriendly Gcode mode!', kv) + } + + for(key in kv){ + if(key.match('[A-EX-Z]')){ + instruction.position[key] = kv[key] + instruction.hasMove = true + } else if (key.match('[F]')){ + this.state.speeds[this.state.mode] = kv.F + } + } + + instruction.speed = this.state.speeds[this.state.mode] + // and this for help later? + instruction.kv = kv + + return instruction + } + + // input functions + var lineIn = (str) => { + var instruction = parseGcode(str) + if (instruction.hasMove){ + this.outputs.instructionOut.emit(instruction) + } else { + this.outputs.modeChange.emit(this.state.mode) + } + } + + // ins and outs + this.inputs = { + lineIn: new Input('string input', 'string', lineIn) + } + + this.outputs = { + instructionOut: new Output('move instruction', 'object'), + modeChange: new Output('mode change', 'string') + } +} + + +// export the module +module.exports = Gcode \ No newline at end of file diff --git a/lib/gcode.js b/src/parsing/gcode.js similarity index 97% rename from lib/gcode.js rename to src/parsing/gcode.js index 55e86307e67c85938f94f4e874bcc050407899bf..4a38f1e940830aae561c91f1ae2af37527ecae84 100644 --- a/lib/gcode.js +++ b/src/parsing/gcode.js @@ -1,5 +1,5 @@ // boilerplate atkapi header -const InOut = require('./inout.js') +const InOut = require('../../lib/inout.js') let Input = InOut.Input let Output = InOut.Output diff --git a/src/ui/panel.js b/src/ui/panel.js new file mode 100644 index 0000000000000000000000000000000000000000..99f080fcf410e98cb0bdbecaffd0b67304862a66 --- /dev/null +++ b/src/ui/panel.js @@ -0,0 +1,43 @@ +// boilerplate atkapi header +const InOut = require('../../lib/inout.js') +let Input = InOut.Input +let Output = InOut.Output + +// bonus reqs +const readline = require('readline') + +// a constructor, a fn, a javascript mess +function Panel(){ + + // object state + this.state = { + width: 64, + height: 48 + } + + // natural inputs / local functions + var rlInterface = readline.createInterface({ + input: process.stdin, + output: process.stdout + }) + + rlInterface.on('line', (data) => { + this.outputs.lineOut.emit(data) + }) + + var post = (str) => { + console.log(str) + } + + // ins and outs + this.inputs = { + lineIn: new Input('line input', 'string', post) + } + + this.outputs = { + lineOut: new Output('line output', 'string') + } +} + +// exports +module.exports = Panel \ No newline at end of file diff --git a/lib/terminal.js b/src/ui/terminal.js similarity index 90% rename from lib/terminal.js rename to src/ui/terminal.js index 84279e22e18181488a21618658e34b850d1f5fa8..5e445a9a31c1dd51bfcf1b976c1f926bed16c13d 100644 --- a/lib/terminal.js +++ b/src/ui/terminal.js @@ -1,5 +1,5 @@ // boilerplate atkapi header -const InOut = require('./inout.js') +const InOut = require('../../lib/inout.js') let Input = InOut.Input let Output = InOut.Output @@ -12,7 +12,8 @@ function Terminal(){ // object state this.state = { width: 64, - height: 48 + height: 48, + text: 'one line' } // natural inputs / local functions