Project

General

Profile

WebDPM Apps

Web applications can now receive ACNET device data directly, rather than through secondary services (like XMLRPC or HTTP GET requests.) Modern browsers can easily update their displays at 15Hz and this API can request data at that rate. Since Javascript doesn't have a way to automatically include other Javascript modules, you have to do it yourself, which means WebDPM apps will have similar, boilerplate code at the start of their page. The following shows an app "skeleton" which includes the minimum modules needed to use DPM:

<html>
  <head>
    <meta charset="utf-8">
    <script src="/acnet/acnet.js"></script>
    <script src="/acnet/proto_lib.js"></script>
    <script src="/acnet/dpm_protocol.js"></script>
    <script src="/acnet/dpm.js"></script>

    <script src="YOUR_JAVASCRIPT.js"></script>

    <title>Page Title</title>
  </head>

  <body>
  </body>
</html>

For details, see the WebDPM API.

Using the DPM Module

In your javascript module, you need to create a DPM object, tell it what data to collect and where to send it. Everything in Javascript is event driven, so you can’t block waiting for the data to come back. Instead, you give it a callback which will get called when device data is received. The callback can update widgets in the web page or it can update some global data model in your app.

First create the DPM object:

const dpm = new DPM();

Next, you tell it what to collect and where to deliver it. The first parameter is the device data specification which uses DRF2 notation, allowing you to also specify an FTD. The second parameter is the callback to receive device data. This function takes a third, optional parameter which, if specified, is a function which gets called when there's an error. If no function is provided, the error is sent to the Javascript console log. For instance, to collect D:OUTTMP on clock event 8F and deliver updates to a function called handleTemperature, do this:

dpm.addRequest("D:OUTTMP@e,8f", handleTemperature);

Your callback takes two arguments. The first is the current reading of the device and the second is the database info of the device. The device info argument will get updated whenever the database is edited (DPM monitors DBNEWS and will tell its clients when a device’s information changes.) The callback will get called every time new data arrives.

You can call addRequest as many times as is necessary to cover your data acquisition needs. When you’re done adding requests, you can start acquiring data by calling:

dpm.start();

Once acquisition starts, the DPM object will try hard to keep it going. If the DPM it's using is taken down, the DPM object will find an alternate DPM and resend the requests to it. In other words, a WebDPM app doesn't have to do any error recovery with a DPM connection.

For our hypothetical handleTemperature callback, one might do this:

function handleTemperature(reading, info) {
    document.getElementById('tempField').innerText = reading.data + ' ' + info.units;
}

The example sets the inner text of the HTML entity with id "tempField" to the reading, appended with the units text.

Stopping and Restarting Acquisition

Data acquisition can be stopped by calling the stop() method:

dpm.stop();

The requests and callbacks are still remembered. Calling start() will restart acquisition, using the same devices, FTDs, and callbacks. A different set of devices can be specified by stopping the collection, clearing the list, and specifying the new devices:

dpm.stop();
dpm.clear();

// Set up new acquisition by calling .addRequest()

Redirection

The WebDPM API supports redirection. The DPM object .start() method takes an optional parameter which specifies the redirection model (if not specified, it gets live data from front-ends.) The following modules are supported:

  • "SRFILE : #" -- Pulls device readings from the specified save/restore file number.
  • "REDIR : * -> NODE" -- Redirects all requests to the node, NODE. NODE can be a node name or an integer representing the trunk/node value.
  • "REDIR : NODEA -> NODEB, NODEC -> NODED, ..." -- Redirects a list of nodes. Requests destined for NODEA will be sent to NODEB. Requests to NODEC will be sent to NODED, etc.

You'll note that specifying a model affects the entire DPM acquisition. If you want to use several models (like live accelerator data and data from a save/restore file), you'll have to use multiple DPM objects. By default, each DPM object will create a new ACNET connection, which is a limited resource. See the "Sharing ACNET connections" section to see how ACNET connections can be shared between the DPM objects.

Redirection services have limitations. The SRFILE model, for instance, isn't multi-user safe so you may receive data from a different user's save file. There is a Redmine issue to fix this (#16780), but it's not ready yet. The REDIR model has it's own limitation: it only works if the target node understands the source node's SSDN values. If not, an error is the best you can hope for and undefined behavior is the worst that can happen. The REDIR model is mainly for redirecting a front-end's requests to a test front-end (which still understands the SSDNs.)

Specifying a DPM

Normally the DPM object auto-discovers an available DPM. But it's useful, sometimes, to specify a particular DPM. The ACNET node FGATE, for instance, usually runs a development version of DPM. To test your web app against the latest DPM code, you may want to force the use of FGATE. The DPM object constructor takes an optional parameter to specify which DPM to use.

For instance, to use FGATE for testing, you could temporarily change your app to choose it:

const dpm = new DPM("FGATE");

Note that when a node is forced, the DPM object won't switch to another DPM if the one you chose goes away (although when it comes back, your app will reconnect and use it.)

Sharing ACNET connections

A given ACNET node only support 256 client connections. By default, the DPM object will create its own ACNET connection. If an app uses several DPM objects, each uses a connection and you greatly reduce how many instances of your web app that can be run on a node. If an app needs more than one DPM connection, they should share an ACNET connection.

The DPM object constructor takes a second, optional argument which is an ACNET connection to use for requests. To share a connection between a save/restore DPM and live data, you could do this (remember the first parameter is used to specify a particular DPM -- we use undefined so it auto-discovers an available DPM):

const con = new ACNET();
const dpm_live = new DPM(undefined, con);
const dpm_file = new DPM(undefined, con);

// Load up the requests...

dpm_live.addRequest(...);
dpm_live.addRequest(...);

dpm_file.addRequest(...);
dpm_file.addRequest(...);

// Start the lists. 'dpm_live' will get accelerator data and
// 'dpm_file' will read save/restore file 100.

dpm_live.start();
dpm_file.start("SRFILE:100");