mirror of
https://github.com/JoelBender/bacpypes
synced 2025-09-28 22:15:23 +08:00

Python 3 byte format used in example (pdu = PDU(b'hello')) for example I've followed along and ran all the code in those files using Windows and Python 3.4 Everything is working Signed-off-by: Christian Tremblay <christian.tremblay@servisys.com>
136 lines
4.2 KiB
ReStructuredText
136 lines
4.2 KiB
ReStructuredText
.. BACpypes IOCB tutorial
|
|
|
|
Controllers and IOCB
|
|
====================
|
|
|
|
The IO Control Block (IOCB) is an object that holds the parameters for some
|
|
kind of operation or function and a place for the result. The IOController
|
|
processes the IOCBs it is given and returns the IOCB back to the caller.
|
|
|
|
For this tutorial section, import the IOCB and IOController::
|
|
|
|
>>> from bacpypes.iocb import IOCB, IOController
|
|
|
|
Building an IOCB
|
|
----------------
|
|
|
|
Build an IOCB with some arguments and keyword arguments::
|
|
|
|
>>> iocb = IOCB(1, 2, a=3)
|
|
|
|
The parameters are kept for processing::
|
|
|
|
>>> iocb.args
|
|
(1, 2)
|
|
>>> iocb.kwargs
|
|
{'a': 3}
|
|
|
|
Make a Controller
|
|
-----------------
|
|
|
|
Now we need a controller to process this request. This controller is just
|
|
going to add and multiply the arguments together::
|
|
|
|
class SomeController(IOController):
|
|
|
|
def process_io(self, iocb):
|
|
self.complete_io(iocb, iocb.args[0] + iocb.args[1] * iocb.kwargs['a'])
|
|
|
|
Now create an instance of the controller and pass it the request::
|
|
|
|
>>> some_controller = SomeController()
|
|
>>> some_controller.request_io(iocb)
|
|
|
|
First, you'll notice that `request_io()` was called rather than the processing
|
|
function directly. This intermediate layer between the caller of the service
|
|
and the thing providing the service can be detached from each other in a
|
|
variety of different ways.
|
|
|
|
For example, there are some types of controllers that can only process one
|
|
request at a time and these are derived from `IOQController`. If the application
|
|
layer requests IOCB processing faster than the controller can manage (perhaps
|
|
because it is waiting for some networking functions) the requests will be queued.
|
|
|
|
In other examples, the application making the request is in a different process
|
|
or on a different machine, so the `request_io()` function builds a remote
|
|
procedure call wrapper around the request and manages the response. This is
|
|
similar to an HTTP proxy server.
|
|
|
|
Similarly, inside the controller it calls `self.complete_io()` so if there is
|
|
some wrapper functionality the code inside the `process_io()` function doesn't
|
|
need to worry about it.
|
|
|
|
Check the Result
|
|
----------------
|
|
|
|
There are a few ways to check to see if an IOCB has been processed. Every
|
|
IOCB has an `Event` from the `threading` built in module, so the application
|
|
can check to see if the event is set::
|
|
|
|
>>> iocb.ioComplete
|
|
<threading._Event object at 0x101349590>
|
|
>>> iocb.ioComplete.is_set()
|
|
True
|
|
|
|
There is also an IOCB state which has one of a collection of enumerated values::
|
|
|
|
>>> import bacpypes
|
|
>>> iocb.ioState == bacpypes.iocb.COMPLETED
|
|
True
|
|
|
|
And the state could also be aborted::
|
|
|
|
>>> iocb.ioState == bacpypes.iocb.ABORTED
|
|
False
|
|
|
|
Almost all controllers return some kind of information back to the requestor
|
|
in the form of some data. In this example, it's just a number::
|
|
|
|
>>> iocb.ioResponse
|
|
7
|
|
|
|
But we can provide some invalid combination of arguments and the exception
|
|
will show up in the `ioError`::
|
|
|
|
>>> iocb = IOCB(1, 2)
|
|
>>> some_controller.request_io(iocb)
|
|
>>> iocb.ioError
|
|
KeyError('a',)
|
|
|
|
The types of results and errors depend on the controller.
|
|
|
|
Getting a Callback
|
|
------------------
|
|
|
|
When a controller completes the processing of a request, the IOCB can contain
|
|
one or more functions to be called. First, define a callback function::
|
|
|
|
def call_me(iocb):
|
|
print("call me, %r or %r" % (iocb.ioResponse, iocb.ioError))
|
|
|
|
Now create a request and add the callback function::
|
|
|
|
>>> iocb = IOCB(1, 2, a=10)
|
|
>>> iocb.add_callback(call_me)
|
|
|
|
Pass the IOCB to the controller and the callback function is called::
|
|
|
|
>>> some_controller.request_io(iocb)
|
|
call me, 21 or None
|
|
|
|
Threading
|
|
---------
|
|
|
|
The IOCB module is thread safe, but the IOController derived classes may
|
|
not be. The thread initiating the request to the controller may simply
|
|
wait for the completion event to be set::
|
|
|
|
>>> some_controller.request_io(iocb)
|
|
>>> iocb.ioComplete.wait()
|
|
|
|
But for this to work correctly, the IOController must be running in a
|
|
separate thread, or there won't be any way for the event to be set.
|
|
|
|
If the iocb has callback functions, they will be executed in the thread
|
|
context of the controller.
|