const fs = require('fs') const Reps = require('./reps.js') const JSUnit = require('./src/jsunit.js') let isStateKey = JSUnit.isStateKey var socket = {} function newProgram(name) { var program = { description: { name: name, id: name }, modules: {} } return program } function loadModuleFromSource(program, path, id) { // source -> heap if (fs.existsSync(path)) { // compile a new object based on definition in path var src = require(path) var mod = new src() // wants unique module id's if (program.description.counter == null) { program.description.counter = 0 } else { program.description.counter++ } // make unique name, or recall from program save if (id) { mod.description.id = id } else { mod.description.id = mod.description.name + '-' + program.description.counter } mod.description.path = path // add to program object program.modules[mod.description.id] = mod // input need references for later hookup for (key in mod.inputs) { mod.inputs[key].parentId = mod.description.id mod.inputs[key].key = key } // state items get wrapped in a getter / setter // so that changes from internal modules can // push to UI mod.state.init(mod.description.id, socket) mod.ui.init(mod.description.id, socket) /* for (key in mod.state) { if(isStateKey(key)){ writeStateObject(mod, key) } } */ console.log('ADDING', mod.description.id, 'to', program.description.id) /* ---------------------------------- */ // WARN! Corner Case should Go Away or Improve at next spiral if (mod.description.isLink) { for (mdlName in program.modules) { if (program.modules[mdlName].description.isLink) { console.log("PRGMEM ONLY BIG ENOUGH FOR ONE LINK") //process.exit() } } } // end corner case code /* ---------------------------------- */ /* ---------------------------------- */ // WARN! Corner Case should Go Away or Improve at next spiral // hardware corner case is hardware corner case // here's what we'll do // if it's hardware, and we're not loading from some saved program (no id) // if (mod.description.isHardware && !mod.description.isLink /* && id == null */ ) { // make sure we haven't already done this, thx var lnk = null for (mdlName in program.modules) { if (program.modules[mdlName].description.isLink) { lnk = mdlName } } if (lnk) { console.log('CORNER CASE: LOADING HW MODULE, LINKING TO LINK') program.modules[lnk].attach(mod.route) } else { console.log('CORNER CASE: LOADING HW MODULE, ALSO LOADING LINK') var link = loadModuleFromSource(program, './modules/hardware/atkseriallink.js') // hook it up auto-like link.attach(mod.route) } } // also return it so that we can write programs without the UI return mod } else { console.log('ERR no module found at ', path) } } /* function writeStateObject(mod, key) { mod.state['_' + key] = mod.state[key] mod.state[key] = {} Object.defineProperty(mod.state, key, { set: function(x) { // update internal value this['_' + key] = x // console.log('SET', key, this['_' + key]) // push to external view if (socket) { pushState(mod, key) } } }) Object.defineProperty(mod.state, key, { get: function() { console.log('KEY', key) console.log("IS", this['_' + key]) //console.log('GET', key, this['_' + key]) return this['_' + key] } }) } */ function removeModuleFromProgram(program, id) { // this simple? delete program.modules[id] for (key in program.modules) { var mdl = program.modules[key] for (otKey in mdl.outputs) { mdl.outputs[otKey].checkLinks(id) } } } /* EXTERNAL HOOKS */ function assignSocket(sckt) { // we can pass this object 'down' by reference, once it loads socket.send = sckt.send } function saveProgram(prgmem, path) { // ok, and we're interested in just copying the relevant things ... var svprgmem = { description: { name: prgmem.description.name, counter: prgmem.description.counter }, modules: {} } var mdls = prgmem.modules for (key in mdls) { var mdl = mdls[key] 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) } function openProgram(path) { var program = {} // get the .json file as an object var prgRep = JSON.parse(fs.readFileSync(path, 'utf8')) console.log('OPENING THIS PRGRAM REP', prgRep) // copy-pasta the program descro, program.description = { name: prgRep.description.name, counter: 0, id: prgRep.description.name + 1, // in another world, we count path: path } // gonna get those modules from source program.modules = {} for (key in prgRep.modules) { var mdlRep = prgRep.modules[key] loadModuleFromSource(program, mdlRep.description.path, mdlRep.description.id) } // 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] if (mdl == null) { console.log('-------------------------------- NULL MDL at openProgram') console.log('prgRep modules', prgRep.modules) console.log('program modules', program.modules) } // hooking outputs -> inputs for (outName in mdlRep.outputs) { var outRep = mdlRep.outputs[outName] // each has some caller ids for (nestedInputRep in outRep.calls) { // conn from tl program -> this hookup var nIRParent = outRep.calls[nestedInputRep].parentId var nIRKey = outRep.calls[nestedInputRep].key var nI = program.modules[nIRParent].inputs[nIRKey] console.log("ATTACHING", nIRKey, 'to', outName) mdl.outputs[outName].attach(nI) } } // restoring state /* the first wrap, only here to prevent link not-reconnecting on program restart, should go away */ if (!mdlRep.description.isLink) { for (key in mdlRep.state) { if (isStateKey(key)) { // I think this is OK? // would prefer to do this before we write getters and setters // for now we walk-around to secret key ... if (mdl.state[key].type == 'button' || mdl.state[key].type == 'multiline') { // defaul vals } else if (key == 'route'){ // absolutely does not belong here // TODO: states: sometimes we load, we want to run the change emitter ... sometimes we don't // what choice ? mdl.state['_' + key] = mdlRep.state[key] mdl.state.emitUIChange('route') } else { mdl.state['_' + key] = mdlRep.state[key] } } } } // and let's run init if it's there if(mdl.init != null){ mdl.init() } //console.log('mdlRep', mdlRep) //console.log('mdl', mdl) // 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 ? return program } module.exports = { new: newProgram, open: openProgram, save: saveProgram, loadModuleFromSource: loadModuleFromSource, removeModule: removeModuleFromProgram, assignSocket: assignSocket }