Using LAPIS in MATLAB - aaronmalone/lapis GitHub Wiki

LAPIS includes a MATLAB client for incorporating steering in MATLAB applications.

Dependencies

To use the LAPIS client in MATLAB, you will need the LAPISData.m and LapisAPI.m files, and the lapis-matlab .jar file (as of the time of this writing, lapis-matlab-0.4.1-jar-with-dependencies.jar).

Use MATLAB's javaaddpath command to add the lapis-matlab .jar file to the Java path within MATLAB. All necessary Java dependencies are packaged within this .jar file, so it will be the only .jar file you need in order to use LAPIS.

The LapisAPI object

The LapisAPI object is the main object through which you will interact with LAPIS. It has methods for publishing variables and setting and retrieving the values of published variables on other nodes.

To start using LAPIS in your MATLAB application, you need to instantiate a LapisAPI object. When you instantiate the object, you also create a LAPIS node. The LAPIS node is potentially part of a network of LAPIS nodes, but does not have to be. Here is an example of instantiating a LapisAPI object:

myNodeName = 'MATLAB-node'
myAddress = 'http://127.0.0.1:7777'
% create coordinator LAPIS node
lapisApi = LapisAPI(myNodeName, myAddress)

Note that the LapisAPI constructor used above--the constructor that takes two arguments: the node name and the node address--creates a coordinator node. If you intend to use LAPIS in a standalone application, and not as part of a network of LAPIS nodes, you should use the constructor that creates a coordinator node.

Create a non-coordinator node

The first example of instantiating the LapisAPI object showed you how to set-up a coordinator node. To set up a non-coordinator node, use the constructor that takes three arguments:

myNodeName = 'non-coordinator-node'
coordinatorAddress = 'http://127.0.0.1:7777'
myAddress = 'http://127.0.0.1:8888'
% create non-coordinator LAPIS node
lapisApi = LapisAPI(myNodeName, coordinatorAddress, myAddress)

Note that a non-coordinator node will immediately attempt to connect to the network coordinator at the specified coordinator address. An exception will be thrown if it is unable to connect.

The LAPISData object

To publish MATLAB variables in LAPIS, you must use LAPISData wrapper objects. The direct publishing of standard MATLAB datatypes (arrays, matrices, etc.) is not supported by LAPIS because MATLAB uses pass-by-value instead of pass-by-reference for these datatypes. See our steerability considerations page for a more detailed explanation.

Here is an example of creating a LAPISData object:

% instantiate a LAPISData object
anArray = LAPISData('anArray', [1 2 3 4 5])

LAPISData objects have two fields. The first, .name, is the name that LAPIS will use for the variable if it is published by the application. The value of the variable can be retrieved, by name, in the REST interface, and other nodes on a LAPIS network can get and set the variable using the published name and the name of the node on which the variable was published. In the code example above, the .name field of the instantiated LAPISData object is set to 'anArray'.

The second field of the LAPISData object is the .data field. This holds a reference to the current value of the variable. In the example code, the .data field is initially assigned an array with five elements.

Publishing variables

Use the LapisAPI instance you have already created to publish your LAPISData object:

anArray = LAPISData('anArray', [1 2 3 4 5])
% lapisApi is a previously-created LapisAPI object
lapisApi.publish(anArray)

The variable is now published, and can be retrieved and set through the REST interface and by other nodes on the network.

Currently, the MATLAB implementation of LAPIS supports the publishing of scalars, strings (char datatype), and one-, two-, and three-dimensional vectors. These datatypes must be wrapped in a LAPISData object. Other datatypes, such as cell arrays, structs, and objects, are not currently supported.

Using a published variable

In your MATLAB code, use the .data field of the published LAPISData object to reference the value of the variable. The value returned by .data includes any changes that may have been made by the variable being set manually through LAPIS's REST interface or programmatically from another LAPIS node. When you set the value of .data from within your application, the new value is available through the REST interface and to other network nodes.

Here are some examples of the use of a published variable:

anArray = LAPISData('anArray', [1 2 3 4 5])
lapisApi.publish(anArray)

% get the current value of the published variable
valueOfTheArray = anArray.data

% increment each element within the array
anArray.data = 1 + anArray.data

% change the value of the array
anArray.data = [-5 -4 -3 -2 -1]

% note that changes to anArray.data are visible to the rest of the LAPIS network

Note that if the value of a published variable is assigned to another variable, and that second variable is subsequently modified, the value of the published variable does not change.

anArray = LAPISData('anArray', [1 2 3 4 5])
lapisApi.publish(anArray)

