-- 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) -- Save listener function for deinit o.modem_cb = 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 -- Deinit function bc:cleanup() event.ignore("modem_message", self.modem_cb) end return bc