2014-12-30 13:27:28 -08:00
|
|
|
/*global define*/
|
|
|
|
|
|
|
|
define(
|
2015-01-30 15:16:36 -08:00
|
|
|
['./TelemetryQueue', './TelemetryTable'],
|
|
|
|
function (TelemetryQueue, TelemetryTable) {
|
2014-12-30 13:27:28 -08:00
|
|
|
"use strict";
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A TelemetrySubscription tracks latest values for streaming
|
|
|
|
* telemetry data and handles notifying interested observers.
|
|
|
|
* It implements the interesting behavior behind the
|
|
|
|
* `telemetrySubscriber` service.
|
|
|
|
*
|
|
|
|
* Subscriptions may also be made directly using the
|
|
|
|
* `telemetry` capability of a domain objcet; the subscriber
|
|
|
|
* uses this as well, but additionally handles delegation
|
|
|
|
* (e.g. for telemetry panels) as well as latest-value
|
|
|
|
* extraction.
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param $q Angular's $q
|
|
|
|
* @param $timeout Angular's $timeout
|
|
|
|
* @param {DomainObject} domainObject the object whose
|
|
|
|
* associated telemetry data is of interest
|
|
|
|
* @param {Function} callback a function to invoke
|
|
|
|
* when new data has become available.
|
2015-01-30 15:16:36 -08:00
|
|
|
* @param {boolean} lossless true if callback should be invoked
|
|
|
|
* once with every data point available; otherwise, multiple
|
|
|
|
* data events in a short period of time will only invoke
|
|
|
|
* the callback once, with access to the latest data
|
2014-12-30 17:54:54 -08:00
|
|
|
*/
|
2015-01-30 15:16:36 -08:00
|
|
|
function TelemetrySubscription($q, $timeout, domainObject, callback, lossless) {
|
2014-12-30 13:27:28 -08:00
|
|
|
var unsubscribePromise,
|
|
|
|
latestValues = {},
|
|
|
|
telemetryObjects = [],
|
2015-01-30 15:16:36 -08:00
|
|
|
pool = lossless ? new TelemetryQueue() : new TelemetryTable(),
|
2015-01-29 09:48:53 -08:00
|
|
|
metadatas,
|
2014-12-30 13:27:28 -08:00
|
|
|
updatePending;
|
|
|
|
|
|
|
|
// Look up domain objects which have telemetry capabilities.
|
|
|
|
// This will either be the object in view, or object that
|
|
|
|
// this object delegates its telemetry capability to.
|
|
|
|
function promiseRelevantObjects(domainObject) {
|
|
|
|
// If object has been cleared, there are no relevant
|
|
|
|
// telemetry-providing domain objects.
|
|
|
|
if (!domainObject) {
|
|
|
|
return $q.when([]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, try delegation first, and attach the
|
|
|
|
// object itself if it has a telemetry capability.
|
|
|
|
return $q.when(domainObject.useCapability(
|
|
|
|
"delegation",
|
|
|
|
"telemetry"
|
|
|
|
)).then(function (result) {
|
|
|
|
var head = domainObject.hasCapability("telemetry") ?
|
|
|
|
[ domainObject ] : [],
|
|
|
|
tail = result || [];
|
|
|
|
return head.concat(tail);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-01-30 15:16:36 -08:00
|
|
|
function updateValuesFromPool() {
|
|
|
|
var values = pool.poll();
|
|
|
|
Object.keys(values).forEach(function (k) {
|
|
|
|
latestValues[k] = values[k];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Invoke the observer callback to notify that new streaming
|
|
|
|
// data has become available.
|
|
|
|
function fireCallback() {
|
2015-01-30 15:16:36 -08:00
|
|
|
// Play back from queue if we are lossless
|
|
|
|
while (!pool.isEmpty()) {
|
|
|
|
updateValuesFromPool();
|
2015-02-17 08:26:34 -08:00
|
|
|
// Fire callback, if one was provided
|
|
|
|
if (callback) {
|
|
|
|
callback();
|
|
|
|
}
|
2015-01-30 15:16:36 -08:00
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Clear the pending flag so that future updates will
|
|
|
|
// schedule this callback.
|
2014-12-30 13:27:28 -08:00
|
|
|
updatePending = false;
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Update the latest telemetry data for a specific
|
|
|
|
// domain object. This will notify listeners.
|
2014-12-30 13:27:28 -08:00
|
|
|
function update(domainObject, telemetry) {
|
|
|
|
var count = telemetry && telemetry.getPointCount();
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Only schedule notification if there isn't already
|
|
|
|
// a notification pending (and if we actually have
|
|
|
|
// data)
|
2014-12-30 13:27:28 -08:00
|
|
|
if (!updatePending && count) {
|
|
|
|
updatePending = true;
|
|
|
|
$timeout(fireCallback, 0);
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Update the latest-value table
|
2014-12-30 13:27:28 -08:00
|
|
|
if (count > 0) {
|
2015-01-30 15:16:36 -08:00
|
|
|
pool.put(domainObject.getId(), {
|
2014-12-30 13:27:28 -08:00
|
|
|
domain: telemetry.getDomainValue(count - 1),
|
|
|
|
range: telemetry.getRangeValue(count - 1)
|
2015-01-30 15:16:36 -08:00
|
|
|
});
|
2014-12-30 13:27:28 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Prepare a subscription to a specific telemetry-providing
|
|
|
|
// domain object.
|
2014-12-30 13:27:28 -08:00
|
|
|
function subscribe(domainObject) {
|
|
|
|
var telemetryCapability =
|
|
|
|
domainObject.getCapability("telemetry");
|
|
|
|
return telemetryCapability.subscribe(function (telemetry) {
|
|
|
|
update(domainObject, telemetry);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-01-29 09:48:53 -08:00
|
|
|
// Look up metadata associated with an object's telemetry
|
|
|
|
function lookupMetadata(domainObject) {
|
|
|
|
var telemetryCapability =
|
|
|
|
domainObject.getCapability("telemetry");
|
|
|
|
return telemetryCapability &&
|
|
|
|
telemetryCapability.getMetadata();
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Prepare subscriptions to all relevant telemetry-providing
|
|
|
|
// domain objects.
|
2014-12-30 13:27:28 -08:00
|
|
|
function subscribeAll(domainObjects) {
|
|
|
|
return domainObjects.map(subscribe);
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:54:54 -08:00
|
|
|
// Cache a reference to all relevant telemetry-providing
|
|
|
|
// domain objects. This will be called during the
|
|
|
|
// initial subscription chain; this allows `getTelemetryObjects()`
|
|
|
|
// to return a non-Promise to simplify usage elsewhere.
|
2014-12-30 13:27:28 -08:00
|
|
|
function cacheObjectReferences(objects) {
|
|
|
|
telemetryObjects = objects;
|
2015-01-29 09:48:53 -08:00
|
|
|
metadatas = objects.map(lookupMetadata);
|
2014-12-30 13:27:28 -08:00
|
|
|
return objects;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a reference to relevant objects (those with telemetry
|
2014-12-30 17:54:54 -08:00
|
|
|
// capabilities) and subscribe to their telemetry updates.
|
|
|
|
// Keep a reference to their promised return values, as these
|
|
|
|
// will be unsubscribe functions. (This must be a promise
|
|
|
|
// because delegation is supported, and retrieving delegate
|
|
|
|
// telemetry-capable objects may be an asynchronous operation.)
|
2014-12-30 13:27:28 -08:00
|
|
|
unsubscribePromise =
|
|
|
|
promiseRelevantObjects(domainObject)
|
|
|
|
.then(cacheObjectReferences)
|
|
|
|
.then(subscribeAll);
|
|
|
|
|
|
|
|
return {
|
2014-12-30 17:54:54 -08:00
|
|
|
/**
|
|
|
|
* Terminate all underlying subscriptions associated
|
|
|
|
* with this object.
|
|
|
|
* @method
|
|
|
|
* @memberof TelemetrySubscription
|
|
|
|
*/
|
2014-12-30 13:27:28 -08:00
|
|
|
unsubscribe: function () {
|
|
|
|
return unsubscribePromise.then(function (unsubscribes) {
|
|
|
|
return $q.all(unsubscribes.map(function (unsubscribe) {
|
|
|
|
return unsubscribe();
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
},
|
2014-12-30 17:54:54 -08:00
|
|
|
/**
|
|
|
|
* Get the most recent domain value that has been observed
|
|
|
|
* for the specified domain object. This will typically be
|
|
|
|
* a timestamp.
|
|
|
|
*
|
|
|
|
* The domain object passed here should be one that is
|
|
|
|
* subscribed-to here; that is, it should be one of the
|
|
|
|
* domain objects returned by `getTelemetryObjects()`.
|
|
|
|
*
|
|
|
|
* @param {DomainObject} domainObject the object of interest
|
|
|
|
* @returns the most recent domain value observed
|
|
|
|
* @method
|
|
|
|
* @memberof TelemetrySubscription
|
|
|
|
*/
|
2014-12-30 13:27:28 -08:00
|
|
|
getDomainValue: function (domainObject) {
|
|
|
|
var id = domainObject.getId();
|
|
|
|
return (latestValues[id] || {}).domain;
|
|
|
|
},
|
2014-12-30 17:54:54 -08:00
|
|
|
/**
|
|
|
|
* Get the most recent range value that has been observed
|
|
|
|
* for the specified domain object. This will typically
|
|
|
|
* be a numeric measurement.
|
|
|
|
*
|
|
|
|
* The domain object passed here should be one that is
|
|
|
|
* subscribed-to here; that is, it should be one of the
|
|
|
|
* domain objects returned by `getTelemetryObjects()`.
|
|
|
|
*
|
|
|
|
* @param {DomainObject} domainObject the object of interest
|
|
|
|
* @returns the most recent range value observed
|
|
|
|
* @method
|
|
|
|
* @memberof TelemetrySubscription
|
|
|
|
*/
|
2014-12-30 13:27:28 -08:00
|
|
|
getRangeValue: function (domainObject) {
|
|
|
|
var id = domainObject.getId();
|
|
|
|
return (latestValues[id] || {}).range;
|
|
|
|
},
|
2014-12-30 17:54:54 -08:00
|
|
|
/**
|
|
|
|
* Get all telemetry-providing domain objects which are
|
|
|
|
* being observed as part of this subscription.
|
|
|
|
*
|
|
|
|
* Capability delegation will be taken into account (so, if
|
|
|
|
* a Telemetry Panel was passed in the constructor, this will
|
|
|
|
* return its contents.) Capability delegation is resolved
|
|
|
|
* asynchronously so the return value here may change over
|
|
|
|
* time; while this resolution is pending, this method will
|
|
|
|
* return an empty array.
|
|
|
|
*
|
|
|
|
* @returns {DomainObject[]} all subscribed-to domain objects
|
|
|
|
* @method
|
|
|
|
* @memberof TelemetrySubscription
|
|
|
|
*/
|
2014-12-30 13:27:28 -08:00
|
|
|
getTelemetryObjects: function () {
|
|
|
|
return telemetryObjects;
|
2015-01-29 09:48:53 -08:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Get all telemetry metadata associated with
|
|
|
|
* telemetry-providing domain objects managed by
|
|
|
|
* this controller.
|
|
|
|
*
|
|
|
|
* This will ordered in the
|
|
|
|
* same manner as `getTelemetryObjects()` or
|
|
|
|
* `getResponse()`; that is, the metadata at a
|
|
|
|
* given index will correspond to the telemetry-providing
|
|
|
|
* domain object at the same index.
|
|
|
|
* @returns {Array} an array of metadata objects
|
|
|
|
*/
|
|
|
|
getMetadata: function () {
|
|
|
|
return metadatas;
|
2014-12-30 13:27:28 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return TelemetrySubscription;
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|