diff --git a/README.md b/README.md index 8f5252e1c1436815c8d426f7e7a53202fcdcb39f..857af22367ca5c7cc4c0a47fb653e83ef8dbf822 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,16 @@ To wake up, I've a small list: - same for comm/ - rebuild / use scrape.js (with //HEADER and //ENDHEADER flags) +There's some UI leftovers in the view. + - want to kick around on spreading things out properly using force layout + - can't add links, did this actually work before ? drag correct zoom factor, and cancel other moves + +- towards using state-load-at-program-load to set retrycount to 0 + +- loading is messy, + - load in state objects before init (rolling through much old code) + - view should have display location for messages you anticipate users needing to see, primarily errors that are responses to view requests: like addlink (type errors) or addhunk (resource not available from cpp) or errors from hunk v8 loading, json program loading, etc. + Once I'm through that, I should be back at happily opening up nautilus-via-cuttlefish. I'll polish my UI, particularly wire views. I'll explicate the list of messages that I need to send to managers, and to links. @@ -140,7 +150,17 @@ Messages to 254 port are debug, should just be printed. Messages to 253 are to t What's stopping me from implementing this? Am I burnt out? -I think this also asks me to reconsider how programs are loaded. Does it? No. It's actually a good test of the existing system. +I think this also asks me to reconsider how programs are loaded. Does it? No. It's actually a good test of the existing system. + +So, there's a lot to do. It's a good thing I love JavaScript: every language's bastard son, in a King of the North kind of way. + + - state objects are typed, + - state objects are written at program load, if they're there. + - does this mean that they go into 'add hunk' message ? + - reload link verbatim but via state-load + - then scrape to nautilus ... and load via ws + - maybe then is view cleanup ? + ## A List of Types diff --git a/gogetter.js b/gogetter.js index ed0dae9a7d4156fd16bf6e1c4155a7890fc7c258..6c2dadfe0d97d1ec52a0bc4f0242e76552814423 100644 --- a/gogetter.js +++ b/gogetter.js @@ -1,12 +1,12 @@ -// fs abstraction for sys +// fs abstraction for sys function GoGetter() { this.findHunks = (debug) => { return new Promise((resolve, reject) => { let htmlTreeDiver = (response) => { - // do we try to pick links out of this plaintext ? like a monkey ? - // I suppose we do - // header, for names + // do we try to pick links out of this plaintext ? like a monkey ? + // I suppose we do + // header, for names let hfront = response.indexOf('<h1>Index of /hunks') + 14 let hback = response.indexOf('</h1>') let header = response.slice(hfront, hback) @@ -20,7 +20,7 @@ function GoGetter() { // git those paths (these aren't names, are they?) let paths = response.split('<a href="/hunks/') // rm first two, and last () - paths = paths.slice(2) // + paths = paths.slice(2) // paths.forEach((path, index) => { let end = path.indexOf('"') @@ -37,10 +37,10 @@ function GoGetter() { jQuery.get(recurse, htmlTreeDiver) } else if (path.includes('.js')) { //console.log('likely hunk', path) - // secrets are rming .js + // secrets are rming .js path = path.slice(0, path.indexOf('.')) if (path === 'hunks' || path === 'manager' || path === 'template' || path.includes('hidden/')) { - // don't add these special hunks + // don't add these special hunks } else { returned.push(path) } @@ -48,9 +48,9 @@ function GoGetter() { }) if (unreturned.length === 0) { - // TODO cull hunks and hidden paths ... - // now, writing menu options for each path - // sort alphabetically + // TODO cull hunks and hidden paths ... + // now, writing menu options for each path + // sort alphabetically returned.sort() //console.log('GG returns this', JSON.parse(JSON.stringify(returned))) resolve(returned) @@ -81,7 +81,7 @@ function GoGetter() { window[tempGlobal] = function(module) { cleanup() - console.log('ADDHUNK (2) import resolves', url) + //console.log('ADDHUNK (2) import resolves', url) resolve(module[Object.keys(module)[0]]) } @@ -101,7 +101,7 @@ function GoGetter() { return new Promise((resolve, reject) => { $.ajax({ url: path, - type: 'GET', + type: 'GET', success: (data) => { resolve(data) }, @@ -113,4 +113,4 @@ function GoGetter() { } } -export default GoGetter \ No newline at end of file +export default GoGetter diff --git a/hunks/hunks.js b/hunks/hunks.js index 9134a88ad982f74b2910e03876cac7ec4d11e8a4..ad8053e6214ced4027d993b11637d1439d67870c 100644 --- a/hunks/hunks.js +++ b/hunks/hunks.js @@ -123,6 +123,7 @@ function Output(type, name) { function State(type, name, startup) { this.name = name this.type = type + // TODO pls add check for missing startup value ? this.value = startup // this is still something of a hack // during load, the manager gets in here and dishes a function 2 us diff --git a/hunks/link.js b/hunks/link.js index c17f88e24305dbb91919cb0eb80566c11ccf07e2..79994e5d8a14013d4e6e448e996d6417c83ba6ea 100644 --- a/hunks/link.js +++ b/hunks/link.js @@ -22,6 +22,14 @@ function Link() { this.inputs.data = new Input('byteArray', 'data') this.outputs.data = new Output('byteArray', 'data') + this.state.inputList = new State('string', 'inputList', "msgs (byteArray)") + this.state.outputList = new State('string', 'outputList', "msgs (byteArray)") + + let inports = [this.inputs.zero, this.inputs.zero, this.inputs.one] + let outports = [this.outputs.zero, this.outputs.zero] + + /* + // these are *special link inputs* keeping track of downstream status this.inputs.zero = new Input('any', 'zero') this.inputs.zero.dss = 'open' @@ -44,10 +52,13 @@ function Link() { this.outputs.one.hold.status = 'open' this.outputs.one.hold.msg = {} + */ + this.init = () => { // manager calls this once // it is loaded and state is updated (from program) this.log('hello Link') + // HERE write those inputs via that list } // so far we won't ack at the link layer, @@ -56,8 +67,6 @@ function Link() { // but we will need to do this for the dmarippers // perhaps that should live in the layer that 'websocket' is at now - let inports = [this.inputs.zero, this.inputs.zero, this.inputs.one] - let outports = [this.outputs.zero, this.outputs.zero] let outbuffer = new Array() @@ -82,6 +91,7 @@ function Link() { this.loop = () => { + /* // for everything we're holding, check our outputs for (let i in outports) { if (outports[i].hold.status === 'occupied' && outports[i].ie) { @@ -168,6 +178,7 @@ function Link() { if (this.outputs.data.ie && outbuffer.length > 0) { this.outputs.data.put(outbuffer.shift()) } + */ } // end loop } diff --git a/hunks/manager.js b/hunks/manager.js index 6ee48c1d956a87364961525c278c4133dfcb7bc5..3f0700e131752972826c2f4d57c230b19a1ce8b9 100644 --- a/hunks/manager.js +++ b/hunks/manager.js @@ -1,617 +1,651 @@ -import { Hunkify, Input, Output, State } from './hunks.js' +import { + Hunkify, + Input, + Output, + State +} from './hunks.js' import GoGetter from '../gogetter.js' function Manager() { - Hunkify(this, 'Manager') - // need this tool - let GG = new GoGetter - - this.inputs.msgs = new Input('message', 'msgs') - this.outputs.msgs = new Output('message', 'msgs') - - let hunkCount = 0 - let hunks = new Array() - - /* --------------------------- ---------------------------- */ - /* -------------------- STD MGR INTERFACE -------------------- */ - /* --------------------------- ---------------------------- */ - - this.getHelloResponses = () => { - // writeMessage does buffering on our end - return [{ - text: 'list available hunks', - header: 'listhunks', - content: ' ' - }, - { - text: 'describe running program', - header: 'listprogram', - content: ' ' - }, - { - text: 'save running program', - header: 'saveprogram', - content: ' ' - } - ] - } - - /* --------------------------- ---------------------------- */ - /* ---------------------- BUILDING LIST ---------------------- */ - /* --------------------------- ---------------------------- */ - - // for now, managers take callback paths to pipe data back through ? - this.getListOfAvailableHunks = () => { - return new Promise((resolve, reject) => { - GG.findHunks().then((list) => { - resolve(list) - }).catch((err) => { - reject(err) - }) - }) + Hunkify(this, 'Manager') + // need this tool + let GG = new GoGetter + + this.inputs.msgs = new Input('message', 'msgs') + this.outputs.msgs = new Output('message', 'msgs') + + let hunkCount = 0 + let hunks = new Array() + + /* --------------------------- ---------------------------- */ + /* -------------------- STD MGR INTERFACE -------------------- */ + /* --------------------------- ---------------------------- */ + + this.getHelloResponses = () => { + // writeMessage does buffering on our end + return [{ + text: 'list available hunks', + header: 'listhunks', + content: ' ' + }, + { + text: 'describe running program', + header: 'listprogram', + content: ' ' + }, + { + text: 'save running program', + header: 'saveprogram', + content: ' ' + } + ] + } + + let errmsg = (message) => { + this.writemessage('error', message) + } + + /* --------------------------- ---------------------------- */ + /* ---------------------- BUILDING LIST ---------------------- */ + /* --------------------------- ---------------------------- */ + + // for now, managers take callback paths to pipe data back through ? + this.getListOfAvailableHunks = () => { + return new Promise((resolve, reject) => { + GG.findHunks().then((list) => { + resolve(list) + }).catch((err) => { + reject(err) + }) + }) + } + + /* --------------------------- ---------------------------- */ + /* ----------------- REPLY WITH DESCRIPTIONS ----------------- */ + /* --------------------------- ---------------------------- */ + + this.describeRunningProgram = () => { + for (let hnk in hunks) { + this.sendHunkAsDef(hunks[hnk]) } + } - /* --------------------------- ---------------------------- */ - /* ----------------- REPLY WITH DESCRIPTIONS ----------------- */ - /* --------------------------- ---------------------------- */ + this.sendHunkAsDef = (hunk) => { + let def = writeDefinition(hunk) + this.writemessage('putdef', def) + } - this.describeRunningProgram = () => { - for (let hnk in hunks) { - this.sendHunkAsDef(hunks[hnk]) - } + // bit confusing this / but is functional code node oo + let writeDefinition = (hunk) => { + if (hunk.id === null) { + throw new Error('hunk with no id in writeHunkDefinition') } - - this.sendHunkAsDef = (hunk) => { - let def = writeDefinition(hunk) - this.writemessage('putdef', def) + // ok + // write tree + let def = { + name: hunk.name, + id: hunk.id, + inputs: {}, + outputs: {}, + state: {} } - // bit confusing this / but is functional code node oo - let writeDefinition = (hunk) => { - if (hunk.id === null) { - throw new Error('hunk with no id in writeHunkDefinition') - } - // ok - // write tree - let def = { - name: hunk.name, - id: hunk.id, - inputs: {}, - outputs: {}, - state: {} - } - - // add inputs - if (Object.keys(hunk.inputs).length > 0) { - for (let key in hunk.inputs) { - let input = hunk.inputs[key] - def.inputs[input.name] = { - name: input.name, - type: input.type - } - } + // add inputs + if (Object.keys(hunk.inputs).length > 0) { + for (let key in hunk.inputs) { + let input = hunk.inputs[key] + def.inputs[input.name] = { + name: input.name, + type: input.type } + } + } - // add outputs - if (Object.keys(hunk.outputs).length > 0) { - for (let key in hunk.outputs) { - let output = hunk.outputs[key] - def.outputs[output.name] = { - name: output.name, - type: output.type, - connections: new Array() - } - if (output.connections.length > 0) { - for (let conn in output.connections) { - let crep = { - input: output.connections[conn].input.name, - parentid: output.connections[conn].parentid - } - def.outputs[output.name].connections.push(crep) - } - } - } + // add outputs + if (Object.keys(hunk.outputs).length > 0) { + for (let key in hunk.outputs) { + let output = hunk.outputs[key] + def.outputs[output.name] = { + name: output.name, + type: output.type, + connections: new Array() } - - // add outputs - if (Object.keys(hunk.state).length > 0) { - for (let key in hunk.state) { - let state = hunk.state[key] - def.state[state.name] = { - name: state.name, - type: state.type, - value: state.value - } + if (output.connections.length > 0) { + for (let conn in output.connections) { + let crep = { + input: output.connections[conn].input.name, + parentid: output.connections[conn].parentid } + def.outputs[output.name].connections.push(crep) + } } - - if (hunk.dom !== null) { - //console.log('WHD has dom', hunk.dom) - def.dom = hunk.dom - } - - return def + } } - this.findHunkById = (id) => { - let theHunk = hunks.find((hunk) => { - return hunk.id === id - }) - if (theHunk !== undefined) { - return theHunk - } else { - return undefined + // add outputs + if (Object.keys(hunk.state).length > 0) { + for (let key in hunk.state) { + let state = hunk.state[key] + def.state[state.name] = { + name: state.name, + type: state.type, + value: state.value } + } } - /* --------------------------- ---------------------------- */ - /* ------------------- ADD / REMOVE A HUNK ------------------- */ - /* --------------------------- ---------------------------- */ - - this.addHunk = (hunkName, id) => { - // hunks are named by path in js - // so that we can add - // so that we can link - // so that we can go to links, websockets, node - let path = './hunks/' + hunkName + '.js' - - return new Promise((resolve, reject) => { - GG.importSource(path).then((src) => { - try { - let hunk = sourceToHunk(src, hunkName, id) - hunks.push(hunk) - this.sendHunkAsDef(hunk) - resolve(hunk) - } catch (err) { - reject(err) - } - }).catch((err) => { - reject(err) - }) - }) + if (hunk.dom !== null) { + //console.log('WHD has dom', hunk.dom) + def.dom = hunk.dom } - let sourceToHunk = (src, name, id) => { - // make the instance from the constructor - let hunk = new src() - // name it (this is just the path) - hunk.name = name - - // id it, with a new ID if it doesn't have one, or - // with the ID from its program (occasionally provided) s - if (id !== undefined && id !== null) { - // TODO: BIGBOI: also check for duplicate ids ?? totally possible, very likely - hunk.id = id - } else { - // TODO: probably there is a better id, and what of program loads? - hunk.id = 'hnk_' + hunkCount - } - hunkCount++ - - // now we open our doors to state changes - if (Object.keys(hunk.state).length > 0) { - for (let st in hunk.state) { - hunk.state[st].hookup = (value) => { - console.log('MGR -> VIEW STATECHANGE') - this.writemessage('putstate', { - id: hunk.id, - name: hunk.state[st].name, - value: value - }) - } - } - } - - // startup code if it exists - if (hunk.init != null) { - try { - hunk.init() - } catch (err) { - throw err - } + return def + } + + this.findHunkById = (id) => { + let theHunk = hunks.find((hunk) => { + return hunk.id === id + }) + if (theHunk !== undefined) { + return theHunk + } else { + return undefined + } + } + + /* --------------------------- ---------------------------- */ + /* ------------------- ADD / REMOVE A HUNK ------------------- */ + /* --------------------------- ---------------------------- */ + + this.addHunk = (hunkName, id, state) => { + // hunks are named by path in js + // so that we can add + // so that we can link + // so that we can go to links, websockets, node + let path = './hunks/' + hunkName + '.js' + + return new Promise((resolve, reject) => { + GG.importSource(path).then((src) => { + try { + let hunk = sourceToHunk(src, hunkName, id, state) + hunks.push(hunk) + this.sendHunkAsDef(hunk) + resolve(hunk) + } catch (err) { + reject(err) } - - return hunk + }).catch((err) => { + reject(err) + }) + }) + } + + let sourceToHunk = (src, name, id, state) => { + // make the instance from the constructor + let hunk = new src() + // name it (this is just the path) + hunk.name = name + + // id it, with a new ID if it doesn't have one, or + // with the ID from its program (occasionally provided) s + if (id !== undefined && id !== null) { + // TODO: BIGBOI: also check for duplicate ids ?? totally possible, very likely + hunk.id = id + } else { + // TODO: probably there is a better id, and what of program loads? + hunk.id = 'hnk_' + hunkCount } - - this.removeHunk = (req) => { - let id = req.id - let name = req.name - if (name == 'manager' || name == 'hidden/view') { - throw new Error("I can't let you do that, Dave") - return false - } else { - let hnk = hunks.find((element) => { - return id === element.id - }) - if (hnk !== undefined) { - // rm then - hunks.splice(hunks.indexOf(hnk), 1) - for (let hks of hunks) { - for (let otp of Object.keys(hks.outputs)) { - hks.outputs[otp].unhook(id) - } - } - return true - } else { - return false - } + hunkCount++ + + // now we open our doors to state changes + if (Object.keys(hunk.state).length > 0) { + for (let st in hunk.state) { + hunk.state[st].hookup = (value) => { + // console.log('MGR -> VIEW STATECHANGE') + this.writemessage('putstate', { + id: hunk.id, + name: hunk.state[st].name, + value: value + }) } + } } - /* --------------------------- ---------------------------- */ - /* ----------------- LINKS HELLO / GOODBYTE ------------------ */ - /* --------------------------- ---------------------------- */ - - this.addLink = (outHunkId, outputName, inHunkId, inputName) => { - // sync, doesn't need to be a promise - let outHunk = hunks.find((hunk) => { - return hunk.id == outHunkId - }) - let inHunk = hunks.find((hunk) => { - return hunk.id == inHunkId - }) - // console.log('found hunks', outHunk, inHunk) - if (outHunk === undefined) { - console.log("MGR on ADDLINK: outHunk is undefined", outHunkId) - return false - } - if (inHunk === undefined) { - console.log("MGR on ADDLINK: inHunk is undefined", inHunkId) - return false - } - if (outHunk.outputs[outputName] === null || outHunk.outputs[outputName] === undefined) { - console.log('MGR on ADDLINK: output hunk is null or undefined:', outHunk.name, outputName) - return false - } else if (inHunk.inputs[inputName] === null || inHunk.inputs[inputName] === undefined) { - console.log('MGR on ADDLINK: input hunk is null or undefined:', inHunk.name, inputName) - return false + // now we update it's state if we have any of *that* to do + if(state !== undefined && state !== null){ + for(let ro in state){ + // ro is the key + if(hunk.state[ro] !== undefined){ + console.log('types', typeof hunk.state[ro].value, typeof state[ro]) + if(typeof hunk.state[ro].value == typeof state[ro]){ + hunk.state[ro].value = state[ro] + } else { + this.writemessage('error', 'problematic state content in program, not same type') + } } else { - this.log(`hooking ${outHunk.id} to ${inHunk.id}`) - outHunk.outputs[outputName].attach(inHunk.inputs[inputName], inHunk.id) - this.writemessage('putlink', { - outId: outHunkId, - outName: outputName, - inId: inHunkId, - inName: inputName - }) - return true + this.writemessage('error', 'problematic state content in program, object not found') } + } } - this.addLinkByObject = (fromHunk, outputName, toHunk, inputName) => { - // TODO: not throwing errors when outputname does not exist - // I.E. addLink doesn't actually use 'name' property, it uses [key] - this.addLink(fromHunk.id, outputName, toHunk.id, inputName) + // startup code if it exists + if (hunk.init != null) { + try { + hunk.init() + } catch (err) { + throw err + } } - this.removeLink = (outHunkId, outputName, inHunkId, inputName) => { - // console.log('REMOVELINK looking for hunk, with name, and in, with name', outHunkId, outputName, inHunkId, inputName) - let outHunk = hunks.find((hunk) => { - return hunk.id == outHunkId - }) - let inHunk = hunks.find((hunk) => { - return hunk.id == inHunkId - }) // console.log('found hunks', outHunk, inHunk) - if (outHunk === undefined || inHunk === undefined) { - console.log("MGR on REMOVELINK: output or input hunks are undefined") - return false - } - if (outHunk.outputs[outputName] === null || outHunk.outputs[outputName] === undefined) { - console.log('MGR on REMOVELINK: output hunk is null or undefined:', outHunk.name, outputName) - return false - } else if (inHunk.inputs[inputName] === null || inHunk.inputs[inputName] === undefined) { - console.log('MGR on REMOVELINK: input hunk is null or undefined:', inHunk.name, inputName) - return false - } else { - this.log(`unhooking ${outHunk.id} to ${inHunk.id}`) - if (outHunk.outputs[outputName].remove(inHunk.inputs[inputName], inHunk.id)) { - this.writemessage('removelink', { - outId: outHunkId, - outName: outputName, - inId: inHunkId, - inName: inputName - }) - return true - } else { - return false - } + return hunk + } + + this.removeHunk = (req) => { + let id = req.id + let name = req.name + if (name == 'manager' || name == 'hidden/view') { + throw new Error("I can't let you do that, Dave") + return false + } else { + let hnk = hunks.find((element) => { + return id === element.id + }) + if (hnk !== undefined) { + // rm then + hunks.splice(hunks.indexOf(hnk), 1) + for (let hks of hunks) { + for (let otp of Object.keys(hks.outputs)) { + hks.outputs[otp].unhook(id) + } } + return true + } else { + return false + } } - - /* --------------------------- ---------------------------- */ - /* ---------------------- STATE CHANGES ---------------------- */ - /* --------------------------- ---------------------------- */ - - this.stateChange = (hunkId, stateName, newValue) => { - return new Promise((resolve, reject) => { - this.log(`to change ${stateName} to ${newValue} in ${hunkId}`) - let theHunk = hunks.find((hunk) => { - return hunk.id == hunkId - }) - if (theHunk !== undefined) { - let theState = theHunk.state[stateName] - if (theState !== null && theState !== undefined) { - theState.change(newValue) - resolve() - } else { - reject(new Error('@ statechange theState is undefined')) - } - } else { - reject(new Error('@ statechange theHunk is undefined')) - } - }) + } + + /* --------------------------- ---------------------------- */ + /* ----------------- LINKS HELLO / GOODBYTE ------------------ */ + /* --------------------------- ---------------------------- */ + + this.addLink = (outHunkId, outputName, inHunkId, inputName) => { + // sync, doesn't need to be a promise + let outHunk = hunks.find((hunk) => { + return hunk.id == outHunkId + }) + let inHunk = hunks.find((hunk) => { + return hunk.id == inHunkId + }) + // console.log('found hunks', outHunk, inHunk) + if (outHunk === undefined) { + errmsg("MGR on ADDLINK: outHunk is undefined: " + outHunkId) + return false } - - /* --------------------------- ---------------------------- */ - /* --------------------- PROGRAM LOADING --------------------- */ - /* --------------------------- ---------------------------- */ - - this.addProgram = (prgname) => { - return new Promise((resolve, reject) => { - let unloaded = new Array() - let loaded = new Array() - // first, we should get the program - // HERE consider this, - // probably re-write addlink to auto-complete as well - // and then: the view init ... the stuffy message buffer ... the flush ? - GG.getJson('programs/' + prgname + '.json').then((obj) => { - if (obj.programname !== null && obj.programname !== undefined) { - this.log(`opening program with name: ${obj.programname}`) - addHunkList(obj.hunks).then((newHunks) => { - this.log(`finished adding hunks from: ${obj.programname}, now links`) - for (let lnk of obj.links) { - if (this.addLink(lnk.outhunk, lnk.outname, lnk.inhunk, lnk.inname)) { - // success! continue - } else { - reject('err in program load, at links', lnk) - } - } - resolve(newHunks) - }).catch((err) => { - reject(err) - }) - } else { - reject(new Error('program loads, not named')) - } - }).catch((err) => { - reject(err) - }) + if (inHunk === undefined) { + errmsg("MGR on ADDLINK: inHunk is undefined: " + inHunkId) + return false + } + if (outHunk.outputs[outputName] === null || outHunk.outputs[outputName] === undefined) { + errmsg('MGR on ADDLINK: output is null or undefined. for hunk: ' + outHunk.id + " and output: " + outputName) + return false + } else if (inHunk.inputs[inputName] === null || inHunk.inputs[inputName] === undefined) { + errmsg('MGR on ADDLINK: input is null or undefined. for hunk: ' + inHunk.id + " and output: " + inputName) + return false + } else { + this.log(`hooking ${outHunk.id} to ${inHunk.id}`) + outHunk.outputs[outputName].attach(inHunk.inputs[inputName], inHunk.id) + this.writemessage('putlink', { + outId: outHunkId, + outName: outputName, + inId: inHunkId, + inName: inputName + }) + return true + } + } + + this.addLinkByObject = (fromHunk, outputName, toHunk, inputName) => { + // TODO: not throwing errors when outputname does not exist + // I.E. addLink doesn't actually use 'name' property, it uses [key] + this.addLink(fromHunk.id, outputName, toHunk.id, inputName) + } + + this.removeLink = (outHunkId, outputName, inHunkId, inputName) => { + // console.log('REMOVELINK looking for hunk, with name, and in, with name', outHunkId, outputName, inHunkId, inputName) + let outHunk = hunks.find((hunk) => { + return hunk.id == outHunkId + }) + let inHunk = hunks.find((hunk) => { + return hunk.id == inHunkId + }) // console.log('found hunks', outHunk, inHunk) + if (outHunk === undefined || inHunk === undefined) { + errmsg("MGR on REMOVELINK: output or input hunks are undefined") + return false + } + if (outHunk.outputs[outputName] === null || outHunk.outputs[outputName] === undefined) { + errmsg('MGR on REMOVELINK: output is null or undefined: ' + outHunk.name + " output: " + outputName) + return false + } else if (inHunk.inputs[inputName] === null || inHunk.inputs[inputName] === undefined) { + errmsg('MGR on REMOVELINK: input is null or undefined: ' + inHunk.name + "input: " + inputName) + return false + } else { + this.log(`unhooking ${outHunk.id} to ${inHunk.id}`) + if (outHunk.outputs[outputName].remove(inHunk.inputs[inputName], inHunk.id)) { + this.writemessage('removelink', { + outId: outHunkId, + outName: outputName, + inId: inHunkId, + inName: inputName }) + return true + } else { + return false + } } - - let addHunkList = (list) => { - return new Promise((resolve, reject) => { - let unloaded = new Array() - let loaded = new Array() - // first, we should get the program - for (let hnk in list) { - let name = list[hnk].name - let id = list[hnk].id - unloaded.push(id) - this.addHunk(name, id).then((hunk) => { - if (unloaded.includes(hunk.id)) { - unloaded.splice(unloaded.indexOf(hunk.id), 1) - loaded.push(hunk) - if (unloaded.length < 1) { - resolve(loaded) - } - } else { - reject(new Error('strange catch when adding a list of hunks')) - } - }).catch((err) => { - reject(err) - }) + } + + /* --------------------------- ---------------------------- */ + /* ---------------------- STATE CHANGES ---------------------- */ + /* --------------------------- ---------------------------- */ + + this.stateChange = (hunkId, stateName, newValue) => { + return new Promise((resolve, reject) => { + this.log(`to change ${stateName} to ${newValue} in ${hunkId}`) + let theHunk = hunks.find((hunk) => { + return hunk.id == hunkId + }) + if (theHunk !== undefined) { + let theState = theHunk.state[stateName] + if (theState !== null && theState !== undefined) { + theState.change(newValue) + resolve() + } else { + reject(new Error('@ statechange theState is undefined')) + } + } else { + reject(new Error('@ statechange theHunk is undefined')) + } + }) + } + + /* --------------------------- ---------------------------- */ + /* --------------------- PROGRAM LOADING --------------------- */ + /* --------------------------- ---------------------------- */ + + this.addProgram = (prgname) => { + return new Promise((resolve, reject) => { + let unloaded = new Array() + let loaded = new Array() + // first, we should get the program + // HERE consider this, + // probably re-write addlink to auto-complete as well + // and then: the view init ... the stuffy message buffer ... the flush ? + GG.getJson('programs/' + prgname + '.json').then((obj) => { + if (obj.programname !== null && obj.programname !== undefined) { + this.log(`opening program with name: ${obj.programname}`) + addHunkList(obj.hunks).then((newHunks) => { + this.log(`finished adding hunks from: ${obj.programname}, now links`) + for (let lnk of obj.links) { + if (this.addLink(lnk.outhunk, lnk.outname, lnk.inhunk, lnk.inname)) { + // success! continue + } else { + reject('watch err in program load, at links', lnk) + } } + resolve(newHunks) + }).catch((err) => { + reject(err) + }) + } else { + reject(new Error('program loads, not named')) + } + }).catch((err) => { + reject(err) + }) + }) + } + + let addHunkList = (list) => { + // list is str8 json 'hunks' list + // objects always have "name" and "id" properties, + // sometimes have "state" properties + return new Promise((resolve, reject) => { + let unloaded = new Array() + let loaded = new Array() + // first, we should get the program + for (let hnk in list) { + let name = list[hnk].name + let id = list[hnk].id + let state = list[hnk].state + unloaded.push(id) + this.addHunk(name, id, state).then((hunk) => { + if (unloaded.includes(hunk.id)) { + unloaded.splice(unloaded.indexOf(hunk.id), 1) + loaded.push(hunk) + if (unloaded.length < 1) { + resolve(loaded) + } + } else { + reject(new Error('strange catch when adding a list of hunks')) + } + }).catch((err) => { + reject(err) }) - } - - /* --------------------------- ---------------------------- */ - /* ---------------------- STARTUP, LOOP ---------------------- */ - /* --------------------------- ---------------------------- */ - - this.init = () => { - // startup by giving ourselves an ID if we haven't been assigned one? - // and then adding ourselves to ourselves ? - hunks.push(this) - this.log(`manager hello, id is ${this.id}`) - } - - this.loop = () => { - if (this.inputs.msgs.io) { - let msg = this.inputs.msgs.get() - let header = msg.header - let content = msg.content - this.log(`gets msg ${header}`) - switch (header) { - case 'hello': - let options = this.getHelloResponses() - this.writemessage('putcontextoptions', options) - break - case 'listhunks': - this.getListOfAvailableHunks().then((list) => { - let options = new Array() - // this *probably* needs work - for (let item of list) { - let option = { - text: item, - header: 'addhunk', - content: item - } - options.push(option) - } - this.writemessage('putcontextoptions', options) - }).catch((err) => { - this.log('ERR while getting list of available', err) - this.writemessage('error', err) - }) - break - case 'addprogram': - // TODO: try muxed up JSON, returns unhandled promise rejection - // in node? - this.addProgram(content).then((newHunks) => { - // newhunks are automatically reported on msgs, and so are new links - // so we just want a catch block here - }).catch((err) => { - this.log('ERR while adding program', err) - }) - break - case 'addhunk': - // TODO same here, addhunk should return automatically - this.log('adding this hunk', content) - this.addHunk(content).then((hunk) => { - this.log('added hunk with id', hunk.id) - }).catch((err) => { - this.log('ERR while adding hunk', err) - this.writemessage('error', err) - }) - break - case 'removehunk': - if (this.removeHunk(content)) { - this.writemessage('removedef', content.id) - } else { - this.writemessage('error', 'could not remove hunk as requested') - } - break - case 'addlink': - if (this.addLink(content.outId, content.outName, content.inId, content.inName)) { - // gr8 success, and addlink automatically updates with a message to the view - } else { - this.writemessage('error', 'could not add link as requested') - } - break - case 'removelink': - if (this.removeLink(content.outId, content.outName, content.inId, content.inName)) { - // gr8 success, continue - } else { - this.writemessage('error', 'cannot remove link as requested') - } - break - case 'statechange': - this.stateChange(content.id, content.name, content.value).then(() => { - // no response for this, message should pass from hunk - }).catch((err) => { - this.log('ERR at statechange', err) - this.writemessage('error', 'could not change state as requested') - }) - break - - default: - console.log('MGR: msg with no switch') - break + } + }) + } + + /* --------------------------- ---------------------------- */ + /* ---------------------- STARTUP, LOOP ---------------------- */ + /* --------------------------- ---------------------------- */ + + this.init = () => { + // startup by giving ourselves an ID if we haven't been assigned one? + // and then adding ourselves to ourselves ? + hunks.push(this) + this.log(`manager hello, id is ${this.id}`) + } + + // flag, + let msgdebug = false + + this.loop = () => { + // getting messages + if (this.inputs.msgs.io) { + let msg = this.inputs.msgs.get() + let header = msg.header + let content = msg.content + if(msgdebug) this.log(`gets msg ${header}`) + switch (header) { + case 'hello': + let options = this.getHelloResponses() + this.writemessage('putcontextoptions', options) + break + case 'listhunks': + this.getListOfAvailableHunks().then((list) => { + let options = new Array() + // this *probably* needs work + for (let item of list) { + let option = { + text: item, + header: 'addhunk', + content: item + } + options.push(option) } - } // end msgs input - - if (this.outmsgbuffer.length > 0) { - let debug = true - if (this.outputs.msgs.ie) { - if (debug) { - let msg = this.outmsgbuffer.shift() - this.log(`msg out ${msg.header}`) - //console.log(msg.content) - this.outputs.msgs.put(msg) + this.writemessage('putcontextoptions', options) + }).catch((err) => { + this.writemessage('error', 'ERR while getting list of available' + err) + }) + break + case 'addprogram': + // TODO: try muxed up JSON, returns unhandled promise rejection + // in node? + this.addProgram(content).then((newHunks) => { + // newhunks are automatically reported on msgs, and so are new links + // so we just want a catch block here + }).catch((err) => { + this.writemessage('error', 'ERR while adding program' + err) + }) + break + case 'addhunk': + // TODO same here, addhunk should return automatically + this.log('adding this hunk', content) + this.addHunk(content).then((hunk) => { + this.log('added hunk with id', hunk.id) + }).catch((err) => { + this.log('ERR while adding hunk', err) + this.writemessage('error', 'ERR on adding hunk' + err) + }) + break + case 'removehunk': + if (this.removeHunk(content)) { + this.writemessage('removedef', content.id) + } else { + this.writemessage('error', 'could not remove hunk as requested') + } + break + case 'addlink': + if (this.addLink(content.outId, content.outName, content.inId, content.inName)) { + // gr8 success, and addlink automatically updates with a message to the view + } else { + this.writemessage('error', 'could not add link as requested') + } + break + case 'removelink': + if (this.removeLink(content.outId, content.outName, content.inId, content.inName)) { + // gr8 success, continue + } else { + this.writemessage('error', 'cannot remove link as requested') + } + break + case 'statechange': + this.stateChange(content.id, content.name, content.value).then(() => { + // no response for this, message should pass from hunk + }).catch((err) => { + this.log('ERR at statechange', err) + this.writemessage('error', 'could not change state as requested') + }) + break + + default: + this.writemessage('error', 'manager receives message with no switch!') + this.log('MGR: msg with no switch') + break + } + } // end msgs input + + if (this.outmsgbuffer.length > 0) { + let debug = true + if (this.outputs.msgs.ie) { + if (debug) { + let msg = this.outmsgbuffer.shift() + if (msgdebug) this.log(`msg out ${msg.header}`) + //console.log(msg.content) + this.outputs.msgs.put(msg) + } else { + this.outputs.msgs.put(this.outmsgbuffer.shift()) + } + } + } // end msgs output check + + // do checks + if (hunks.length > 0) { + // STEP ONE: transport outputs (posted on last loop) to inputs + hunks.forEach((hunk) => { + for (let key of Object.keys(hunk.outputs)) { + let output = hunk.outputs[key] + if (output.connections.length > 0) { + // if we have connections + if (output.io) { + // and the output is occupied (having data) + if (!output.posted) { + // if it has not been posted to inputs (it's fresh) + // double check that all inputs are clear + let clear = true + for (let pair of output.connections) { + if (pair.input.io) { + clear = false + } + } + // if all clear, move the output data to each input + if (clear) { + for (let pair of output.connections) { + // TODO: BIG copy per-input (within put function call) + pair.input.put(output.data) + } + output.posted = true } else { - this.outputs.msgs.put(this.outmsgbuffer.shift()) + console.log('ERR: Output was put before inputs were clear') } - } - } // end msgs output check - - // do checks - if (hunks.length > 0) { - // STEP ONE: transport outputs (posted on last loop) to inputs - hunks.forEach((hunk) => { - for (let key of Object.keys(hunk.outputs)) { - let output = hunk.outputs[key] - if (output.connections.length > 0) { - // if we have connections - if (output.io) { - // and the output is occupied (having data) - if (!output.posted) { - // if it has not been posted to inputs (it's fresh) - // double check that all inputs are clear - let clear = true - for (let pair of output.connections) { - if (pair.input.io) { - clear = false - } - } - // if all clear, move the output data to each input - if (clear) { - for (let pair of output.connections) { - // TODO: BIG copy per-input (within put function call) - pair.input.put(output.data) - } - output.posted = true - } else { - console.log('ERR: Output was put before inputs were clear') - } - } else { - // output has been posted, check if all clear - let clear = true - for (let pair of output.connections) { - if (pair.input.io) { - clear = false - } - } - if (clear) { - output.clear() - } - } - } // end if-has-data - } else { - // and output with no connections is automatically cleared - // into the aether - if (output.io) { - output.clear() - } - } + } else { + // output has been posted, check if all clear + let clear = true + for (let pair of output.connections) { + if (pair.input.io) { + clear = false + } + } + if (clear) { + output.clear() } - }) // end connection passing loop - // STEP TWO: give each hunk some time to run - // the 0is us, let's not call its loop - for (let i = 1; i < hunks.length; i++) { - // TODO: watch for ordered calls ? if one emits ... another catches, - // we want cah-chunking ? - hunks[i].loop() + } + } // end if-has-data + } else { + // and output with no connections is automatically cleared + // into the aether + if (output.io) { + output.clear() } - } // end if-have-hunks conditional - - /* - // need this at the bootloop, not here also - // setup to loop return - if (this.isBrowser) { - setTimeout(this.loop) - } else { - setImmediate(this.loop) + } } - */ - } // end loop + }) // end connection passing loop + // STEP TWO: give each hunk some time to run + // the 0is us, let's not call its loop + for (let i = 1; i < hunks.length; i++) { + // TODO: watch for ordered calls ? if one emits ... another catches, + // we want cah-chunking ? + hunks[i].loop() + } + } // end if-have-hunks conditional + + /* + // need this at the bootloop, not here also + // setup to loop return + if (this.isBrowser) { + setTimeout(this.loop) + } else { + setImmediate(this.loop) + } + */ + } // end loop - /* --------------------------- ---------------------------- */ - /* --------------------- MESSAGES OUTPUT --------------------- */ - /* --------------------------- ---------------------------- */ + /* --------------------------- ---------------------------- */ + /* --------------------- MESSAGES OUTPUT --------------------- */ + /* --------------------------- ---------------------------- */ - this.outmsgbuffer = new Array() + this.outmsgbuffer = new Array() - this.writemessage = (header, content) => { - // guaranteed consumption here - let msg = { - header: header, - content: content - } - if (this.outputs.msgs.ie && this.outmsgbuffer.length < 1) { - // str8 shooters - this.log(`msg out ${header}`) - //console.log(content) - this.outputs.msgs.put(msg) - } else { - // gotta buffer - this.outmsgbuffer.push(msg) - this.log(`MGR OUTBUFFER LEN IS ${this.outmsgbuffer.length}`) - } + this.writemessage = (header, content) => { + // guaranteed consumption here + let msg = { + header: header, + content: content + } + if (this.outputs.msgs.ie && this.outmsgbuffer.length < 1) { + // str8 shooters + if(msgdebug) this.log(`msg out ${header}`) + //console.log(content) + this.outputs.msgs.put(msg) + } else { + // gotta buffer + this.outmsgbuffer.push(msg) + this.log(`MGR OUTBUFFER LEN IS ${this.outmsgbuffer.length}`) } + } } export default Manager -// file scraped from cuttlefish on Thu Mar 28 2019 10:38:28 GMT-0400 (Eastern Daylight Time) \ No newline at end of file +// file scraped from cuttlefish on Thu Mar 28 2019 10:38:28 GMT-0400 (Eastern Daylight Time) diff --git a/hunks/view.js b/hunks/view.js index de7e6b7e0f1dc455535c26a677e1f46aed340ab8..7a61b40791e53eca1bf5d46a72d2e12a963f9c7f 100644 --- a/hunks/view.js +++ b/hunks/view.js @@ -46,6 +46,8 @@ function View() { this.dom = $('<div>').addClass('view').get(0) // for nested dom elements, this.plane = $('<div>').addClass('plane').get(0) + // to log, type, etc + this.msgbox = $('<div>').addClass('msgbox').get(0) // init transform let dft = { @@ -83,6 +85,29 @@ function View() { // append $(this.dom).append(this.plane) + $(this.dom).append(this.msgbox) + } + + let writeToMessageBox = (data) => { + // write the message + this.msgbox.append($('<div>' + data + '</div>').addClass('msgboxmsg').get(0)) + // def check + let heightcheck = () => { + let height = 0; + $(this.msgbox).children().each(function(child){ + // jquery.each() syntax is a bit odd / different than elsewhere, sorry for inconsistency + height += this.clientHeight + }) + return height + } + // if too tall, remove + if(heightcheck() > $(this.msgbox).clientHeight){ + $(this.msgbox).children().get(0).remove() + // two at most, sloppy but fast + if(heightcheck() > $(this.msgbox).clientHeight){ + $(this.msgbox).children().get(0).remove() + } + } } let mouseWheelListener = (evt) => { @@ -626,6 +651,8 @@ function View() { // the events let evtDrag = (drag) => { + drag.preventDefault() + drag.stopPropagation() let cp = readTransform(floater) cp.x += drag.movementX cp.y += drag.movementY @@ -662,6 +689,8 @@ function View() { // startup the floater dom.addEventListener('mousedown', (down) => { + down.stopPropagation() + down.preventDefault() console.log('MOUSEDOWN for', port.type, port.name, down) // get the location to make the floater, let cp = BZ.getRightHandle(dom, this.plane.subscale) @@ -687,7 +716,7 @@ function View() { } let writeStateDom = (state, def) => { - let dom = $('<li>' + state.name + '</li>').get(0) + let dom = $('<li>' + state.name + "(" + state.type + ")" + '</li>').get(0) dom.id = def.id + '_state_' + state.name switch (typeof state.value) { case 'string': @@ -753,11 +782,12 @@ function View() { if (blkstate !== null && blkstate !== undefined) { // find value / etc let inp = $(blkstate).children('input').get(0) + // huh ? if (inp !== null && inp !== undefined) { inp.value = value } else { if (typeof value !== 'boolean') { - console.log('ERR State Change for non Boolean on Boolean Type') + writeToMessageBox('View ERR: State Change for non Boolean on Boolean Type: ' + parentid + ", state: " + name + ", value: " + value) } else { $(blkstate).children('span').text(value) } @@ -771,6 +801,8 @@ function View() { this.outmsgbuffer = new Array() + let msgdebug = false + this.writemessage = (header, content) => { // guaranteed consumption here let msg = { @@ -779,8 +811,8 @@ function View() { } if (this.outputs.msgs.ie && this.outmsgbuffer.length < 1) { // str8 shooters - this.log('msg out', msg.header) - console.log(msg.content) + if(msgdebug) this.log('msg out', msg.header) + if(msgdebug) console.log(msg.content) this.outputs.msgs.put(msg) } else { // gotta buffer @@ -805,7 +837,7 @@ function View() { let pult = this.inputs.msgs.get() let header = data.header let content = data.content - this.log(`gets msg ${header}`) + if(msgdebug) this.log(`gets msg ${header}`) switch (header) { case 'putcontextoptions': addContextOptions(content) @@ -826,8 +858,7 @@ function View() { receiveStateChange(content.id, content.name, content.value) break case 'error': - console.log('RECV MANAGER ERROR') - console.log(content) + writeToMessageBox(content) break default: console.log('VIEW: msg with no switch') diff --git a/programs/mvnv.json b/programs/mvnv.json index e9ed8a26233761ada04dd9994a1f398484447461..5c610baf8dbb1788230241fa17d3b8c08d3864b0 100644 --- a/programs/mvnv.json +++ b/programs/mvnv.json @@ -15,11 +15,18 @@ }, { "name": "link", - "id": "lnkone" + "id": "lnkone", + "state": { + "inputList": "msgs (byteArray), lnkIpOne (uint32)", + "outputList": "msgs (byteArray), linkReturnOne (uint32)" + } }, { "name": "comm/websocketclient", - "id": "wsclient" + "id": "wsclient", + "state": { + "retrycount": 0 + } }, { "name": "view", diff --git a/style.css b/style.css index ab603aae98261fe1b01fb647f072219443dfa1ab..6a367eaf12f33996211486b0ee2be63c8cf8b7f5 100644 --- a/style.css +++ b/style.css @@ -54,6 +54,23 @@ body { background-size: 100px 100px; } +.msgbox { + width: 300px; + height: 500px; + padding: 10px; + margin: 10px; + float: right; + background-color: rgb(192, 209, 237, 0.5); +} + +.msgboxmsg{ + padding: 3px; + margin-bottom: 3px; + background-color: white; + font-family: Courier; + font-size: 11px; +} + .plane { position: absolute; width: 100px;