Project

General

Profile

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.

End the session

Where you are done, cleaning up things is done in a straightforward way by deleting the objects you no longer need:

>> delete(demo)
>> delete(client)

Deleting the client closes the connection to the genomix server.