From 9322f1a4aa6eb11c9982fb66ba914ccf34332792 Mon Sep 17 00:00:00 2001
From: Jake <jake.read@cba.mit.edu>
Date: Fri, 14 Dec 2018 19:58:29 -0500
Subject: [PATCH] big UI elements refactor

---
 README.md                              |   6 ++
 client/divtools.js                     |   7 +-
 client/ui/threeCanvas.js               |   4 +-
 client/ui/uiButton.js                  |  11 ++-
 modules/flowcontrol/and.js             |  12 ++-
 modules/hardware/atkbreadboardboard.js |  25 ++++--
 modules/hardware/atkmrobot.js          |  17 ++--
 modules/hardware/atkseriallink.js      |  13 ++--
 modules/hardware/atkstepper.js         |  12 ++-
 modules/motion/planner.js              |  26 ++++---
 modules/ui/button.js                   |  12 ++-
 modules/ui/multiline.js                |  93 +++++++++++++++-------
 modules/ui/robotCanvas.js              |  69 ++++++++++++++++
 modules/ui/stest.js                    |  13 ++--
 modules/ui/string.js                   |  12 ++-
 modules/util/andgate.js                |  12 ++-
 modules/util/array.js                  |  12 ++-
 modules/util/collector.js              |  12 ++-
 modules/util/gate.js                   |  12 ++-
 modules/util/gateCounter.js            |  17 ++--
 modules/util/number.js                 |  12 ++-
 modules/util/parallelContencator.js    |  12 ++-
 programs/temp.json                     |  55 ++++++-------
 py/leastSquares.py                     |   4 +-
 robot.js                               |  12 ++-
 src/atkunit.js                         | 104 +++++++++++++------------
 src/jsui.js                            |  58 +++++++++-----
 src/ui/multiline.js                    |  55 -------------
 src/ui/threeCanvas.js                  |  46 -----------
 src/ui/uiButton.js                     |  34 --------
 30 files changed, 442 insertions(+), 347 deletions(-)
 create mode 100644 modules/ui/robotCanvas.js
 delete mode 100644 src/ui/multiline.js
 delete mode 100644 src/ui/threeCanvas.js
 delete mode 100644 src/ui/uiButton.js

diff --git a/README.md b/README.md
index f0b96be..05eb066 100644
--- a/README.md
+++ b/README.md
@@ -267,6 +267,11 @@ View.assignProgram(program)
 
 ## Immediately
 
+- go for generic ui class like remote hardware: subscribes etc 
+
+- three robot canvas 
+- set module display width 
+
 - saved program doesn't save state booleans 
 
 - ui objects ... refresh ... use state model ? 
@@ -281,6 +286,7 @@ View.assignProgram(program)
 
 - tuning:
  - mrobot having PI, PID terms
+ - probably good to do a remote-get ... 
  - having position-set input (also gets output?)
  - having position-actual output
  - graphing these things
diff --git a/client/divtools.js b/client/divtools.js
index 60ea797..dc8f3cd 100644
--- a/client/divtools.js
+++ b/client/divtools.js
@@ -265,13 +265,13 @@ function loadUIScript(container, rep, key) {
     }
 
     ui.script.onload = function(msg) {
-        console.log('UI script loaded')
+        console.log('UI script loaded and - ')
         // each ui script writes this function to 
         // window. (global scope) on load,
         // so we can use it now to get hooks
         registerNewModule(rep.description.id, key)
         // that function will be used to append to
-        console.log('UI script hooked, having lump at', program.modules[rep.description.id].ui[key].lump)
+        console.log('hooked, having lump at', program.modules[rep.description.id].ui[key].lump)
         container.appendChild(program.modules[rep.description.id].ui[key].lump.domElem)
     }
 }
@@ -279,8 +279,7 @@ function loadUIScript(container, rep, key) {
 function writeUiElement(container, rep, key) {
     // pull the representation object from what we were sent 
     var ui = rep.ui[key]
-    console.log('write ui', ui)
-    console.log('at rep', rep)
+    console.log('write ui', ui.clientPath, 'at rep', rep.description.id)
 
     // first, check if ui also has lib to load 
     if (ui.libPath != null) {
diff --git a/client/ui/threeCanvas.js b/client/ui/threeCanvas.js
index dd5868b..0beeaf0 100644
--- a/client/ui/threeCanvas.js
+++ b/client/ui/threeCanvas.js
@@ -97,7 +97,9 @@
         var data = {
             id: threeCanvas.parentId,
             key: threeCanvas.key,
-            msg: 'onload'
+            msg: {
+                key: 'onload'
+            }
         }
         socketSend('put ui change', data)
     }
diff --git a/client/ui/uiButton.js b/client/ui/uiButton.js
index 82d0935..1a032d0 100644
--- a/client/ui/uiButton.js
+++ b/client/ui/uiButton.js
@@ -12,7 +12,9 @@
 		var data = {
 			id: btn.parentId,
 			key: btn.key,
-			msg: 'onclick'
+			msg: {
+				key: 'onclick'
+			}
 		}
 		socketSend('put ui change', data)
 	})
@@ -29,8 +31,7 @@
 	// upstream, and besides, wouldn't be able to get anything from
 	// the server 
 	btn.onMessage = function(msg){
-		console.log('got message in client side ui object', msg)
-		if(msg.call == 'setText'){
+		if(msg.calls == 'setText'){
 			btn.domElem.innerHTML = msg.argument
 		}
 	}
@@ -48,7 +49,9 @@
 		var data = {
 			id: btn.parentId,
 			key: btn.key,
-			msg: 'onload'
+			msg: {
+				key: 'onload'
+			}
 		}
 		socketSend('put ui change', data)
 	}
