1
0
mirror of https://github.com/JoelBender/bacpypes synced 2025-09-28 22:15:23 +08:00
bacpypes/doc/source/tutorial/iocb.rst
Christian Tremblay b265525036 Minor modifications and correction to the doc
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>
2016-11-15 22:27:18 -05:00

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.