Shared Variables

From SpinetiX Support Wiki

(Redirected from Shared Variable)
Jump to: navigation, search

Introduction

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

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 a player, which will react to those messages in a certain manner (like 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 player, thus the first would act as a controller for the second one.

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.

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).

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

Persistence

The Shared Variables are automatically saved into localStorage, starting with 3.1.0 release, which means that changing a Shared Variable triggers a modification of the local storage data, which is kept upon the player reboot. Because of this, the Shared Variables can also be manipulated through APIs related to Web Storage.

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.

How to update a Shared Variable

There are different ways to update a Shared Variable, for instance you could:

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:

How to enable SV Network API

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

Network API settings

For HMP400, HMP400W, and HMP350 devices with firmware 4.3.0 or above, follow these steps:

  1. Open HMP Control Center and go to Advanced Applications > Network API page.
  2. Click on the checkbox labeled "Enable secure server".
  3. If the legacy server is required, click on the checkbox labeled "Enable legacy unprotected server"; change the port if needed.
  4. Click the "Apply" button and reboot the player.


Network API settings

For HMP350 devices with firmware below 4.3.0, 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 and reboot the player.


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 and reboot the player.


To use Elementi X as a Shared Variable server, follow these steps:

  1. Go to Settings > Network API...
  2. Click "Enable Network API".
  3. If necessary, change the default "1234" port.
  4. Click "OK"

Secure server implementation

Starting with firmware 4.3.0, a secure Shared Variable Network API, based on RFC8323 (CoAP REST API over TLS-PSK), is available.

  • The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks on the Internet of Things. The protocol is designed for machine-to-machine (M2M) applications such as building automation.
  • Transport Layer Security pre-shared key ciphersuites (TLS-PSK) is a set of cryptographic protocols that provide secure communication based on pre-shared keys (PSKs). These pre-shared keys are symmetric keys shared in advance among the communicating parties.
  • Only coaps+tcp flavor is supported. The server uses the standard coaps port number (5684), listens both for IPv4 ad IPv6 connections.

Endpoints

Two endpoints are exposed: webstorage and uievents.

webstorage

This endpoint allows updating Shared Variables, through the following operations:

GETcoaps+tcp://HMP_address/webstorage?v={key}[&Observe={0/1}]

Get the current value of the specified variable.
Returns 203 if an ETag header was present and the variable has not been modified. Otherwise, returns 205 and a payload with the current content. The ETag header is always set on the answer, has a fixed length of 8 bytes and is equal to the last modification date in microseconds since the UNIX epoch as a little-endian 64-bit integer.
The Observe option can be used for automatic notification of changes to the variable’s value - GET responses triggered by such requests always have a 205 response code. Possible values are:
  • 0 (register) adds the entry to the list, if not present;
  • 1 (deregister) removes the entry from the list, if present.

PUTcoaps+tcp://HMP_address/webstorage?v={key}

Set a new value for the variable with the current server timestamp. The request body contains the new variable value.
The response is always 204 on success.

POSTcoaps+tcp://HMP_address/webstorage?v={key}[&t={timestamp}][&x={expected}]

Modify a variable in local storage with optional timestamping and precondition. The request must include a payload with the new value.
t={timestamp} is optional; if present it is the number of microseconds since the UNIX epoch.
x={expected} is optional; if present it adds a precondition that the variable will be modified only if its current value is equal to the specified string.
Returns 204 on success or 412 if the precondition failed.
uievents

This endpoint allows to send a UI event to the player, through the following operations:

POSTcoaps+tcp://HMP_address/uievents

Send a UI event. The payload has one or more ASCII text lines with 3 space separated fields: timestamp, event name and event parameters.

GETcoaps+tcp://HMP_address/uievents

Always return an empty payload with 205 code but if the "observe" flag is set, notifications will be sent in the future with the same payload format as in POST above.

Synchronize multiple players

Multiple players can be synchronized together, so that Shared Variables changes on a "master" player can be broadcasted to multiple "client"/"slave" players. This is useful for interactive content within a multiscreen content context. For this, follow these steps:

  1. Enable the Network API on the "master" player as detailed above.
  2. Generate a client configuration file from the master.
  3. On each "slave"
    1. Go to Advanced Applications > Network API page
    2. Click the "Upload Client Config" button and apply that file.
    3. Once done, the IP address, usually IPv6, of the master would appear in the "Default network API server address" field.
Note Note:
Elementi X can be configured as a client as well - for that import the client configuration file generated from the master under Settings > Network Credentials... dialog. That shall add an entry coaps+tcp://IP in the list; the same IP shall also be added as "Default Server" under Settings > Network API... dialog.

Legacy server implementation

Note  

The legacy implementation provides unprotected access to the Shared Variables server through raw TCP socket connections or HTTP GET requests to the predefined end-points.

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

where:

  • 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.
Note Notes:
  • The pairs of square brackets are just used as a conventional way to specify that the term is optional.
  • 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 \\.
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%").

Examples

Using Telnet:

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

See also this tutorial about how to send commands to the HMP using a Telnet client.

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 endpoints

The following endpoint are exposed:

  • GET http(s)://HMP_address:port/update?name1=value1&name2=value2...
    Updates one or more Shared Variables on the player.
  • GET http(s)://HMP_address:port/event?name1=value1&name2=value2...
    Triggers one or more events on the player.

Where:

  • port
    The port number, as configured on the Network Settings page. Usually, this is 1234.
  • update or event
    A lowercase command (as opposed to uppercase for the TCP interface) to update a Shared Variable or to trigger an 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.
Note 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).
  • There are no endpoints for subscribe / unsubscribe commands, thus it is not possible to get the value of a Shared Variable using this interface.
  • For a sample script using the (unprotected) "update" endpoint, see the "Shared Variable Updater" html script.
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

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.

The SharedVariable object is a JavaScript object containing three read-only properties (name, value and lastUpdateTime) and four methods (addUpdateListener, removeUpdateListener, set and testAndSet), further detailed below.

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

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 );
};

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:

SharedVariable object

The SharedVariable object is a JavaScript object containing three 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.

The SharedVariable object has four methods (addUpdateListener, removeUpdateListener, set and testAndSet), further detailed below.

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.

.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 firmware 3.0.0 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.

.removeUpdateListener()

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

.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.

.testAndSet()

SharedVariable.testAndSet( test, value );
Added in firmware 3.2.0.
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.

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" );

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.

Web Storage bridge

Shared Variables are automatically saved and persisted into localStorage object, starting with 3.1.0 release, which means that the Shared Variables can also be manipulated with the following APIs:

See also

This page was last modified on 21 October 2020, at 11:41.