You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
4.7 KiB

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 = {
local_nouns = {},
local_verbs = {},
remote_nouns = {},
remote_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.local_verbs[name] ~= nil then
error("\""..name.."\" already registered")
else
self.local_verbs[name] = value
end
else
if self.local_nouns[name] ~= nil then
error("\""..name.."\" already registered")
else
self.local_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.local_nouns[noun] ~= nil or self.remote_nouns[noun] ~= nil
end
function BaseControl:nouns(local_only)
local nouns = {}
for noun in pairs(self.local_nouns) do
table.insert(nouns, noun)
end
-- TODO: Remote
return nouns
end
function BaseControl:get(noun, timeout)
if self.local_nouns[noun] ~= nil then
return self.local_nouns[noun]
elseif self.local_verbs[noun] ~= nil then
error("\""..noun.."\" is not a noun")
else
return nil
end
end
function BaseControl:set(name, value)
if self.local_nouns[name] ~= nil then
if type(value) == "function" then
error("\""..name.."\" can't be cast into a verb")
else
self.local_nouns[name] = value
end
elseif self.local_verbs[name] ~= nil then
if type(value) ~= "function" then
error("\""..name.."\" can't be cast into a noun")
else
self.local_verbs[name] = value
end
else
error("\""..name.."\" is not a local")
end
end
-- }}}
-- Verbs {{{
function BaseControl:has_verb(verb)
return self.local_verbs[verb] ~= nil or self.remote_verbs[verb] ~= nil
end
function BaseControl:verbs(local_only)
local verbs = {}
for verb in pairs(self.local_verbs) do
table.insert(verbs, verb)
end
-- TODO: Remote
return verbs
end
function BaseControl:call(verb, ...)
if self.local_verbs[verb] ~= nil then
self.local_verbs[verb](...)
else
error("unknown verb")
end
end
-- }}}
-- }}}
return setmetatable(BaseControl, {__call=BaseControl.new})