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.

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.