From 250ff884d7e974a128914e6b1d547aa710e538bc Mon Sep 17 00:00:00 2001
From: Jake <jake.read@cba.mit.edu>
Date: Mon, 12 Nov 2018 19:46:03 -0500
Subject: [PATCH] hardware abstraction

---
 README.md                                     |  8 ++
 client/client.js                              | 10 ++-
 lib/atkport.js                                | 27 -------
 lib/atkroute.js                               | 36 +++++++++
 lib/atkunit.js                                | 50 +++++++++++++
 main.js                                       | 19 ++++-
 modules/hardware/atkbbbs.js                   | 63 ----------------
 modules/hardware/atkbbs.js                    | 69 ++++++++++++++++++
 .../{serialATKLink.js => atkseriallink.js}    | 73 ++++++++++---------
 programs.js                                   | 13 ++++
 10 files changed, 240 insertions(+), 128 deletions(-)
 delete mode 100644 lib/atkport.js
 create mode 100644 lib/atkroute.js
 create mode 100644 lib/atkunit.js
 delete mode 100644 modules/hardware/atkbbbs.js
 create mode 100644 modules/hardware/atkbbs.js
 rename modules/hardware/{serialATKLink.js => atkseriallink.js} (75%)

diff --git a/README.md b/README.md
index b32cc14..6fbf4f7 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,14 @@ This project serves the developement environment / api we use to write and repre
 
 ## For MW
 
+ - serial link OK, attach
+ - routes have route, test send
+  - thru route, append route
+  - thru link, to send. 
+   - callback key subscribe fn's 
+   - then: route change propogation? 
+  - test with test: callback 
+  - then draw something, and how does UI hook up, and when route changes ? 
 
  - change /src to /modules or /units ?
 
diff --git a/client/client.js b/client/client.js
index 2c72cdc..62b5160 100644
--- a/client/client.js
+++ b/client/client.js
@@ -1,6 +1,10 @@
 //
 //
 // new node controller / VIEW 
+// reconfigurable unviersal numeric dataflow machine controller
+// 'RUN DMC'
+// dataflow numeric controller
+// DNC 
 //
 // client.js
 //
