Update README

Signed-off-by: Rahix <rahix@rahix.de>
dev
rahix 7 years ago
parent b205779a29
commit 132cfb9921

@ -1,57 +1,237 @@
oc-basecontrol oc-basecontrol
============== ==============
oc-basecontrol is an OpenComputers generic base control library. `oc-basecontrol` is a library for distributed base control systems in
OpenComputers.
## Documentation ##
Load oc-basecontrol into your application: Concept
-------
Each node in the base network exports a set of _nouns_ and _verbs_.
- _Nouns_ are values which can be queried by other nodes and which
can change over time. Other nodes can also request to be notified
about updates of nouns. This is called _listening_. Examples: The power
a capacitor has stored or the fluid level in a tank.
- _Verbs_ are functions that can be triggered remotely. There are two
ways this can be done:
- _Asynchroneously_, which is "fire-and-forget". There is no checking that
the call succeeded and no way to pass back a return value.
- _Synchroneous_ verb calling, where the caller will block until a response
with a return value is returned or the call timed out.
- _Listening_: Nodes can request a notification on a noun change event.
There are a few different queries that can be used here:
- `Query.Change`: Notify whenever the noun changes
- `Query.Rising`: Notify whenever the noun's value increases
- `Query.Falling`: Notify whenever the noun's value decreases
- `Query.Equals(v)`: Notify whenever the noun's value equals a given value
- `Query.Above(v)`: Notify whenever the noun's value changes and is above
a given limit
- `Query.Below(v)`: Notify whenever the noun's value changes and is below
a given limit
Basic API
---------
### `BaseControl:new([modem])`
There are a few different ways to construct the base-controller. The easiest
is to just use the object created during loading the library:
```lua ```lua
bc = require("bc"):init(<nouns>, <verbs>) local bc = require("bc"):new()
bc:register("foo_noun", 1234)
bc:finalize()
``` ```
where `<nouns>` is a table containing all nouns (described later) and their
initial value and `<verbs>` is a table containing all verbs and their callbacks.
The basic idea behind basecontrol is, that network nodes have a set of **nouns**, If you need finer grained control, you can also use the initial object as a
which represent different values. A computer near a reactor for example could have class to create base-controllers:
a noun called `power-output`. Network nodes also have **verbs**, actions that other ```lua
nodes can perform. The reactor computer could have a `shutdown` verb for example. local BaseControl = require("bc")
local bc = BaseControl()
-- or
local bc = BaseControl:new()
```
The main job of the constructor is to initialize the network interface.
This means as soon as the constructor is called, our node will go "online".
You can give a custom network to `new` if you do not want to use the default
one. More info under _Network Configuration_.
### `BaseControl:register(name, value)`
Registers a new noun or verb named `name` with the initial value of `value`.
If `value` is a _function_, it will be a verb. `register` will fail if `name`
is already known as a local noun or verb. Note that locals always take
precedence over remotes so `register` will happily add a local which a remote
also exports.
```lua
local bc = require("bc")
-- Register a noun
bc:register("some_noun", 12345)
-- Register a verb
bc:register("some_verb", function(param_a, param_b)
print("Verb called with "..param_a.." and "..param_b..".")
end)
bc:finalize()
```
You can also batch-register multiple nouns/verbs:
```lua
local bc = require("bc")
bc:register{
some_noun=1234,
other_noun=4321,
a_verb=function(param)
print("Param: ", param)
end,
}
```
### `BaseControl:finalize([waits], [timeout])`
After creating a new base-controller, it will not immediately announce its data
into the network. It will first acuumulate local nouns and verbs and then send
them out in one batch. This happens when you call `finalize`.
Finalize takes two optional parameters:
- `waits`: A list of remote nouns/verbs that this node requires to be available.
`finalize` will only return once all of them have been registered by remote
nodes.
- `timeout`: Return early if timeout was reached before all waits were found.
If finalize is called again, it will start waiting once more, but it will not
reannounce its own presence. `finalize` can also be used as a constructor,
like this:
```lua
local bc = require("bc"):finalize{"some_noun"}
-- With timeout
local bc = require("bc"):finalize({"some_noun"}, 10)
```
The `bc` object has the following methods to interface with verbs and nouns: ### `BaseControl:set(name, value)`
Set a noun to a new value. This only works if the noun `name` is a local one.
You can not set remote nouns. If `name` is a verb, value **must** be a function
which will be the new callback.
```lua ```lua
-- Retrieve a noun from it's network node local bc = require("bc"):new()
val = bc:get_noun("<noun>") bc:register("some_noun", 1234)
bc:finalize()
-- Set a noun on the local machine -- Later ...
bc:set_noun("<noun>", value) bc:set("some_noun", 4321)
```
-- Call a verb on it's network node ### `BaseControl:get(noun, [timeout])`
bc:call_verb("<verb>", param) Get the value of a noun named `noun`. `get` can not be called for verbs.
If `timeout` is supplied, the call will return early if no response came before
the timeout expired.
```lua
local bc = require("bc"):finalize{"some_noun"}
print("Value: ", bc:get("some_noun"))
``` ```
Additionally, there is ### `BaseControl:call(verb, ...)`
Call a verb **asynchroneously**. All parameters following `verb` will be given
to the remote function. There is no guarantee that the verb was actually run.
_Note_: For local verbs, the call will still be synchroneous.
```lua ```lua
id = bc:listen_noun("<noun>", event_type, event_arg, function callback) local bc = require("bc")
bc:register("some_verb", function(param_a, param_b)
print("Verb called with "..param_a.." and "..param_b..".")
end)
bc:finalize()
-- This call can either happen locally or on another node
bc:call("some_verb", "param_a_value", "param_b_value")
``` ```
which is used to register asynchroneous callbacks on (possibly remote) noun ### `BaseControl:call_sync(verb, timeout, ...)`
changes. `event_type` is one of Call a verb **synchroneously**. All parameters following `verb` will be given
to the remote function. `call_sync` will return the remote function's return
value. If timeout is not `nil` or `0`, `call_sync` will return early upon
the timeout expiring.
```lua
local bc = require("bc")
bc:register("stupid_add", function(a, b)
return a + b
end)
bc:finalize()
* `onchange` => whenever the value changes, `event_arg` is ignored -- This call can either happen locally or on another node
* `onrising` => whenever the value gets bigger, `event_arg` is ignored local res = bc:call_sync("stupid_add", nil, 12, 34)
* `onfalling` => whenever the value gets smaller, `event_arg` is ignored print("12 + 34 = "..res)
* `onvalue` => whenever the value equals `event_arg` ```
* `onabove` => whenever the value is bigger than `event_arg`
* `onbelow` => whenever the value smaller than `event_arg`
A listener can be removed using ### `BaseControl:listen(noun, query, callback)`
Install a listener on changes of remote nouns. `noun` must be a remote noun.
If it is not known at this time, the listener is still installed in hope that
the node will come online later. `query` must be a valid query, chosen from
`BaseControl.Query`. A full list of currently supported queries was shown above.
The `callback` function will get one argument: The new value of the noun that
triggered the event.
```lua ```lua
bc:listen_cancel("<noun>", id) bc = require("bc"):finalize{"some_noun"}
bc:listen("some_noun", bc.Query.Change, function(value)
print("'some_noun' is now '"..value.."'")
end)
bc:listen("some_noun", bc.Query.Below(0), function(value)
print("'some_noun' is negative! ("..value..")")
end)
``` ```
## Testing ## Introspection API
Testing is done using a "fake" backend and typing -----------------
```console The following methods give you insight into the network:
$ ./lunit test_bc.lua
### `BaseControl:has_noun(noun)`
Whether `noun` is known at this time (either local or remote). Note that even if
a noun is known, it might still not be available if the remote node exited
without deregistering.
### `BaseControl:has_verb(verb)`
Whether `verb` is known at this time (either local or remote). Note that even if
a verb is known, calling it might still not succeed if the remote node exited
without deregistering. If you need to know for sure, call using `call_sync`.
### `BaseControl:nouns()`
Returns a list of all known nouns (either local or remote).
### `BaseControl:verbs()`
Returns a list of all known verbs (either local or remote).
Network Configuration
---------------------
_oc-basecontrol_ uses the raw [Modem API](https://ocdoc.cil.li/component:modem) with the default modem
(`component.modem`), if no alternative network is supplied to `BaseControl:new`.
If you have multiple modems and want to base-control to use a specific one,
you can set it like this:
```lua
local BaseControl = require("bc")
local bc = BaseControl:new(BaseControl.Network:new(my_moden))
``` ```
The Network constructor looks like this: `function Network:new([modem], [port])`.
You can leave out the modem (it will use the default) and just specify a port,
if you just don't like the default port. If you leave out the port or give no
custom network at all, _oc-basecontrol_ will use a port that is derived from the
version number. This prevents different versions of _oc-basecontrol_ clashing.
Alternatively, you can use a completely different networking stack by writing
your own implementation of `Network`. Take a look at the sources to see how
the default implementation works. A custom network needs to have the following
methods:
### `Network:start(callback)`
Start this network connection (eg. open the port) and install `callback` as a
listener for incoming messages. `callback` has the following signature:
`function callback(remote_addr, msg_tbl)` where `remote_addr` is the address of
the other side (any string works here) and `msg_tbl` is the unserialized message.
For serialization, please use `OpenOS`'s `serialization` library.
### `Network:send(addr, msg_tbl)`
Send a message to the remote party identified by `addr`. `msg_tbl` should be
serialized using `OpenOS`'s `serialization` library.
### `Network:broadcast(msg_tbl)`
Send a message to all connected nodes on the network.
### `Network:stop()`
Close this connection and uninstall the message handler.

@ -1,21 +0,0 @@
inspect = require("inspect").inspect
bc = require("bc")
network = require("network")
bc1 = bc:init({["light"]=true}, {["toggle_light"]=function(b)
b:set_noun("light", not b:get_noun("light"))
end})
a1 = network.get_scene()
bc2 = bc:init({}, {})
a2 = network.get_scene()
network.set_scene(a1)
print(true, bc1:call_verb("toggle_light"))
print(false, bc1:get_noun("light"))
network.set_scene(a2)
print(true, bc2:call_verb("toggle_light"))
network.set_scene(a1)
print(true, bc1:get_noun("light"))
Loading…
Cancel
Save