% this does NOT change the value of anArray.data
someValue = anArray.data
someValue = 1 + someValue
% someValue now [2 3 4 5 6]
% anArray.data is still [1 2 3 4 5]

% this DOES change the value of anArray.data
anArray.data = anArray.data + 1
% anArray.data is now [2 3 4 5 6]

Note that you should not re-assign the variable that refers to your published LAPISData object if the intention is to still use the variable on a LAPIS network.

% re-assigning a LAPISData variable -- do not do this
anArray = LAPISData('anArray', [1 2 3 4 5])
anArray = [5 6 7 8]

Re-assigning a LAPISData variable, as in the example above, causes the LAPISData object to be overwritten, resulting in the loss of the reference to the LAPISData object. This will make it impossible for the application programmer to modify the underlying variable to which LAPIS holds a reference. In order to allow the LAPIS API to keep the reference to the LAPISData object and subsequently allow the network to maintain a current record of the LAPIS variable data, the reassignment of data should occur by reference the .data property of the LAPISData object.

Redact a published variable

To "un-publish" a published variable, use the redact method:

% lapisApi is an instance of LapisAPI
% lapisData is an instance of LAPISData that has already been published
lapisApi.redact(lapisData)

It is rare that applications will need to un-publish variables, so use of the redact method will not be necessary in most applications.

Setting and retrieving published variables on the network

When you have multiple nodes on a LAPIS network, you can retrieve the value of variables that have been published by other nodes. If you have a variable called var1 that has been published by a node called node1, you would retrieve the variable value like this.

% retrieve the value of var1, a variable that was published by node1
valueOfVar1 = lapisApi.get('node1', 'var1')

Note that the value returned by the get call is not a LAPISData object. It is most likely an array or matrix. It is equal to the value returned by a call to the .data field of the published LAPISData object on node1. Changes to the retrieved value are not propagated back to the publishing node (node1 in this example).

Set the value of variables published by other nodes

If a node has published a variable and it has not been published as "read-only", then other LAPIS nodes on the same network can set the value of the variable.

% set the value of var1, a variable published by node1
valueToSet = magic(10)
lapisApi.set('node1', 'var1', valueToSet)

Shutting down a LAPIS node

To shut down a LAPIS node created through the MATLAB API, use the shutdown method:

lapisApi.shutdown

If you intend to use the clear command to clear your MATLAB environment and continue working, it is important that you shutdown any LAPIS nodes that you have created in the environment. Otherwise, the LAPIS nodes will continue to run "in the background", and this can cause very confusing errors as you continue to work.

Using waitForReadyNode

To facilitate coordination among multiple nodes in a LAPIS network, LAPIS allows applications to pause while waiting for other nodes to join a network, perform any initialization processing, and then declare themselves ready.

To wait for another node to declare that it is ready, use the waitForReadyNode or waitForReadyNodeWithTimeout methods, as in the example below:

% waits 10000 milliseconds--or 10 seconds--for 'Node1' to become 'ready'
lapisApi.waitForReadyNodeWithTimeout('Node1', 10000);

% waits indefinitely for 'Node2' to become 'ready'
lapisApi.waitForReadyNode('Node2');

A node does not have to already be present on the LAPIS network for another node to begin waiting for it. The waitForReadyNodeWithTimeout function blocks until the specified node has become ready or the timeout is reached. If the timeout is reached, an exception will be thrown, so catch the exception if you wish to continue processing in the event of a timeout. The waitForReadyNode function blocks indefinitely. It will not return until the specified node has joined the network and declared itself to be ready. Use of waitForReadyNodeWithTimeout should be preferred over use of waitForReadyNode.

Nodes upon which other nodes are waiting will need to declare themselves ready using the ready method of the LapisAPI object. This 'ready' state is visible to all other nodes on the same LAPIS network. Any node currently waiting on the node that becomes ready will continue processing (the waitForReadyNode or waitForReadyNodeWithTimeout function will return), and any node that subsequently attempts to wait on the 'ready' node will continue processing almost immediately.

Nodes which must no longer appear as 'ready' to the rest of the network should call the notReady function. Note that calling the notReady function when ready has not been called will have no effect. Similarly, if a node is already in the 'ready' state, further calls to ready have no effect.

Applications do not have to use the functionality that LAPIS provides for waiting on nodes and declaring nodes 'ready', but these features help to build networks of applications that work together.

Note that calling notReady from one node does not prevent other nodes from interacting with the node unless those nodes have called waitForReadyNode or waitForReadyNodeWithTimeout with the not-ready node as a target.