Commit 70057d15 authored by Jake Read's avatar Jake Read

save from browser, and position state retention

parent a6d9e8ab
......@@ -25,6 +25,11 @@ This project serves the developement environment / api we use to write and repre
- rm modules
- change settings
- right now
- add module
- l for load
- s for save
- *don't forget*
- onload, load state...
- when hooking back to server, server.emitChange(key) ... from writeStateObject
......
......@@ -111,11 +111,11 @@ function socketRecv(evt) {
case 'put module':
console.log('RECV NEW MODULE')
heapSendsNewModule(data)
break
break
case 'put state':
console.log('RECV STATE CHANGE')
heapSendsNewState(data)
break
break
default:
console.log('ERR recv with non recognized type', recv)
break
......@@ -144,7 +144,7 @@ function heapSendsModuleMenu(tree) {
li.id = path
li.addEventListener('click', function(evt) {
var data = this.id
socketSend('add module', data)
socketSend('put module', data)
wrapper.removeChild(document.getElementById('menu'))
})
ul.appendChild(li)
......@@ -189,15 +189,15 @@ function heapSendsNewProgram(prgm) {
redrawLinks()
}
function heapSendsNewModule(mdl){
if(program.description == null){
function heapSendsNewModule(mdl) {
if (program.description == null) {
program.description.name = 'unnamed program'
}
if(program.modules == null){
if (program.modules == null) {
program.modules = {}
}
addRepToView(mdl)
program.modules[mdl.description.id] = mdl
program.modules[mdl.description.id] = mdl
redrawLinks()
}
......@@ -224,19 +224,30 @@ function addRepToView(rep) {
title.alt = rep.description.alt
domElem.appendChild(title)
var uiSetFlag
// place in pos if info present
// the rep.ui object will store references to the module's related DOM elements
if (rep.ui != null) {
if (rep.ui.left != null) {
domElem.style.left = rep.ui.left + 'px'
console.log(rep.description.position)
if (rep.description.position != null) {
console.log("FOUND POS")
uiSetFlag = false
if (rep.description.position.left != null) {
console.log("FOUND LEFT")
domElem.style.left = rep.description.position.left + 'px'
}
if (rep.ui.top != null) {
domElem.style.top = rep.ui.top + 'px'
if (rep.description.position.top != null) {
console.log("FOUND TOP")
domElem.style.top = rep.description.position.top + 'px'
}
} else {
uiSetFlag = true
rep.description.position = {}
rep.description.position.left = lastPos.x
rep.description.position.top = lastPos.y
}
if(rep.ui == null){
rep.ui = {}
rep.ui.left = lastPos.x
rep.ui.top = lastPos.y
}
rep.ui.domElem = domElem
......@@ -292,8 +303,8 @@ function addRepToView(rep) {
document.addEventListener('mousemove', domElemMouseMove)
title.onmouseup = function() {
rep.ui.left = parseInt(domElem.style.left, 10)
rep.ui.top = parseInt(domElem.style.top, 10)
rep.description.position.left = parseInt(domElem.style.left, 10)
rep.description.position.top = parseInt(domElem.style.top, 10)
putUi(rep)
document.removeEventListener('mousemove', domElemMouseMove)
title.onmouseup = null
......@@ -301,6 +312,9 @@ function addRepToView(rep) {
}
wrapper.appendChild(rep.ui.domElem)
if(uiSetFlag){
putUi(rep)
}
}
// update state from server to UI
......@@ -308,10 +322,10 @@ function heapSendsNewState(data) {
console.log('HEAP SENDS CHANGE STATE IN MODULE', data)
var rep = reps[data.id]
for (var key in data.state) {
if (data.state[key].isButton) {
if (data.state[key].type == 'button') {
console.log('BUTTON UPDATE')
} else if (data.state[key].isMultiLine) {
//console.log("rep", rep.ui.state[key].value, 'data', data.state[key].value)
} else if (data.state[key].type == 'multiline') {
console.log('MULTILINE UPDATE')
rep.ui.state[key].value = data.state[key].value
} else {
// two ?
......@@ -331,6 +345,7 @@ UI -> HEAP ---------------------------------------------------
// push new state from UI to server
function putState(rep) {
// ship it all home: not perfect, but hey
var data = {
id: rep.id,
state: rep.state
......@@ -341,10 +356,12 @@ function putState(rep) {
// save ui position to server for reload
function putUi(rep) {
var data = {
id: rep.id,
ui: {
left: rep.ui.left,
top: rep.ui.top
description: {
id: rep.description.id,
position: {
left: rep.description.position.left,
top: rep.description.position.top
}
}
}
......@@ -372,9 +389,9 @@ function redrawLinks() {
svg.removeChild(svg.firstChild)
}
// redraw thru all links, just look at reps
for(mdlName in program.modules){
for (mdlName in program.modules) {
var mdlRep = program.modules[mdlName]
for(key in mdlRep.outputs){
for (key in mdlRep.outputs) {
var output = mdlRep.outputs[key]
var outputUi = mdlRep.ui.outputs[key]
for (input in output.calls) {
......@@ -421,6 +438,35 @@ oncontextmenu = function(evt) {
return false
}
document.onmousemove = function(evt){
lastPos.x = evt.pageX
lastPos.y = evt.pageY
}
document.onkeydown = function(evt){
console.log(evt)
switch(evt.key){
case 'Escape':
location.reload()
break
case 's':
// get path ?
var path = prompt("path? starting at atkapi/programs/")
socketSend('save program', path)
break
case 'l':
console.log('LOADID')
break
case 'm':
socketSend('get module menu', '')
break
default:
break
}
}
// input / output click hookups
var clkState = false
var oClk = {}
var tmpBz = {}
......
......@@ -28,7 +28,7 @@ const Programs = require('./programs.js')
var program = {}
program = Programs.open('save/onesave.json')
program = Programs.open('programs/default.json')
const View = require('./views.js')
View.startHttp()
......
const fs = require('fs')
const Reps = require('./reps.js')
const JSUnit = require('./lib/jsunit.js')
let isStateKey = JSUnit.isStateKey
......@@ -16,7 +19,7 @@ function loadModuleFromSource(program, path) {
}
// make unique name
mod.description.id = mod.description.id = mod.description.name + '-' + program.description.counter
mod.description.id = mod.description.name + '-' + program.description.counter
mod.description.path = path
// add to program object
......@@ -112,11 +115,13 @@ function saveProgram(prgmem, path) {
for(key in mdls){
var mdl = mdls[key]
var og = makeRepFromModule(mdl)
var og = Reps.makeFromModule(mdl)
svprgmem.modules[mdl.description.id] = og
}
fs.writeFileSync(path, JSON.stringify(svprgmem, null, 2), 'utf8')
console.log('PROGRAM SAVED AT', path)
}
......@@ -146,7 +151,9 @@ function openProgram(path){
// restore saved state and links
for(modName in prgRep.modules){
// keys should be identical for rep and heap
// this is the representation that we're opening up from
var mdlRep = prgRep.modules[modName]
// this is the actual object, having functions and whatnot
var mdl = program.modules[modName]
// hooking outputs -> inputs
......@@ -172,6 +179,13 @@ function openProgram(path){
// rename .onChange for 'onUiChange' or something
}
}
// restore position / UI state
if(mdlRep.description.position != null){
mdl.description.position = {}
mdl.description.position.left = mdlRep.description.position.left
mdl.description.position.top = mdlRep.description.position.top
}
}
// once modules exist, link inputs / outputs / copy state ?
......
......@@ -9,7 +9,11 @@
"id": "gate-1",
"name": "gate",
"alt": "in ... out",
"path": "./src/util/gate.js"
"path": "./src/util/gate.js",
"position": {
"left": 10,
"top": 10
}
},
"inputs": {
"thru": {
......@@ -23,10 +27,6 @@
{
"parentId": "delay-2",
"key": "thru"
},
{
"parentId": "logger-3",
"key": "thru"
}
]
}
......@@ -45,7 +45,11 @@
"id": "delay-2",
"name": "delay",
"alt": "in ... out",
"path": "./src/util/delay.js"
"path": "./src/util/delay.js",
"position": {
"left": 132,
"top": 225
}
},
"inputs": {
"thru": {
......@@ -72,7 +76,11 @@
"id": "logger-3",
"name": "logger",
"alt": "in ... out to console",
"path": "./src/util/log.js"
"path": "./src/util/log.js",
"position": {
"left": 123,
"top": 367
}
},
"inputs": {
"thru": {
......
{
"description": {
"name": "tstprgmem",
"counter": 4
},
"modules": {
"gate-1": {
"description": {
"id": "gate-1",
"name": "gate",
"alt": "in ... out",
"path": "./src/util/gate.js"
},
"inputs": {
"thru": {
"accepts": "any"
}
},
"outputs": {
"out": {
"emits": "any",
"calls": [
{
"parentId": "delay-2",
"key": "thru"
}
]
}
},
"state": {
"toggle": {
"type": "button",
"isPressed": false,
"label": "Open / Close"
},
"message": "closed"
}
},
"delay-2": {
"description": {
"id": "delay-2",
"name": "delay",
"alt": "in ... out",
"path": "./src/util/delay.js"
},
"inputs": {
"thru": {
"accepts": "any"
}
},
"outputs": {
"out": {
"emits": "any",
"calls": [
{
"parentId": "logger-3",
"key": "thru"
}
]
}
},
"state": {
"ms": 100
}
},
"logger-3": {
"description": {
"id": "logger-3",
"name": "logger",
"alt": "in ... out to console",
"path": "./src/util/log.js"
},
"inputs": {
"thru": {
"accepts": "any"
}
},
"outputs": {
"throughput": {
"emits": "any",
"calls": []
}
},
"state": {
"prefix": "LOGGER:",
"message": "---"
}
},
"delay-4": {
"description": {
"id": "delay-4",
"name": "delay",
"alt": "in ... out",
"path": "./src/util/delay.js"
},
"inputs": {
"thru": {
"accepts": "any"
}
},
"outputs": {
"out": {
"emits": "any",
"calls": []
}
},
"state": {
"ms": 100
}
}
}
}
\ No newline at end of file
......@@ -11,6 +11,10 @@ function makeRepFromModule(mdl) {
}
}
if(mdl.description.position){
rep.description.position = mdl.description.position
}
// TODO: making rep. of input / output should be a f'n of that object ...
// input, outputs, state objs should be known /sysobjects
// everything else is free play
......
[{"description":{"name":"gate","alt":"in ... out"},"state":{"emitters":{},"toggle":{"isButton":true,"isPressed":false,"label":"Open / Close"},"message":"closed","_toggle":{"isButton":true,"isPressed":false,"label":"Open / Close"},"_message":"closed"},"isOpen":false,"inputs":{"thru":{"accepts":"any","parentId":0,"key":"thru"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":1,"key":"thru"},{"accepts":"any","parentId":2,"key":"thru"},{"accepts":"any","parentId":3,"key":"reset"},{"accepts":"event","parentId":4,"key":"trigger"},{"accepts":"event","parentId":8,"key":"rmtrig"},{"accepts":"event","parentId":9,"key":"rmtrig"}]}},"id":0,"path":"./src/util/gate.js","ui":{"left":160,"top":130}},{"description":{"name":"delay!","alt":"in ... out"},"state":{"emitters":{},"ms":100,"_ms":100},"inputs":{"thru":{"accepts":"any","parentId":1,"key":"thru"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":3,"key":"A"}]}},"id":1,"path":"./src/util/delay.js","ui":{"left":160,"top":260}},{"description":{"name":"logger!","alt":"in ... out to console"},"state":{"emitters":{},"prefix":"LOGGER:","message":"---","_prefix":"LOGGER:","_message":"---"},"inputs":{"thru":{"accepts":"any","parentId":2,"key":"thru"}},"id":2,"path":"./src/util/log.js","ui":{"left":160,"top":400}},{"description":{"name":"andflow","alt":"in ... out"},"state":{"emitters":{},"toggle":{"isButton":true,"isPressed":false,"label":"Reset"},"A":0,"B":0,"_toggle":{"isButton":true,"isPressed":false,"label":"Reset"},"_A":0,"_B":0},"inputs":{"reset":{"accepts":"any","parentId":3,"key":"reset"},"A":{"accepts":"any","parentId":3,"key":"A"},"B":{"accepts":"any","parentId":3,"key":"B"}},"outputs":{"out":{"emits":"any","calls":[{"accepts":"any","parentId":0,"key":"thru"}]}},"id":3,"path":"./src/flowcontrol/and.js","ui":{"left":700,"top":300}},{"description":{"name":"CAMERA Request","alt":"webcam","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"REQUEST IMAGE"},"counter":0,"_button":{"isButton":true,"isPressed":false,"label":"REQUEST IMAGE"},"_counter":0},"inputs":{"trigger":{"accepts":"event","parentId":4,"key":"trigger"}},"outputs":{"image":{"emits":"image","calls":[]},"callback":{"emits":"event","calls":[{"accepts":"any","parentId":3,"key":"B"}]}},"id":4,"path":"./src/hardware/webcam.js","ui":{"left":500,"top":700}},{"description":{"name":"Breadboard ADC Request","alt":"bbadc","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"REQUEST CONVERSION"},"adcVal":0,"_button":{"isButton":true,"isPressed":false,"label":"REQUEST CONVERSION"},"_adcVal":0},"inputs":{"packet":{"accepts":"headless packet","parentId":5,"key":"packet"},"request":{"accepts":"event","parentId":5,"key":"request"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"B"}]}},"id":5,"path":"./src/hardware/adc.js","ui":{"left":1629,"top":180}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":6,"key":"packet"},"trigger":{"accepts":"event","parentId":6,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"B"}]}},"id":6,"path":"./src/hardware/test.js","ui":{"left":1060,"top":918}},{"description":{"name":"ATK Hardware Bridge","alt":"talks over serialport","isHardware":true},"state":{"emitters":{},"rA":"0,0","rB":"0,1","rC":"0,2","rD":"0,3","rE":"0,4","rF":"0,5","rG":"0","findPort":{"isButton":true,"isPressed":false,"label":"click to find atk port"},"portName":"---","connect":{"isButton":true,"isPressed":false,"label":"click to connect"},"portStatus":"closed","terminal":"address | key:values","sendRawPacket":{"isButton":true,"isPressed":false,"label":"sendRaw"},"_rA":"0,0","_rB":"0,1","_rC":"0,2","_rD":"0,3","_rE":"0,4","_rF":"0,5","_rG":"0","_findPort":{"isButton":true,"isPressed":false,"label":"click to find atk port"},"_portName":"---","_connect":{"isButton":true,"isPressed":false,"label":"click to connect"},"_portStatus":"closed","_terminal":"address | key:values","_sendRawPacket":{"isButton":true,"isPressed":false,"label":"sendRaw"}},"inputs":{"A":{"accepts":"headless packet","parentId":7,"key":"A"},"B":{"accepts":"headless packet","parentId":7,"key":"B"},"C":{"accepts":"headless packet","parentId":7,"key":"C"},"D":{"accepts":"headless packet","parentId":7,"key":"D"},"E":{"accepts":"headless packet","parentId":7,"key":"E"},"F":{"accepts":"headless packet","parentId":7,"key":"F"},"G":{"accepts":"headless packet","parentId":7,"key":"G"}},"outputs":{"A":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":8,"key":"packet"},{"accepts":"headless packet","parentId":10,"key":"packet"}]},"B":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":5,"key":"packet"},{"accepts":"headless packet","parentId":6,"key":"packet"}]},"C":{"emits":"headless packet","calls":[]},"D":{"emits":"headless packet","calls":[]},"E":{"emits":"headless packet","calls":[]},"F":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":9,"key":"packet"},{"accepts":"headless packet","parentId":11,"key":"packet"}]},"G":{"emits":"headless packet","calls":[]}},"pairs":{"A":{"route":"0,0","output":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":8,"key":"packet"},{"accepts":"headless packet","parentId":10,"key":"packet"}]}},"B":{"route":"0,1","output":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":5,"key":"packet"},{"accepts":"headless packet","parentId":6,"key":"packet"}]}},"C":{"route":"0,2","output":{"emits":"headless packet","calls":[]}},"D":{"route":"0,3","output":{"emits":"headless packet","calls":[]}},"E":{"route":"0,4","output":{"emits":"headless packet","calls":[]}}},"id":7,"path":"./src/hardware/bridge.js","ui":{"left":1629,"top":890}},{"description":{"name":"ATK Network Stepper Driver","alt":"software representation of stepper","isHardware":true},"state":{"emitters":{},"axis":"Y","spu":75,"rawMove":0.25,"makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"lead":0,"position":0,"_axis":"Y","_spu":75,"_rawMove":0.25,"_makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"_lead":0,"_position":0},"inputs":{"move":{"accepts":"move instruction","parentId":8,"key":"move"},"packet":{"accepts":"headless packet","parentId":8,"key":"packet"},"rmtrig":{"accepts":"event","parentId":8,"key":"rmtrig"}},"outputs":{"ack":{"emits":"move acknowledgement","calls":[]},"q":{"emits":"number","calls":[]},"packet":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":7,"key":"A"}]}},"id":8,"path":"./src/hardware/stepper.js","ui":{"left":1629,"top":372}},{"description":{"name":"ATK Network Stepper Driver","alt":"software representation of stepper","isHardware":true},"state":{"emitters":{},"axis":"Y","spu":-75,"rawMove":0.25,"makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"lead":0,"position":0,"_axis":"Y","_spu":-75,"_rawMove":0.25,"_makeMove":{"isButton":true,"isPressed":false,"label":"test move"},"_lead":0,"_position":0},"inputs":{"move":{"accepts":"move instruction","parentId":9,"key":"move"},"packet":{"accepts":"headless packet","parentId":9,"key":"packet"},"rmtrig":{"accepts":"event","parentId":9,"key":"rmtrig"}},"outputs":{"ack":{"emits":"move acknowledgement","calls":[]},"q":{"emits":"number","calls":[]},"packet":{"emits":"headless packet","calls":[{"accepts":"headless packet","parentId":7,"key":"F"}]}},"id":9,"path":"./src/hardware/stepper.js","ui":{"left":1629,"top":626}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":10,"key":"packet"},"trigger":{"accepts":"event","parentId":10,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"A"}]}},"id":10,"path":"./src/hardware/test.js","ui":{"left":1060,"top":1050}},{"description":{"name":"Test Packet","alt":"net state","isHardware":true},"state":{"emitters":{},"button":{"isButton":true,"isPressed":false,"label":"TEST"},"message":"no test started","_button":{"isButton":true,"isPressed":false,"label":"TEST"},"_message":"no test started"},"inputs":{"packet":{"accepts":"headless packet","parentId":11,"key":"packet"},"trigger":{"accepts":"event","parentId":11,"key":"trigger"}},"outputs":{"packet":{"emits":"number","calls":[{"accepts":"headless packet","parentId":7,"key":"F"}]}},"id":11,"path":"./src/hardware/test.js","ui":{"left":1060,"top":1170}}]
\ No newline at end of file
{
"description":
{
"name": "tstprgmem"
},
"modules": [
{
"description":
{
"id": "gate-1",
"name": "gate",
"alt": "in ... out",
"path": "./src/util/gate.js"
},
"inputs":
{
"thru":
{
"accepts": "any"
}
},
"outputs":
{
"out":
{
"emits": "any",
"calls": [
{
"parentId": "delay-2",
"key": "thru"
},
{
"parentId": "logger-3",
"key": "thru"
}]
}
},
"state":
{
"toggle":
{
"isButton": true,
"isPressed": false,
"label": "Open / Close"
},
"message": "closed"
}
},
{
"description":
{
"id": "delay-2",
"name": "delay",
"alt": "in ... out",
"path": "./src/util/delay.js"
},
"inputs":
{
"thru":
{
"accepts": "any"
}
},
"outputs":
{
"out":
{
"emits": "any",
"calls": [
{
"parentId": "logger-3",
"key": "thru"
}]
}
},
"state":
{
"ms": 100
}
},
{
"description":
{
"id": "logger-3",
"name": "logger",
"alt": "in ... out to console",
"path": "./src/util/log.js"
},
"inputs":
{
"thru":
{
"accepts": "any"
}
},
"outputs":
{},
"state":
{
"prefix": "LOGGER:",
"message": "---"
}
}]
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ const http = require('http').Server(app)
const WebSocket = require('ws')
const Reps = require('./reps.js')
const Programs = require('./programs.js')
/*
......@@ -87,8 +88,11 @@ function socketRecv(evt) {
uiRequestModuleMenu()
break
case 'load program':
uiRequestLoadProgram()
uiRequestLoadProgram(data)
break
case 'save program':
uiRequestSaveProgram(data)
break
case 'put module':
uiRequestNewModule(data)
break
......@@ -137,7 +141,6 @@ function uiRequestCurrentProgram() {
},
modules: {}
}
for (mdlName in program.modules) {
var mdlRep = Reps.makeFromModule(program.modules[mdlName])
prgRep.modules[mdlName] = mdlRep
......@@ -160,7 +163,6 @@ function uiRequestModuleMenu() {
}
}
}
socketSend('put module menu', availableSourceRep)
}
......@@ -174,13 +176,27 @@ function uiRequestLoadProgram(data){
console.log('UI REQUEST TO OPEN NEW PROGRAM', data)
}
function uiRequestSaveProgram(data){
console.log('UI REQUEST TO SAVE PROGRAM', data)
// is data a path? add .json ?
if(!data.includes('.json')){
data = data + '.json'
}
path = 'programs/' + data
Programs.save(program, path)
}
function uiRequestNewModule(data) {
console.log('UI REQUEST ADD MODULE TO PROGRAM', data)
Programs.loadModuleFromSource(program, data)
// bit of a mess to pick out the last entered module
var keys = Object.keys(program.modules)
socketSend('put module', Reps.makeFromModule(program.modules[keys[keys.length - 1]]))
}
function uiRequestStateChange(data) {
console.log('UI REQUEST CHANGE STATE IN MODULE', data)
// do state.obj.emit
// and don't forget state.obj.emit
}
function uiRequestLinkChange(data) {
......@@ -189,7 +205,8 @@ function uiRequestLinkChange(data) {
function uiRequestUiChange(data) {
console.log('UI REQUEST ADD / CHANGE UI INFO TO MODULE', data)
// do it in module.description
var mod = program.modules[data.description.id]
mod.description.position = data.description.position
}
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment