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
This commit is contained in:
parent
45904104f7
commit
20c8c7426f
3 changed files with 109 additions and 24 deletions
44
bc.lua
44
bc.lua
|
|
@ -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
|
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)
|
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
|
||||||
|
|
|
||||||
81
test_bc.lua
81
test_bc.lua
|
|
@ -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…
Add table
Add a link
Reference in a new issue