Skip to content

Latest commit

 

History

History
647 lines (492 loc) · 25.2 KB

examples-basic.rst

File metadata and controls

647 lines (492 loc) · 25.2 KB

Basic Communication

The essential form of communication in |project| consists in :term:`participants <participant>` sending and receiving :term:`events <event>`. The following sections explain:

Sending Data

To send data in |project|, in the simplest case, an :term:`informer` has to be created for the desired destination :term:`scope` and the data then has to be passed to it.

A :py:class:`rsb.Informer` object is created by calling :py:func:`rsb.createInformer` with

Once the :term:`informer` has been created, data is published by calling :py:meth:`rsb.Informer.publishData`.

Note

The :ref:`context manager protocol <python:typecontextmanager>` implementation of |project| takes care of correctly deactivating the :term:`informer` at the end of the :keyword:`with` statement. In case you are not using a :keyword:`with` statement, the :py:class:`rsb.Informer` object has to be deactivated using its :py:meth:`rsb.Informer.deactivate` method at the end of use.

.. literalinclude:: upstream/rsb-python/examples/informer.py
   :language:        python
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 10,13
   :linenos:

:download:`Download this example <upstream/rsb-python/examples/informer.py>`

A :cpp:class:`rsb::Informer` object is created by calling obtaining the |project| factory via :cpp:member:`rsb::Factory::getInstance` and then calling its :cpp:member:`rsb::Factory::createInformer` method with

Once the :term:`informer` has been created, data is published by calling :cpp:member:`rsb::Informer::publish`.

.. literalinclude:: upstream/rsb-cpp/examples/informer/informer.cpp
   :language:        cpp
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 13,17,18,24,27
   :linenos:

:download:`Download this example <upstream/rsb-cpp/examples/informer/informer.cpp>`

A rsb.Informer object is created by obtaining the |project| factory via rsb.Factory.getInstance and then calling its rsb.Factory.createInformer method with the desired :term:`scope` (which can be specified as a string literal). The generic parameter of the rsb.Informer class determines the :term:`data type` of the :term:`informer`.

The rsb.Informer has to activated before and deactivated after use via the rsb.Informer.activate and rsb.Informer.deactivate methods.

Once the :term:`informer` has been created and activated, data is published by calling rsb.Informer.send.

.. literalinclude:: upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/InformerExample.java
   :language:        java
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 12-13,16,20,24
   :linenos:

:download:`Download this example <upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/InformerExample.java>`

The macro rsb:with-participant can be used to create an :term:`informer` for a particular :term:`scope` and :term:`data type` (which can be cl:t). The method rsb:send can then be used to send data. rsb:with-participant takes care of destroying the :term:`informer` after use.

.. literalinclude:: upstream/rsb-cl/examples/informer.lisp
   :language:    cl
   :start-after: mark-start::with-participant
   :end-before:  mark-end::with-participant
   :linenos:

Alternatively, rsb:make-participant can be used to obtain an :term:`informer` without automatic destruction:

.. literalinclude:: upstream/rsb-cl/examples/informer.lisp
   :language:    cl
   :start-after: mark-start::variable
   :end-before:  mark-end::variable
   :linenos:

:download:`Download this example <upstream/rsb-cl/examples/informer.lisp>`

Receiving Data

Receiving data can be performed in two different ways in |project|:

:ref:`Synchronous <tutorial-receive-sync>`

Wait until :term:`events <event>` are received.

:ref:`Asynchronous <tutorial-receive-async>`

Continue execution and execute a callback function (called :term:`handler` in |project|) when :term:`events <event>` are received.

The following two sections explain the two ways of receiving data.

Receiving Data Synchronously

To receive data synchronously, a :term:`reader` object has to be created for the :term:`scope` from which :term:`events <event>` should be received. Then, individual :term:`events <event>` have to be retrieved explicitly from the :term:`reader` object, hence synchronous receiving.

Note

Synchronous receiving of data is not currently implemented in Python.

A :term:`reader` is created by obtaining the |project| factory via :cpp:member:`rsb::Factory::getInstance` (line 16) and then calling its :cpp:member:`rsb::Factory::createReader` method with the desired :term:`scope` (which can be specified as :cpp:class:`std::string` object, for example, a string literal, line 17).

Once the :term:`reader` has been created, individual :term:`events <event>` are received by calling the :cpp:member:`rsb::Reader::read` method (line 21).

.. literalinclude:: upstream/rsb-cpp/examples/reader/reader.cpp
   :language:        c++
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :linenos:
   :emphasize-lines: 16,17,21

