diff --git a/platform/commonUI/formats/src/UTCTimeFormat.js b/platform/commonUI/formats/src/UTCTimeFormat.js index 680d20c296..6339bb0266 100644 --- a/platform/commonUI/formats/src/UTCTimeFormat.js +++ b/platform/commonUI/formats/src/UTCTimeFormat.js @@ -121,6 +121,9 @@ define([ }; UTCTimeFormat.prototype.parse = function (text) { + if (typeof text === 'number') { + return text; + } return moment.utc(text, DATE_FORMATS).valueOf(); }; diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 708f84e21f..efa857f263 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -29,7 +29,8 @@ define([ '../../example/imagery/plugin', '../../platform/import-export/bundle', './summaryWidget/plugin', - './URLIndicatorPlugin/URLIndicatorPlugin' + './URLIndicatorPlugin/URLIndicatorPlugin', + './telemetryMean/plugin' ], function ( _, UTCTimeSystem, @@ -39,7 +40,8 @@ define([ ExampleImagery, ImportExport, SummaryWidget, - URLIndicatorPlugin + URLIndicatorPlugin, + TelemetryMean ) { var bundleMap = { CouchDB: 'platform/persistence/couch', @@ -124,8 +126,8 @@ define([ }; plugins.ExampleImagery = ExampleImagery; - plugins.SummaryWidget = SummaryWidget; + plugins.TelemetryMean = TelemetryMean; plugins.URLIndicatorPlugin = URLIndicatorPlugin; return plugins; diff --git a/src/plugins/telemetryMean/plugin.js b/src/plugins/telemetryMean/plugin.js new file mode 100755 index 0000000000..68ada7f737 --- /dev/null +++ b/src/plugins/telemetryMean/plugin.js @@ -0,0 +1,54 @@ +define(['./src/MeanTelemetryProvider'], function (MeanTelemetryProvider) { + var DEFAULT_SAMPLES = 10; + + function plugin() { + return function install(openmct) { + openmct.types.addType('telemetry-mean', { + name: 'Telemetry Mean', + description: 'Provides telemetry values that represent the mean of the last N values of a telemetry stream', + creatable: true, + cssClass: 'icon-telemetry', + initialize: function (domainObject) { + domainObject.samples = DEFAULT_SAMPLES; + domainObject.telemetry = {}; + domainObject.telemetry.values = + openmct.time.getAllTimeSystems().map(function (timeSystem, index) { + return { + key: timeSystem.key, + name: timeSystem.name, + hints: { + domain: index + 1 + } + } + }); + domainObject.telemetry.values.push({ + key: "value", + name: "Value", + hints: { + range: 1 + } + }); + }, + form: [ + { + "key": "telemetryPoint", + "name": "Telemetry Point", + "control": "textfield", + "required": true, + "cssClass": "l-input-lg" + }, + { + "key": "samples", + "name": "Samples to Average", + "control": "textfield", + "required": true, + "cssClass": "l-input-sm" + } + ] + }); + openmct.telemetry.addProvider(new MeanTelemetryProvider(openmct)); + }; + } + + return plugin; +}); diff --git a/src/plugins/telemetryMean/src/MeanTelemetryProvider.js b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js new file mode 100644 index 0000000000..053fe15be3 --- /dev/null +++ b/src/plugins/telemetryMean/src/MeanTelemetryProvider.js @@ -0,0 +1,114 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2017, 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. + *****************************************************************************/ + +define([ + '../../../api/objects/object-utils', + './TelemetryAverager' +], function (objectUtils, TelemetryAverager) { + + function MeanTelemetryProvider(openmct) { + this.openmct = openmct; + this.telemetryAPI = openmct.telemetry; + this.timeAPI = openmct.time; + this.objectAPI = openmct.objects; + this.perObjectProviders = {}; + } + + MeanTelemetryProvider.prototype.canProvideTelemetry = function (domainObject) { + return domainObject.type === 'telemetry-mean'; + }; + + MeanTelemetryProvider.prototype.supportsRequest = + MeanTelemetryProvider.prototype.supportsSubscribe = + MeanTelemetryProvider.prototype.canProvideTelemetry; + + MeanTelemetryProvider.prototype.subscribe = function (domainObject, callback) { + var wrappedUnsubscribe; + var unsubscribeCalled = false; + var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + var samples = domainObject.samples; + + this.objectAPI.get(objectId).then(function (linkedDomainObject) { + if (!unsubscribeCalled) { + wrappedUnsubscribe = this.subscribeToAverage(linkedDomainObject, samples, callback); + } + }.bind(this)); + + return function unsubscribe(){ + unsubscribeCalled = true; + if (wrappedUnsubscribe !== undefined) { + wrappedUnsubscribe(); + } + } + }; + + MeanTelemetryProvider.prototype.subscribeToAverage = function (domainObject, samples, callback) { + var telemetryAverager = new TelemetryAverager(this.telemetryAPI, this.timeAPI, domainObject, samples); + + return this.telemetryAPI.subscribe(domainObject, function (telemetryDatum) { + var avgData = telemetryAverager.createAverageDatum(telemetryDatum); + + if (telemetryAverager.sampleCount() === samples){ + callback(avgData); + } + }.bind(this)); + } + + MeanTelemetryProvider.prototype.request = function (domainObject, request) { + var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + var samples = domainObject.samples; + + return this.objectAPI.get(objectId).then(function (linkedDomainObject) { + return this.requestAverageTelemetry(linkedDomainObject, request, samples); + }.bind(this)); + }; + + /** + * @private + */ + MeanTelemetryProvider.prototype.requestAverageTelemetry = function (domainObject, request, samples) { + var averageData = []; + var telemetryAverager = new TelemetryAverager(this.telemetryAPI, this.timeAPI, domainObject, samples); + + return this.telemetryAPI.request(domainObject, request).then(function (telemetryData){ + telemetryData.forEach(function (datum){ + var avgData = telemetryAverager.createAverageDatum(datum); + + if (telemetryAverager.sampleCount() === samples){ + averageData.push(avgData); + } + }.bind(this)); + + return averageData; + }.bind(this)); + } + + /** + * @private + */ + MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) { + var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint); + return this.objectAPI.get(objectId); + }; + + return MeanTelemetryProvider; +}); diff --git a/src/plugins/telemetryMean/src/TelemetryAverager.js b/src/plugins/telemetryMean/src/TelemetryAverager.js new file mode 100644 index 0000000000..b363e9df3d --- /dev/null +++ b/src/plugins/telemetryMean/src/TelemetryAverager.js @@ -0,0 +1,112 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2017, 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. + *****************************************************************************/ + +define([], function () { + + function TelemetryAverager(telemetryAPI, timeAPI, domainObject, samples) { + this.telemetryAPI = telemetryAPI; + this.timeAPI = timeAPI; + + this.domainObject = domainObject; + this.samples = samples; + this.averagingWindow = []; + + this.rangeKey = undefined + this.rangeFormatter = undefined; + this.setRangeKeyAndFormatter(); + + // Defined dynamically based on current time system + this.domainKey = undefined; + this.domainFormatter = undefined; + } + + TelemetryAverager.prototype.createAverageDatum = function (telemetryDatum) { + this.setDomainKeyAndFormatter(); + + var timeValue = this.domainFormatter.parse(telemetryDatum); + var rangeValue = this.rangeFormatter.parse(telemetryDatum); + + this.averagingWindow.push(rangeValue); + if (this.averagingWindow.length > this.samples) { + this.averagingWindow.shift(); + } + var averageValue = this.calculateMean(); + + var meanDatum = {}; + meanDatum[this.domainKey] = timeValue; + meanDatum['value'] = averageValue + + return meanDatum; + }; + + /** + * @private + */ + TelemetryAverager.prototype.calculateMean = function () { + var sum = 0; + var i = 0; + + for (; i < this.averagingWindow.length; i++) { + sum += this.averagingWindow[i]; + } + + return sum / this.averagingWindow.length; + }; + + /** + * @private + */ + TelemetryAverager.prototype.setDomainKeyAndFormatter = function () { + var domainKey = this.timeAPI.timeSystem().key; + if (domainKey !== this.domainKey) { + this.domainKey = domainKey; + this.domainFormatter = this.getFormatter(domainKey); + } + }; + + /** + * @private + */ + TelemetryAverager.prototype.setRangeKeyAndFormatter = function () { + var metadatas = this.telemetryAPI.getMetadata(this.domainObject); + var rangeValues = metadatas.valuesForHints(['range']); + + this.rangeKey = rangeValues[0].key; + this.rangeFormatter = this.getFormatter(this.rangeKey); + } + + /** + * @private + */ + TelemetryAverager.prototype.getFormatter = function (key) { + var objectMetadata = this.telemetryAPI.getMetadata(this.domainObject); + var valueMetadata = objectMetadata.value(key); + + return this.telemetryAPI.getValueFormatter(valueMetadata); + }; + + TelemetryAverager.prototype.sampleCount = function () { + return this.averagingWindow.length; + }; + + return TelemetryAverager; +});