mirror of
https://github.com/nasa/openmct.git
synced 2025-05-28 21:24:20 +00:00
342 lines
14 KiB
JavaScript
342 lines
14 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Open MCT includes source code licensed under additional open source
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
* this source code distribution or the Licensing information page available
|
|
* at runtime from the About dialog for additional information.
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Module defining TelemetryCapability. Created by vwoeltje on 11/12/14.
|
|
*/
|
|
define(
|
|
[
|
|
'../../../src/api/objects/object-utils',
|
|
'lodash'
|
|
],
|
|
function (
|
|
objectUtils,
|
|
_
|
|
) {
|
|
|
|
var ZERO = function () {
|
|
return 0;
|
|
},
|
|
EMPTY_SERIES = {
|
|
getPointCount: ZERO,
|
|
getDomainValue: ZERO,
|
|
getRangeValue: ZERO
|
|
};
|
|
|
|
/**
|
|
* Provides metadata about telemetry associated with a
|
|
* given domain object.
|
|
*
|
|
* @typedef TelemetryMetadata
|
|
* @property {string} source the machine-readable identifier for
|
|
* the source of telemetry data for this object; used by
|
|
* {@link TelemetryService} implementations to determine
|
|
* whether or not they provide data for this object.
|
|
* @property {string} key the machine-readable identifier for
|
|
* telemetry data associated with this specific object,
|
|
* within that `source`.
|
|
* @property {TelemetryDomainMetadata[]} domains supported domain
|
|
* options for telemetry data associated with this object,
|
|
* to use in interpreting a {@link TelemetrySeries}
|
|
* @property {TelemetryRangeMetadata[]} ranges supported range
|
|
* options for telemetry data associated with this object,
|
|
* to use in interpreting a {@link TelemetrySeries}
|
|
*/
|
|
|
|
/**
|
|
* Provides metadata about range options within a telemetry series.
|
|
* Range options describe distinct properties within any given datum
|
|
* of a telemetry series; for instance, a telemetry series containing
|
|
* both raw and uncalibrated values may provide separate ranges for
|
|
* each.
|
|
*
|
|
* @typedef TelemetryRangeMetadata
|
|
* @property {string} key machine-readable identifier for this range
|
|
* @property {string} name human-readable name for this range
|
|
* @property {string} [units] human-readable units for this range
|
|
* @property {string} [format] data format for this range; usually,
|
|
* one of `number`, or `string`. If `undefined`,
|
|
* should presume to be a `number`. Custom formats
|
|
* may be indicated here.
|
|
*/
|
|
|
|
/**
|
|
* Provides metadata about domain options within a telemetry series.
|
|
* Domain options describe distinct properties within any given datum
|
|
* of a telemtry series; for instance, a telemetry series containing
|
|
* both spacecraft event time and earth received times may provide
|
|
* separate domains for each.
|
|
*
|
|
* Domains are typically used to represent timestamps in a telemetry
|
|
* series, but more generally may express any property which will
|
|
* have unique values for each datum in a series. It is this property
|
|
* which makes domains distinct from ranges, as it makes these values
|
|
* appropriate and meaningful for use to sort and bound a series.
|
|
*
|
|
* @typedef TelemetryDomainMetadata
|
|
* @property {string} key machine-readable identifier for this range
|
|
* @property {string} name human-readable name for this range
|
|
* @property {string} [system] machine-readable identifier for the
|
|
* time/date system associated with this domain;
|
|
* used by {@link DateService}
|
|
*/
|
|
|
|
/**
|
|
* A telemetry capability provides a means of requesting telemetry
|
|
* for a specific object, and for unwrapping the response (to get
|
|
* at the specific data which is appropriate to the domain object.)
|
|
*
|
|
* @memberof platform/telemetry
|
|
* @implements {Capability}
|
|
* @constructor
|
|
*/
|
|
function TelemetryCapability(openmct, $injector, $q, $log, domainObject) {
|
|
// We could depend on telemetryService directly, but
|
|
// there isn't a platform implementation of this.
|
|
this.initializeTelemetryService = function () {
|
|
try {
|
|
return (this.telemetryService =
|
|
$injector.get("telemetryService"));
|
|
} catch (e) {
|
|
$log.info("Telemetry service unavailable");
|
|
return (this.telemetryService = null);
|
|
}
|
|
};
|
|
|
|
this.openmct = openmct;
|
|
this.$q = $q;
|
|
this.$log = $log;
|
|
this.domainObject = domainObject;
|
|
}
|
|
|
|
// Build a request object. This takes the request that was
|
|
// passed to the capability, and adds source, id, and key
|
|
// fields associated with the object (from its type definition
|
|
// and/or its model)
|
|
TelemetryCapability.prototype.buildRequest = function (request) {
|
|
// Start with any "telemetry" field in type; use that as a
|
|
// basis for the request.
|
|
var domainObject = this.domainObject,
|
|
type = domainObject.getCapability("type"),
|
|
typeRequest = (type && type.getDefinition().telemetry) || {},
|
|
modelTelemetry = domainObject.getModel().telemetry,
|
|
fullRequest = Object.create(typeRequest),
|
|
newObject = objectUtils.toNewFormat(
|
|
domainObject.getModel(),
|
|
domainObject.getId()
|
|
),
|
|
metadata = this.openmct.telemetry.getMetadata(newObject),
|
|
bounds,
|
|
timeSystem;
|
|
|
|
// Add properties from the telemetry field of this
|
|
// specific domain object.
|
|
Object.keys(modelTelemetry).forEach(function (k) {
|
|
fullRequest[k] = modelTelemetry[k];
|
|
});
|
|
|
|
// Add properties from this specific requestData call.
|
|
Object.keys(request).forEach(function (k) {
|
|
fullRequest[k] = request[k];
|
|
});
|
|
|
|
// Ensure an ID and key are present
|
|
if (!fullRequest.id) {
|
|
fullRequest.id = domainObject.getId();
|
|
}
|
|
if (!fullRequest.key) {
|
|
fullRequest.key = domainObject.getId();
|
|
}
|
|
|
|
if (request.start === undefined && request.end === undefined) {
|
|
bounds = this.openmct.time.bounds();
|
|
fullRequest.start = bounds.start;
|
|
fullRequest.end = bounds.end;
|
|
}
|
|
|
|
if (request.domain === undefined) {
|
|
timeSystem = this.openmct.time.timeSystem();
|
|
if (timeSystem !== undefined) {
|
|
fullRequest.domain = timeSystem.key;
|
|
}
|
|
}
|
|
|
|
if (!fullRequest.ranges) {
|
|
fullRequest.ranges = metadata.valuesForHints(['range']);
|
|
}
|
|
|
|
if (!fullRequest.domains) {
|
|
fullRequest.domains = metadata.valuesForHints(['domain']);
|
|
}
|
|
|
|
return fullRequest;
|
|
};
|
|
|
|
function asSeries(telemetry, defaultDomain, defaultRange, sourceMap) {
|
|
function getValue(index, key) {
|
|
return telemetry[index][sourceMap[key].source];
|
|
}
|
|
|
|
return {
|
|
getRangeValue: function (index, range) {
|
|
return getValue(index, range || defaultRange);
|
|
},
|
|
getDomainValue: function (index, domain) {
|
|
return getValue(index, domain || defaultDomain);
|
|
},
|
|
getPointCount: function () {
|
|
return telemetry.length;
|
|
},
|
|
getData: function () {
|
|
return telemetry;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Request telemetry data for this specific domain object.
|
|
* @param {TelemetryRequest} [request] parameters for this
|
|
* specific request
|
|
* @returns {Promise} a promise for the resulting telemetry
|
|
* object
|
|
*/
|
|
TelemetryCapability.prototype.requestData = function requestTelemetry(request) {
|
|
// Bring in any defaults from the object model
|
|
var fullRequest = this.buildRequest(request || {});
|
|
var source = fullRequest.source;
|
|
var key = fullRequest.key;
|
|
var telemetryService = this.telemetryService ||
|
|
this.initializeTelemetryService(); // Lazy initialization
|
|
|
|
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
|
|
var telemetryAPI = this.openmct.telemetry;
|
|
|
|
var metadata = telemetryAPI.getMetadata(domainObject);
|
|
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
|
|
var defaultRange = metadata.valuesForHints(['range'])[0];
|
|
defaultRange = defaultRange ? defaultRange.key : undefined;
|
|
|
|
var sourceMap = _.indexBy(metadata.values(), 'key');
|
|
|
|
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
|
|
telemetryAPI.legacyProvider;
|
|
|
|
// Pull out the relevant field from the larger,
|
|
// structured response.
|
|
function getRelevantResponse(response) {
|
|
return ((response || {})[source] || {})[key] ||
|
|
EMPTY_SERIES;
|
|
}
|
|
|
|
// Issue a request to the service
|
|
function requestTelemetryFromService() {
|
|
return telemetryService.requestTelemetry([fullRequest]);
|
|
}
|
|
|
|
if (isLegacyProvider) {
|
|
// If a telemetryService is not available,
|
|
// getTelemetryService() should reject, and this should
|
|
// bubble through subsequent then calls.
|
|
return telemetryService &&
|
|
requestTelemetryFromService().then(getRelevantResponse);
|
|
} else {
|
|
return telemetryAPI.request(domainObject, fullRequest).then(function (telemetry) {
|
|
return asSeries(telemetry, defaultDomain, defaultRange, sourceMap);
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get metadata about this domain object's associated
|
|
* telemetry.
|
|
* @returns {TelemetryMetadata} metadata about this object's telemetry
|
|
*/
|
|
TelemetryCapability.prototype.getMetadata = function () {
|
|
// metadata just looks like a request,
|
|
// so use buildRequest to bring in both
|
|
// type-level and object-level telemetry
|
|
// properties
|
|
return (this.metadata = this.metadata || this.buildRequest({}));
|
|
};
|
|
|
|
/**
|
|
* Subscribe to updates to telemetry data for this domain
|
|
* object.
|
|
* @param {Function} callback a function to call when new
|
|
* data becomes available; the telemetry series
|
|
* containing the data will be given as an argument.
|
|
* @param {TelemetryRequest} [request] parameters for the
|
|
* subscription request
|
|
*/
|
|
TelemetryCapability.prototype.subscribe = function subscribe(callback, request) {
|
|
var fullRequest = this.buildRequest(request || {});
|
|
var telemetryService = this.telemetryService ||
|
|
this.initializeTelemetryService(); // Lazy initialization
|
|
|
|
var domainObject = objectUtils.toNewFormat(this.domainObject.getModel(), this.domainObject.getId());
|
|
var telemetryAPI = this.openmct.telemetry;
|
|
|
|
var metadata = telemetryAPI.getMetadata(domainObject);
|
|
var defaultDomain = metadata.valuesForHints(['domain'])[0].key;
|
|
var defaultRange = metadata.valuesForHints(['range'])[0];
|
|
defaultRange = defaultRange ? defaultRange.key : undefined;
|
|
|
|
var sourceMap = _.indexBy(metadata.values(), 'key');
|
|
|
|
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
|
|
telemetryAPI.legacyProvider;
|
|
|
|
function update(telemetry) {
|
|
callback(asSeries([telemetry], defaultDomain, defaultRange, sourceMap));
|
|
}
|
|
|
|
// Unpack the relevant telemetry series
|
|
function updateLegacy(telemetries) {
|
|
var source = fullRequest.source,
|
|
key = fullRequest.key,
|
|
result = ((telemetries || {})[source] || {})[key];
|
|
if (result) {
|
|
callback(result);
|
|
}
|
|
}
|
|
|
|
// Avoid a loop here...
|
|
if (isLegacyProvider) {
|
|
return telemetryService &&
|
|
telemetryService.subscribe(updateLegacy, [fullRequest]);
|
|
} else {
|
|
return telemetryAPI.subscribe(domainObject, update, fullRequest);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The telemetry capability is applicable when a
|
|
* domain object model has a "telemetry" field.
|
|
*/
|
|
TelemetryCapability.appliesTo = function (model) {
|
|
return (model && model.telemetry) ? true : false;
|
|
};
|
|
|
|
return TelemetryCapability;
|
|
}
|
|
);
|