Make oc-basecontrol more robust

- Allow init without params
	- Remove errors, instead indicate failures with return values
	- Add has_noun and has_verb
	- Allow late init of listeners
dev
Rahix 9 years ago
parent 45904104f7
commit 20c8c7426f

@ -31,7 +31,7 @@ function List:iter()
i = i + 1 i = i + 1
if i <= last then if i <= last then
if list[i] ~= nil then if list[i] ~= nil then
return list[i] return i, list[i]
end end
else else
return nil return nil
@ -63,6 +63,8 @@ CFG_PORT = 1234
bc = {} bc = {}
function bc:init(my_address, local_nouns, local_verbs) function bc:init(my_address, local_nouns, local_verbs)
local_nouns = local_nouns or {}
local_verbs = local_verbs or {}
local o = {} local o = {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
@ -93,6 +95,18 @@ function bc:init(my_address, local_nouns, local_verbs)
end end
for i,noun in ipairs(message.nouns) do for i,noun in ipairs(message.nouns) do
o.remote_nouns[noun] = remoteAddress 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 end
-- Respond with own verbs and nouns -- Respond with own verbs and nouns
local mynouns = {} local mynouns = {}
@ -145,22 +159,27 @@ function bc:get_noun(n)
return self.local_nouns[n] return self.local_nouns[n]
elseif self.remote_nouns[n] ~= nil then -- This noun is remote 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})) self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_noun, noun=n}))
while self._nr == nil do if self._nr == nil then
-- Nothing, wait for response, TODO: Research performance impact return nil
end end
local value = self._nr local value = self._nr
self._nr = nil -- Reset self._nr = nil -- Reset
return value return value
else else
error("Noun not found, Node might be offline") -- Noun not found
return nil
end
end end
function bc:has_noun(n)
return self.local_nouns[n] ~= nil or self.remote_nouns[n] ~= nil
end end
function bc:set_noun(n, value) function bc:set_noun(n, value)
if self.local_nouns[n] ~= nil then if self.local_nouns[n] ~= nil then
-- Call local listeners -- Call local listeners
if self.local_listeners[n] ~= nil then if self.local_listeners[n] ~= nil then
for listener in self.local_listeners[n]:iter() do for id, listener in self.local_listeners[n]:iter() do
if (listener.query == "onchange" and self.local_nouns[n] ~= value) if (listener.query == "onchange" and self.local_nouns[n] ~= value)
or (listener.query == "onrising" 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 == "onfalling" and self.local_nouns[n] > value)
@ -202,13 +221,19 @@ end
function bc:call_verb(v, param) function bc:call_verb(v, param)
if self.local_verbs[v] ~= nil then if self.local_verbs[v] ~= nil then
self.local_verbs[v](self, param) self.local_verbs[v](self, param)
return true
elseif self.remote_verbs[v] ~= nil then 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})) self.modem:send(self.remote_verbs[v], CFG_PORT, serializer.serialize({ty=message_type.call_verb, verb=v, param=param}))
return true
else else
error("Verb not found, Node might be offline") return false
end end
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) function bc:listen_noun(n, query, qparam, callback)
if self.local_nouns[n] ~= nil then -- Local listening if self.local_nouns[n] ~= nil then -- Local listening
self.local_listeners[n] = self.local_listeners[n] or List:new() self.local_listeners[n] = self.local_listeners[n] or List:new()
@ -221,7 +246,10 @@ function bc:listen_noun(n, query, qparam, callback)
self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_listening, noun=n, query=query, qparam=qparam, id=id})) 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 return id
else else
error("Noun not found, Node might be offline") -- 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
end end
@ -236,7 +264,7 @@ function bc:listen_cancel(n, id)
self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_stop_listening, noun=n, id=id})) self.modem:send(self.remote_nouns[n], CFG_PORT, serializer.serialize({ty=message_type.request_stop_listening, noun=n, id=id}))
end end
else else
error("Noun not found, Node might be offline") error("Trying to cancel non existing listener")
end end
end end

@ -17,8 +17,10 @@ function modem:open(port)
end end
function modem:send(address, port, msg) function modem:send(address, port, msg)
if self.network[address] ~= nil then
self.network[address](address, self.address, port, 0, msg) self.network[address](address, self.address, port, 0, msg)
end end
end
function modem:broadcast(port, msg) function modem:broadcast(port, msg)
for addr in pairs(self.network) do for addr in pairs(self.network) do
@ -28,4 +30,8 @@ function modem:broadcast(port, msg)
end end
end end
function modem:remove_self()
self.network[self.address] = nil
end
return modem return modem

