Object Proxy
ObjectProxy is a procedural client meant to consume a Thing instance where the interactions with a property, action or event can be abstracted as operations like:
- Read/Write/Observe Property
- Invoke Action
- Subscribe/Unsubscribe Event
One would require a Thing Description to construct the client. The ThingDescription contains the metadata of the Thing like available properties, actions and events, their data types, their protocols and endpoints (called forms), among other metadata, in JSON format.
Info
See some online hosted examples at the examples website -
Camera |
Spectrometer |
Oscilloscope
The purpose of this JSON metadata is to provide both a human and machine readable description of the Thing and its capabilities, so that clients can automatically discover and interact with it without prior knowledge. In hololinked, this JSON document is automatically generated and served by the server protocols. There is lesser requirement for manually creating said ThingDescription, unless one wants to highly customize it.
To instantiate an ObjectProxy, use the ClientFactory for one protocol at a time:
Note
Only one protocol is allowed per ObjectProxy client. You can always create multiple clients if you need multiple protocols.
| HTTP Client | |
|---|---|
/resources/wot-td to the URL to load an automatically generated Thing Description
from the HTTP server serving the Thing.
For TCP:
| ZMQ Client TCP | |
|---|---|
For IPC:
| ZMQ Client IPC | |
|---|---|
For ZMQ, one needs to specify the server_id, thing_id and the access_point (say, TCP or IPC) where the server is accessible. These values are customizable while instantiating an instance of the ZMQServer. If the run() method on the Thing instance was used, the server_id defaults to thing_id.
When using ZMQ-TCP, on the server side one may specify the address as access_point="tcp://*:5555" to bind on all interfaces. On the client side, however, one must use the explicit address containing the machine hostname, like access_point="tcp://my-raspberry-pi:5555" or access_point="tcp://localhost:5555".
The Thing Description is fetched automatically from the server while mediating the connection.
| MQTT Consumer | |
|---|---|
The Thing Description is published to the MQTT Broker under the topic <thing_id>/thing-description by the server,
and the ClientFactory subsribes to the Thing Description and constructs the ObjectProxy.
MQTT currently supports only events and properties that publish change events.
read and write properties
To read and write properties by name, one can use read_property and write_property, or the dot operator:
To read and write multiple properties:
invoke actions
One can also access actions with dot operator and supply positional and keyword arguments:
One can also use invoke_action to invoke an action by name
oneway scheduling
oneway scheduling do not fetch return value and exceptions that might occur while executing a property or an action. The server schedules the operation and returns an empty response to the client, allowing it to process further logic. It is possible to set a property, set multiple or all properties or invoke an action in oneway. Other operations are not supported.
Simply provide the keyword argument oneway=True to the operation method.
oneway must be always specified as a keyword argument. Due to this reason, one cannot have an action argument or a property on the server named oneway as it is a reserved keyword argument to such methods on the client. At least they become inaccessible on the ObjectProxy.
no-block scheduling
noblock allows scheduling a property or action and collecting the reply later:
When using read_reply(), noblock calls raise exception on the client if the server raised its own exception or fetch the return value .
Note
One cannot combine oneway and noblock - oneway takes precedence over noblock.
async client-side scheduling
All operations on the ObjectProxy can also be invoked in an asynchronous manner within an async function.
Simply prefix async_ to the method name, like async_read_property, async_write_property, async_invoke_action etc.:
There is no support for dot operator based access for asyncio. One may also note that async operations
do not change the nature of the execution on the server side.
asyncio on ObjectProxy is purely a client-side non-blocking network call, so that one can
simultaneously perform other async operations while the client is waiting for said network operation to complete.
Note
oneway and noblock are not supported for async calls due to the asynchronous nature of the
operation themselves.
subscribe and unsubscribe events
To subscribe to an event, use subscribe_event method and pass a callback function that accepts a single argument, the event data:
It is possible to also use bound methods as callbacks. To unsubscribe from an event, use unsubscribe_event method:
| unsubscribe_event() | |
|---|---|
One can also supply multiple callbacks to be executed in series or concurrently, schedule an async callback etc., see events section for further details.
observe and unobserve properties
Observing properties work similar to event subcriptions, provided the property was specified as observable on the server side.
To observe a property:
| observe_property() | |
|---|---|
To unobserve a property:
| unobserve_property() | |
|---|---|
Once again, to customize callback scheduling, see events section for further details.
customizations
foreign attributes on client
Normally, there cannot be user defined attributes on the ObjectProxy as the attributes on the client must mimic the available properties, actions and events on the server. An accidental setting of an unknown property must raise an AttributeError, when not found on the server, instead of silently setting said property on the client itself:
| foreign attributes raise AttributeError | |
|---|---|
The requirement for this behaviour is due to python's duck typing. If one intends to set a property named foo, and instead types it as fooo (misspelt), it is better to raise an error instead of silently setting a new attribute fooo on the client.
One can overcome this by setting allow_foreign_attributes to True:
| foreign attributes allowed | |
|---|---|
controlling timeouts for non-responsive server
For invoking any operation (say property read/write & action call), two types of timeouts can be configured:
invokation_timeout- the amount of time the server has to wait for an operation to be scheduledexecution_timeout- the amount of time the server has to complete the operation once scheduled
When the invokation_timeout expires, the operation is guaranteed to be never executed. When the execution_timeout expires, the operation is scheduled but returns without the expected response. In both cases, a TimeoutError is raised on the client specifying the timeout type. If an operation is scheduled but not completed within the execution_timeout, the server may still complete the operation but client does not know about it.
| timeout specification | |
|---|---|
Currently only a global customization of these values are supported. In future, one may be able to specify timeouts per operation.