mirror of
https://github.com/nasa/openmct.git
synced 2025-05-07 02:58:30 +00:00
* Move to webpack build * Use webpack for building openmct. Move SCSS to one folder and load all core css up front. Remove bower, begin removing gulp in favor of npm run. * Uses eslint instead of jshint and jscs. Merge style checking rules into .eshintrc.js, carrying over core parts of crockford style and our adaptations. Current code base fails to pass the linter, want to separate linter changes from fixes to linting rules. * Support for Vue SFC with example * Remove outdated examples * Use HTML loader for html (supports relative imports of resources e.g. images) and raw-loader for when javascript must be loaded as text.
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;
|
|
}
|
|
);
|