parent
b2a12cc2dc
commit
3508870c7f
@ -1,261 +1,184 @@
|
||||
local component = require("component")
|
||||
local event = require("event")
|
||||
local serializer = require("serialization")
|
||||
local serialization = require("serialization")
|
||||
local uuid = require("uuid")
|
||||
|
||||
local BC_VERSION = {0, 1}
|
||||
local BC_PORT = 0xBC00 | (BC_VERSION[1] << 4) | BC_VERSION[2]
|
||||
local Version = {0, 1}
|
||||
|
||||
local Message = {
|
||||
Hello = 0x48454c4f,
|
||||
Register = 0x00524547,
|
||||
Disband = 0x44524547,
|
||||
Register = 0x52454749,
|
||||
Deregister = 0x44524547,
|
||||
NounRequest = 0x4e524551,
|
||||
NounResponse = 0x4e524553,
|
||||
VerbRequest = 0x56524551,
|
||||
VerbResponse = 0x56524553,
|
||||
ListenRequest = 0x4c524551,
|
||||
ListenNotify = 0x4c4e4f54,
|
||||
ListenCancel = 0x4c535450,
|
||||
}
|
||||
|
||||
local Event = {
|
||||
Change = 1,
|
||||
Rising = 2,
|
||||
Falling = 3,
|
||||
Equals = 4,
|
||||
Above = 5,
|
||||
Below = 6,
|
||||
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}),
|
||||
}
|
||||
|
||||
local bc = {
|
||||
_version = BC_VERSION,
|
||||
_default_port = BC_PORT,
|
||||
Event = Event,
|
||||
Message = Message,
|
||||
-- Network ---------------------------------------------------------------- {{{
|
||||
local Network = {
|
||||
default_port = 0xBC00 | (Version[1] << 4) | Version[2],
|
||||
}
|
||||
bc.__index = bc
|
||||
|
||||
-- Helpers
|
||||
local function send_msg(self, remote, msg_table)
|
||||
self.modem.send(remote, self.port, serializer.serialize(msg_table))
|
||||
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})
|
||||
|
||||
local function broadcast_msg(self, msg_table)
|
||||
self.modem.broadcast(self.port, serializer.serialize(msg_table))
|
||||
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 bc:has_noun(noun)
|
||||
return self.local_nouns[noun] ~= nil or self.remote_nouns[noun] ~= nil
|
||||
function Network:send(addr, msg)
|
||||
self.modem.send(addr, self.port, serialization.serialize(msg))
|
||||
end
|
||||
|
||||
function bc:set_noun(noun, value)
|
||||
local last_value = self.local_nouns[noun]
|
||||
if last_value ~= nil then
|
||||
self.local_nouns[noun] = value
|
||||
for id, par in pairs(self.remote_listeners[noun]) do
|
||||
if (par.event == Event.Change and value ~= last_value)
|
||||
or (par.event == Event.Rising and value > last_value)
|
||||
or (par.event == Event.Falling and value < last_value)
|
||||
or (par.event == Event.Equals and value == par.evparam)
|
||||
or (par.event == Event.Above and value > par.evparam)
|
||||
or (par.event == Event.Below and value < par.evparam)
|
||||
then
|
||||
send_msg(self, par.addr, {ty=BC_MESSAGE.LISTEN_NOTIFY, id=id, value=value})
|
||||
end
|
||||
end
|
||||
else
|
||||
error("Noun \""..noun.."\" does not exist or is non-local!")
|
||||
end
|
||||
function Network:broadcast(msg)
|
||||
self.modem.broadcast(self.port, serialization.serialize(msg))
|
||||
end
|
||||
|
||||
function bc:get_noun(noun)
|
||||
if self.local_nouns[noun] ~= nil then
|
||||
return self.local_nouns[noun]
|
||||
elseif self.remote_nouns[noun] ~= nil then
|
||||
send_msg(self, self.remote_nouns[noun], {
|
||||
ty = BC_MESSAGE.NOUN_REQUEST,
|
||||
noun = noun,
|
||||
})
|
||||
local value
|
||||
event.pullFiltered(self.timeout, function(ev, ...)
|
||||
if ev ~= "modem_message" then return false end
|
||||
if select(2, ...) ~= self.remote_nouns[noun] then return false end
|
||||
if select(3, ...) ~= self.port then return false end
|
||||
local msg = serializer.unserialize(select(5, ...))
|
||||
if msg.ty ~= BC_MESSAGE.NOUN_RESPONSE then return false end
|
||||
if msg.noun ~= noun then return false end
|
||||
function Network:stop()
|
||||
require("event").ignore("modem_message", self.listener)
|
||||
self.modem.close(self.port)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
value = msg.value
|
||||
return true
|
||||
end)
|
||||
return value
|
||||
else -- Not found at all
|
||||
return nil
|
||||
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 bc:listen(noun, event, evparam, callback)
|
||||
-- You can leave out evparam
|
||||
if type(evparam) == "function" then
|
||||
callback = evparam
|
||||
evparam = nil
|
||||
function BaseControl:register(name, value)
|
||||
if self.live then
|
||||
error("can't register \""..name.."\" after finalizing")
|
||||
end
|
||||
|
||||
local remote_addr = self.remote_nouns[noun]
|
||||
if remote_addr == nil then
|
||||
error("Noun \""..noun.."\" is not listenable!")
|
||||
if type(value) == "function" then
|
||||
if self.locals.verbs[name] ~= nil then
|
||||
error("\""..name.."\" already registered")
|
||||
else
|
||||
local id = uuid.next()
|
||||
self.local_listeners[id] = {addr=remote_addr, callback=callback}
|
||||
send_msg(self, remote_addr, {
|
||||
ty=BC_MESSAGE.LISTEN_REQUEST,
|
||||
noun=noun,
|
||||
event=event,
|
||||
evparam=evparam,
|
||||
id=id,
|
||||
})
|
||||
return id
|
||||
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
|
||||
|
||||
function bc:cancel(id)
|
||||
local l = self.local_listeners[id]
|
||||
send_msg(self, l.addr, {ty=BC_MESSAGE.LISTEN_CANCEL, noun=l.noun, id=id})
|
||||
self.local_listeners[id] = nil
|
||||
-- Nouns {{{
|
||||
function BaseControl:has_noun(noun)
|
||||
return self.locals.nouns[noun] ~= nil or self.remotes.nouns[noun] ~= nil
|
||||
end
|
||||
|
||||
function bc:has_verb(verb)
|
||||
return self.local_verbs[verb] ~= nil or self.remote_verbs[verb] ~= nil
|
||||
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 bc:call_verb(verb, ...)
|
||||
if self.local_verbs[verb] ~= nil then
|
||||
self.local_verbs[verb](self, nil, ...)
|
||||
elseif self.remote_verbs[verb] ~= nil then
|
||||
send_msg(self, self.remote_verbs[verb], {
|
||||
ty=BC_MESSAGE.VERB_REQUEST, verb=verb, param={...},
|
||||
})
|
||||
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
|
||||
error("Verb \""..verb.."\" does not exist!")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function bc:cleanup()
|
||||
event.ignore("modem_message", self._modem_listener)
|
||||
local nouns, verbs = self:locals()
|
||||
broadcast_msg(self, {
|
||||
ty=BC_MESSAGE.DISBAND,
|
||||
nouns=nouns,
|
||||
verbs=verbs,
|
||||
})
|
||||
function BaseControl:set(name, value)
|
||||
error("set unimplemented")
|
||||
end
|
||||
-- }}}
|
||||
|
||||
function bc:locals()
|
||||
local nouns, verbs = {}, {}
|
||||
for noun in pairs(self.local_nouns) do table.insert(nouns, noun) end
|
||||
for verb in pairs(self.local_verbs) do table.insert(verbs, verb) end
|
||||
return nouns, verbs
|
||||
-- Verbs {{{
|
||||
function BaseControl:has_verb(verb)
|
||||
return self.locals.verbs[verb] ~= nil or self.remotes.verbs[verb] ~= nil
|
||||
end
|
||||
|
||||
local function new(_, local_nouns, local_verbs, overrides)
|
||||
local self = {
|
||||
local_nouns=local_nouns or {},
|
||||
local_verbs=local_verbs or {},
|
||||
remote_nouns={},
|
||||
remote_verbs={},
|
||||
|
||||
local_listeners={},
|
||||
remote_listeners={},
|
||||
|
||||
port=BC_PORT,
|
||||
modem=component.modem,
|
||||
timeout=5,
|
||||
}
|
||||
if overrides ~= nil then
|
||||
for name, value in pairs(overrides) do
|
||||
self[name] = value
|
||||
end
|
||||
end
|
||||
setmetatable(self, bc)
|
||||
|
||||
for noun in pairs(self.local_nouns) do
|
||||
self.remote_listeners[noun] = {}
|
||||
end
|
||||
|
||||
self._modem_listener = function(_, _, remote_addr, port, _, msg)
|
||||
if port ~= self.port then -- Ignore other ports
|
||||
return
|
||||
end
|
||||
|
||||
local msg = serializer.unserialize(msg)
|
||||
if msg.ty == BC_MESSAGE.HELLO then
|
||||
local nouns, verbs = self:locals()
|
||||
send_msg(self, remote_addr, {
|
||||
ty=BC_MESSAGE.REGISTER,
|
||||
nouns=nouns,
|
||||
verbs=verbs,
|
||||
})
|
||||
elseif msg.ty == BC_MESSAGE.REGISTER then
|
||||
for _, noun in ipairs(msg.nouns or {}) do
|
||||
self.remote_nouns[noun] = remote_addr
|
||||
end
|
||||
for _, verb in ipairs(msg.verbs or {}) do
|
||||
self.remote_verbs[verb] = remote_addr
|
||||
function BaseControl:verbs(local_only)
|
||||
local verbs = {}
|
||||
for verb in pairs(self.locals.verbs) do
|
||||
table.insert(verbs, verb)
|
||||
end
|
||||
elseif msg.ty == BC_MESSAGE.NOUN_REQUEST then
|
||||
send_msg(self, remote_addr, {
|
||||
ty=BC_MESSAGE.NOUN_RESPONSE,
|
||||
noun=msg.noun,
|
||||
value=self.local_nouns[msg.noun],
|
||||
})
|
||||
elseif msg.ty == BC_MESSAGE.VERB_REQUEST then
|
||||
local callback = self.local_verbs[msg.verb]
|
||||
if callback ~= nil then
|
||||
callback(self, remote_addr, table.unpack(msg.param))
|
||||
end
|
||||
elseif msg.ty == BC_MESSAGE.LISTEN_REQUEST then
|
||||
if self.local_nouns[msg.noun] ~= nil then
|
||||
self.remote_listeners[msg.noun][msg.id] = {
|
||||
event=msg.event,
|
||||
evparam=msg.evparam,
|
||||
addr=remote_addr,
|
||||
}
|
||||
end
|
||||
elseif msg.ty == BC_MESSAGE.LISTEN_NOTIFY then
|
||||
local listener = self.local_listeners[msg.id]
|
||||
if listener ~= nil and listener.addr == remote_addr then
|
||||
listener.callback(msg.value)
|
||||
end
|
||||
elseif msg.ty == BC_MESSAGE.LISTEN_CANCEL then
|
||||
if self.remote_listeners[msg.noun] ~= nil then
|
||||
self.remote_listeners[msg.noun][msg.id] = nil
|
||||
end
|
||||
elseif msg.ty == BC_MESSAGE.DISBAND then
|
||||
for _, noun in ipairs(msg.nouns or {}) do
|
||||
if self.remote_nouns[noun] == remote_addr then
|
||||
self.remote_nouns[noun] = nil
|
||||
end
|
||||
end
|
||||
for _, verb in ipairs(msg.verbs or {}) do
|
||||
if self.remote_verbs[verb] == remote_addr then
|
||||
self.remote_verbs[verb] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup connection and say hello
|
||||
self.modem.open(self.port)
|
||||
event.listen("modem_message", self._modem_listener)
|
||||
broadcast_msg(self, {ty=BC_MESSAGE.HELLO})
|
||||
local nouns, verbs = self:locals()
|
||||
broadcast_msg(self, {
|
||||
ty=BC_MESSAGE.REGISTER,
|
||||
nouns=nouns,
|
||||
verbs=verbs,
|
||||
})
|
||||
-- TODO: Remote
|
||||
return verbs
|
||||
end
|
||||
|
||||
return self
|
||||
function BaseControl:call(verb, ...)
|
||||
error("call unimplemented")
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return setmetatable(bc, {__call=new, __index={new=new}})
|
||||
return setmetatable(BaseControl, {__call=BaseControl.new})
|
||||
|
||||
Loading…
Reference in new issue