Shared Variables

From SpinetiX Support Wiki

Jump to: navigation, search

Introduction

The HMP "Shared Variables" mechanism was developed to allow sharing data internally (i.e. passing JavaScript values from one SVG document to another) and externally (with other devices via Ethernet). It also includes a reactive component which triggers on-update events whenever the value of the Shared Variable is changed - this can be used to create interactive content.

Usage

A typical usage is to use an external device (e.g., another HMP, a web page, a telnet client, a Control System device etc.) and send messages to an HMP that will react to these messages in a certain manner (e.g., start playing a media file, displaying a message on the screen etc.). See more on the Interactivity via Ethernet page.

Another typical usage is for one SVG document to get the user action (mouse click, key press etc.) and update a Shared Variable, while another SVG document listens for updates of that Shared Variable and performs a certain action when it is updated to a certain value. See more on the Interactivity via USB page.

Furthermore, the second SVG document could be on another HMP player, thus the first HMP would act as a controller for the second HMP.

Sample projects

The following projects are using Shared Variables: Instant messaging, JavaScript API tester, LocalStorage tester, Project:RFID.

See also the "Add/remove a video in a multi-screen project" example using Shared Variables and jSignage.

Architecture

The "Shared Variables" mechanism is based on a client / server model whereby the HMP maintains an internal server managing a collection of variables and different clients connect to this server to:

  • subscribe / unsubscribe from receiving real-time update notifications about a certain variable from the server;
  • set the value of a certain variable;
  • send events to be executed by the server.

The above operations are available through two APIs: SV Network API and SV JavaScript API.

A client could be either internal (i.e. another SVG document) or external (e.g., another HMP, a web page, a telnet client, a Control System device etc.). To allow external clients to access the SV server, the SV Network API must be enabled on the HMP (this feature is disabled by default).

Persistence

Before 3.1.0 release, the Shared Variables were stored in a non-persistent manner on the "server" HMP, meaning that a reboot of that HMP would reset all the Shared Variables to empty values. The solution was to manually enter the Shared Variable into localStorage to be persisted.

Starting with 3.1.0 release, the Shared Variables are automatically saved into localStorage (i.e. changing a Shared Variable triggers a modification of the local storage), thus their values are kept upon the HMP reboot.

SV Network API

The SV Network API specifies how to send commands toward an HMP from another device / application. It offers two interfaces for this purpose: TCP interface and HTTP interface; the former offers more functionalities, while the latter is easier to use.


Note Notes:
  • The SV Network API is not available on HMP300 devices.
  • In Elementi, the Network API is disabled, thus Elementi cannot be used as a Shared Variables server, nevertheless, it can communicate with SV servers set on HMP devices.
  • In HMD, the Network API can be enabled / disabled from "Settings" menu > "Enable network API".
  • The port used by Elementi / HMD is always 1234 and cannot be changed.

How to enable SV Network API

To allow external clients to access the Shared Variable server, the SV Network API must be enabled on the HMP (this feature is disabled by default).

Network API settings

For HMP350 devices, follow these steps:

  1. Open HMP Control Center and go to Advanced Applications > Network API page.
  2. Click on the checkbox labeled "Network API".
  3. If necessary, change the default "1234" port.
  4. Click the "Apply" button.


For HMP200, HMP130, and HMP100 devices, follow these steps:

  1. Open HMP Control Center and go to Network Settings page > Advanced tab.
  2. Click on the checkbox labeled "Enable API server using port" option.
  3. If necessary, change the default "1234" port.
  4. Click the "Apply" button.

TCP interface

The TCP interface can be accessed by opening a raw TCP socket connection to the server HMP and sending a string of characters in a predefined format:

COMMAND [timestamp] "name" ["value"]\r\n

Note: the pairs of square brackets are just used as a conventional way to specify that the term is optional.

  • COMMAND
    An uppercase command to be executed by the server; can be one of the following: UPDATE, EVENT, SUBSCRIBE or UNSUBSCRIBE.
  • timestamp
    An optional Unix time(i.e. the number of microseconds since 00:00:00 UTC on January 1, 1970) to use for UPDATE or EVENT commands. If omitted, the server will use the time when the command is received as the timestamp.
  • name
    The Shared Variable name to which the command is applied or the event name in case of an "EVENT" command.
  • value
    The value to which a Shared Variable is updated in case of an "UPDATE" command or the event parameters in case of an "EVENT" command.

Notes:

  • Individual commands are separated by the \r\n (0x0D 0x0A) two-byte sequence. This sequence should not appear inside the command.
  • Some special characters need to be escaped with a backslash: newline (0x0A) with \n, carriage return (0x0D) with \r, double quote (0x22) with \", null byte (0x00) with \0 and backslash itself with \\.

Examples

