Commit 15d7b772 authored by Jake Read's avatar Jake Read

dynamic script load bones of

parent 091bd07e
......@@ -73,7 +73,7 @@ To run the program, we launch the main.js file with node, from the command line.
cd to the rndmc folder and run:
``node main``
``node rundmc``
It's handy to keep a terminal window open beside a browser when running the software - it's not perfect yet - I do this:
......@@ -149,11 +149,11 @@ Besides Inputs, Outputs, a Description and State object, anything else goes. I.E
```javascript
// boilerplate rndmc header
const InOut = require('../../lib/jsunit.js')
let Input = InOut.Input
let Output = InOut.Output
let State = InOut.State
let Button = InOut.Button
const JSUnit = require('../../src/jsunit.js')
let Input = JSUnit.Input
let Output = JSUnit.Output
let State = JSUnit.State
let Button = JSUnit.Button
// a constructor, a fn, a javascript mess
function uiNum() {
......@@ -265,27 +265,47 @@ View.assignProgram(program)
# Development Notes
## For MW
## Immediately
- want that better-planner
- do axis pullout separately from stepper motor ?
- ui / button
- bug hunting
- dereferenced events / stepper axis vector
- multiple button calls ...
- stepper / planner bug
- is load / save really consistent ? what is the state answer ?
- would like to send board with new buck out to fab
- just do stepper23, bc if that's it, that's it ?
- title is still 'xperiment'
- stepper move.vector is a ref issue? derefed before running calcTrap() ? runs ? in planner ?
- tuning:
- mrobot having PI, PID terms
- having position-set input (also gets output?)
- having position-actual output
- graphing these things
- use UI slider (external) to set PID terms?
- should be able to use the same slider element directly inline
- example modules
- cleaning up reps
- looking for heirarchy demonstration
- imagining microcontrollers
- working on robot to inform desires: i.e. ui charts and graphs
## For Madison Park
- want that better-planner, better-stepper, better-inputs and graphic ui w/ 3js
- want network to not blow
- tokens w/ crc bit-level ?
- app does keepalive / green-when-on etc ?
- example of setup-for-consistent-feedback of variable, on a timer? tuning loops ... search ...
- do axis pullout separately from stepper motor ? accel command ? deep jog desire ... architecture still messy though
- bug hunting
- multiline change input paste doesn't work ... big state problem
- is load / save really consistent ? what is the state answer ?
- expected behavior: pressing the button on the raw move module should result in a move for every button press
- observed behavior: to send another raw move (via the button on the raw move module), we must reset the motor drivers.
- add reset button to hardware
- add router for reset, test
## Documentation
- GIFS
## Forever
- open the door, no cuffs
- option for 'native' multithreading by opening workers on each event ?
## Questionable Moves
- module deletion seems unclean
......@@ -294,8 +314,6 @@ View.assignProgram(program)
## WRT Representations
OK, should write this out properly at some point.
Module have
Inputs
Outputs
......@@ -320,6 +338,11 @@ To assemble a representation of these, we want to have a kind of 'netlist' that,
- 's' for save program uses hack-asf DOM alert to ask for path
## Desires
- reload / edit individual modules ?
- modules spawn new inputs / outputs ?
- big UI ?
- arrows catch for jogging
- editing ?
- heirarchy zoom
- architectural clarity betwixt UI and Heap
- some auto load / save currently.json file so that we can restart program w/o pain ... maybe just save on new user inputs ?
......@@ -333,6 +356,7 @@ To assemble a representation of these, we want to have a kind of 'netlist' that,
- consistent dereferencing, type checking implementation?
## UI Desires
- scroll / grab events for touchpads etc ... find a mac user and workshop this for one afternoon ?
- modules have visual ways to throw errors - i.e. flashing red, popping up...
- off-screen divs get pointers-to so that we don't get lost
- 'h' or something to zoom-to-extents
......
......@@ -31,6 +31,8 @@ var lastPos = { x: 10, y: 30 }
var wrapper = {}
var nav = {}
var verbose = false
/*
STARTUP ---------------------------------------------------
......@@ -64,7 +66,7 @@ window.onload = function() {
sckt = this
// say hello
socketSend('console', 'hello server')
console.log('socket open')
console.log('SCKT: socket open')
// ask for the current program
socketSend('get current program', '')
// main socket entry point
......@@ -75,10 +77,10 @@ window.onload = function() {
this.onerror = (err) => {
alert('link to server is broken')
location.reload()
console.log('socket error', err)
console.log('SCKT: socket error', err)
}
this.onclose = (evt) => {
console.log('socket closed', evt)
console.log('SCKT: socket closed', evt)
sckt = null
}
}
......@@ -110,33 +112,37 @@ function socketRecv(evt) {
console.log('RECV CONSOLE:', data)
break
case 'put module menu':
console.log('RECV MODULE MENU')
if(verbose) console.log('RECV MODULE MENU')
heapSendsModuleMenu(data)
break
case 'put program menu':
console.log('RECV PRG MENU')
if(verbose) console.log('RECV PRG MENU')
heapSendsProgramMenu(data)
break
case 'put program':
console.log('RECV PROGRAM')
if(verbose) console.log('RECV PROGRAM')
heapSendsNewProgram(data)
break
case 'put module':
console.log('RECV NEW MODULE')
if(verbose) console.log('RECV NEW MODULE')
heapSendsNewModule(data)
break
case 'put module change':
console.log('RECV MODULE CHANGE')
if(verbose) console.log('RECV MODULE CHANGE')
heapSendsModuleChange(data)
break
case 'put state change':
console.log('RECV STATE CHANGE')
if(verbose) console.log('RECV STATE CHANGE')
heapSendsStateChange(data)
break
case 'put ui change':
if(verbose) console.log('RECV UI CHANGE')
heapSendsUiChange(data)
break
case 'restart':
location.reload()
default:
console.log('ERR recv with non recognized type', recv)
console.log('ERR: recv with non recognized type', recv)
break
}
}
......@@ -157,7 +163,7 @@ HEAP -> SERVER ---------------------------------------------------
// always a rep, tho
var program = {}
// re-writes the program, adds a description,
// re-writes t program, adds a description,
// and loads multiple representations of modules to the view
function heapSendsNewProgram(prgm) {
......@@ -166,7 +172,7 @@ function heapSendsNewProgram(prgm) {
program = prgm
// 1st we want to git rm old files ...
// when adding links, we'll have to add all and then draw links
console.log(program)
if(verbose) console.log('LOAD PROGRAM', program)
for (mdlName in program.modules) {
addRepToView(program.modules[mdlName])
}
......@@ -186,11 +192,11 @@ function heapSendsNewModule(mdl) {
}
// writes DOM elements to represent the module, appends to the wrapper
// and appends to the rep object a .ui object
// and appends to the rep object a .dom object
// containing references to those DOM objects
function heapSendsModuleChange(data) {
console.log(data)
if(verbose) console.log('HEAP SENDS MODULE CHANGE', data)
// data should be rep of changed module
var rep = program.modules[data.description.id]
// we want a general case, but for now we know we're looking for
......@@ -206,7 +212,7 @@ function heapSendsModuleChange(data) {
var stateItem = rep.state[key]
if (stateItem != data.state[key]) {
stateItem = data.state[key]
rep.ui.state[key].value = data.state[key]
rep.dom.state[key].value = data.state[key]
}
}
// wreckless or wonderful?
......@@ -216,16 +222,21 @@ function heapSendsModuleChange(data) {
// update state from server to UI
function heapSendsStateChange(data) {
console.log('HEAP SENDS CHANGE STATE IN MODULE', data)
if(verbose) console.log('HEAP SENDS CHANGE STATE IN MODULE', data)
var rep = program.modules[data.id]
rep.state[data.key] = data.val
if (rep.state[data.key].type == 'multiline') {
rep.ui.state[data.key].value = data.val.value
if (typeof data.val == 'boolean') {
rep.dom.state[data.key].innerHTML = data.key + ':\t\t' + rep.state[data.key].toString()
} else {
rep.ui.state[data.key].value = data.val
rep.dom.state[data.key].value = data.val
}
}
function heapSendsUiChange(data){
if(true) console.log('HEAP SENDS CHANG UI IN MODULE', data)
//
}
/*
UI -> HEAP ---------------------------------------------------
......@@ -298,14 +309,6 @@ UTILITIES ---------------------------------------------------
*/
function isStateKey(key) {
if (key.indexOf('_') == 0 || key == 'emitters' || key == 'onChange' || key == 'emitChange') {
return false
} else {
return true
}
}
function redrawLinks() {
// probably not a great way to do this, we're removing everything
// svg -rm -r
......@@ -313,14 +316,14 @@ function redrawLinks() {
svg.removeChild(svg.firstChild)
}
// draw origin
var og1 = newLine(-15,0,15,0,5, false)
var og2 = newLine(0,-15,0,15,5, false)
var og1 = newLine(-15, 0, 15, 0, 5, false)
var og2 = newLine(0, -15, 0, 15, 5, false)
// find that link
var lnkPt
var nLnk = 0
for (mdlName in program.modules) {
if (program.modules[mdlName].description.isLink) {
lnkPt = getLeftWall(program.modules[mdlName].ui.domElem)
lnkPt = getLeftWall(program.modules[mdlName].dom.domElem)
}
}
// redraw thru all links, just look at reps
......@@ -328,11 +331,11 @@ function redrawLinks() {
var mdlRep = program.modules[mdlName]
for (key in mdlRep.outputs) {
var output = mdlRep.outputs[key]
var outputUi = mdlRep.ui.outputs[key]
var outputUi = mdlRep.dom.outputs[key]
for (input in output.calls) {
var toId = output.calls[input].parentId
var toKey = output.calls[input].key
var inputUi = program.modules[toId].ui.inputs[toKey]
var inputUi = program.modules[toId].dom.inputs[toKey]
var outPos = getOutputArrow(outputUi)
var inPos = getInputArrow(inputUi)
if (inputUi.isHovering || outputUi.isHovering) {
......@@ -344,7 +347,7 @@ function redrawLinks() {
}
if (mdlRep.description.isHardware && !mdlRep.description.isLink) {
nLnk++
var hwPt = getRightWall(mdlRep.ui.domElem)
var hwPt = getRightWall(mdlRep.dom.domElem)
lnkPt.y += 5 * nLnk
var ln = newLine(hwPt.x, hwPt.y, lnkPt.x, lnkPt.y, 7, true)
}
......@@ -463,7 +466,6 @@ function mouseUpDragListener(evt) {
// get json menu item and render
// and ask for module at /obj/key
oncontextmenu = function(evt) {
console.log(evt.target)
if (evt.target.className == 'modname') {
var modRep = program.modules[evt.target.innerHTML]
if (modRep) {
......@@ -503,6 +505,9 @@ document.onkeydown = function(evt) {
case 'm':
socketSend('get module menu', '')
break
case 'd':
console.log(program)
break
default:
break
}
......@@ -511,8 +516,8 @@ document.onkeydown = function(evt) {
function writeModuleOptionMenu(modRep) {
var menuDom = document.createElement('div')
menuDom.id = 'perModuleMenu'
menuDom.style.left = 10 + modRep.ui.domElem.offsetLeft + modRep.ui.domElem.offsetWidth + 'px'
menuDom.style.top = modRep.ui.domElem.offsetTop + 'px'
menuDom.style.left = 10 + modrep.dom.domElem.offsetLeft + modrep.dom.domElem.offsetWidth + 'px'
menuDom.style.top = modRep.dom.domElem.offsetTop + 'px'
// future: rm all inputs, rm all outputs, rename, open (heirarchy)
var opts = ['delete', 'copy']
for (i in opts) {
......
......@@ -48,7 +48,7 @@ function addRepToView(rep) {
var uiSetFlag
// place in pos if info present
// the rep.ui object will store references to the module's related DOM elements
// the rep.dom object will store references to the module's related DOM elements
if (rep.description.position != null) {
uiSetFlag = false
if (rep.description.position.left != null) {
......@@ -64,50 +64,61 @@ function addRepToView(rep) {
rep.description.position.top = lastPos.y
}
if (rep.ui == null) {
rep.ui = {}
if (rep.dom == null) {
rep.dom = {}
}
rep.ui.domElem = domElem
rep.dom.domElem = domElem
// WRITE UI STATE ELEMENTS
// WRITE STATE ELEMENTS
var stateElem = document.createElement('div')
stateElem.className = 'state'
rep.ui.state = {}
rep.dom.state = {}
for (st in rep.state) {
var inputItem = writeStateRep(stateElem, rep, st)
rep.ui.state[st] = inputItem
rep.dom.state[st] = inputItem
}
// WRITE INPUTS
var inElem = document.createElement('div')
inElem.className = 'inputs'
rep.ui.inputs = {}
rep.dom.inputs = {}
for (ip in rep.inputs) {
var li = writeEventRep(rep, 'input', ip)
inElem.appendChild(li)
rep.ui.inputs[ip] = li
rep.dom.inputs[ip] = li
}
// WRITE OUTPUTS
var outElem = document.createElement('div')
outElem.className = 'outputs'
rep.ui.outputs = {}
rep.dom.outputs = {}
for (op in rep.outputs) {
var li = writeEventRep(rep, 'output', op)
outElem.appendChild(li)
rep.ui.outputs[op] = li
rep.dom.outputs[op] = li
}
// HANDLE UNIQUE UIS
if (rep.ui != null) {
var uiElem = document.createElement('div')
uiElem.className = 'uidiv'
rep.dom.ui = {}
for (ui in rep.ui) {
writeUiElement(uiElem, rep, ui)
}
}
// APPEND TO CONTAINER
domElem.appendChild(inElem)
domElem.appendChild(outElem)
domElem.appendChild(stateElem)
domElem.appendChild(uiElem)
var clearElem = document.createElement('div')
clearElem.className = 'clear'
domElem.appendChild(clearElem)
wrapper.appendChild(rep.ui.domElem)
wrapper.appendChild(rep.dom.domElem)
if (uiSetFlag) {
putUi(rep)
}
......@@ -124,21 +135,21 @@ function writeEventRep(rep, type, key) {
name: key,
evt: evt
}
console.log('clicked', key)
if (verbose) console.log('EVENT HOOKUP CLK: ', key)
evtConnectHandler(ipclk)
})
li.addEventListener('mouseover', (evt) => {
if (type == 'input') {
rep.ui.inputs[key].isHovering = true
rep.dom.inputs[key].isHovering = true
} else if (type == 'output') {
rep.ui.outputs[key].isHovering = true
rep.dom.outputs[key].isHovering = true
}
redrawLinks()
li.addEventListener('mouseout', (evt) => {
if (type == 'input') {
rep.ui.inputs[key].isHovering = false
rep.dom.inputs[key].isHovering = false
} else if (type == 'output') {
rep.ui.outputs[key].isHovering = false
rep.dom.outputs[key].isHovering = false
}
redrawLinks()
})
......@@ -148,70 +159,62 @@ function writeEventRep(rep, type, key) {
function writeStateRep(container, rep, key) {
var variable = rep.state[key]
switch (variable.type) {
case 'button':
console.log('BUTTON!')
switch (typeof variable) {
case 'string':
var li = document.createElement('li')
li.appendChild(document.createTextNode(variable.label))
li.addEventListener('click', function() {
li.appendChild(document.createTextNode(key))
var input = document.createElement('input')
input.type = 'text'
input.size = 24
input.value = variable
input.addEventListener('change', function() {
rep.state[key] = input.value
putState(rep, key)
})
li.appendChild(input)
container.appendChild(li)
return li
return input
break
case 'multiline':
console.log('MULTILINE!')
case 'number':
var li = document.createElement('li')
li.appendChild(document.createTextNode(variable.label))
li.appendChild(document.createElement('br'))
var txtArea = document.createElement('textarea')
txtArea.rows = variable.rows
txtArea.cols = 25
txtArea.value = variable.value
txtArea.addEventListener('change', function() {
rep.state[key].value = txtArea.value
li.appendChild(document.createTextNode(key))
var input = document.createElement('input')
input.type = 'text'
input.size = 24
input.value = variable.toString()
input.addEventListener('change', function(evt) {
rep.state[key] = parseFloat(input.value)
putState(rep, key)
})
li.appendChild(txtArea)
li.appendChild(input)
container.appendChild(li)
return txtArea
return input
break
default:
if (typeof variable == 'string') {
var li = document.createElement('li')
li.appendChild(document.createTextNode(key))
var input = document.createElement('input')
input.type = 'text'
input.size = 24
input.value = variable
input.addEventListener('change', function() {
rep.state[key] = input.value
putState(rep, key)
})
li.appendChild(input)
container.appendChild(li)
return input
} else if (typeof variable == 'number') {
case 'boolean':
var li = document.createElement('li')
li.innerHTML = key + ':\t\t' + variable.toString()
// TODO: tag align-right?
li.addEventListener('click', function() {
if (rep.state[key]) {
rep.state[key] = false
} else {
rep.state[key] = true
}
putState(rep, key)
})
container.appendChild(li)
return li
// TODO : return what ?
break
case 'object':
// first, handle arrays
if (Array.isArray(variable)) {
var li = document.createElement('li')
li.appendChild(document.createTextNode(key))
var input = document.createElement('input')
input.type = 'text'
input.size = 24
input.value = variable.toString()
input.addEventListener('change', function(evt) {
rep.state[key] = parseFloat(input.value)
putState(rep, key)
})
li.appendChild(input)
container.appendChild(li)
return input
} else if (typeof variable == 'object') {
if (Array.isArray(variable)) {
var li = document.createElement('li')
li.appendChild(document.createTextNode(key))
var input = document.createElement('input')
input.type = 'text'
input.size = 24
if (typeof variable[0] == 'number') {
input.value = variable.toString()
input.addEventListener('change', function() {
var arr = input.value.split(',')
......@@ -224,12 +227,86 @@ function writeStateRep(container, rep, key) {
li.appendChild(input)
container.appendChild(li)
return input
} else if (typeof variable[0] == 'string') {
input.value = variable.toString()
input.addEventListener('change', function() {
var arr = input.value.split(',')
arr.forEach(function(element, index, array) {
array[index] = element
})
rep.state[key] = arr
putState(rep, key)
})
li.appendChild(input)
container.appendChild(li)
return input
} else if (typeof variable[0] == 'object') {
throw 'ERR not going to handle object arrays'
}
} else {
console.log("unui'd type:", typeof variable)
throw 'ERR not going to handle objects in state'
}
break
default:
console.log("ERR: state walked and no reciprocal code")
}
}
function writeUiElement(container, rep, key) {
// pull the representation object from what we were sent
var ui = rep.ui[key]
console.log('write ui', ui)
// load this thing,
ui.script = document.createElement('script')
ui.script.onerror = function(err){
console.log('ERR from ui script', err)
}
ui.script.onload = function(msg){
console.log('script loaded ?')
}
container.appendChild(ui.script)
ui.script.src = ui.clientPath
// make an xhttp request for the code
/*
var request = new XMLHttpRequest()
request.open('GET', ui.clientPath)
request.responseType = 'text/javascript'
request.onLoad = function() {
console.log(request.response)
}
request.send()
fetch(ui.clientPath).then(function(response) {
response.text().then(function(text) {
console.log(text)
console.log(ui)
ui.script.appendChild(document.createTextNode(text))
container.apppendChild(ui.script)
})
})
*/
/*
// give it access to the socket,
ui.thing.sendToHeap = function(msg) {
var data = {
id: rep.description.id,
key: key,
msg: msg
}
socketSend('put ui change', data)
}
container.appendChild(ui.thing.domElement)
rep.dom.ui[key] = ui.thing.domElement
*/
}
......@@ -302,11 +379,11 @@ function newLine(x1, y1, x2, y2, stroke, dashed) {
var ln = {}
ln.elem = document.createElementNS(svgns, 'line')
ln.elem.style.stroke = '#1a1a1a'
if(dashed){
if (dashed) {
ln.elem.setAttribute('stroke-dasharray', '21, 7, 7, 7')
}
ln.elem.style.fill = 'none'
if(stroke){
if (stroke) {
ln.elem.style.strokeWidth = stroke + 'px'
} else {
ln.elem.style.strokeWidth = '6px'
......
......@@ -7,8 +7,6 @@
<body>
<link href="style.css" rel="stylesheet">
<!-- <script type="text/javascript" src="dat.gui.js"></script>
<script type="text/javascript" src="dummies.js"></script> -->
<script type="text/javascript" src="divtools.js"></script>
<script type="text/javascript" src="client.js"></script>
<div id = "nav">
......
// MIT License:
//
// Copyright (c) 2010-2013, Joe Walnes
// 2013-2018, Drew Noakes
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/**
* Smoothie Charts - http://smoothiecharts.org/
* (c) 2010-2013, Joe Walnes
* 2013-2018, Drew Noakes
*
* v1.0: Main charting library, by Joe Walnes
* v1.1: Auto scaling of axis, by Neil Dunn
* v1.2: fps (frames per second) option, by Mathias Petterson
* v1.3: Fix for divide by zero, by Paul Nikitochkin
* v1.4: Set minimum, top-scale padding, remove timeseries, add optional timer to reset bounds, by Kelley Reynolds
* v1.5: Set default frames per second to 50... smoother.
* .start(), .stop() methods for conserving CPU, by Dmitry Vyal
* options.interpolation = 'bezier' or 'line', by Dmitry Vyal
* options.maxValue to fix scale, by Dmitry Vyal
* v1.6: minValue/maxValue will always get converted to floats, by Przemek Matylla
* v1.7: options.grid.fillStyle may be a transparent color, by Dmitry A. Shashkin
* Smooth rescaling, by Kostas Michalopoulos
* v1.8: Set max length to customize number of live points in the dataset with options.maxDataSetLength, by Krishna Narni
* v1.9: Display timestamps along the bottom, by Nick and Stev-io
* (https://groups.google.com/forum/?fromgroups#!topic/smoothie-charts/-Ywse8FCpKI%5B1-25%5D)
* Refactored by Krishna Narni, to support timestamp formatting function
* v1.10: Switch to requestAnimationFrame, removed the now obsoleted options.fps, by Gergely Imreh
* v1.11: options.grid.sharpLines option added, by @drewnoakes
* Addressed warning seen in Firefox when seriesOption.fillStyle undefined, by @drewnoakes
* v1.12: Support for horizontalLines added, by @drewnoakes
* Support for yRangeFunction callback added, by @drewnoakes
* v1.13: Fixed typo (#32), by @alnikitich