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.
327 lines
8.2 KiB
327 lines
8.2 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 = 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
|
|
return true
|
|
end, timeout)
|
|
if answer ~= nil then
|
|
return answer.value
|
|
else
|
|
error("timeout")
|
|
end
|
|
else
|
|
return error("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={...},
|
|
})
|
|
end
|
|
end
|
|
|
|
function BaseControl:call_sync(verb, ...)
|
|
if self.local_verbs[verb] ~= nil then
|
|
return self.local_verbs[verb](...)
|
|
elseif self.remote_verbs[verb] ~= nil then
|
|
self.network:send(self.remote_verbs[verb], {
|
|
ty=Message.VerbRequest,
|
|
verb=verb,
|
|
param={...},
|
|
sync=true,
|
|
})
|
|
local answer = self.network:pull(function(remote, msg)
|
|
if remote ~= self.remote_verbs[verb] then return false end
|
|
if msg.ty ~= Message.VerbResponse then return false end
|
|
if msg.verb ~= verb then return false end
|
|
return true
|
|
end, timeout)
|
|
if answer ~= nil then
|
|
return table.unpack(answer.ret)
|
|
else
|
|
error("timeout")
|
|
end
|
|
else
|
|
error("unknown")
|
|
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
|
|
local ret = table.pack(self.local_verbs[msg.verb](table.unpack(msg.param)))
|
|
if msg.sync then
|
|
self.network:send(remote, {
|
|
ty=Message.VerbResponse,
|
|
verb=msg.verb,
|
|
ret=ret,
|
|
})
|
|
end
|
|
end
|
|
elseif msg.ty == Message.VerbResponse then
|
|
-- Handled via pull
|
|
else
|
|
error("TODO: MessageType Unknown")
|
|
end
|
|
end
|
|
-- }}}
|
|
-- }}}
|
|
|
|
return setmetatable(BaseControl, {__call=BaseControl.new})
|