@ -6,18 +6,20 @@ ser = require("serialization")
module("test_bc", package.seeall, lunit.testcase) module("test_bc", package.seeall, lunit.testcase)
function test_init() function test_init()
bci = bc:init("a1", {["light"]=false}, {["toggle_light"]=function() end}) local bci = bc:init("a1", {["light"]=false}, {["toggle_light"]=function() end})
assert_true(bci:has_noun("light"), "Nouns were not initialized")
assert_true(bci:has_verb("toggle_light"), "Verbs were not initialized")
end end
function test_set_get_noun() function test_set_get_noun()
bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function() end}) local bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function() end})
assert_equal(true, bci:get_noun("light"), "First get failed with wrong value") assert_equal(true, bci:get_noun("light"), "First get failed with wrong value")
bci:set_noun("light", false) bci:set_noun("light", false)
assert_equal(false, bci:get_noun("light"), "Second get failed with wrong value") assert_equal(false, bci:get_noun("light"), "Second get failed with wrong value")
end end
function test_request_noun() function test_request_noun()
bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function() end}) local bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function() end})
local i = false local i = false
local expct = true local expct = true
remote = require("modem"):init("some_addr", function(laddr, raddr, p, d, msg) remote = require("modem"):init("some_addr", function(laddr, raddr, p, d, msg)
@ -37,7 +39,7 @@ function test_request_noun()
end end
function test_call_verb() function test_call_verb()
bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function(b) local bci = bc:init("a1", {["light"]=true}, {["toggle_light"]=function(b)
b:set_noun("light", not b:get_noun("light")) b:set_noun("light", not b:get_noun("light"))
end}) end})
remote = require("modem"):init("some_addr", function(laddr, raddr, p, d, msg) end) remote = require("modem"):init("some_addr", function(laddr, raddr, p, d, msg) end)
@ -47,29 +49,29 @@ function test_call_verb()
end end
function test_multinode_call_verb() function test_multinode_call_verb()
bc1 = bc:init("a1", {["light"]=true}, {["toggle_light"]=function(b) local bc1 = bc:init("a1", {["light"]=true}, {["toggle_light"]=function(b)
b:set_noun("light", not b:get_noun("light")) b:set_noun("light", not b:get_noun("light"))
end}) end})
bc2 = bc:init("a2", {}, {}) local bc2 = bc:init("a2", {}, {})
bc1:call_verb("toggle_light") assert_true(bc1:call_verb("toggle_light"))
assert_equal(false, bc1:get_noun("light"), "First verb invokation did not go to plan") assert_equal(false, bc1:get_noun("light"), "First verb invokation did not go to plan")
bc2:call_verb("toggle_light") assert_true(bc2:call_verb("toggle_light"))
assert_equal(true, bc1:get_noun("light"), "Second verb invokation did not go to plan") assert_equal(true, bc1:get_noun("light"), "Second verb invokation did not go to plan")
end end
function test_multinode_get_noun() function test_multinode_get_noun()
key = "foobar" local key = "foobar"
bc1 = bc:init("a1", {["light"]=key}, {["toggle_light"]=function(b) local bc1 = bc:init("a1", {["light"]=key}, {["toggle_light"]=function(b)
b:set_noun("light", not b:get_noun("light")) b:set_noun("light", not b:get_noun("light"))
end}) end})
bc2 = bc:init("a2", {}, {}) local bc2 = bc:init("a2", {}, {})
assert_equal(key, bc1:get_noun("light"), "Local get failed") assert_equal(key, bc1:get_noun("light"), "Local get failed")
assert_equal(key, bc2:get_noun("light"), "Remote get failed") assert_equal(key, bc2:get_noun("light"), "Remote get failed")
end end
function test_multinode_listening() function test_multinode_listening()
bc1 = bc:init("a1", {["foo"]=123}, {}) local bc1 = bc:init("a1", {["foo"]=123}, {})
bc2 = bc:init("a2", {}, {}) local bc2 = bc:init("a2", {}, {})
local i = false local i = false
-- Local listening -- Local listening
local id = bc1:listen_noun("foo", "onchange", nil, function(bc, foo) local id = bc1:listen_noun("foo", "onchange", nil, function(bc, foo)
@ -93,11 +95,11 @@ function test_multinode_listening()
end end
function test_listen_modes() function test_listen_modes()
bci = bc:init("a1", {["noun"]=10}, {}) local bci = bc:init("a1", {["noun"]=10}, {})
local i = false local i = false
local id = 0 local id = 0
-- onchange -- onchange
id = bci:listen_noun("noun", "onchange", nil, function(bc, noun) local id = bci:listen_noun("noun", "onchange", nil, function(bc, noun)
i = true i = true
end) end)
bci:set_noun("noun", 11) bci:set_noun("noun", 11)
@ -154,3 +156,52 @@ function test_listen_modes()
assert_true(i, "Listening \"onbelow\" failed(B)") assert_true(i, "Listening \"onbelow\" failed(B)")
bci:listen_cancel("noun", id) bci:listen_cancel("noun", id)
end end
function test_get_unknown_noun()
local bci = bc:init("a1")
assert_false(bci:has_noun("bar"))
assert_equal(nil, bci:get_noun("bar"))
end
function test_get_offline_node()
local bc1 = bc:init("a1")
local bc2 = bc:init("a2", {["foo"]=true})
-- Simulate node going offline
bc2.modem:remove_self()
bc2 = nil
local val = bc1:get_noun("foo")
assert_equal(nil, val)
end
function test_call_unknown_verb()
local bci = bc:init("a1")
assert_false(bci:has_verb("unknown"))
assert_equal(false, bci:call_verb("unknown"))
end
function test_install_listen_on_unknown_noun()
local bci = bc:init("a1")
assert_false(bci:has_noun("unknown"))
bci:listen_noun("unknown", "onchange", nil, function() end)
end
function test_late_install()
local bc1 = bc:init("a1")
local i = false
assert_false(bc1:has_noun("foo"))
bc1:listen_noun("foo", "onchange", nil, function()
i = true
end)
local bc2 = bc:init("a2", {["foo"] = 123})
bc2:set_noun("foo", 321)
assert_true(i, "Listener was not called")
end

Loading…
Cancel
Save