:download:`Download this example <upstream/rsb-cpp/examples/reader/reader.cpp>`

Note

Synchronous receiving of data is not currently implemented in Java.

The macro rsb:with-participant can be used to create a :term:`reader` for a particular :term:`scope`. The method rsb:receive can then be used to receive individual :term:`events <event>` data. rsb:with-participant takes care of destroying the :term:`reader` after use.

.. literalinclude:: upstream/rsb-cl/examples/reader.lisp
   :language:    cl
   :start-after: mark-start::with-participant
   :end-before:  mark-end::with-participant
   :linenos:

Alternatively, rsb:make-participant can be used to obtain a :term:`reader` without automatic destruction:

.. literalinclude:: upstream/rsb-cl/examples/reader.lisp
   :language:    cl
   :start-after: mark-start::variable
   :end-before:  mark-end::receive/block
   :linenos:

:download:`Download this example <upstream/rsb-cl/examples/reader.lisp>`

Receiving Data Asynchronously

To receive data asynchronously, a :term:`listener` object has to be created for the :term:`scope` from which :term:`events <event>` should be received. Then, individual :term:`events <event>` are received automatically and in parallel to the execution of the program. For each received :term:`event`, a user-supplied callback function (a :term:`handler` in |project| terminology) is executed to process the :term:`event`.

A :py:class:`rsb.Listener` object is created by calling :py:func:`rsb.createListener` with the desired :term:`scope` (which can be specified as :ref:`str <typesseq>` object, for example, a string literal, line 16)

Once the :term:`listener` has been created, :term:`handlers <handler>` can be added by calling :py:meth:`rsb.Listener.addHandler` (line 20). Any :py:func:`callable` can be used as a :term:`handler`.

Note

The :ref:`context manager protocol <python:typecontextmanager>` implementation of |project| takes care of correctly deactivating the :term:`listener` at the end of the :keyword:`with` statement. In case you are not using a :keyword:`with` statement, the :py:class:`rsb.Listener` object has to be deactivated using its :py:meth:`rsb.Listener.deactivate` method at the end of use.

.. literalinclude:: upstream/rsb-python/examples/listener.py
   :language:        python
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :linenos:
   :emphasize-lines: 16,20

:download:`Download this example <upstream/rsb-python/examples/listener.py>`

A :term:`listener` is created by obtaining the |project| factory via :cpp:member:`rsb::Factory::getInstance` (line 19) and then calling its :cpp:member:`rsb::Factory::createListener` method with the desired :term:`scope` (which can be specified as :cpp:class:`std::string` object, for example, a string literal, line 27).

Once the :term:`listener` has been created, individual :term:`handlers <handler>` can be added by calling the :cpp:member:`rsb::Listener::addHandler` method (line 36). In general, :term:`handlers <handler>` are objects which implement the :cpp:class:`rsb::Handler` interface. However, there are specialized :term:`handlers <handler>` such as :cpp:class:`rsb::DataFunctionHandler` which allow using different things such as ordinary functions as :term:`handlers <handler>`.

.. literalinclude:: upstream/rsb-cpp/examples/listener/listener.cpp
   :language:        c++
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :linenos:
   :emphasize-lines: 19,27,36

:download:`Download this example <upstream/rsb-cpp/examples/listener/listener.cpp>`

A rsb.Listener object is created by obtaining the |project| factory via rsb.Factory.getInstance (line 15) and then calling its rsb.Factory.createListener method with the desired :term:`scope` (which can be specified as a string literal, line 20).

The rsb.Listener has to activated before and deactivated after use via the rsb.Listener.activate (line 21) and rsb.Listener.deactivate (line 34) methods.

Once the :term:`listener` has been created and activated, :term:`handlers <handler>` can be added by calling the rsb.Listener.addHandler method (line 26). Objects implementing the rsb.Handler interface can be used as :term:`handlers <handler>`.

.. literalinclude:: upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/EventListenerExample.java
   :language:        java
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :linenos:
   :emphasize-lines: 15,20-21,26,34

:download:`Download this example <upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/EventListenerExample.java>`

The macro rsb:with-participant can be used to create a :term:`listener` for a particular :term:`scope`. Inside the lexical scope of rsb:with-participant (or for :term:`listeners <listener>` created differently), the macro rsb:with-handler can be used to add a :term:`handler` to the :term:`listener`. While the body of rsb:with-handler executes, :term:`events <event>` are handled by the supplied code.