Using Telnet (see also this tutorial about sending commands to the HMP using a Telnet client):

UPDATE "var1" "some value"
UPDATE 1391763640616399 "var2" "some value"
EVENT "mouseup" "x=33,y=44,button=0"
SUBSCRIBE "var2"

Using PHP:

// updating a Shared Variable
function send_update( $host, $port, $name, $value ) { 
    $s = fsockopen( $host, $port ); 
    fwrite( $s, "UPDATE \"$name\" \"$value\"\r\n" ); 
    fclose( $s ); 
}

// sending a keydown event for the key 'V' with control modifier pressed
function send_copy_key( $host, $port ) {
    $s = fsockopen( $host, $port ); 
    fwrite( $s, "EVENT \"keydown\" \"Ctrl+V\"\r\n" );
    fclose( $s );
}

Note: this simplified example does not handle the escaping of control characters.

HTTP interface

The HTTP interface allows updating Shared Variables or sending events onto the HMP, by simply sending an HTTP GET request, having the following predefined format:

http://HMP_address:port/command?name1=value1&name2=value2...
  • HMP_address
    The HMP address.
  • port
    The port number, as configured on the Network Settings page. Usually, this is 1234.
  • command
    A lowercase command (as opposed to uppercase for the TCP interface) to be executed by the server; can be one either update or event.
  • name1, name2, ...
    The Shared Variable name in case of an "update" command or the event name in case of an "event" command.
  • value1, value2, ...
    The value to which a Shared Variable is updated in case of an "update" command or the event parameters in case of an "event" command.

Notes:

  • The server response to the HTTP GET request is "204 No Content" and the connection is closed immediately.
  • Multiple name=value pairs can be chained using the ampersand sign (&).
  • If name or value contain special characters (e.g., "?", "=", "&" etc.), these characters must be encoded (e.g., use "%3D" for the equal sign).

Examples

Type the following in the address bar of the browser and replace the HMP serial number or IP with the one of your HMP player.

http://spx-hmp-001d50100033:1234/update?var1=value1
http://192.168.1.10:1234/update?var1=new value 1&var2=value2
http://172.3.21.21:1234/event?mousedown=x%3D45,y%3D20,button%3D0&mouseup=x%3D45,y%3D20,button%3D0

Sample script

For updating Shared Variables via the SV Network API, you can also open the "Shared Variable Updater" html script in a browser.

How to get the value

Let's say that there's a web application which sends SV updates towards the HMP, but would also want to get their values back. The HTTP interface doesn't provide subscribe / unsubscribe commands, thus it is not possible to get the value of a Shared Variable using this interface. The solution is to register an update listener on the HMP receiving the updates and within the callback function, have a GET / POST request towards the web application, passing the Shared Variable value as a parameter.

List of commands

The TCP interface allows all four commands detailed below, while the HTTP interface only allows the first two.

Update command

The UPDATE / update command is used to change (update) the value of a Shared Variable. If the Shared Variable doesn't exists, it is created on the server.

Event command

The EVENT / event command is used to send an UI event to the player; in fact, this command updates a special Shared Variable "%EVENTS%". The following events are supported:

  • keydown, keyup
    The value can be an SVG key identifier (e.g., Enter, PageUp, F5 etc.), a character (e.g., A ... Z, a ... z etc.) or an hexadecimal Unicode character (e.g., U+0041, U+007A etc.). It may be prefixed by one or more of the following modifiers: Ctrl+, Shift+, Alt+, Meta+, AltGr+ (e.g., Shift+A, Ctrl+z etc.).
  • textInput
    The value is an utf-8 encoded text string.
  • mousemove
    The value is a string containing the x and y coordinates, separated by comma (e.g., "x=45,y=20").
  • mousedown, mouseup
    The value is a string containing the x and y coordinates, and the button type, separated by comma (e.g., "x=45,y=20,button=0"). The button type can be: 0 for left button, 1 for middle button and 2 for right button; for mice configured for left handed usage, the values are reversed.
  • mousewheel
    The value is a string containing the x and y coordinates, and the delta value, separated by comma (e.g., "x=45,y=20,delta=8"). The delta value is the distance the wheel has rotated around the y-axis. A positive value indicates that the wheel was rotated up (or left), while a negative indicates the opposite.
  • focusin, focusout
    The value is an empty string (i.e. "").

Subscribe command

