Library¶
The API library provides several interfaces, discussed below. All interfaces share the methods described in Section Methods.
Generic interface¶
The Interface
class can be used when the type of device is not known
beforehand, A class instance is made by passing either the path to a device or
a URI to the constructor.
>>> from simple_rpc import Interface
>>> interface = Interface('/dev/ttyACM0')
The constructor takes the following parameters.
name |
optional |
description |
---|---|---|
|
no |
Device name. |
|
yes |
Baud rate. |
|
yes |
Time in seconds before communication starts. |
|
yes |
Automatically connect. |
|
yes |
Load interface definition from file. |
Please see the list of handlers for a full description of the supported interface types.
Serial interface¶
When a path to a serial device is given, the Interface
constructor returns
a SerialInterface
class instance.
>>> from simple_rpc import Interface
>>> interface = Interface('/dev/ttyACM0')
>>> interface.__class__
<class 'simple_rpc.simple_rpc.SerialInterface'>
Alternatively, the SerialInterface
class can be used directly.
>>> from simple_rpc import SerialInterface
>>> interface = SerialInterface('/dev/ttyACM0')
Socket interface¶
When a socket URI is given, the Interface
constructor returns a
SocketInterface
class instance.
>>> interface = Interface('socket://192.168.1.50:10000')
>>> interface.__class__
<class 'simple_rpc.simple_rpc.SocketInterface'>
Alternatively, the SocketInterface
class can be used directly.
>>> from simple_rpc import SocketInterface
>>> interface = SocketInterface('socket://192.168.1.50:10000')
Methods¶
The Interface
class provides the following methods.
name |
description |
---|---|
|
Connect to device. |
|
Disconnect from device. |
|
Query device state. |
|
Execute a method. |
|
Save the interface definition to a file. |
The open()
function is used to connect to a device, this is needed when
autoconnect=False
is passed to the constructor.
>>> interface = Interface('/dev/ttyACM0', autoconnect=False)
>>> # Do something.
>>> interface.open()
The open()
function accepts the optional parameter handle
, which can be
used to load an interface definition from a file. This can be useful when
working with low throughput networks.
>>> interface.open(open('interface.yml'))
The connection state can be queried using the is_open()
function and it can
be closed using the close()
function.
>>> if interface.is_open():
>>> interface.close()
Additionally, the with
statement is supported for easy opening and closing.
>>> with Interface('/dev/ttyACM0') as interface:
>>> interface.ping(10)
The class instance has a public member variable named device
which
contains the device definitions and its exported method definitions.
>>> list(interface.device['methods'])
['inc', 'set_led']
Example of a method definition.
>>> interface.device['methods']['inc']
{
'doc': 'Increment a value.',
'index': 2,
'name': 'inc',
'parameters': [
{
'doc': 'Value.',
'name': 'a',
'fmt': 'h',
'typename': 'int'
}
],
'return': {
'doc': 'a + 1.',
'fmt': 'h',
'typename': 'int'}
}
Every exported method will show up as a class method of the interface
class
instance. These methods can be used like any normal class methods.
Alternatively, the exported methods can be called by name using the
call_method()
function.
The save()
function is used to save the interface definition to a file.
This can later be used by the constructor or the open()
function to
initialise the interface without having to query the device.
>>> interface.save(open('interface.yml', 'w'))
Basic usage¶
In the example given in the device library documentation, the inc
method
is exported, which is now present as a class method of the class instance.
>>> interface.inc(1)
2
Alternatively, the exported method can be called using the call_mathod()
function.
>>> interface.call_method('inc', 1)
2
To get more information about this class method, the built-in help()
function can be used.
>>> help(interface.inc)
Help on method inc:
inc(a) method of simple_rpc.simple_rpc.SerialInterface instance
Increment a value.
:arg int a: Value.
:returns int: a + 1.
Note that strings should be encoded as bytes
objects. If, for example, we
have a function named test
that takes a string as parameter, we should call
this function as follows.
>>> interface.test(b'hello world')
Complex objects¶
Some methods may have complex objects like Tuples, Objects or Vectors as parameters or return type.
In the following example, we call a method that takes a Vector of integers and returns a Vector of floats.
>>> interface.vector([1, 2, 3, 4])
[1.40, 2.40, 3.40, 4.40]
In this example, we call a method that takes an Object containing a byte and an other Object. A similar Object is returned.
>>> interface.object((b'a', (10, b'b')))
(b'b', (11, b'c'))
Note
In this implementation, the Tuple
type is regarded as a flat sequence
of elements, not as a separate type. The Object
type is used to assign
this sequence of elements to a Python tuple
and the Vector
type is
used to assign this sequence of elements to a Python list
. Bare tuples
are not supported in this implementation.
Using Tuples may lead to some other counter intuitive results. For example, a Vector of length l containing Tuples of size n is received as a list containing l·n elements.