diff --git a/README.md b/README.md index 509b6be1759fa1e32a6af3d280d1d63d07a2689d..d2f4db3dd510a0278222f346f716463596772e7c 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,7 @@ View.assignProgram(program) - module deletion seems unclean - input / output objects should be able to unhook themselves: - keep references of what-is-attached ? + - would like to be able to default prevent state variables from being changed by users, i.e. in 'collector' the 'count' variable ## WRT Representations diff --git a/client/client.js b/client/client.js index 851796ac6bf4c18ce73b7708ab302daba78cd751..4ca0cd2086559216f6ee591737c4ad38e2b8569e 100644 --- a/client/client.js +++ b/client/client.js @@ -510,6 +510,8 @@ document.onkeydown = function(evt) { case 'd': console.log(program) break + case 'k': + socketSend('save program', 'temp') default: break } diff --git a/modules/util/array.js b/modules/util/array.js index ade6932ad357882ef4a232e5e9496993a7ea2e5d..9de3e74512ad64096593ef5f0d043092d5ea8e81 100644 --- a/modules/util/array.js +++ b/modules/util/array.js @@ -49,12 +49,12 @@ function UIArray() { // here's our input callback, specified in the input constructor function onThruInput(input){ - if(typeof input == 'number'){ - state.number = input + if(Array.isArray(input)){ + state.array = input + onArrayDesire() } else { - state.number = parseFloat(input) + console.log("ERR input to array module is non-array") } - onArrayDesire() } function onArrayDesire(){ diff --git a/modules/util/collector.js b/modules/util/collector.js new file mode 100644 index 0000000000000000000000000000000000000000..d34e8aba2664b7d80628ef2ad97d2daf2880416d --- /dev/null +++ b/modules/util/collector.js @@ -0,0 +1,77 @@ +// boilerplate rndmc 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 + +/* +collector + +takes samples from what-ever output, stores in an array until a 'dump' input, +outputs that array, clears internals + +*/ + +// a constructor, a fn, a javascript mess +function Collector() { + + // this is the tiny program-as-and-object that we'll load into rundmc + // description / name is required to load successfully + var collector = { + description: { + name: 'collector', + alt: 'collect and dump' + } + } + + // the State() object is what the system scrapes for ui variables / updates from the UI + // this includes things like Button('title', callback), which are unique state variables + // they can also be found in jsunit.js + collector.state = State() + // alias ! + var state = collector.state + + state.count = 0 + + var collection = new Array() + + collector.ui = UI() + var ui = collector.ui + ui.addElement('onDumpButton', './ui/uiButton.js', onDumpDesire) + ui.onDumpButton.onload = function() { + ui.onDumpButton.setText('array out ->') + } + + // inputs are required, and must be Input('type', callback) + collector.inputs = { + collect: Input('any', onNewItem), // makes anything into num event + dump: Input('evt', onDumpDesire) + } + + // outputs: Output('type') + collector.outputs = { + pass: Output('any') + } + + // here's our input callback, specified in the input constructor + function onNewItem(input){ + collection.push(input) + state.count = collection.length + } + + function onDumpDesire(){ + collector.outputs.pass.emit(collection) + collection = new Array() + state.count = collection.length + } + + // gotta give the program this thing we made + return collector +} + +// this for node.js's require() function +module.exports = Collector \ No newline at end of file diff --git a/modules/util/gate.js b/modules/util/gate.js index 737861566d5a89e1b2b05674ed24acc298f91443..7a24c4bdb7da9d7fa8cc08c9f2ea0d16fdd6cb2e 100644 --- a/modules/util/gate.js +++ b/modules/util/gate.js @@ -29,7 +29,7 @@ function Gate() { var ui = gate.ui ui.addElement('openButton', './ui/uiButton.js', onButtonPress) ui.openButton.onload = function() { - ui.openButton.setText('toggle gate') + ui.openButton.setText('click to open gate') } // yikes @@ -44,14 +44,14 @@ function Gate() { } function onButtonPress(evt) { - console.log("GATE BUTTON") if (gate.isOpen) { gate.isOpen = false state.message = 'closed' + ui.openButton.setText('click to open gate') } else { gate.isOpen = true state.message = 'open' - //gate.outputs.out.emit('go') + ui.openButton.setText('click to close gate') } } diff --git a/modules/util/gateCounter.js b/modules/util/gateCounter.js new file mode 100644 index 0000000000000000000000000000000000000000..b367b56f2ca2e53518c15fbf525b6e16428fb55f --- /dev/null +++ b/modules/util/gateCounter.js @@ -0,0 +1,79 @@ +// boilerplate 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 GateCounter() { + + var gateCounter = { + // descriptions are used in UI + description: { + name: 'gateCounter', + alt: 'in ... out' + } + } + + gateCounter.state = State() + // alias ! + var state = gateCounter.state + + state.addThis = 10 + state.count = 0 + + 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') + } + + state.onUiChange('addThis', function(){ + ui.openButton.setText('click to add ' + state.addThis.toString() + ' to count') + }) + + // yikes + gateCounter.isOpen = false + + gateCounter.inputs = { + thru: Input('any', gateCounterKeeper), // makes anything into '1' event + setCount: Input('number', onSetCount), + addEvent: Input('any', onAddEvent) + } + + gateCounter.outputs = { + out: Output('any') + } + + function onButtonPress(evt) { + console.log("gateCounter BUTTON") + state.count += state.addThis + } + + function onSetCount(num){ + state.addThis = num + state.count = num + } + + function onAddEvent(evt){ + state.count = state.addThis + } + + function gateCounterKeeper(input) { + if (state.count > 0) { + var outVar = JSON.parse(JSON.stringify(input)) + gateCounter.outputs.out.emit(outVar) + state.count -- + } + } + + return gateCounter +} + +// exports +module.exports = GateCounter \ No newline at end of file diff --git a/modules/util/parallelContencator.js b/modules/util/parallelContencator.js new file mode 100644 index 0000000000000000000000000000000000000000..14647e24535670bf67045e29d272f4e1fea34519 --- /dev/null +++ b/modules/util/parallelContencator.js @@ -0,0 +1,93 @@ +// boilerplate rndmc 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 *hot* javascript mess +function ParallelContencator() { + + // this is the tiny program-as-and-object that we'll load into rundmc + // description / name is required to load successfully + var parallelContencator = { + description: { + name: 'parallelContencator', + alt: 'inputs -> arrays' + } + } + + // the State() object is what the system scrapes for ui variables / updates from the UI + // this includes things like Button('title', callback), which are unique state variables + // they can also be found in jsunit.js + parallelContencator.state = State() + // alias ! + var state = parallelContencator.state + + state.gateKeep = true + + parallelContencator.ui = UI() + var ui = parallelContencator.ui + ui.addElement('onOutputButton', './ui/uiButton.js', doThroughput) + ui.onOutputButton.onload = function() { + ui.onOutputButton.setText('array out ->') + } + + // inputs are required, and must be Input('type', callback) + parallelContencator.inputs = { + in0: Input('any', onIn0), + in1: Input('any', onIn1), + push: Input('evt', doThroughput) // makes anything into num event + } + + // outputs: Output('type') + parallelContencator.outputs = { + out: Output('array') + } + + var contencated = [0, 0] + var recent = [false, false] + + function onIn0(inp) { + contencated[0] = inp + recent[0] = true + doGateCheck() + } + + function onIn1(inp) { + contencated[1] = inp + recent[1] = true + doGateCheck() + } + + function doGateCheck() { + if (state.gateKeep) { + var count = 0 + recent.forEach(function(element) { + if (element) { + count++ + } + }) + if (count == recent.length) { + doThroughput() + } + } else { + doThroughput() + } + } + + function doThroughput() { + // here's how we fire an output. + parallelContencator.outputs.out.emit(contencated) + recent.fill(false) + } + + // gotta give the program this thing we made + return parallelContencator +} + +// this for node.js's require() function +module.exports = ParallelContencator \ No newline at end of file diff --git a/programs/temp.json b/programs/temp.json new file mode 100644 index 0000000000000000000000000000000000000000..904ccd346ed6226fa286624debb5077a1122fda3 --- /dev/null +++ b/programs/temp.json @@ -0,0 +1,564 @@ +{ + "description": { + "name": "new program", + "counter": 14 + }, + "modules": { + "SerialportATKLink-0": { + "description": { + "isHardware": true, + "isLink": true, + "name": "SerialportATKLink", + "alt": "window into hardware world", + "id": "SerialportATKLink-0", + "path": "./modules/hardware/atkseriallink.js", + "position": { + "left": 734, + "top": -191 + } + }, + "inputs": {}, + "outputs": {}, + "state": { + "portName": "---", + "portStatus": "closed", + "log": true + }, + "ui": { + "kickButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "atk-math-robot-joint-1": { + "description": { + "name": "atk-math-robot-joint", + "alt": "software representation of networked hardware object", + "isHardware": true, + "id": "atk-math-robot-joint-1", + "path": "./modules/hardware/atkmrobot.js", + "position": { + "left": 600, + "top": 50 + } + }, + "inputs": { + "tick": { + "accepts": "event" + }, + "set_pc_t": { + "accepts": "number" + }, + "get_pos": { + "accepts": "event" + } + }, + "outputs": { + "ok": { + "emits": "nothing-yet", + "calls": [] + }, + "pos": { + "emits": "uint16_t", + "calls": [ + { + "parentId": "logger-6", + "key": "thru" + }, + { + "parentId": "gateCounter-9", + "key": "thru" + } + ] + } + }, + "state": { + "message": "no packet yet", + "route": "0,0", + "enc_cnt": 16384, + "pc_t": 2048, + "pKp": 4.5, + "pKi": 0, + "pKd": 0, + "cKp": 4, + "cKi": 0, + "walk": 1024 + }, + "ui": { + "walkValButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "atk-math-robot-joint-2": { + "description": { + "name": "atk-math-robot-joint", + "alt": "software representation of networked hardware object", + "isHardware": true, + "id": "atk-math-robot-joint-2", + "path": "./modules/hardware/atkmrobot.js", + "position": { + "left": 600, + "top": 450 + } + }, + "inputs": { + "tick": { + "accepts": "event" + }, + "set_pc_t": { + "accepts": "number" + }, + "get_pos": { + "accepts": "event" + } + }, + "outputs": { + "ok": { + "emits": "nothing-yet", + "calls": [] + }, + "pos": { + "emits": "uint16_t", + "calls": [ + { + "parentId": "gateCounter-12", + "key": "thru" + }, + { + "parentId": "logger-14", + "key": "thru" + } + ] + } + }, + "state": { + "message": "no packet yet", + "route": "0,1", + "enc_cnt": 16384, + "pc_t": 2048, + "pKp": 4.5, + "pKi": 0, + "pKd": 0, + "cKp": 4, + "cKi": 0, + "walk": 1024 + }, + "ui": { + "walkValButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "Button-3": { + "description": { + "name": "Button", + "alt": "for clicking", + "id": "Button-3", + "path": "./modules/ui/button.js", + "position": { + "left": 90, + "top": 50 + } + }, + "inputs": { + "thru": { + "accepts": "any" + } + }, + "outputs": { + "whammy": { + "emits": "number", + "calls": [ + { + "parentId": "atk-math-robot-joint-1", + "key": "get_pos" + }, + { + "parentId": "delay-4", + "key": "thru" + }, + { + "parentId": "atk-math-robot-joint-2", + "key": "get_pos" + } + ] + } + }, + "state": {}, + "ui": { + "btn": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "delay-4": { + "description": { + "name": "delay", + "alt": "in ... out", + "id": "delay-4", + "path": "./modules/util/delay.js", + "position": { + "left": 90, + "top": 250 + } + }, + "inputs": { + "thru": { + "accepts": "any" + } + }, + "outputs": { + "out": { + "emits": "any", + "calls": [ + { + "parentId": "gate-5", + "key": "thru" + } + ] + } + }, + "state": { + "ms": 100 + }, + "ui": {} + }, + "gate-5": { + "description": { + "name": "gate", + "alt": "in ... out", + "id": "gate-5", + "path": "./modules/util/gate.js", + "position": { + "left": 90, + "top": 400 + } + }, + "inputs": { + "thru": { + "accepts": "any" + } + }, + "outputs": { + "out": { + "emits": "any", + "calls": [ + { + "parentId": "Button-3", + "key": "thru" + } + ] + } + }, + "state": { + "message": "closed" + }, + "ui": { + "openButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "logger-6": { + "description": { + "name": "logger", + "alt": "in ... out to console", + "id": "logger-6", + "path": "./modules/util/log.js", + "position": { + "left": 1318, + "top": -170 + } + }, + "inputs": { + "thru": { + "accepts": "any" + } + }, + "outputs": { + "throughput": { + "emits": "any", + "calls": [] + } + }, + "state": { + "prefix": "JTN1:", + "message": "---" + }, + "ui": {} + }, + "ThreeJS-Canvas-7": { + "description": { + "name": "ThreeJS-Canvas", + "alt": "graphix", + "id": "ThreeJS-Canvas-7", + "path": "./modules/ui/threeCanvas.js", + "position": { + "left": 2263, + "top": 326 + } + }, + "inputs": { + "xy1": { + "accepts": "array" + }, + "xy2": { + "accepts": "array" + } + }, + "outputs": {}, + "state": {}, + "ui": { + "threeCanvas": { + "type": "threeCanvas", + "clientPath": "ui/threeCanvas.js", + "libPath": "ui/libs/three.js" + } + } + }, + "collector-8": { + "description": { + "name": "collector", + "alt": "collect and dump", + "id": "collector-8", + "path": "./modules/util/collector.js", + "position": { + "left": 1728, + "top": 1057 + } + }, + "inputs": { + "collect": { + "accepts": "any" + }, + "dump": { + "accepts": "evt" + } + }, + "outputs": { + "pass": { + "emits": "any", + "calls": [] + } + }, + "state": { + "count": 0 + }, + "ui": { + "onDumpButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "gateCounter-9": { + "description": { + "name": "gateCounter", + "alt": "in ... out", + "id": "gateCounter-9", + "path": "./modules/util/gateCounter.js", + "position": { + "left": 732, + "top": 976 + } + }, + "inputs": { + "thru": { + "accepts": "any" + }, + "setCount": { + "accepts": "number" + }, + "addEvent": { + "accepts": "any" + } + }, + "outputs": { + "out": { + "emits": "any", + "calls": [ + { + "parentId": "parallelContencator-11", + "key": "in0" + } + ] + } + }, + "state": { + "addThis": 10, + "count": 0 + }, + "ui": { + "openButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "parallelContencator-11": { + "description": { + "name": "parallelContencator", + "alt": "inputs -> arrays", + "id": "parallelContencator-11", + "path": "./modules/util/parallelContencator.js", + "position": { + "left": 1232, + "top": 1053 + } + }, + "inputs": { + "in0": { + "accepts": "any" + }, + "in1": { + "accepts": "any" + }, + "push": { + "accepts": "evt" + } + }, + "outputs": { + "out": { + "emits": "array", + "calls": [ + { + "parentId": "collector-8", + "key": "collect" + } + ] + } + }, + "state": { + "gateKeep": true + }, + "ui": { + "onOutputButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "gateCounter-12": { + "description": { + "name": "gateCounter", + "alt": "in ... out", + "id": "gateCounter-12", + "path": "./modules/util/gateCounter.js", + "position": { + "left": 726, + "top": 1174 + } + }, + "inputs": { + "thru": { + "accepts": "any" + }, + "setCount": { + "accepts": "number" + }, + "addEvent": { + "accepts": "any" + } + }, + "outputs": { + "out": { + "emits": "any", + "calls": [ + { + "parentId": "parallelContencator-11", + "key": "in1" + } + ] + } + }, + "state": { + "addThis": 10, + "count": 0 + }, + "ui": { + "openButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "number-output-13": { + "description": { + "name": "number-output", + "alt": "for clicking", + "id": "number-output-13", + "path": "./modules/util/number.js", + "position": { + "left": 168, + "top": 1045 + } + }, + "inputs": { + "thru": { + "accepts": "any" + }, + "evt": { + "accepts": "any" + } + }, + "outputs": { + "out": { + "emits": "number", + "calls": [ + { + "parentId": "gateCounter-9", + "key": "setCount" + }, + { + "parentId": "gateCounter-12", + "key": "setCount" + } + ] + } + }, + "state": { + "number": 1 + }, + "ui": { + "onNumberButton": { + "type": "button", + "clientPath": "ui/uiButton.js" + } + } + }, + "logger-14": { + "description": { + "name": "logger", + "alt": "in ... out to console", + "id": "logger-14", + "path": "./modules/util/log.js", + "position": { + "left": 1319, + "top": -44 + } + }, + "inputs": { + "thru": { + "accepts": "any" + } + }, + "outputs": { + "throughput": { + "emits": "any", + "calls": [] + } + }, + "state": { + "prefix": "JTN2:", + "message": "---" + }, + "ui": {} + } + } +} \ No newline at end of file diff --git a/robot.js b/robot.js index ae66d5681cc31ec213e39278c4fa1bb7a6dddf09..6ec40ee6d16400a3008c099e4eb57ad3fdea7d58 100644 --- a/robot.js +++ b/robot.js @@ -16,11 +16,6 @@ var program = Programs.new('new program') - robot does forward transform with live [t1, t2] - robot displays forward transform with [t1, t2] -- modules needed - - object collector (i.e. collects inputs into a list, has 'output' and 'reset' input triggers / buttons as well) - - two-up contencator (i.e. takes two inputs, puts them into arrays) - - gate opens, lets a count thru, shuts - */ var link = Programs.loadModuleFromSource(program, './modules/hardware/atkseriallink.js') @@ -55,6 +50,12 @@ mrbotone.outputs.pos.attach(log.inputs.thru) var canvas = Programs.loadModuleFromSource(program, './modules/ui/threeCanvas.js') Programs.setUI(canvas, 1500, 650) +var collector = Programs.loadModuleFromSource(program, './modules/util/collector.js') +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/rundmc.js b/rundmc.js index 0e9922f09af5127ece32d54ec1151574bb0d99b2..189ac6897a24d3c8942e73d82cab851d5b5af59b 100644 --- a/rundmc.js +++ b/rundmc.js @@ -29,7 +29,9 @@ const Reps = require('./reps.js') const Programs = require('./programs.js') // the program object: real simple, just has a description, and a 'modules' -var program = Programs.new('new program') +// var program = Programs.new('new program') + +var program = Programs.open('./programs/temp.json') /* var stest = Programs.loadModuleFromSource(program, './modules/ui/stest.js')