The SUBSCRIBE command is used to subscribe for receiving notifications from the server whenever a certain Shared Variable is updated (e.g., SUBSCRIBE "var1") or an event is generated on the server (upon subscribing to the special Shared Variable "%EVENTS%" - i.e. SUBSCRIBE "%EVENTS%").

  • The clients will receive from the server either an UPDATE command (the first case) or an EVENT command (the second case). The notification always carries an absolute UTC timestamp.
  • If a Shared Variable has a value and a client subscribes to it, the client will immediately receive a notification from the server. Note that only a notification with the latest value is sent by the server regardless of the Shared Variable being updated multiple times before the moment of subscription.
  • In the case of a server reboot, all the Shared Variables are reset. The server doesn't notify the subscribed clients about this reset (so the clients are keeping the last-know value), but it will resume sending notifications as before the reboot. This is possible because the HMP clients are constantly sending connection keep-alive messages and the TCP socket connection is restored after the server reboot.

Unsubscribe command

The UNSUBSCRIBE command is used to stop receiving notifications from the server about a certain Shared Variable (e.g., UNSUBSCRIBE "var1") or the events generated on the server (i.e. UNSUBSCRIBE "%EVENTS%").

RPC interface

Starting with 3.1.0 release, Shared Variables are automatically saved into localStorage object, which means that they can also be manipulated through the RPC interface, which is offering different RPC commands to access the Web Storage items (and, implicitly, any Shared Variable used within the project). The RPC interface is based on JSON-RPC protocol over HTTP using POST calls. On HMP300 and HMP350 devices, the RPC API can also be accessed using a secured URL (HTTPS).


"Technical note"
Since firmware 4.2.0/3.4.0, when doing an RPC request using AJAX from a web page located on another host, i.e. using CORS, an additional query string parameter spx-api-key must be present in the request, i.e /rpc?spx-api-key=[rpc-api-key]. The API Key can be configured using Control Center or <rpc-api-key>, in the HMP Configuration API.

List command

The webstorage_list RPC command is used to list all available Shared Variables.

For instance, to list all Shared Variable via RPC, an AJAX POST call to http://HMP_address/rpc/can be used:

$.ajax( { 
    type: "POST", url: "http://HMP_address/rpc", contentType: 'application/json',
    xhrFields: { withCredentials: true }, // This line is necessary when using CORS 
    data: JSON.stringify( {
        method: "webstorage_list", params: [ ], id: 1
    } )
}).done( function( data ) {
    if ( data.result ){
// data.result is a JavaScript array with the list of Shared Variables
    }
});

This correspond to the following HTTP request:

POST /rpc HTTP/1.1
Host: HMP_address
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=

{ "method":"webstorage_list", "params":[ ], "id":1 }

And the following response from the player

{"result":[ "var1", "var2" ],"error":null,"id":1}

Set command

The webstorage_set RPC command is used to set one or more Shared Variables.

For instance, to set / update a Shared Variable via RPC, an AJAX POST call to http://HMP_address/rpc/can be used:

$.ajax( { 
    type: "POST", url: "http://HMP_address/rpc", contentType: 'application/json',
    xhrFields: { withCredentials: true }, // This line is necessary when using CORS 
    data: JSON.stringify( {
        method: "webstorage_set", params: [[ { name: "var1", value: "value1" } ]], id: 1
    } )
});

This correspond to the following HTTP request:

POST /rpc HTTP/1.1
Host: HMP_address
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=

{ "method":"webstorage_set", "params":[[{ "name": "var1", "value": "value1"}]], "id":1 }

Note Note: The RPC call will return the previous value of the variable and information if the update has been successful or not. If you are not interested in the answer, the "id" can be omitted.

Get command

The webstorage_get RPC command is used to retrieve one or more Shared Variables.

For instance, to get a Shared Variable via RPC, an AJAX POST call to http://HMP_address/rpc/can be used:

$.ajax( { 
    type: "POST", url: "http://HMP_address/rpc", contentType: 'application/json',
    xhrFields: { withCredentials: true }, // This line is necessary when using CORS 
    data: JSON.stringify( {
        method: "webstorage_get", params: [[ "var1" ]], id: 1
    } )
}).done( function( data ) {
    if ( data.result ){            
// The value of each Shared Variable is returned in an object such as:
// data.result: [ { "name": "var1", "success": true, "value": "bar", "timestamp": "1478796743230" } ]
    }
});

Sample script

For updating Shared Variables via the RPC interface, you can also open the "Shared Variable AJAX Updater" html script in a browser.

SV JavaScript API

The SV JavaScript API specifies how Shared Variables can be used within the JavaScript code. The API main entry point is the global function createSharedVariable() which returns a SharedVariable object that can be used to register listeners and / or set the value of Shared Variables. The global function fireSharedEvent() can be used to generate events dynamically, without using an interactive device.

IDL definition

interface SharedVariable {
   readonly attribute DOMString name;
   readonly attribute DOMString value;
   readonly attribute float lastUpdateTime;
   
   void addUpdateListener( UpdateListener callback, boolean timeSync = true );
   void removeUpdateListener( UpdateListener callback );
   void set( DOMString value );
   void testAndSet( DOMString test, DOMString set );
};

void UpdateListener( SharedVariable sv );

