Skip to content
Snippets Groups Projects
programs.js 9.18 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jake Read's avatar
    Jake Read committed
    const fs = require('fs')
    
    
    const Reps = require('./reps.js')
    
    
    Jake Read's avatar
    Jake Read committed
    const JSUnit = require('./src/jsunit.js')
    
    Jake Read's avatar
    Jake Read committed
    let isStateKey = JSUnit.isStateKey
    
    Jake Read's avatar
    Jake Read committed
    var socket = {}
    
    
    Jake Read's avatar
    Jake Read committed
    function newProgram(name) {
    
    Jake Read's avatar
    Jake Read committed
        var program = {
            description: {
    
    Jake Read's avatar
    Jake Read committed
                name: name,
    
    Jake Read's avatar
    Jake Read committed
                id: name
            },
            modules: {}
        }
    
        return program
    }
    
    
    function loadModuleFromSource(program, path, id) {
    
    Jake Read's avatar
    Jake Read committed
        // source -> heap
        if (fs.existsSync(path)) {
    
    Jake Read's avatar
    Jake Read committed
            // compile a new object based on definition in path
    
    Jake Read's avatar
    Jake Read committed
            var src = {}
            try {
                src = require(path)
            }
            catch(err){
                console.log('ERR on module load, at path', path, err)
                return false 
            }
            
    
    Jake Read's avatar
    Jake Read committed
            var mod = new src()
    
            // wants unique module id's 
    
            if (program.description.counter == null) {
    
    Jake Read's avatar
    Jake Read committed
                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
            }
    
    Jake Read's avatar
    Jake Read committed
            mod.description.path = path
    
            // add to program object 
            program.modules[mod.description.id] = mod
    
    
    Jake Read's avatar
    Jake Read committed
            // input need references for later hookup
            for (key in mod.inputs) {
                mod.inputs[key].parentId = mod.description.id
                mod.inputs[key].key = key
            }
    
    
    Jake Read's avatar
    Jake Read committed
            // state items get wrapped in a getter / setter
            // so that changes from internal modules can
            // push to UI 
    
    Jake Read's avatar
    Jake Read committed
            // and not all have state
            if(mod.state != null){
                mod.state.init(mod.description.id, socket)
            }
    
    Jake Read's avatar
    Jake Read committed
            // not all have ui,
            if(mod.ui != null){
                mod.ui.init(mod.description.id, socket)
            }
    
    Jake Read's avatar
    Jake Read committed
            for (key in mod.state) {
    
    Jake Read's avatar
    Jake Read committed
                if(isStateKey(key)){
    
    Jake Read's avatar
    Jake Read committed
                    writeStateObject(mod, key)
                }
            }
    
    Jake Read's avatar
    Jake Read committed
            */
    
            console.log('ADDING', mod.description.id, 'to', program.description.id)
    
    Jake Read's avatar
    Jake Read committed
            /* ---------------------------------- */
            // 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()
    
    Jake Read's avatar
    Jake Read committed
            // end corner case code 
            /* ---------------------------------- */
    
    Jake Read's avatar
    Jake Read committed
    
            /* ---------------------------------- */
            // 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 */ ) {
    
    Jake Read's avatar
    Jake Read committed
                // make sure we haven't already done this, thx 
    
    Jake Read's avatar
    Jake Read committed
                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)
                }
            }
    
    
    Jake Read's avatar
    Jake Read committed
            // also return it so that we can write programs without the UI 
            return mod
        } else {
            console.log('ERR no module found at ', path)
        }
    }
    
    
    Jake Read's avatar
    Jake Read committed
    function writeStateObject(mod, key) {
    
    Jake Read's avatar
    Jake Read committed
        mod.state['_' + key] = mod.state[key]
        mod.state[key] = {}
    
    
    Jake Read's avatar
    Jake Read committed
        Object.defineProperty(mod.state, key, {
            set: function(x) {
                // update internal value 
                this['_' + key] = x
    
                // console.log('SET', key, this['_' + key])
    
    Jake Read's avatar
    Jake Read committed
                // push to external view
    
    Jake Read's avatar
    Jake Read committed
                    pushState(mod, key)
                }
    
    Jake Read's avatar
    Jake Read committed
            }
        })
        Object.defineProperty(mod.state, key, {
            get: function() {
    
    Jake Read's avatar
    Jake Read committed
                console.log('KEY', key)
                console.log("IS", this['_' + key])
    
    Jake Read's avatar
    Jake Read committed
                //console.log('GET', key, this['_' + key])
                return this['_' + key]
            }
        })
    }
    
    function removeModuleFromProgram(program, id) {
    
    Jake Read's avatar
    Jake Read committed
        // this simple? 
        delete program.modules[id]
    
    
        for (key in program.modules) {
    
    Jake Read's avatar
    Jake Read committed
            var mdl = program.modules[key]
    
            for (otKey in mdl.outputs) {
    
    Jake Read's avatar
    Jake Read committed
                mdl.outputs[otKey].checkLinks(id)
            }
        }
    }
    
    
    Jake Read's avatar
    Jake Read committed
    /*
    
    EXTERNAL HOOKS
    
    */
    
    
    function assignSocket(sckt) {
    
    Jake Read's avatar
    Jake Read committed
        // we can pass this object 'down' by reference, once it loads 
        socket.send = sckt.send 
    
    Jake Read's avatar
    Jake Read committed
    }
    
    
    Jake Read's avatar
    Jake Read committed
    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
    
    
    Jake Read's avatar
    Jake Read committed
            var mdl = mdls[key]
    
            var og = Reps.makeFromModule(mdl)
    
            svprgmem.modules[mdl.description.id] = og
    
    Jake Read's avatar
    Jake Read committed
        }
    
        fs.writeFileSync(path, JSON.stringify(svprgmem, null, 2), 'utf8')
    
    
        console.log('PROGRAM SAVED AT', path)
    
    function openProgram(path) {
    
    Jake Read's avatar
    Jake Read committed
        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) {
    
    Jake Read's avatar
    Jake Read committed
            var mdlRep = prgRep.modules[key]
    
            loadModuleFromSource(program, mdlRep.description.path, mdlRep.description.id)
    
    Jake Read's avatar
    Jake Read committed
        // restore saved state and links 
    
        for (modName in prgRep.modules) {
    
    Jake Read's avatar
    Jake Read committed
            // keys should be identical for rep and heap
    
            // this is the representation that we're opening up from 
    
    Jake Read's avatar
    Jake Read committed
            var mdlRep = prgRep.modules[modName]
    
    Jake Read's avatar
    Jake Read committed
    
    
            // this is the actual object, having functions and whatnot 
    
    Jake Read's avatar
    Jake Read committed
            var mdl = program.modules[modName]
    
    
                console.log('-------------------------------- NULL MDL at openProgram')
    
                console.log('prgRep modules', prgRep.modules)
                console.log('program modules', program.modules)
            }
    
    
    Jake Read's avatar
    Jake Read committed
            // hooking outputs -> inputs 
    
            for (outName in mdlRep.outputs) {
    
    Jake Read's avatar
    Jake Read committed
                var outRep = mdlRep.outputs[outName]
                // each has some caller ids 
    
                for (nestedInputRep in outRep.calls) {
    
    Jake Read's avatar
    Jake Read committed
                    // conn from tl program -> this hookup 
                    var nIRParent = outRep.calls[nestedInputRep].parentId
    
                    var nIRKey = outRep.calls[nestedInputRep].key
    
    Jake Read's avatar
    Jake Read committed
                    var nI = program.modules[nIRParent].inputs[nIRKey]
                    console.log("ATTACHING", nIRKey, 'to', outName)
                    mdl.outputs[outName].attach(nI)
                }
            }
    
    Jake Read's avatar
    Jake Read committed
    
            // 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]
    
    Jake Read's avatar
    Jake Read committed
                            mdl.state.emitUIChange('route')
    
                        } else {
                            mdl.state['_' + key] = mdlRep.state[key]
                        }
                    }
    
    Jake Read's avatar
    Jake Read committed
                }
            }
    
            // 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) {
    
    Jake Read's avatar
    Jake Read committed
                // ??
    
                mdl.description.position = {}
                mdl.description.position.left = mdlRep.description.position.left
    
                mdl.description.position.top = mdlRep.description.position.top
    
    Jake Read's avatar
    Jake Read committed
        }
    
        // once modules exist, link inputs / outputs / copy state ? 
    
        return program
    }
    
    module.exports = {
    
    Jake Read's avatar
    Jake Read committed
        new: newProgram,
    
    Jake Read's avatar
    Jake Read committed
        open: openProgram,
        save: saveProgram,
    
    Jake Read's avatar
    Jake Read committed
        loadModuleFromSource: loadModuleFromSource,
    
    Jake Read's avatar
    Jake Read committed
        removeModule: removeModuleFromProgram,
    
    Jake Read's avatar
    Jake Read committed
        assignSocket: assignSocket