@@ -404,6 +408,8 @@ onwheel = function(evt) {
 onmousedown = function(evt) {
     var el = document.elementFromPoint(evt.pageX, evt.pageY)
     if (elementIsNotModule(el)) {
+        evt.preventDefault()
+        evt.stopPropagation()
         window.addEventListener('mousemove', mouseMoveDragListener)
         window.addEventListener('mouseup', mouseUpDragListener)
     }
@@ -492,14 +498,14 @@ function writeModuleOptionMenu(modRep) {
         if (opts[i] == 'delete') {
             li.addEventListener('click', function(evt) {
                 var data = {
-                    id: modRep.description.id 
+                    id: modRep.description.id
                 }
                 socketSend('remove module', data)
                 wrapper.removeChild(document.getElementById('perModuleMenu'))
             })
         } else if (opts[i] == 'copy') {
             li.addEventListener('click', function(evt) {
-                var data = modRep.description.path 
+                var data = modRep.description.path
                 socketSend('put module', data)
                 wrapper.removeChild(document.getElementById('perModuleMenu'))
             })
diff --git a/lib/atkport.js b/lib/atkport.js
deleted file mode 100644
index 778cc4f..0000000
--- a/lib/atkport.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// object to extend for things-that-are-hardware
-
-// and pass f'n to call on received messages
-function ATKPort(route, calls) {
-    var atkport = {
-        isAtkPort: true,
-        link: null, // pls, deliver 2 me a hw outlet 
-        route: route,
-        calls: calls
-    }
-
-    atkport.send = function(msg) {
-        if (this.link != null) {
-            this.link.send(msg, this)
-        } else {
-            console.log("NO LINK NO SEND")
-        }
-    }
-
-    atkport.onMessage = function(msg){
-        this.calls(msg)
-    }
-
-    return atkport
-}
-
-module.exports = ATKPort
\ No newline at end of file
diff --git a/lib/atkroute.js b/lib/atkroute.js
new file mode 100644
index 0000000..52a0f1a
--- /dev/null
+++ b/lib/atkroute.js
@@ -0,0 +1,36 @@
+// object to extend for things-that-are-hardware
+
+// and pass f'n to call on received messages
+function ATKRoute(route, calls) {
+    var atkroute = {
+        isAtkRoute: true,
+        link: null, // pls, deliver 2 me a hw outlet 
+        route: route,
+        calls: {}
+    }
+
+    atkroute.send = function(msg) {
+        if (this.link != null) {
+            // CHECKS and append route, then send 
+            this.link.send(msg, this)
+        } else {
+            console.log("NO LINK NO SEND")
+        }
+    }
+
+    atkroute.subscribe = function(key, callback){
+        this.calls[key.toString()] = callback
+    }
+
+    atkroute.onMessage = function(msg){
+        // one key at a time, for now
+        var key = msg[0].toString()
+        if(this.calls[key] != null){
+            this.calls[key](msg)
+        }
+    }
+
+    return atkroute
+}
+
+module.exports = ATKRoute
\ No newline at end of file
diff --git a/lib/atkunit.js b/lib/atkunit.js
new file mode 100644
index 0000000..38fb152
--- /dev/null
+++ b/lib/atkunit.js
@@ -0,0 +1,50 @@
+// software object for reciprocal hardware object 
+
+// boilerplate atkapi header
+const InOut = require('./jsunit.js')
+let Input = InOut.Input
+let Output = InOut.Output
+let State = InOut.State
+let Button = InOut.Button
+
+const ATKRoute = require('./atkroute.js')
+
+function Hardware(route){
+	var hardware = {
+		description:{
+			name: 'hardwareUnit',
+			alt: 'software representation of networked hardware object',
+			isHardware: true 
+		},
+		route: ATKRoute(route)
+	}
+
+	hardware.state = State()
+	var state = hardware.state 
+
+	state.test = Button('test network', onNetworkTest)
+	state.message = 'click above to test network'
+	state.route = route 
+
+	state.onChange('route', function(){
+		console.log("route change, need to git it thru")
+	})
+
+	function onNetworkTest(){
+		var tstpck = new Array()
+		tstpck.push(127)
+		state.message = 'test packet out'
+		hardware.route.send(tstpck)
+	}
+
+	hardware.route.subscribe(127, testReturn)
+
+	function testReturn(msg){
+		state.message = 'test OK'
+		console.log('test returns with msg', msg)
+	}
+
+	return hardware
+}
+
+module.exports = Hardware
\ No newline at end of file
diff --git a/main.js b/main.js
index aa864d4..864b017 100644
--- a/main.js
+++ b/main.js
@@ -27,9 +27,24 @@ programs are assemblies of modules
 const Reps = require('./reps.js')
 const Programs = require('./programs.js')
 
-var program = {} 
+var program = Programs.new('hw unit test')
 
-program = Programs.open('programs/default.json')
+var link = Programs.loadModuleFromSource(program, './modules/hardware/atkseriallink.js')
+var atkbbs = Programs.loadModuleFromSource(program, './modules/hardware/atkbbs.js')
+
+atkbbs.description.position = {
+	left: 10,
+	top: 200
+}
+
+link.description.position = {
+	left:475,
+	top: 100
+}
+
+link.attach(atkbbs.route)
+
+//program = Programs.open('programs/default.json')
 
 // UI 
 const View = require('./views.js')
diff --git a/modules/hardware/atkbbbs.js b/modules/hardware/atkbbbs.js
deleted file mode 100644
index 5358147..0000000
--- a/modules/hardware/atkbbbs.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// boilerplate atkapi header
-const InOut = require('../../lib/jsunit.js')
-let Input = InOut.Input
-let Output = InOut.Output
-let State = InOut.State
-let Button = InOut.Button
-
-const ATKPort = require('../../lib/atkport.js')
-const PCKT = require('../../lib/packets.js')
-
-// a constructor, a fn, a javascript mess
-function ATKBreadBoardServo() {
-
-    var atkbbs = {
-        description: {
-            name: 'Breadboard Servo Signal Generator',
-            alt: 'servo',
-            isHardware: true
-        },
-        port: ATKPort('0', onPacket), // inputs / outputs to external hw 
-        inputs: {
-            servoVal: Input('number', onServoValChange)
-        }, 
-        outputs: {
-            conf: Output('event')
-        }, 
-    }
-
-    atkbbs.state = State()
-    // alias !
-    var state = atkbbs.state
-
-    state.button = Button('SEND')
-    state.onChange('button', onServoValChange)
-    state.servoVal = 0 // 0->100 does 1 -> 2ms duty on 20ms period
-    state.onChange('servoVal', onServoValChange)
-
-    function onServoValChange(evt) {
-        var pwm = state.servoVal
-        if (pwm > 100) {
-            pwm = 100
-        } else if (pwm < 0) {
-            pwm = 0
-        }
-
-        var microval = Math.round(7.5 * pwm)
-        console.log('pwm on line', microval)
-
-        var pwmpack = PCKT.pack32(microval)
-        pwmpack.unshift(141)
-
-        atkbbs.port.send(pwmpack)
-    }
-
-    function onPacket(pckt) {
-        console.log('ATKBBBS Packet Return', pckt)
-    }
-
-    return atkbbs
-}
-
-// exports 
-module.exports = ATKBreadBoardServo
\ No newline at end of file
diff --git a/modules/hardware/atkbbs.js b/modules/hardware/atkbbs.js
new file mode 100644
index 0000000..b0e3665
--- /dev/null
+++ b/modules/hardware/atkbbs.js
@@ -0,0 +1,69 @@
+// boilerplate atkapi header
+const InOut = require('../../lib/jsunit.js')
+let Input = InOut.Input
+let Output = InOut.Output
+let State = InOut.State
+let Button = InOut.Button
+
+const Hardware = require('../../lib/atkunit.js')
+const PCKT = require('../../lib/packets.js')
+
+// a constructor, a fn, a javascript mess
+function ATKBreadBoardServo() {
+
+    // we make the module, starting from this base 
+    // '0,1' is the route to our piece of hardware
+    // 'onPacket' is the function that will be called
+    // when data arrives from that port 
+    var atkbbs = Hardware('0,1')
+
+    // change the name ... 
+    atkbbs.description.name = 'ATKBBB-Servo'
+
+    // inputs and outputs as usual 
+    atkbbs.inputs = {
+        servoVal: Input('number', onServoValChange)
+    }
+    atkbbs.outputs = {
+        conf: Output('event')
+    }
+
+    // and state as well 
+    var state = atkbbs.state 
+
+    state.button = Button('SEND VALUE', onServoValChange)
+    state.servoVal = 0 // 0->100 does 1 -> 2ms duty on 20ms period
+    state.onChange('servoVal', onServoValChange)
+
+    // to send things down the well, we can use
+    // atkbbs.route.send(packet) 
+    // where packet is a byte array
+
+    function onServoValChange(evt) {
+        var pwm = state.servoVal
+        if (pwm > 100) {
+            pwm = 100
+        } else if (pwm < 0) {
+            pwm = 0
+        }
+
+        var microval = Math.round(7.5 * pwm)
+        console.log('pwm on line', microval)
+
+        var pwmpack = PCKT.pack32(microval)
+        pwmpack.unshift(141)
+
+        atkbbs.route.send(pwmpack)
+    }
+
+    // to get replies to certain packets, we can
+
+    atkbbs.route.subscribe(141, function(msg){
+        console.log('logging return', msg)
+    })
+
+    return atkbbs
+}
+
+// exports 
+module.exports = ATKBreadBoardServo
\ No newline at end of file
diff --git a/modules/hardware/serialATKLink.js b/modules/hardware/atkseriallink.js
similarity index 75%
rename from modules/hardware/serialATKLink.js
rename to modules/hardware/atkseriallink.js
index 2f54d17..281d0ee 100644
--- a/modules/hardware/serialATKLink.js
+++ b/modules/hardware/atkseriallink.js
@@ -8,23 +8,22 @@ let Button = InOut.Button
 
 const SerialPort = require('serialport')
 
-function SerialATKLink() {
-    var serialATKLink = {
+function ATKSerialLink() {
+    var atkSerialLink = {
         description: {
             isHardware: true,
             name: 'Serialport ATK Link',
             alt: 'window into hardware world'
         },
-        ports: new Array()
+        routes: new Array()
     }
 
     var state = State()
-    serialATKLink.state = state
+    atkSerialLink.state = state
 
-    state.serialPortName = '---'
-    state.connect = Button('click to find and connect')
-    state.onChange('connect', findSerialPort)
-    state.serialPortStatus = 'closed' // or we hope it will be 
+    state.portName = '---'
+    state.connect = Button('click to find and connect', findSerialPort)
+    state.portStatus = 'closed' // or we hope it will be 
 
     /*
     ------------------------------------------------------
@@ -32,10 +31,10 @@ function SerialATKLink() {
     ------------------------------------------------------
     */
 
-    serialATKLink.attach = function(port) {
-        console.log('PORT->LINK HOOKUP', port.route)
-        this.ports.push(port)
-        port.link = this
+    atkSerialLink.attach = function(route) {
+        console.log('PORT->LINK HOOKUP', route.route)
+        this.routes.push(route)
+        route.link = this
     }
 
     /*
@@ -52,7 +51,7 @@ function SerialATKLink() {
             ports.forEach(function(serialport) {
                 if (serialport.manufacturer == 'Silicon Labs') {
                     console.log('found cp2102 serialport')
-                    state.serialPortName = serialport.comName
+                    state.portName = serialport.comName
                     openSerialPort()
                 }
             })
@@ -60,18 +59,18 @@ function SerialATKLink() {
     }
 
     function openSerialPort() {
-        if (state.serialPortName == '---') {
+        if (state.portName == '---') {
             findSerialPort()
         } else {
             if (serialport == null) {
-                serialport = new SerialPort(state.serialPortName, {
+                serialport = new SerialPort(state.portName, {
                     baudRate: 250000
                 })
                 serialport.on('open', function() {
-                    state.serialPortStatus = 'open'
+                    state.portStatus = 'open'
                 })
                 serialport.on('error', function(err) {
-                    state.serialPortStatus = err.message
+                    state.portStatus = err.message
                 })
                 serialport.on('data', onSerialPortData)
             }
@@ -84,24 +83,30 @@ function SerialATKLink() {
     ------------------------------------------------------
     */
 
-    serialATKLink.send = function(msg, port) {
+    atkSerialLink.send = function(msg, route) {
         // it would be responsible to check this over now, but hey
-        console.log('send', msg, 'on', port)
+        console.log('send', msg, 'on', route.route)
         // dereference this 
         var pckt = JSON.parse(JSON.stringify(msg))
         if (Array.isArray(pckt)) {
             pckt.unshift(255) // end delimiter 
             pckt.unshift(254) // ptr 
-            var route = port.route.split(',')
-            pckt = route.concat(pckt) // add route 
+            var literalRoute = route.route.split(',')
+            pckt = literalRoute.concat(pckt) // add route 
             pckt.unshift(pckt.length + 1) // add length byte 
             if (writeToSerialPort(pckt)) {
                 console.log('PCKT OOT >>', pckt.toString())
             } else {
-                console.log('ERR: attempt to send to hardware, port not writable')
+                // try to open ? 
+                openSerialPort()
+                if (writeToSerialPort(pckt)) {
+                    console.log('PCKT OOT >>', pckt.toString())
+                } else {
+                    console.log('ERR: attempt to send to hardware, port not writable')
+                }
             }
         } else {
-            console.log('non-array on serialATKLink input')
+            console.log('non-array on atkSerialLink input')
         }
     }
 
@@ -132,7 +137,7 @@ function SerialATKLink() {
 
     var thisPacket = new Array()
 
-    function onSerialPort(data) {
+    function onSerialPortData(data) {
         // we'll make sure it's what we think it will be
         // console.log("PORT DATA")
         var dtArray = new Array()
@@ -180,20 +185,20 @@ function SerialATKLink() {
             returnRoute[i] = incomingRoute[incomingRoute.length - 1 - i]
         }
         // now we'll look for a reciprocal port from our list 
-        var match = null
-        for (key in serialATKLink.ports) {
-            if (returnRoute.toString() === serialATKLink.ports[key].route.toString()) {
-                console.log('RETURN LINK AT', serialATKLink.ports[key])
+        var match = false
+        for (key in atkSerialLink.routes) {
+            if (returnRoute.toString() === atkSerialLink.routes[key].route.toString()) {
+                console.log('RETURN LINK AT', atkSerialLink.routes[key])
                 // strip header and return message 
-                var msg = pckt.slice(pckt.indexOf(255))
+                var msg = pckt.slice(pckt.indexOf(255) + 1)
                 console.log("RETURN MSG", msg)
-                serialATKLink.ports[key].onMessage(msg)
+                match = true 
+                atkSerialLink.routes[key].onMessage(msg)
             }
         }
-        if (match == null) {
+        if (!match) {
             console.log("PACKET RETURN AND NO KEY FOUND")
             console.log(pckt)
-            console.log(serialATKLink.pairs)
         }
     }
 
@@ -216,7 +221,7 @@ function SerialATKLink() {
         // console.log('shifted', pckt)
     }
 
-    return serialATKLink
+    return atkSerialLink
 }
 
-module.exports = SerialATKLink
\ No newline at end of file
+module.exports = ATKSerialLink
\ No newline at end of file
diff --git a/programs.js b/programs.js
index 369a8f0..1c5a080 100644
--- a/programs.js
+++ b/programs.js
@@ -5,6 +5,18 @@ const Reps = require('./reps.js')
 const JSUnit = require('./lib/jsunit.js')
 let isStateKey = JSUnit.isStateKey
 
+function newProgram(name){
+    var program = {
+        description: {
+            name: name, 
+            id: name
+        },
+        modules: {}
+    }
+
+    return program
+}
+
 function loadModuleFromSource(program, path, id) {
     // source -> heap
     if (fs.existsSync(path)) {
@@ -224,6 +236,7 @@ function openProgram(path) {
 }
 
 module.exports = {
+    new: newProgram,
     open: openProgram,
     save: saveProgram,
     loadModuleFromSource: loadModuleFromSource,
-- 
GitLab