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.