diff --git a/modules/flowcontrol/and.js b/modules/flowcontrol/and.js
index 15db836..8c27a7e 100644
--- a/modules/flowcontrol/and.js
+++ b/modules/flowcontrol/and.js
@@ -29,10 +29,14 @@ function AndFlow() {
 
     andFlow.ui = UI()
     var ui = andFlow.ui
-    ui.addElement('btnReset', './ui/uiButton.js', onReset)
-    ui.btnReset.onload = function() {
-        ui.btnReset.setText('reset')
-    }
+    ui.addElement('btnReset', 'ui/uiButton.js')
+    ui.btnReset.subscribe('onload', function(msg){
+        ui.btnReset.send({
+            calls: 'setText',
+            argument: 'reset'
+        })
+    })
+    ui.btnReset.subscribe('onclick', onReset)
 
     andFlow.inputs = {
         reset: Input('any', onReset),
diff --git a/modules/hardware/atkbreadboardboard.js b/modules/hardware/atkbreadboardboard.js
index ae00978..2f2a76c 100644
--- a/modules/hardware/atkbreadboardboard.js
+++ b/modules/hardware/atkbreadboardboard.js
@@ -37,14 +37,23 @@ function ATKBreadBoardBoard() {
     state.onUiChange('servoVal', onServoValChange)
 
     var ui = atkbbb.ui 
-    ui.addElement('servoButton', './ui/uiButton.js', onServoValChange)
-    ui.servoButton.onload = function(){
-        ui.servoButton.setText('click to send servo value')
-    }
-    ui.addElement('adcReqButton', './ui/uiButton.js', onADCRequest)
-    ui.adcReqButton.onload = function(){
-        ui.adcReqButton.setText('click to request adc conversion')
-    }
+    ui.addElement('servoButton', 'ui/uiButton.js')
+    ui.servoButton.subscribe('onload', function(msg){
+        ui.servoButton.send({
+            calls: 'setText',
+            argument: 'click to send servo value'
+        })
+    })
+    ui.servoButton.subscribe('onclick', onServoValChange)
+
+    ui.addElement('adcReqButton', 'ui/uiButton.js')
+    ui.adcReqButton.subscribe('onload', function(msg){
+        ui.adcReqButton.send({
+            calls: 'setText',
+            argument: 'click to request adc conversion'
+        })
+    })
+    ui.adcReqButton.subscribe('onclick', onADCRequest)
 
     // to send things down the well, we can use
     // atkbbb.route.send(packet) 
diff --git a/modules/hardware/atkmrobot.js b/modules/hardware/atkmrobot.js
index eae2766..34215a9 100644
--- a/modules/hardware/atkmrobot.js
+++ b/modules/hardware/atkmrobot.js
@@ -49,14 +49,15 @@ function ATKMathRobot() {
     state.enc_cnt = 16384
     state.enc_offset = 0
     state.enc_reverse = false 
+    state.pc_r = 0
     state.pc_t = 2048
     state.onUiChange('pc_t', onPositionTargetUserChange)
 
-    state.pKp = 4.5
+    state.pKp = 10
     state.onUiChange('pKp', onKValsUpdate)
-    state.pKi = 0.0
+    state.pKi = 0.005
     state.onUiChange('pKi', onKValsUpdate)
-    state.pKd = 0.0
+    state.pKd = 40.0
     state.onUiChange('pKd', onKValsUpdate)
     state.cKp = 4.0
     state.onUiChange('cKp', onKValsUpdate)
@@ -67,10 +68,11 @@ function ATKMathRobot() {
 
     atkmr.ui = UI()
     var ui = atkmr.ui
-    ui.addElement('walkValButton', './ui/uiButton.js', onPositionTickTock)
-    ui.walkValButton.onload = function() {
-        ui.walkValButton.setText('walk value')
-    }
+    ui.addElement('walkValButton', 'ui/uiButton.js')
+    ui.walkValButton.subscribe('onload', function(msg){
+        ui.walkValButton.call('setText', 'click to walk val')
+    })
+    ui.walkValButton.subscribe('onclick', onPositionTickTock)
 
     // to send things down the well, we can use
     // atkmr.route.send(packet) 
@@ -94,6 +96,7 @@ function ATKMathRobot() {
 
     atkmr.route.subscribe(145, function(msg){
         var pos = PCKT.unPack32(msg.slice(1))
+        state.pc_r = pos 
         if(state.enc_reverse){
             pos = state.enc_cnt - pos 
         }
diff --git a/modules/hardware/atkseriallink.js b/modules/hardware/atkseriallink.js
index dd541a5..6427385 100644
--- a/modules/hardware/atkseriallink.js
+++ b/modules/hardware/atkseriallink.js
@@ -34,11 +34,14 @@ function ATKSerialLink() {
     atkSerialLink.ui = UI() 
     var ui = atkSerialLink.ui 
 
-    ui.addElement('kickButton', './ui/uiButton.js', findSerialPort)
-
-    ui.kickButton.onload = function(){
-        ui.kickButton.setText('click to find and connect')
-    }
+    ui.addElement('kickButton', 'ui/uiButton.js')
+    ui.kickButton.subscribe('onload', function(msg){
+        ui.kickButton.send({
+            calls: 'setText',
+            argument: 'click to find and connect'
+        })
+    })
+    ui.kickButton.subscribe('onclick', findSerialPort)
 
     atkSerialLink.init = function(){
         //findSerialPort()
diff --git a/modules/hardware/atkstepper.js b/modules/hardware/atkstepper.js
index e17e81c..22e86c5 100644
--- a/modules/hardware/atkstepper.js
+++ b/modules/hardware/atkstepper.js
@@ -51,10 +51,14 @@ function Stepper() {
     state.rawMove = -10
 
     var ui = stepper.ui 
-    ui.addElement('rawButton', './ui/uiButton.js', onRawMove)
-    ui.rawButton.onload = function(){
-        ui.rawButton.setText('click to send test move')
-    }
+    ui.addElement('rawButton', 'ui/uiButton.js')
+    ui.rawButton.subscribe('onload', function(msg){
+        ui.rawButton.send({
+            calls: 'setText',
+            argument: 'click to send test move'
+        })
+    })
+    ui.rawButton.subscribe('onclick', onRawMove)
 
     state.lead = 0
     state.position = 0 // in steps 
diff --git a/modules/motion/planner.js b/modules/motion/planner.js
index 5c38c75..1430809 100644
--- a/modules/motion/planner.js
+++ b/modules/motion/planner.js
@@ -47,15 +47,23 @@ function Planner() {
 
     planner.ui = UI()
     var ui = planner.ui 
-    ui.addElement('resetButton', './ui/uiButton.js', onPlannerReset)
-    ui.resetButton.onload = function(){
-        ui.resetButton.setText('reset planner')
-    }
-
-    ui.addElement('startStopButton', './ui/uiButton.js', onStartStop)
-    ui.startStopButton.onload = function(){
-        ui.startStopButton.setText('start / stop planner')
-    }
+    ui.addElement('resetButton', 'ui/uiButton.js')
+    ui.resetButton.subscribe('onload', function(msg){
+        ui.resetButton.send({
+            calls: 'setText',
+            argument: 'reset planner'
+        })
+    })
+    ui.resetButton.subscribe('onclick', onPlannerReset)
+
+    ui.addElement('startStopButton', 'ui/uiButton.js')
+    ui.startStopButton.subscribe('onload', function(msg){
+        ui.startStopButton.send({
+            calls: 'setText',
+            argument: 'start / stop planner'
+        })
+    })
+    ui.startStopButton.subscribe('onclick', onStartStop)
 
     planner.inputs = {
         instruction: Input('move instruction', onNewInstruction),
diff --git a/modules/ui/button.js b/modules/ui/button.js
index cc0151e..935c3de 100644
--- a/modules/ui/button.js
+++ b/modules/ui/button.js
@@ -25,10 +25,14 @@ function uiButton() {
 
     button.ui = UI() 
     var ui = button.ui 
-    ui.addElement('btn', './ui/uiButton.js', onButtonPress)
-    ui.btn.onload = function(){
-        ui.btn.setText('click!')
-    }
+    ui.addElement('btn', 'ui/uiButton.js')
+    ui.btn.subscribe('onload', function(msg){
+        ui.btn.send({
+            calls: 'setText',
+            argument: 'click!'
+        })
+    })
+    ui.btn.subscribe('onclick', onButtonPress)
 
     button.inputs = {
         thru: Input('any', onButtonPress) // makes anything into '1' event
diff --git a/modules/ui/multiline.js b/modules/ui/multiline.js
index 9c7e687..ba14cc0 100644
--- a/modules/ui/multiline.js
+++ b/modules/ui/multiline.js
@@ -6,13 +6,19 @@ let State = JSUnit.State
 
 // interface elements
 const JSUI = require('../../src/jsui.js')
-let UI = JSUI.UI 
+let UI = JSUI.UI
+
+/* WARN
+not refactored since UI API refactor
+*/
 
 const fs = require('fs')
 
 // a constructor, a fn, a javascript mess
 function MultiLineIn() {
 
+    console.log("WARNING - LOADING MULTILINE - not refactored since UI API refactor")
+
     var multilinein = {
         // descriptions are used in UI
         description: {
@@ -26,33 +32,61 @@ function MultiLineIn() {
     var state = multilinein.state
 
     multilinein.ui = UI()
-    var ui = multilinein.ui 
-    ui.addElement('thruButton', './ui/uiButton.js', lineThru)
-    ui.thruButton.onload = function(){
-        ui.thruButton.setText('click to advance line-by-line')
-    }
-
-    ui.addElement('previously', './ui/multiline.js', null)
-    ui.previously.onload = function(){
-        ui.previously.setContents('-')
-        ui.previously.setLabel('previously:')
-        ui.previously.setRows(15)
-    }
+    var ui = multilinein.ui
+    ui.addElement('thruButton', 'ui/uiButton.js')
+    ui.thruButton.subscribe('onload', function(msg) {
+        ui.thruButton.send({
+            calls: 'setText',
+            argument: 'click to advance line-by-line'
+        })
+    })
+    ui.thruButton.subscribe('onclick', lineThru)
+
+    ui.addElement('previously', './ui/multiline.js')
+    ui.previously.subscribe('onload', function(msg) {
+        ui.previously.send({
+            calls: 'setContents',
+            argument: '-'
+        })
+        ui.previously.send({
+            calls: 'setLabel',
+            argument: 'previously:'
+        })
+        ui.previously.send({
+            calls: 'setRows',
+            argument: 15
+        })
+    })
 
-    ui.addElement('justNow', './ui/multiline.js', null)
-    ui.justNow.onload = function(){
-        ui.justNow.setContents('-')
-        ui.justNow.setLabel('just now:')
-        ui.justNow.setRows(1)
-    }
+    ui.addElement('justNow', './ui/multiline.js')
+    ui.justNow.subscribe('onload', function(msg) {
+        ui.justNow.send({
+            calls: 'setContents',
+            argument: '-'
+        })
+        ui.justNow.send({
+            calls: 'setlabel',
+            argument: 'just now:'
+        })
+        ui.justNow.send({
+            calls: 'setRows',
+            argument: 1
+        })
+    })
 
-    ui.addElement('incoming', './ui/multiline.js', null)
-    ui.incoming.onload = function(){
+    ui.addElement('incoming', './ui/multiline.js')
+    ui.incoming.subscribe('onload', function(msg) {
         //ui.incoming.setContents('G0 F50 X10Y10Z10\nG0 X20Y20Z0\nG0 X0\nG0 Y10\nG0 F50 X10Y10Z10\nG0 X20Y20Z0\nG0 X0\nG0 Y10\nG0 F50 X10Y10Z10\nG0 X20Y20Z0\nG0 X0\nG0 Y10\nG0 F50 X10Y10Z10\nG0 X20Y20Z0\nG0 X0\nG0 Y10\n')
-        ui.incoming.setLabel('incoming:')
-        ui.incoming.setRows(50)
+        ui.incoming.send({
+            calls: 'setLabel',
+            argument: 'incoming:'
+        })
+        ui.incoming.send({
+            calls: 'setRows',
+            argument: 50
+        }) 
         onLoadFile('./files/dogbone.gcode')
-    }
+    })
 
     multilinein.inputs = {
         req: Input('number', onLineRequest),
@@ -68,7 +102,7 @@ function MultiLineIn() {
 
     function lineThru() {
         // get all as arrays with linebreak as delimiter 
-        
+
         var outBox = ui.previously.contents.split('\n')
         var box = ui.justNow.contents
         var inBox = ui.incoming.contents.split('\n')
@@ -88,8 +122,8 @@ function MultiLineIn() {
         }
     }
 
-    function checkContent(string){
-        if(typeof string == 'string'){
+    function checkContent(string) {
+        if (typeof string == 'string') {
             return string
         } else {
             return '-'
@@ -108,7 +142,10 @@ function MultiLineIn() {
             if (err) throw err;
             console.log('Loading:')
             console.log(data);
-            ui.incoming.setContents(data)
+            ui.incoming.send({
+                calls: 'setContents',
+                argument: data
+            })
         })
     }
 
diff --git a/modules/ui/robotCanvas.js b/modules/ui/robotCanvas.js
new file mode 100644
index 0000000..1250f4b
--- /dev/null
+++ b/modules/ui/robotCanvas.js
@@ -0,0 +1,69 @@
+// boilerplate atkapi header
+const JSUnit = require('../../src/jsunit.js')
+let Input = JSUnit.Input
+let Output = JSUnit.Output
+let State = JSUnit.State
+
+// interface elements
+const JSUI = require('../../src/jsui.js')
+let UI = JSUI.UI
+
+// a constructor, a fn, a javascript mess
+function RobotCanvas() {
+
+    var rbtCanvas = {
+        // descriptions are used in UI
+        description: {
+            name: 'ThreeJS-Canvas',
+            alt: 'graphix'
+        }
+    }
+
+    rbtCanvas.state = State()
+    // alias !
+    var state = rbtCanvas.state
+
+    rbtCanvas.inputs = {
+        xy1: Input('array', onNewXY1),
+        xy2: Input('array', onNewXY2),
+        // do some canvas stuff 
+    }
+
+    rbtCanvas.outputs = {
+        //log: Output('string')
+    }
+
+    rbtCanvas.ui = UI()
+    var ui = rbtCanvas.ui
+    ui.addElement('threeCanvas', 'ui/threeCanvas.js')
+    // add bonus lib path 
+    ui.threeCanvas.libPath = 'ui/libs/three.js'
+    ui.threeCanvas.subscribe('onload', function(msg){
+        console.log('catch canvas load', msg)
+    })
+
+    /*
+    ui.threeCanvas.onload = function() {
+        console.log('canvas is loaded')
+    }
+    */
+    
+    function onNewXY1(array) {
+        ui.threeCanvas.send({
+            calls: "updateXY1",
+            argument: [array[0], 0, array[1]]
+        })
+    }
+
+    function onNewXY2(array){
+        ui.threeCanavs.send({
+            calls: "updateXY2",
+            argument: [array[0], 0, array[1]]
+        })
+    }
+
+    return rbtCanvas
+}
+
+// exports 
+module.exports = RobotCanvas
\ No newline at end of file
diff --git a/modules/ui/stest.js b/modules/ui/stest.js
index 4f43d49..2add0b7 100644
--- a/modules/ui/stest.js
+++ b/modules/ui/stest.js
@@ -48,15 +48,18 @@ function STest() {
 	// other items for interaction are explicitly UI 
 	stest.ui = UI() 
 	var ui = stest.ui 
-	ui.addElement('btnex', './ui/uiButton.js', onButtonData)
-
-	ui.btnex.onload = function(){
+	ui.addElement('btnex', 'ui/uiButton.js')
+	ui.btnex.subscribe('onload', function(msg){
 		console.log('ok, yes, loaded')
-	}
+    })
+    ui.btnex.subscribe('onclick', onButtonData)
 
 	function onButtonData(evt){
 		console.log('on module callback', evt)
-		ui.btnex.setText('hello from ss')
+		ui.btnex.send({
+            calls: 'setText',
+            argument: 'hello from stest'
+        })
 	}
 
 	return stest
diff --git a/modules/ui/string.js b/modules/ui/string.js
index 1b47b82..c33ae8a 100644
--- a/modules/ui/string.js
+++ b/modules/ui/string.js
@@ -31,10 +31,14 @@ function uiString() {
 
     uistring.ui = UI() 
     var ui = uistring.ui 
-    ui.addElement('onNumberButton', './ui/uiButton.js', onStringDesire)
-    ui.onNumberButton.onload = function() {
-        ui.onNumberButton.setText('number out ->')
-    }
+    ui.addElement('onNumberButton', 'ui/uiButton.js')
+    ui.onNumberButton.subscribe('onload', function(msg){
+        ui.onNumberButton.send({
+            calls: 'setText',
+            argument: 'number out ->'
+        })
+    })
+    ui.onNumberButton.subscribe('onclick', onStringDesire)
 
     // inputs are required, and must be Input('type', callback) 
     uistring.inputs = {
diff --git a/modules/util/andgate.js b/modules/util/andgate.js
index 1c970f6..16ba69f 100644
--- a/modules/util/andgate.js
+++ b/modules/util/andgate.js
@@ -29,10 +29,14 @@ function AndFlowControl() {
 
     andGate.ui = UI() 
     var ui = andGate.ui 
-    ui.addElement('openButton', './ui/uiButton.js', onButtonPress)
-    ui.openButton.onload = function() {
-        ui.openButton.setText('open / close')
-    }
+    ui.addElement('openButton', 'ui/uiButton.js')
+    ui.openButton.subscribe('onload', function(msg){
+        ui.openButton.send({
+            calls: 'setText',
+            argument: 'open / close'
+        })
+    })
+    ui.openButton.subscribe('onclick', onButtonPress)
 
     andGate.inputs = {
         thru: Input('any', andGateKeeper) // makes anything into '1' event
diff --git a/modules/util/array.js b/modules/util/array.js
index 9de3e74..52df7ed 100644
--- a/modules/util/array.js
+++ b/modules/util/array.js
@@ -31,10 +31,14 @@ function UIArray() {
 
     uiArray.ui = UI() 
     var ui = uiArray.ui 
-    ui.addElement('onArrayButton', './ui/uiButton.js', onArrayDesire)
-    ui.onArrayButton.onload = function() {
-        ui.onArrayButton.setText('array out ->')
-    }
+    ui.addElement('onArrayButton', 'ui/uiButton.js')
+    ui.onArrayButton.subscribe('onload', function(msg){
+        ui.onArrayButton.send({
+            calls: 'setText',
+            argument: 'array out ->'
+        })
+    })
+    ui.onArrayButton.subscribe('onclick', onArrayDesire)
 
     // inputs are required, and must be Input('type', callback) 
     uiArray.inputs = {
diff --git a/modules/util/collector.js b/modules/util/collector.js
index a5cb44a..6425fca 100644
--- a/modules/util/collector.js
+++ b/modules/util/collector.js
@@ -42,10 +42,14 @@ function Collector() {
 
     collector.ui = UI() 
     var ui = collector.ui 
-    ui.addElement('onDumpButton', './ui/uiButton.js', onDumpDesire)
-    ui.onDumpButton.onload = function() {
-        ui.onDumpButton.setText('array out ->')
-    }
+    ui.addElement('onDumpButton', 'ui/uiButton.js')
+    ui.onDumpButton.subscribe('onload', function(msg){
+        ui.onDumpButton.send({
+            calls: 'setText',
+            argument: 'array out ->'
+        })
+    })
+    ui.onDumpButton.subscribe('onclick', onDumpDesire)
 
     // inputs are required, and must be Input('type', callback) 
     collector.inputs = {
diff --git a/modules/util/gate.js b/modules/util/gate.js
index 7a24c4b..d4e5f17 100644
--- a/modules/util/gate.js
+++ b/modules/util/gate.js
@@ -27,10 +27,14 @@ function Gate() {
 
     gate.ui = UI()
     var ui = gate.ui
-    ui.addElement('openButton', './ui/uiButton.js', onButtonPress)
-    ui.openButton.onload = function() {
-        ui.openButton.setText('click to open gate')
-    }
+    ui.addElement('openButton', 'ui/uiButton.js')
+    ui.openButton.subscribe('onload', function(msg){
+        ui.openButton.send({
+            calls: 'setText',
+            argument: 'click to open gate'
+        })
+    })
+    ui.openButton.subscribe('onclick', onButtonPress)
 
     // yikes 
     gate.isOpen = false
diff --git a/modules/util/gateCounter.js b/modules/util/gateCounter.js
index b367b56..0e356c0 100644
--- a/modules/util/gateCounter.js
+++ b/modules/util/gateCounter.js
@@ -28,13 +28,20 @@ function GateCounter() {
 
     gateCounter.ui = UI()
     var ui = gateCounter.ui
-    ui.addElement('openButton', './ui/uiButton.js', onButtonPress)
-    ui.openButton.onload = function() {
-        ui.openButton.setText('click to add ' +  state.addThis.toString() + ' to count')
-    }
+    ui.addElement('openButton', 'ui/uiButton.js')
+    ui.openButton.subscribe('onload', function(msg){
+        ui.openButton.send({
+            calls: 'setText',
+            argument: 'click to add ' +  state.addThis.toString() + ' to count'
+        })
+    })
+    ui.openButton.subscribe('onclick', onButtonPress)
 
     state.onUiChange('addThis', function(){
-        ui.openButton.setText('click to add ' +  state.addThis.toString() + ' to count')
+        ui.openButton.send({
+            calls: 'setText',
+            argument: 'click to add ' +  state.addThis.toString() + ' to count'
+        })
     })
 
     // yikes 
diff --git a/modules/util/number.js b/modules/util/number.js
index 6f429bd..650d66b 100644
--- a/modules/util/number.js
+++ b/modules/util/number.js
@@ -31,10 +31,14 @@ function uiNum() {
 
     uinum.ui = UI() 
     var ui = uinum.ui 
-    ui.addElement('onNumberButton', './ui/uiButton.js', onNumberDesire)
-    ui.onNumberButton.onload = function() {
-        ui.onNumberButton.setText('number out ->')
-    }
+    ui.addElement('onNumberButton', 'ui/uiButton.js')
+    ui.onNumberButton.subscribe('onload', function(msg){
+        ui.onNumberButton.send({
+            calls: 'setText',
+            argument: 'number out ->'
+        })
+    })
+    ui.onNumberButton.subscribe('onclick', onNumberDesire)
 
     // inputs are required, and must be Input('type', callback) 
     uinum.inputs = {
diff --git a/modules/util/parallelContencator.js b/modules/util/parallelContencator.js
index 14647e2..894866e 100644
--- a/modules/util/parallelContencator.js
+++ b/modules/util/parallelContencator.js
@@ -31,10 +31,14 @@ function ParallelContencator() {
 
     parallelContencator.ui = UI()
     var ui = parallelContencator.ui
-    ui.addElement('onOutputButton', './ui/uiButton.js', doThroughput)
-    ui.onOutputButton.onload = function() {
-        ui.onOutputButton.setText('array out ->')
-    }
+    ui.addElement('onOutputButton', 'ui/uiButton.js')
+    ui.onOutputButton.subscribe('onload', function(msg){
+        ui.onOutputButton.send({
+            calls: 'setText',
+            argument: 'array out ->'
+        })
+    })
+    ui.onOutputButton.subscribe('onclick', doThroughput)
 
     // inputs are required, and must be Input('type', callback) 
     parallelContencator.inputs = {
diff --git a/programs/temp.json b/programs/temp.json
index a8a8a11..d0a0eec 100644
--- a/programs/temp.json
+++ b/programs/temp.json
@@ -20,9 +20,9 @@
       "inputs": {},
       "outputs": {},
       "state": {
-        "portName": "COM18",
-        "portStatus": "open",
-        "log": false
+        "portName": "---",
+        "portStatus": "closed",
+        "log": true
       },
       "ui": {
         "kickButton": {
@@ -81,8 +81,9 @@
         "message": "no packet yet",
         "route": "0,0",
         "enc_cnt": 16384,
-        "enc_offset": 13500,
+        "enc_offset": 13390,
         "enc_reverse": true,
+        "pc_r": 0,
         "pc_t": 2048,
         "pKp": 4.5,
         "pKi": 0,
@@ -106,8 +107,8 @@
         "id": "atk-math-robot-joint-2",
         "path": "./modules/hardware/atkmrobot.js",
         "position": {
-          "left": 604,
-          "top": 492
+          "left": 596,
+          "top": 515
         }
       },
       "inputs": {
@@ -148,8 +149,9 @@
         "message": "no packet yet",
         "route": "0,1",
         "enc_cnt": 16384,
-        "enc_offset": 1350,
+        "enc_offset": 1700,
         "enc_reverse": false,
+        "pc_r": 0,
         "pc_t": 2048,
         "pKp": 4.5,
         "pKi": 0,
@@ -301,7 +303,7 @@
       },
       "state": {
         "prefix": "JTN1:",
-        "message": "6.069578482466511"
+        "message": "6.026243525208741"
       },
       "ui": {}
     },
@@ -341,8 +343,8 @@
         "id": "collector-8",
         "path": "./modules/util/collector.js",
         "position": {
-          "left": 1649,
-          "top": 1157
+          "left": 1629,
+          "top": 954
         }
       },
       "inputs": {
@@ -365,7 +367,8 @@
         }
       },
       "state": {
-        "count": 1
+        "count": 0,
+        "dumpOnGo": true
       },
       "ui": {
         "onDumpButton": {
@@ -381,8 +384,8 @@
         "id": "gateCounter-9",
         "path": "./modules/util/gateCounter.js",
         "position": {
-          "left": 732,
-          "top": 976
+          "left": 723,
+          "top": 994
         }
       },
       "inputs": {
@@ -425,8 +428,8 @@
         "id": "parallelContencator-11",
         "path": "./modules/util/parallelContencator.js",
         "position": {
-          "left": 1193,
-          "top": 1147
+          "left": 1192,
+          "top": 969
         }
       },
       "inputs": {
@@ -468,8 +471,8 @@
         "id": "gateCounter-12",
         "path": "./modules/util/gateCounter.js",
         "position": {
-          "left": 726,
-          "top": 1174
+          "left": 724,
+          "top": 1187
         }
       },
       "inputs": {
@@ -512,8 +515,8 @@
         "id": "number-output-13",
         "path": "./modules/util/number.js",
         "position": {
-          "left": 168,
-          "top": 1045
+          "left": 177,
+          "top": 1028
         }
       },
       "inputs": {
@@ -573,7 +576,7 @@
       },
       "state": {
         "prefix": "JTN2:",
-        "message": "0.14802914603096437"
+        "message": "0.28148547457701517"
       },
       "ui": {}
     },
@@ -584,8 +587,8 @@
         "id": "forwardTransform Parser-15",
         "path": "./modules/robot/forwardTransform.js",
         "position": {
-          "left": 1323,
-          "top": 718
+          "left": 1208,
+          "top": 678
         }
       },
       "inputs": {
@@ -639,8 +642,8 @@
         "id": "leastSquares Parser-16",
         "path": "./modules/robot/leastSquares.js",
         "position": {
-          "left": 1823,
-          "top": 991
+          "left": 1648,
+          "top": 793
         }
       },
       "inputs": {
@@ -669,8 +672,8 @@
         }
       },
       "state": {
-        "c": 1,
-        "d": -2
+        "c": 0,
+        "d": -0.113
       },
       "ui": {}
     }
diff --git a/py/leastSquares.py b/py/leastSquares.py
index 0840679..efbfd6c 100644
--- a/py/leastSquares.py
+++ b/py/leastSquares.py
@@ -37,7 +37,7 @@ def constructmatA(C, theta1s, theta2s):
         #print theta1s[i]
         theta1 = float(theta1s[i])
         theta2 = float(theta2s[i])
-        a.append([cfloat*m.cos(theta1)-m.sin(theta1), cfloat*m.cos(theta2)-m.sin(theta2)])
+        a.append([cfloat*m.cos(theta1)-m.sin(theta1), cfloat*m.cos(theta1+theta2)-m.sin(theta1+theta2)])
     return a
     
 def constructmatb(D, theta1s):
@@ -64,7 +64,7 @@ fullRes = np.linalg.lstsq(A,b, rcond=None)
 
 lengths = [fullRes[0][0][0], fullRes[0][1][0]]
 
-print lengths
+print (lengths)
 sys.stdout.flush()
 
 '''
diff --git a/robot.js b/robot.js
index 6ec40ee..e33085d 100644
--- a/robot.js
+++ b/robot.js
@@ -18,6 +18,7 @@ var program = Programs.new('new program')
 
 */
 
+
 var link = Programs.loadModuleFromSource(program, './modules/hardware/atkseriallink.js')
 link.startUp()
 link.state.log = false 
@@ -26,7 +27,14 @@ Programs.setUI(link, 1050, 50)
 var mrbotone = Programs.loadModuleFromSource(program, './modules/hardware/atkmrobot.js')
 Programs.setUI(mrbotone, 600, 50)
 var mrbottwo = Programs.loadModuleFromSource(program, './modules/hardware/atkmrobot.js')
-Programs.setUI(mrbottwo, 600, 450)
+Programs.setUI(mrbottwo, 600, 550)
+var mrbotthree = Programs.loadModuleFromSource(program, './modules/hardware/atkmrobot.js')
+Programs.setUI(mrbotthree, 600, 1050)
+
+var rbtcanvas = Programs.loadModuleFromSource(program, './modules/ui/robotCanvas.js')
+Programs.setUI(rbtcanvas, 1200, 50)
+
+/*
 
 var button = Programs.loadModuleFromSource(program, './modules/ui/button.js')
 var delay = Programs.loadModuleFromSource(program, './modules/util/delay.js')
@@ -56,6 +64,8 @@ Programs.setUI(collector, 1050, 800)
 var gateCounter = Programs.loadModuleFromSource(program, './modules/util/gateCounter.js')
 Programs.setUI(gateCounter, 600, 850)
 
+*/
+
 /*
 var stest = Programs.loadModuleFromSource(program, './modules/ui/stest.js')
 
diff --git a/src/atkunit.js b/src/atkunit.js
index b3b355b..2b328d5 100644
--- a/src/atkunit.js
+++ b/src/atkunit.js
@@ -9,65 +9,71 @@ let Button = JSUnit.Button
 
 // interface elements
 const JSUI = require('./jsui.js')
-let UI = JSUI.UI 
+let UI = JSUI.UI
 
 let ATKRoute = require('./atkroute.js')
 
-function Hardware(){
-	var hardware = {
-		description:{
-			name: 'hardwareUnit',
-			alt: 'software representation of networked hardware object',
-			isHardware: true 
-		},
-		route: ATKRoute('0,0')
-	}
-
-	hardware.state = State()
-	var state = hardware.state 
-
-	state.message = 'click below to test network'
-	state.route = '0,0' // default  
-
-	hardware.ui = UI() 
-    var ui = hardware.ui 
-
-    ui.addElement('resetButton', './ui/uiButton.js', onReset)
-    ui.resetButton.onload = function(){
-        ui.resetButton.setText('reset hardware')
+function Hardware() {
+    var hardware = {
+        description: {
+            name: 'hardwareUnit',
+            alt: 'software representation of networked hardware object',
+            isHardware: true
+        },
+        route: ATKRoute('0,0')
     }
 
-    ui.addElement('testButton', './ui/uiButton.js', onNetworkTest)
-    ui.testButton.onload = function(){
-        ui.testButton.setText('test network')
+    hardware.state = State()
+    var state = hardware.state
+
+    state.message = 'click below to test network'
+    state.route = '0,0' // default  
+
+    hardware.ui = UI()
+    var ui = hardware.ui
+
+    ui.addElement('resetButton', 'ui/uiButton.js')
+    ui.resetButton.subscribe('onload', function(msg) {
+        ui.resetButton.send({
+            calls: 'setText',
+            argument: 'reset hardware'
+        })
+    })
+
+    ui.addElement('testButton', 'ui/uiButton.js')
+    ui.testButton.subscribe('onload', function(msg) {
+        ui.testButton.send({
+            calls: 'setText',
+            argument: 'test network'
+        })
+    })
+
+    state.onUiChange('route', function() {
+        hardware.route.route = state.route
+    })
+
+    function onReset() {
+        var rstpck = new Array()
+        rstpck.push(128)
+        state.message = 'reset command issued'
+        hardware.route.send(rstpck)
     }
 
-	state.onUiChange('route', function(){
-		hardware.route.route = state.route 
-	})
-
-	function onReset(){
-		var rstpck = new Array()
-		rstpck.push(128)
-		state.message = 'reset command issued'
-		hardware.route.send(rstpck)
-	}
-
-	function onNetworkTest(){
-		var tstpck = new Array()
-		tstpck.push(127)
-		state.message = 'test packet out'
-		hardware.route.send(tstpck)
-	}
+    function onNetworkTest() {
+        var tstpck = new Array()
+        tstpck.push(127)
+        state.message = 'test packet out'
+        hardware.route.send(tstpck)
+    }
 
-	hardware.route.subscribe(127, testReturn)
+    hardware.route.subscribe(127, testReturn)
 
-	function testReturn(msg){
-		state.message = 'test OK'
-		console.log('test returns with msg', msg)
-	}
+    function testReturn(msg) {
+        state.message = 'test OK'
+        console.log('test returns with msg', msg)
+    }
 
-	return hardware
+    return hardware
 }
 
 module.exports = Hardware
\ No newline at end of file
diff --git a/src/jsui.js b/src/jsui.js
index 9e3a99a..ad5ae61 100644
--- a/src/jsui.js
+++ b/src/jsui.js
@@ -1,35 +1,56 @@
 function UI() {
     var ui = {}
 
-    ui.addElement = function(keyName, srcPath, callback){
-    	var src = require(srcPath)
-    	ui[keyName] = new src() 
-    	ui[keyName].keyName = keyName 
-    	ui[keyName].callback = callback 
-    	ui[keyName].sendToUi = function(msg){
-    		var data = {
-    			id: ui.parentId,
-    			key: keyName, 
-    			msg: msg
-    		}
-    		ui.socket.send('put ui change', data)
-    	}
+    ui.addElement = function(keyName, clientPath){
+        ui[keyName] = UIElement(keyName, clientPath, this)
     }
 
     ui.init = function(parentModId, socket){
     	// get hookups from top level program 
         this.parentId = parentModId
         this.socket = socket
-        for (key in this) {
-            if (isUiKey(key)) {
-            	// see if they have init functions?
-            }
-        }
     }
 
     return ui
 }
 
+function UIElement(keyName, clientPath, ui){
+    var element = {
+        clientPath: clientPath,
+        calls: {}
+    }
+
+    element.call = function(functionName, argument){
+        this.send({
+            calls: functionName,
+            argument: argument
+        })
+    }
+
+    element.send = function(msg){
+        var data = {
+            id: ui.parentId,
+            key: keyName, 
+            msg: msg
+        }
+        ui.socket.send('put ui change', data)
+    }
+
+    element.subscribe = function(msgKey, callback){
+        this.calls[msgKey] = callback
+    }
+
+    element.onMessage = function(msg){
+        if(this.calls[msg.key] != null){
+            this.calls[msg.key](msg)
+        } else {
+            console.log('UI Element from ID', ui.parentId, 'calls for msg having no callback:', msg)
+        }
+    }
+
+    return element
+}
+
 function isUiKey(key) {
     if (key == 'parentId' || key == 'socket' || key == 'init' || key == 'addElement' || key == 'pushToUI' || key == 'emitters') {
         return false
@@ -40,6 +61,7 @@ function isUiKey(key) {
 
 module.exports = {
 	UI: UI,
+    UIElement: UIElement,
 	isUiKey: isUiKey
 }
 
diff --git a/src/ui/multiline.js b/src/ui/multiline.js
deleted file mode 100644
index 98c2de9..0000000
--- a/src/ui/multiline.js
+++ /dev/null
@@ -1,55 +0,0 @@
-function Multiline() {
-    // server-side button object 
-    var multiline = {
-        type: 'button',
-        clientPath: 'ui/multiline.js',
-        callback: null, // this is loaded into our scope on load 
-        contents: null, // current value of multiline obj 
-        label: null,
-        rows: null
-    }
-
-    // hook to recv messages from the ui counterpart
-    multiline.onMessage = function(msg) {
-        //console.log('message into server side object', msg)
-        if(msg == 'onload'){
-        	this.onload() 
-        } else {
-        	this.contents = msg 
-        }
-    }
-
-    multiline.setLabel = function(string){
-    	this.label = string 
-    	var msg = {
-    		call: 'setLabel',
-    		argument: string
-    	}
-    	this.sendToUi(msg)
-    }
-
-    multiline.setRows = function(num){
-    	this.rows = num 
-    	var msg = {
-    		call: 'setRows',
-    		argument: num 
-    	}
-    	this.sendToUi(msg)
-    }
-
-    // example of a function to use within the module 
-    multiline.setContents = function(string) {
-    	this.contents = string 
-        // ex. of how to send data up to client 
-        var msg = {
-            call: 'setContents',
-            argument: string
-        }
-        // this.sendToUi is given to us during load
-        this.sendToUi(msg)
-    }
-
-    return multiline
-}
-
-module.exports = Multiline
\ No newline at end of file
diff --git a/src/ui/threeCanvas.js b/src/ui/threeCanvas.js
deleted file mode 100644
index 0f5d098..0000000
--- a/src/ui/threeCanvas.js
+++ /dev/null
@@ -1,46 +0,0 @@
-function ThreeCanvasUIElement() {
-    // server-side button object 
-    var threeCanvasUIElement = {
-        type: 'threeCanvas',
-        clientPath: 'ui/threeCanvas.js',
-        libPath: 'ui/libs/three.js',
-        callback: null // this is loaded into our scope on load 
-    }
-
-    // hook to recv messages from the ui counterpart
-    threeCanvasUIElement.onMessage = function(msg) {
-        //console.log('message into server side object', msg)
-        if (msg == 'onload') {
-            this.onload()
-        } else {
-            // do other message stuff?
-            // is this why callback ?
-            this.callback(msg)
-            console.log('unhandled message from server to ui element', this.type, msg)
-        }
-    }
-
-    threeCanvasUIElement.updateXY1 = function(arary) {
-        // ex. of how to send data up to client 
-        var msg = {
-            call: 'setXY1',
-            argument: arary
-        }
-        // this.sendToUi is given to us during load
-        this.sendToUi(msg)
-    }
-
-    threeCanvasUIElement.updateXY2 = function(arary) {
-        // ex. of how to send data up to client 
-        var msg = {
-            call: 'setXY2',
-            argument: arary
-        }
-        // this.sendToUi is given to us during load
-        this.sendToUi(msg)
-    }
-
-    return threeCanvasUIElement
-}
-
-module.exports = ThreeCanvasUIElement
\ No newline at end of file
diff --git a/src/ui/uiButton.js b/src/ui/uiButton.js
deleted file mode 100644
index e85b937..0000000
--- a/src/ui/uiButton.js
+++ /dev/null
@@ -1,34 +0,0 @@
-function UIButton() {
-    // server-side button object 
-    var uiButton = {
-        type: 'button',
-        clientPath: 'ui/uiButton.js',
-        callback: null, // this is loaded into our scope on load 
-        isPressed: false
-    }
-
-    // hook to recv messages from the ui counterpart
-    uiButton.onMessage = function(msg) {
-        //console.log('message into server side object', msg)
-        if(msg == 'onload'){
-        	this.onload() 
-        } else {
-        	this.callback(msg)
-        }
-    }
-
-    // example of a function to use within the module 
-    uiButton.setText = function(text) {
-        // ex. of how to send data up to client 
-        var msg = {
-            call: 'setText',
-            argument: text
-        }
-        // this.sendToUi is given to us during load
-        this.sendToUi(msg)
-    }
-
-    return uiButton
-}
-
-module.exports = UIButton
\ No newline at end of file
-- 
GitLab