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.

293 lines
7.3 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,
event = require("event"),
}
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
self.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:pull(filter, timeout)
local last_msg
local ev = self.event.pullFiltered(timeout, function(ev, addr_lo, addr_remote, port, _, msg)
if ev ~= "modem_message" then return false end
if addr_lo ~= self.modem.address then return false end
if port ~= self.port then return false end
last_msg = serialization.unserialize(msg)
return filter(addr_remote, last_msg)
end)
if ev ~= "nil" then
return last_msg
else
return nil, "timeout"
end
end
function Network:stop()
self.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(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(remote, msg)
self:_network_handler(remote, msg)
end)
-- Announce own presence so registers start coming in
self.network:broadcast{ty=Message.Hello}
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
-- Announce own nouns and verbs
self.network:broadcast{
ty=Message.Register,
nouns=self:nouns(true),
verbs=self:verbs(true),
}
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
if not local_only then
for noun in pairs(self.remote_nouns) do
table.insert(nouns, noun)
end
end
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")
elseif self.remote_nouns[noun] ~= nil then
self.network:send(self.remote_nouns[noun], {
ty=Message.NounRequest,
noun=noun,
})
local answer, err = self.network:pull(function(remote, msg)
if remote ~= self.remote_nouns[noun] then return false end
if msg.ty ~= Message.NounResponse then return false end
if msg.noun ~= noun then return false end
end, timeout)
if answer == nil then
return nil, err
else
return answer.value
end
else
return nil, "noun \""..noun.."\" unknown"
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
if not local_only then
for verb in pairs(self.remote_verbs) do
table.insert(verbs, verb)
end
end
return verbs
end
function BaseControl:call(verb, ...)
if self.local_verbs[verb] ~= nil then
self.local_verbs[verb](...)
elseif self.remote_verbs[verb] ~= nil then
self.network:send(self.remote_verbs[verb], {
ty=Message.VerbRequest,
verb=verb,
param={...},
})
else
error("unknown verb")
end
end
-- }}}
-- Network Handler {{{
function BaseControl:_network_handler(remote, msg)
if msg.ty == Message.Hello then
if self.live then
-- If we are live already, answer with own nouns and verbs
self.network:send(remote, {
ty=Message.Register,
nouns=self:nouns(true),
verbs=self:verbs(true),
})
end
elseif msg.ty == Message.Register then
for _, noun in ipairs(msg.nouns or {}) do
self.remote_nouns[noun] = remote
end
for _, verb in ipairs(msg.verbs or {}) do
self.remote_verbs[verb] = remote
end
elseif msg.ty == Message.NounRequest then
self.network:send(remote, {
ty=Message.NounResponse,
noun=msg.noun,
value=self.local_nouns[msg.noun],
})
elseif msg.ty == Message.NounResponse then
-- Handled via pull
elseif msg.ty == Message.VerbRequest then
if self.local_verbs[msg.verb] ~= nil then
self.local_verbs[msg.verb](table.unpack(msg.param))
end
else
error("TODO: MessageType Unknown")
end
end
-- }}}
-- }}}
return setmetatable(BaseControl, {__call=BaseControl.new})