.. literalinclude:: upstream/rsb-cl/examples/listener.lisp
   :language:    cl
   :start-after: mark-start::with-participant
   :end-before:  mark-end::with-participant
   :linenos:

Alternatively, rsb:make-participant can be used to obtain a :term:`listener` without automatic destruction:

.. literalinclude:: upstream/rsb-cl/examples/listener.lisp
   :language:    cl
   :start-after: mark-start::variable
   :end-before:  mark-end::variable
   :linenos:

:download:`Download this example <upstream/rsb-cl/examples/listener.lisp>`

Remote Procedure Calls

.. seealso::

   :ref:`specification-request-reply`
     For a detailed description of the underlying implementation.

Remote procedure calls (RPCs) execute methods of objects located in different processes, and potentially different computers, than the calling entity. Some things are easier to implement using RPCs than using :term:`events <event>`. However, using RPCs generally makes a system less flexible and often more error-prone. |project| includes means for providing and using a simple form of remote procedure calls.

The following two sections describe

Client

The RPC client calls methods provided by one or more RPC servers. In |project|, such an RPC client is implemented as a :term:`remote server` object which is similar to other :term:`participants <participant>`. Such an object has to be created in order to perform method calls.

After the :term:`remote server` object has been created, a method can be called by supplying its name as string and, optionally, the parameter (there are only one or zero parameters). Methods can be called in blocking and non-blocking way:

  • When called in a blocking way, the method call returns only after the server has processed the request and returned a result.
  • When called in a non-blocking way, the method call returns immediately and the result can be obtained later, when the server completes its processing.

Important

When a non-existent method is called (for example, because the name of the method has been misspelled), nothing happens: blocking calls block forever and non-blocking calls never provide a result.

Conversely, if a method is provided by multiple servers, all servers process the request but only one reply is returned to the caller. It is unspecified, which reply is received by the caller, in such a situation.

A :py:class:`rsb.patterns.RemoteServer` object is created by calling :py:meth:`rsb.createRemoteServer` with the :term:`scope` on which the service is provided (line 12). Remote methods can then be called on the :py:class:`rsb.patterns.RemoteServer` object as if they were ordinary Python methods using the function call syntax :samp:`{OBJECT}.{METHOD}({ARGUMENTS})` (see line 17). Asynchronous calls can be made by using the syntax :samp:`{OBJECT}.{METHOD}.async({ARGUMENTS})` (see line 20).

Note

The :ref:`context manager protocol <python:typecontextmanager>` implementation of |project| takes care of correctly deactivating the :term:`remote server` at the end of the :keyword:`with` statement. In case you are not using a :keyword:`with` statement, the :py:class:`rsb.patterns.RemoteServer` object has to be deactivated using its :py:meth:`rsb.patterns.RemoteServer.deactivate` method at the end of use.

.. literalinclude:: upstream/rsb-python/examples/client.py
   :language:        python
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 12,17,20
   :linenos:

:download:`Download this example <upstream/rsb-python/examples/client.py>`

A :cpp:class:`rsb::patterns::RemoteServer` object is created by calling :cpp:member:`rsb::Factory::createRemoteServer` with the :term:`scope` on which the service is provided (lines 12 and 13). Remote methods can then be called using the :cpp:member:`rsb::patterns::RemoteServer::call` method (see line 21) and the :cpp:member:`rsb::patterns::RemoteServer::callAsync` method (see lines 30 to 36). The expected return type is specified as a template argument to the function call while the argument type is derived from the supplied argument.

.. literalinclude:: upstream/rsb-cpp/examples/server/client.cpp
   :language:        c++
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 12,13,21,30-36
   :linenos:

:download:`Download this example <upstream/rsb-cpp/examples/server/client.cpp>`

A rsb.patterns.RemoteServer object is created by calling rsb.Factory.createRemoteServer with the :term:`scope` on which the service is provided (line 10). Remote methods can then be called using the rsb.patterns.RemoteServer.call method (see line 18) and the rsb.patterns.RemoteServer.callAsync method (see lines 20 and 21).

.. literalinclude:: upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/ClientExample.java
   :language:        java
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 10,18,20-21
   :linenos:

:download:`Download this example <upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/ClientExample.java>`

A :term:`remote server` can be created and managed with the rsb:with-participant macro. The rsb.patterns.request-reply:call method can be used on the :term:`remote server` object to call remote methods. The method name and the argument of the call have to be passed as the second and third argument respectively.

.. literalinclude:: upstream/rsb-cl/examples/patterns/request-reply/client.lisp
   :language:    cl
   :start-after: mark-start::with-participant
   :end-before:  mark-end::with-participant
   :linenos:

