local serialization = require("serialization") local uuid = require("uuid") local Version = {0, 1} local Message = { Hello = 0x48454c4f, Register = 0x52454749, Deregister = 0x44524547, NounRequest = 0x4e524551, NounResponse = 0x4e524553, VerbRequest = 0x56524551, VerbResponse = 0x56524553, ListenRequest = 0x4c524551, ListenNotify = 0x4c4e4f54, ListenCancel = 0x4c535450, } local function query_param(self, v) return {ty=self.ty, v=v} end local Query = { Change = {ty=0x434847}, Rising = {ty=0x524953}, Falling = {ty=0x46414c}, Equals = setmetatable({ty=0x455155}, {__call=query_param}), Above = setmetatable({ty=0x414256}, {__call=query_param}), Below = setmetatable({ty=0x424c4f}, {__call=query_param}), } -- Network ---------------------------------------------------------------- {{{ local Network = { default_port = 0xBC00 | (Version[1] << 4) | Version[2], } function Network:new(modem, port) if type(modem) == "number" then port = modem modem = nil end local self = { modem = modem or require("component").modem, port = port or Network.default_port, } setmetatable(self, {__index=Network}) return self end setmetatable(Network, {__call=Network.new}) function Network:start(callback) self.modem.open(self.port) self.listener = function(_, addr_lo, addr_remote, port, _, msg) -- Filter everything we don't care about if addr_lo ~= self.modem.address then return end if port ~= self.port then return end callback(addr_remote, serialization.unserialize(msg)) end require("event").listen("modem_message", self.listener) end function Network:send(addr, msg) self.modem.send(addr, self.port, serialization.serialize(msg)) end function Network:broadcast(msg) self.modem.broadcast(self.port, serialization.serialize(msg)) end function Network:stop() require("event").ignore("modem_message", self.listener) self.modem.close(self.port) end -- }}} -- BaseControl ------------------------------------------------------------ {{{ local BaseControl = { version = Version, Message = Message, Query = Query, Network = Network, } function BaseControl:new(self, network) local self = { locals = { nouns = {}, verbs = {}, }, remotes = { nouns = {}, verbs = {}, }, live = false, network = network or Network:new(), } setmetatable(self, {__index=BaseControl, __call=BaseControl.new}) self.network:start(function() error("net unimplemented") end) return self end function BaseControl:register(name, value) if self.live then error("can't register \""..name.."\" after finalizing") end if type(value) == "function" then if self.locals.verbs[name] ~= nil then error("\""..name.."\" already registered") else self.locals.verbs[name] = value end else if self.locals.nouns[name] ~= nil then error("\""..name.."\" already registered") else self.locals.nouns[name] = value end end end function BaseControl:finalize(waits, timeout) local self = self if self == BaseControl then -- Called as a constructor self = BaseControl:new() end self.live = true return self end -- Nouns {{{ function BaseControl:has_noun(noun) return self.locals.nouns[noun] ~= nil or self.remotes.nouns[noun] ~= nil end function BaseControl:nouns(local_only) local nouns = {} for noun in pairs(self.locals.nouns) do table.insert(nouns, noun) end -- TODO: Remote return nouns end function BaseControl:get(noun, timeout) if self.locals.nouns[noun] ~= nil then return self.locals.nouns[noun] elseif self.locals.verbs[noun] ~= nil then error("\""..noun.."\" is not a noun") else return nil end end function BaseControl:set(name, value) if self.locals.nouns[name] ~= nil then if type(value) == "function" then error("\""..name.."\" can't be cast into a verb") else self.locals.nouns[name] = value end elseif self.locals.verbs[name] ~= nil then if type(value) ~= "function" then error("\""..name.."\" can't be cast into a noun") else self.locals.verbs[name] = value end else error("\""..name.."\" is not a local") end end -- }}} -- Verbs {{{ function BaseControl:has_verb(verb) return self.locals.verbs[verb] ~= nil or self.remotes.verbs[verb] ~= nil end function BaseControl:verbs(local_only) local verbs = {} for verb in pairs(self.locals.verbs) do table.insert(verbs, verb) end -- TODO: Remote return verbs end function BaseControl:call(verb, ...) if self.locals.verbs[verb] ~= nil then self.locals.verbs[verb](...) else error("unknown verb") end end -- }}} -- }}} return setmetatable(BaseControl, {__call=BaseControl.new})