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.
272 lines
8.9 KiB
272 lines
8.9 KiB
-- For testing
|
|
modem = require("modem")
|
|
|
|
-- For deployment
|
|
-- modem = require("component").modem
|
|
|
|
serializer = require("serialization")
|
|
|
|
List = {last = -1}
|
|
|
|
function List:new()
|
|
local o = {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end
|
|
|
|
function List:insert(value)
|
|
local last = self.last + 1
|
|
self.last = last
|
|
self[last] = value
|
|
return last
|
|
end
|
|
|
|
function List:iter()
|
|
local i = -1
|
|
local last = self.last
|
|
local list = self
|
|
return function()
|
|
while true do
|
|
i = i + 1
|
|
if i <= last then
|
|
if list[i] ~= nil then
|
|
return i, list[i]
|
|
end
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function List:remove(id)
|
|
local value = self[id]
|
|
self[id] = nil
|
|
return value
|
|
end
|
|
|
|
message_type = {
|
|
request_noun = 1,
|
|
call_verb = 2,
|
|
noun_response = 3,
|
|
hello = 4,
|
|
goodbye = 5,
|
|
hello_response = 6,
|
|
request_listening = 7,
|
|
request_stop_listening = 8,
|
|
listener_update = 9,
|
|
}
|
|
|
|
CFG_PORT = 1234
|
|
|
|
bc = {}
|
|
|
|
function bc:init(my_address, local_nouns, local_verbs)
|
|
local_nouns = local_nouns or {}
|
|
local_verbs = local_verbs or {}
|
|
local o = {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
o.remote_verbs = {}
|
|
o.remote_nouns = {}
|
|
o.local_listeners = {}
|
|
o.remote_listeners = {}
|
|
o.listening_remotes = {}
|
|
-- Modem listener
|
|
function modem_listener(localAddress, remoteAddress, port, dist, message)
|
|
dbg = message
|
|
message = serializer.unserialize(message)
|
|
if port == CFG_PORT then
|
|
if message.ty == message_type.request_noun then
|
|
msg = {ty=message_type.noun_response,
|
|
noun=message.noun,
|
|
value=o.local_nouns[message.noun]
|
|
}
|
|
o.modem:send(remoteAddress, port, serializer.serialize(msg))
|
|
elseif message.ty == message_type.noun_response then
|
|
-- TODO: Make this a bit better, this is dumb
|
|
o._nr = message.value
|
|
elseif message.ty == message_type.call_verb then
|
|
o.local_verbs[message.verb](o, message.param)
|
|
elseif message.ty == message_type.hello then
|
|
for i,verb in ipairs(message.verbs) do
|
|
o.remote_verbs[verb] = remoteAddress
|
|
end
|
|
for i,noun in ipairs(message.nouns) do
|
|
o.remote_nouns[noun] = remoteAddress
|
|
-- Check for potential dormant listeners
|
|
if o.remote_listeners[noun] ~= nil then
|
|
for id, listener in o.remote_listeners[noun]:iter() do
|
|
o.modem:send(remoteAddress, port, serializer.serialize({
|
|
ty=message_type.request_listening,
|
|
noun=noun,
|
|
query=listener.query,
|
|
qparam=listener.qparam,
|
|
id=id,
|
|
}))
|
|
end
|
|
end
|
|
end
|
|
-- Respond with own verbs and nouns
|
|
local mynouns = {}
|
|
for noun in pairs(o.local_nouns) do
|
|
table.insert(mynouns, noun)
|
|
end
|
|
local myverbs = {}
|
|
for verb in pairs(o.local_verbs) do
|
|
table.insert(myverbs, verb)
|
|
end
|
|
o.modem:send(remoteAddress, port, serializer.serialize({ty=message_type.hello_response, verbs=myverbs, nouns=mynouns}))
|
|
elseif message.ty == message_type.hello_response then
|
|
for i,verb in ipairs(message.verbs) do
|
|
o.remote_verbs[verb] = remoteAddress
|
|
end
|
|
for i,noun in ipairs(message.nouns) do
|
|
o.remote_nouns[noun] = remoteAddress
|
|
end
|
|
elseif message.ty == message_type.request_listening then
|
|
o.listening_remotes[message.noun] = o.listening_remotes[message.noun] or {}
|
|
o.listening_remotes[message.noun][remoteAddress] = o.listening_remotes[message.noun][remoteAddress] or {}
|
|
o.listening_remotes[message.noun][remoteAddress][message.id] = {query=message.query, qparam=message.qparam}
|
|
elseif message.ty == message_type.request_stop_listening then
|
|
o.listening_remotes[message.noun][remoteAddress][message.id] = nil
|
|
elseif message.ty == message_type.listener_update then
|
|
o.remote_listeners[message.noun][message.id].callback(o, message.value)
|
|
end
|
|
end
|
|
end
|
|
-- Change this for deployment VVV
|
|
o.modem = modem:init(my_address, modem_listener)
|
|
-- Init local stuff
|
|
o.local_verbs = local_verbs or {}
|
|
o.local_nouns = local_nouns or {}
|
|
-- Send own nouns and verbs and request all remotes to send theirs
|
|
local mynouns = {}
|
|
for noun in pairs(local_nouns) do
|
|
table.insert(mynouns, noun)
|
|
end
|
|
local myverbs = {}
|
|
for verb in pairs(local_verbs) do
|
|
table.insert(myverbs, verb)
|
|
end
|
|
o.modem:broadcast(CFG_PORT, serializer.serialize({ty=message_type.hello, verbs=myverbs, nouns=mynouns}))
|
|
return o
|
|
end
|
|
|
|
function bc:get_noun(n)
|
|
if self.local_nouns[n] ~= nil then -- This noun is local, makes it easy
|
|
return self.local_nouns[n]
|
|
elseif self.remote_nouns[n] ~= nil then -- This noun is remote
|
|
self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_noun, noun=n}))
|
|
if self._nr == nil then
|
|
return nil
|
|
end
|
|
local value = self._nr
|
|
self._nr = nil -- Reset
|
|
return value
|
|
else
|
|
-- Noun not found
|
|
return nil
|
|
end
|
|
end
|
|
|
|
function bc:has_noun(n)
|
|
return self.local_nouns[n] ~= nil or self.remote_nouns[n] ~= nil
|
|
end
|
|
|
|
function bc:set_noun(n, value)
|
|
if self.local_nouns[n] ~= nil then
|
|
-- Call local listeners
|
|
if self.local_listeners[n] ~= nil then
|
|
for id, listener in self.local_listeners[n]:iter() do
|
|
if (listener.query == "onchange" and self.local_nouns[n] ~= value)
|
|
or (listener.query == "onrising" and self.local_nouns[n] < value)
|
|
or (listener.query == "onfalling" and self.local_nouns[n] > value)
|
|
or (listener.query == "onvalue" and value == listener.qparam)
|
|
or (listener.query == "onabove" and value > listener.qparam)
|
|
or (listener.query == "onbelow" and value < listener.qparam)
|
|
then
|
|
self.local_nouns[n] = value -- Apply here, too, because else there could be glitches
|
|
listener.callback(self, value)
|
|
end
|
|
end
|
|
end
|
|
-- Call remote listeners
|
|
if self.listening_remotes[n] ~= nil then
|
|
for address,remote in pairs(self.listening_remotes[n]) do
|
|
for id, listener in pairs(remote) do
|
|
if (listener.query == "onchange" and self.local_nouns[n] ~= value)
|
|
or (listener.query == "onrising" and self.local_nouns[n] < value)
|
|
or (listener.query == "onfalling" and self.local_nouns[n] > value)
|
|
or (listener.query == "onvalue" and value == listener.qparam)
|
|
or (listener.query == "onabove" and value > listener.qparam)
|
|
or (listener.query == "onbelow" and value < listener.qparam)
|
|
then
|
|
self.local_nouns[n] = value -- Apply here, too, because else there could be glitches
|
|
-- Send update
|
|
self.modem:send(address, CFG_PORT, serializer.serialize({ty=message_type.listener_update, noun=n, value=value, id=id}))
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
-- Apply change
|
|
self.local_nouns[n] = value
|
|
else
|
|
error("Can't set remote nouns")
|
|
end
|
|
end
|
|
|
|
function bc:call_verb(v, param)
|
|
if self.local_verbs[v] ~= nil then
|
|
self.local_verbs[v](self, param)
|
|
return true
|
|
elseif self.remote_verbs[v] ~= nil then
|
|
self.modem:send(self.remote_verbs[v], CFG_PORT, serializer.serialize({ty=message_type.call_verb, verb=v, param=param}))
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function bc:has_verb(v)
|
|
return self.local_verbs[v] ~= nil or self.remote_verbs[v] ~= nil
|
|
end
|
|
|
|
function bc:listen_noun(n, query, qparam, callback)
|
|
if self.local_nouns[n] ~= nil then -- Local listening
|
|
self.local_listeners[n] = self.local_listeners[n] or List:new()
|
|
local id = self.local_listeners[n]:insert({query=query, qparam=qparam, callback=callback})
|
|
return id
|
|
elseif self.remote_nouns[n] ~= nil then -- Remote listening
|
|
self.remote_listeners[n] = self.remote_listeners[n] or List:new()
|
|
local id = self.remote_listeners[n]:insert({query=query, qparam=qparam, callback=callback})
|
|
-- Request remote listening
|
|
self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_listening, noun=n, query=query, qparam=qparam, id=id}))
|
|
return id
|
|
else
|
|
-- Install it, with hope, that the node will come online later
|
|
self.remote_listeners[n] = self.remote_listeners[n] or List:new()
|
|
local id = self.remote_listeners[n]:insert({query=query, qparam=qparam, callback=callback})
|
|
return id
|
|
end
|
|
end
|
|
|
|
function bc:listen_cancel(n, id)
|
|
if self.local_nouns[n] ~= nil then -- Local listening
|
|
if self.local_listeners[n] ~= nil then
|
|
self.local_listeners[n]:remove(id)
|
|
end
|
|
elseif self.remote_nouns[n] ~= nil then -- Remote listening
|
|
if self.remote_listeners[n] ~= nil then
|
|
self.remote_listeners[n]:remove(id)
|
|
self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_stop_listening, noun=n, id=id}))
|
|
end
|
|
else
|
|
error("Trying to cancel non existing listener")
|
|
end
|
|
end
|
|
|
|
return bc
|