interface SVGGLobal {
   SharedVariable createSharedVariable( DOMString name );
   void fireSharedEvent( DOMString eventName, DOMString eventParams );
};

SharedVariable object

The SharedVariable object is a JavaScript object containing properties (name, value and lastUpdateTime) and methods (addUpdateListener, removeUpdateListener and set) specific to Shared Variables.

Properties

The SharedVariable object contains the following read-only properties:

  • SharedVariable.name
    Type: String.
    The name of the Shared Variable.
  • SharedVariable.value
    Type: String or Null.
    The value of the Shared Variable. Initially, this is set to null.
  • SharedVariable.lastUpdateTime
    Type: Number.
    The time when the Shared Variable was last updated, as number of milliseconds since 1 January, 1970 UTC. Initially, this is set to 0.
"Technical note"
It is recommended to read these properties within an update listener (see details below) or directly from the localStorage (requires firmware 3.1.0 or later) to ensure having the real values because the implicit update listener is executed asynchronously.

SharedVariable.addUpdateListener()

SharedVariable.addUpdateListener( callback );
SharedVariable.addUpdateListener( callback, timeSync );    // Version added: 3.0.0.
Returns: undefined.
Registers a listener that will invoke the callback function whenever the SharedVariable is updated. The method takes one or two parameters:
  • callback
    Type: Callback.
    The callback function. A reference to the SharedVariable is automatically passed as argument to the callback function.
  • timeSync
    Type: Boolean. Default: true.
    Determines whether the callback time of execution is synchronized with SharedVariable.lastUpdateTime or not (i.e current time is used). This parameter was introduced in 3.0.0 release with a default value of true for backwards compatibility (before that releases, the callback time of execution was synchronized all the time), however, except for the case of synchronized content, this parameter should be set to false.

SharedVariable.removeUpdateListener()

SharedVariable.removeUpdateListener( callback );
Returns: undefined.
Removes a registered listener. The method takes one parameter: callback. (See definition above).

SharedVariable.set()

SharedVariable.set( value );
Returns: undefined.
Sets the SharedVariable.value property to a string value. If any listeners are registered, this action will trigger them.

SharedVariable.testAndSet()

Version added 3.2

SharedVariable.testAndSet( test, value );
Returns: undefined.
Sets the SharedVariable.value property to a string value if and only if the SharedVariable.value is equal to test. If any listeners are registered, this action will trigger them.

Global object functions

createSharedVariable()

createSharedVariable( name );
Returns: SharedVariable Object.
Creates a SharedVariable object linked to a local or a remote Shared Variable. The method takes one parameter:
  • name
    Type: String.
    The name of the Shared Variable. The format of the name determines if the it's a local or remote Shared Variable.
    To connect to a remote Shared Variable, use the variable name followed by '@' and the hostname or IP address of the remote HMP and, optionally, by a TCP port (if the port number on the server is different from the port number on the local host) - e.g., var@remotehost:4567.

Note Note: In the background, a SUBSCRIBE command is sent to the local / remote server in order to receive notification whenever the Shared Variable is updated though any method. An implicit listener is attached to the SharedVariable object, that will asynchronously update the object properties when such a notification is received. To execute some code at the time of the update, it is recommended to register a custom listener.

fireSharedEvent()

fireSharedEvent( eventName, eventParams );
Returns: undefined.
Generates an UI event (as if an interactive device is used) by updating a special Shared Variable "%EVENTS%". The method takes two parameters:

Code example

// update listener function
function onUpdate( sv ) {
    alert( sv.name + ' : ' +sv.value );
}

var v1 = createSharedVariable( 'var1' );    // create a local Shared Variable
v1.addUpdateListener( onUpdate, false );    // register the update listener
v1.set( 'value1' );                         // update the Shared Variable value


var v2 = createSharedVariable( 'var2@spx-hmp-001d50100033' );    // create a remote Shared Variable
v2.addUpdateListener( onUpdate, false );                         // register the update listener
v2.set( 'value2' );                                              // update the Shared Variable value

// simulate a mouse click at coordinates (250,20)
fireSharedEvent( "mousedown", "x=250,y=20,button=0" );
fireSharedEvent( "mouseup",   "x=250,y=20,button=0" );

Sample projects for testing

You can test the SV JavaScript API using any of these projects:

Troubleshooting

  • SharedVariable.set( value ); code is ignored.
    This could happen because the SharedVariable.lastUpdateTime property value is grater than the timestamp of the current update command.
    Note that when using schedules or synchronized content, the JavaScript code is executed by default "in the past" (according to the beginning time of the event / project), thus the update command also gets a timestamp in the past. To avoid this behavior, add a spx:start-at-random-time="on" attribute inside the svg document containing the code above.
This page was last modified on 17 October 2017, at 19:03.