Tutorial using the 'demo' componment
Launch the 'demo' component, a genomix server and MATLAB
First, you need to run the components you wish to control and the genomix server. For example, to use the 'demo-ros' compiled for the ROS middleware, run the following commands:
% roscore & # the ROS communication node.
% genomixd & # A genomix server
% demo-ros -b # The 'demo' component compiled for ROS.
Then, start MATLAB and make sure the matlab-genomix
installation directory is
in your userpath
.
% matlab &
>> userpath('<prefix>/lib/matlab')
(where <prefix>
is the path configured during the installation step).
Connect MATLAB to the genomix server
From MATLAB, connect to the running genomix server. By default, the connection
is made on localhost
, port 8080
, but this can be overriden by passing some
extra arguments:
>> client = genomix.client('example.com:80'); % connection to example.com:80
>> client = genomix.client; % connection to localhost:8080
client =
client with no properties.
On success, these commands return a client
handle.
Load a remote software component
Use the client
handle returned by a successful connection to load new
components in MATLAB. Components may have specific load options. You can check
the available options before loading a given component with the extra -h
argument:
>> client.load('demo', '-h')
ans =
Usage:
client.load('demo', [-h], [client options])
Client options:
-i|--name instance name
-t|--topic_timeout timeout subsciptions after that many seconds
For instance, loading the 'demo' component without specific options:
>> demo = client.load('demo')
demo =
component with properties:
genom_state: [function_handle]
Monitor: [function_handle]
Stop: [function_handle]
GetSpeed: [function_handle]
abort_activity: [function_handle]
MoveDistance: [function_handle]
SetSpeed: [function_handle]
Mobile: [function_handle]
connect_service: [function_handle]
connect_port: [function_handle]
kill: [function_handle]
GotoPosition: [function_handle]
On success, a handle on the demo
component is returned. Each property in the
returned handle is a service or port of the component.
In case of error, it might be that the client cannot be found automatically by
genomix. In this case, you need to specify the load path. This can be done
either by using the 'rpath' handle subcommand (see next section) or by
specifying the full path to the client. genom3
clients are found in the
…/lib/genom/*/plugins
directory, where *
denotes the middleware used,
so the following would be required if the demo ros client was installed to the
/home/jdoe
prefix:
>> demo = client.load('/home/jdoe/lib/genom/ros/plugins/demo.so');
Setting a load path
It can be inconvenient to specify the load path for each component. The rpath
client handle command allows to specify a list of paths where genomix will
search in order.
If the demo
ros client was for instance installed in the /home/jdoe
prefix,
the following can be used:
>> client.rpath('/home/jdoe/lib/genom/ros/plugins')
ans =
'/home/jdoe/lib/genom/ros/plugins'
>> demo = client.load('demo');
You need to set the rpath
only once for all load requests done with a
specific client
instance, but you can issue multiple rpath
commands in
order to specify a list of paths to be searched in order.
Invoke a service
The component handle returned after a successful load is used to invoke remote services. Some services may require input arguments. If you don’t pass any, there are prompted interactively:
>> demo.GotoPosition();
double posRef: Goto position in m (0) > 1.0
Here, the posRef
input argument was asked because GotoPosition()
was
invoked without any argument.
You can also check the required input arguments by using the -h
option:
>> demo.GotoPosition('-h')
ans =
Usage:
demo.GotoPosition [-a|-ack] [-t|-timeout secs] [-f|-flat]
[-args] [-h] [--] input [callback]
Input dictionary:
double posRef: Goto position in m (0)
Finally, you can pass required argument as a flat list, or using a MATLAB
struct
with appropriate fields:
>> demo.GotoPosition(-1.0);
>> s = struct();
>> s.posRef = 1.0;
>> demo.GotoPosition(s);
Retrieve services output
A request
handle is returned for each invoked service. This handle can be
used to query the service result after completion:
>> r = demo.GetSpeed();
>> r.status
ans =
done
>> r.result
ans =
speedRef: '::demo::SLOW'
A result is valid only if the request has the status done
. If the service
invocation was not successful, the request has the status error
and the
exception
property of the request handle is filled with details:
>> r = demo.GotoPosition(2.0)
r =
request with properties:
status: 'error'
result: []
exception: [1x1 struct]
>> r.exception
ans =
ex: '::demo::TOO_FAR_AWAY'
detail: [1x1 struct]
>> r.exception.detail
ans =
overshoot: 1
Call services asynchronously
All services can be invoked without blocking. This is especially useful for long lasting actions, where you do not want your MATLAB program to be blocked waiting for the completion of the service.
The first way to invoke a service asynchronously is to use the -a
option of
the service invocation:
>> r = demo.GotoPosition('-a', 1.0)
r =
request with properties:
status: 'sent'
result: []
exception: []
Here the request handle r
is returned immediately. It has the status sent
initially, meaning that the request was successfully sent but has not completed
yet. Later on, it is possible to wait for completion with the wait
method of
the request handle, with or without a timeout:
>> r.wait(0.1) % wait at most 100ms
Error using genomix.request/wait (line 125)
timeout
>> r.wait % wait until completion or error
>> r
r =
request with properties:
status: 'done'
result: [1x1 struct]
exception: []
Or you can also do some polling:
>> while strcmp(r.status, 'sent') pause(0.1); end
The second way to invoke a service asynchronously is to pass a callback as the last argument of a the service invocation:
>> r = demo.GotoPosition(0.0, @(r) disp(['callback with status: ' r.status]))
callback with status: sent
r =
request with properties:
status: 'sent'
result: []
exception: []
The callback function is invoked each time the request status is updated
sent. In the example above it is invoked when the status changes to sent
after a successful invocation.
Callback events are processed implicitly each time a 'genomix' object is
accessed (e.g. checking the status of any ongoing request, or calling any
service of any component, etc.). 'genomix' also provides an explicit update
method in client handles for checking pending events, that can be invoked
anywhere in your code:
>> client.update
callback with status: done
In this example, the callback registered in the previous request invocation was
triggered because the asynchronous request had completed at the time the
update
method was invoked.
Read component ports
In addition to functions for calling services, it is also possible to read the
data ports of components. For instance, the demo
component has a port called
Mobile
:
>> p = demo.Mobile()
p =
Mobile: [1x1 struct]
>> p.Mobile
ans =
position: 0
speed: 0
Ports are always read synchronously.