Alternatively, rsb:make-participant can be used to obtain a :term:`remote server` without automatic destruction:

.. literalinclude:: upstream/rsb-cl/examples/patterns/request-reply/client.lisp
   :language:    cl
   :start-after: mark-start::variable
   :end-before:  mark-end::variable
   :linenos:

Blocking and non-blocking calls are both performed by calling rsb.patterns.request-reply:call. The :block? keyword parameter controls blocking. :block? nil causes a future object to be returned from which the result can be obtained via rsb.patterns.request-reply:future-result at a later point in time.

.. literalinclude:: upstream/rsb-cl/examples/patterns/request-reply/client.lisp
   :language:    cl
   :start-after: mark-start::calls
   :end-before:  mark-end::calls
   :linenos:

:download:`Download this example <upstream/rsb-cl/examples/patterns/request-reply/client.lisp>`

Server

Methods which are callable via RPC are provided by :term:`local server` objects which are similar to other :term:`participants <participant>`. To provide such methods a :term:`local server` object has be created.

After the :term:`local server` object has been created, methods have to be registered, supplying the desired method name as a string and a callback function which implements the desired behavior of the method.

A :py:class:`rsb.patterns.LocalServer` object is created by calling :py:meth:`rsb.createLocalServer` with the :term:`scope` on which the service is provided (line 12). Methods with their request and reply :term:`data types <data type>` and the :py:func:`callable` s implementing their behavior are registered using the :py:meth:`rsb.patterns.LocalServer.addMethod` method (line 21).

Note

The :ref:`context manager protocol <python:typecontextmanager>` implementation of |project| takes care of correctly deactivating the :term:`local server` at the end of the :keyword:`with` statement. In case you are not using a :keyword:`with` statement, the :py:class:`rsb.patterns.LocalServer` object has to be deactivated using its :py:meth:`rsb.patterns.LocalServer.deactivate` method at the end of use.

.. literalinclude:: upstream/rsb-python/examples/server.py
   :language:        python
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 12,17-18,21
   :linenos:

:download:`Download this example <upstream/rsb-python/examples/server.py>`

A :cpp:class:`rsb::patterns::Server` object is created by calling :cpp:member:`rsb::Factory::createServer` with the :term:`scope` on which the server should provide its service (line 20). Methods and the callback objects implementing their behavior can be registered using the :cpp:member:`rsb::patterns::LocalServer::registerMethod` method (see line 23). Callback classes are derived from :cpp:class:`rsb::patterns::Server::Callback` (with template arguments specifying the request and reply :term:`data types <data type>`) and override the :cpp:member:`rsb::patterns::Server::Callback::call` method (see lines 8 to 14).

.. literalinclude:: upstream/rsb-cpp/examples/server/server.cpp
   :language:    c++
   :start-after:     mark-start::body
   :end-before:      mark-end::body
   :emphasize-lines: 8-14,20,23
   :linenos:

:download:`Download this example <upstream/rsb-cpp/examples/server/server.cpp>`

A rsb.patterns.LocalServer object is created by calling rsb.Factory.createLocalServer with the :term:`scope` on which server should provide its service (line 20). Methods are registered by calling the rsb.patterns.LocalServer.addMethod method (see line 25) with a suitable callback object. The callback class supplies the behavior of server methods by overriding the rsb.patterns.EventCallback.invoke method (see lines 8 to 15).

.. literalinclude:: upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/ServerExample.java
   :language:    java
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :emphasize-lines: 20-21,25,8-15
   :linenos:

:download:`Download this example <upstream/rsb-java/rsb-java-examples/src/main/java/rsb/examples/ServerExample.java>`

A :term:`local server` can be created and managed with the rsb:with-participant macro. The rsb.patterns.request-reply:with-methods macro can be used to register methods and their implementations in the :term:`local server`.

.. literalinclude:: upstream/rsb-cl/examples/patterns/request-reply/server.lisp
   :language:    cl
   :start-after: mark-start::with-participant
   :end-before:  mark-end::with-participant
   :linenos:

Alternatively, rsb:make-participant can be used to obtain a :term:`local server` without automatic destruction. Similarly, methods can be added without the rsb.patterns.request-reply:with-methods macro:

.. literalinclude:: upstream/rsb-cl/examples/patterns/request-reply/server.lisp
   :language:    cl
   :start-after: mark-start::variable
   :end-before:  mark-end::variable
   :linenos:

:download:`Download this example <upstream/rsb-cl/examples/patterns/request-reply/server.lisp>`