diff --git a/example/generator/src/GeneratorProvider.js b/example/generator/GeneratorProvider.js similarity index 94% rename from example/generator/src/GeneratorProvider.js rename to example/generator/GeneratorProvider.js index 52a9a5646c..6d6093928e 100644 --- a/example/generator/src/GeneratorProvider.js +++ b/example/generator/GeneratorProvider.js @@ -41,6 +41,10 @@ define([ return domainObject.type === 'generator'; }; + GeneratorProvider.prototype.supportsRequest = + GeneratorProvider.prototype.supportsSubscribe = + GeneratorProvider.prototype.canProvideTelemetry; + GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) { var props = [ 'amplitude', diff --git a/example/generator/src/SinewaveConstants.js b/example/generator/SinewaveConstants.js similarity index 98% rename from example/generator/src/SinewaveConstants.js rename to example/generator/SinewaveConstants.js index f9f48dc0e2..13a547e769 100644 --- a/example/generator/src/SinewaveConstants.js +++ b/example/generator/SinewaveConstants.js @@ -19,7 +19,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,Promise*/ +/*global define*/ define({ START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day. diff --git a/example/generator/src/SinewaveDeltaFormat.js b/example/generator/SinewaveDeltaFormat.js similarity index 98% rename from example/generator/src/SinewaveDeltaFormat.js rename to example/generator/SinewaveDeltaFormat.js index e85fda2b28..4b48133042 100644 --- a/example/generator/src/SinewaveDeltaFormat.js +++ b/example/generator/SinewaveDeltaFormat.js @@ -19,12 +19,11 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,Promise*/ +/*global define*/ define( ['./SinewaveConstants', 'moment'], function (SinewaveConstants, moment) { - "use strict"; var START_TIME = SinewaveConstants.START_TIME, FORMAT_REGEX = /^-?\d+:\d+:\d+$/, diff --git a/example/generator/src/SinewaveLimitCapability.js b/example/generator/SinewaveLimitCapability.js similarity index 100% rename from example/generator/src/SinewaveLimitCapability.js rename to example/generator/SinewaveLimitCapability.js diff --git a/example/generator/src/SinewaveTelemetryProvider.js b/example/generator/SinewaveTelemetryProvider.js similarity index 100% rename from example/generator/src/SinewaveTelemetryProvider.js rename to example/generator/SinewaveTelemetryProvider.js diff --git a/example/generator/src/SinewaveTelemetrySeries.js b/example/generator/SinewaveTelemetrySeries.js similarity index 100% rename from example/generator/src/SinewaveTelemetrySeries.js rename to example/generator/SinewaveTelemetrySeries.js diff --git a/example/generator/src/WorkerInterface.js b/example/generator/WorkerInterface.js similarity index 100% rename from example/generator/src/WorkerInterface.js rename to example/generator/WorkerInterface.js diff --git a/example/generator/bundle.js b/example/generator/bundle.js deleted file mode 100644 index 8ea0205f31..0000000000 --- a/example/generator/bundle.js +++ /dev/null @@ -1,184 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, 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. - *****************************************************************************/ -/*global define*/ - -define([ - "./src/SinewaveTelemetryProvider", - "./src/SinewaveLimitCapability", - "./src/SinewaveDeltaFormat", - 'legacyRegistry' -], function ( - SinewaveTelemetryProvider, - SinewaveLimitCapability, - SinewaveDeltaFormat, - legacyRegistry -) { - "use strict"; - - legacyRegistry.register("example/generator", { - "name": "Sine Wave Generator", - "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", - "extensions": { - "components": [ - { - "implementation": SinewaveTelemetryProvider, - "type": "provider", - "provides": "telemetryService", - "depends": [ - "$q", - "$timeout" - ] - } - ], - "capabilities": [ - { - "key": "limit", - "implementation": SinewaveLimitCapability - } - ], - "formats": [ - { - "key": "example.delta", - "implementation": SinewaveDeltaFormat - } - ], - "constants": [ - { - "key": "TIME_CONDUCTOR_DOMAINS", - "value": [ - { - "key": "time", - "name": "Time" - }, - { - "key": "yesterday", - "name": "Yesterday" - }, - { - "key": "delta", - "name": "Delta" - } - ], - "priority": -1 - } - ], - "types": [ - { - "key": "generator", - "name": "Sine Wave Generator", - "cssClass": "icon-telemetry", - "description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", - "priority": 10, - "features": "creation", - "model": { - "telemetry": { - "period": 10, - "amplitude": 1, - "offset": 0, - "dataRateInHz": 1 - } - }, - "telemetry": { - "source": "generator", - "domains": [ - { - "key": "utc", - "name": "Time", - "format": "utc" - }, - { - "key": "yesterday", - "name": "Yesterday", - "format": "utc" - }, - { - "key": "delta", - "name": "Delta", - "format": "example.delta" - } - ], - "ranges": [ - { - "key": "sin", - "name": "Sine" - }, - { - "key": "cos", - "name": "Cosine" - } - ] - }, - "properties": [ - { - "name": "Period", - "control": "textfield", - "cssClass": "l-input-sm l-numeric", - "key": "period", - "required": true, - "property": [ - "telemetry", - "period" - ], - "pattern": "^\\d*(\\.\\d*)?$" - }, - { - "name": "Amplitude", - "control": "textfield", - "cssClass": "l-input-sm l-numeric", - "key": "amplitude", - "required": true, - "property": [ - "telemetry", - "amplitude" - ], - "pattern": "^\\d*(\\.\\d*)?$" - }, - { - "name": "Offset", - "control": "textfield", - "cssClass": "l-input-sm l-numeric", - "key": "offset", - "required": true, - "property": [ - "telemetry", - "offset" - ], - "pattern": "^\\d*(\\.\\d*)?$" - }, - { - "name": "Data Rate (hz)", - "control": "textfield", - "cssClass": "l-input-sm l-numeric", - "key": "dataRateInHz", - "required": true, - "property": [ - "telemetry", - "dataRateInHz" - ], - "pattern": "^\\d*(\\.\\d*)?$" - } - ] - } - ] - } - }); -}); diff --git a/example/generator/src/generatorWorker.js b/example/generator/generatorWorker.js similarity index 100% rename from example/generator/src/generatorWorker.js rename to example/generator/generatorWorker.js diff --git a/example/generator/plugin.js b/example/generator/plugin.js new file mode 100644 index 0000000000..fd71a98b43 --- /dev/null +++ b/example/generator/plugin.js @@ -0,0 +1,171 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, 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. + *****************************************************************************/ +/*global define*/ + +define([ + "./GeneratorProvider", + "./SinewaveLimitCapability", + "./SinewaveDeltaFormat" +], function ( + GeneratorProvider, + SinewaveLimitCapability, + SinewaveDeltaFormat +) { + + var legacyExtensions = { + "capabilities": [ + { + "key": "limit", + "implementation": SinewaveLimitCapability + } + ], + "formats": [ + { + "key": "example.delta", + "implementation": SinewaveDeltaFormat + } + ], + "constants": [ + { + "key": "TIME_CONDUCTOR_DOMAINS", + "value": [ + { + "key": "time", + "name": "Time" + }, + { + "key": "yesterday", + "name": "Yesterday" + }, + { + "key": "delta", + "name": "Delta" + } + ], + "priority": -1 + } + ] + } + + return function(openmct){ + //Register legacy extensions for things not yet supported by the new API + Object.keys(legacyExtensions).forEach(function (type){ + var extensionsOfType = legacyExtensions[type]; + extensionsOfType.forEach(function (extension) { + openmct.legacyExtension(type, extension) + }) + }); + openmct.types.addType("generator", { + label: "Sine Wave Generator", + description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.", + cssClass: "icon-telemetry", + creatable: true, + form: [ + { + name: "Period", + control: "textfield", + cssClass: "l-input-sm l-numeric", + key: "period", + required: true, + property: [ + "telemetry", + "period" + ], + pattern: "^\\d*(\\.\\d*)?$" + }, + { + name: "Amplitude", + control: "textfield", + cssClass: "l-input-sm l-numeric", + key: "amplitude", + required: true, + property: [ + "telemetry", + "amplitude" + ], + pattern: "^\\d*(\\.\\d*)?$" + }, + { + name: "Offset", + control: "textfield", + cssClass: "l-input-sm l-numeric", + key: "offset", + required: true, + property: [ + "telemetry", + "offset" + ], + pattern: "^\\d*(\\.\\d*)?$" + }, + { + name: "Data Rate (hz)", + control: "textfield", + cssClass: "l-input-sm l-numeric", + key: "dataRateInHz", + required: true, + property: [ + "telemetry", + "dataRateInHz" + ], + pattern: "^\\d*(\\.\\d*)?$" + } + ], + initialize: function (object) { + object.telemetry = { + period: 10, + amplitude: 1, + offset: 0, + dataRateInHz: 1, + domains: [ + { + key: "utc", + name: "Time", + format: "utc" + }, + { + key: "yesterday", + name: "Yesterday", + format: "utc" + }, + { + key: "delta", + name: "Delta", + format: "example.delta" + } + ], + ranges: [ + { + key: "sin", + name: "Sine" + }, + { + key: "cos", + name: "Cosine" + } + ] + }; + } + }); + openmct.telemetry.addProvider(new GeneratorProvider()); + }; + +}); diff --git a/index.html b/index.html index 7f0a7c8891..6e769611a7 100644 --- a/index.html +++ b/index.html @@ -31,14 +31,14 @@ require(['openmct'], function (openmct) { [ 'example/imagery', - 'example/eventGenerator', - 'example/generator' + 'example/eventGenerator' ].forEach( openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) ); openmct.install(openmct.plugins.MyItems()); openmct.install(openmct.plugins.LocalStorage()); openmct.install(openmct.plugins.Espresso()); + openmct.install(openmct.plugins.Generator()); openmct.start(); }); diff --git a/platform/telemetry/bundle.js b/platform/telemetry/bundle.js index c7e18cc91c..6aaacfd7ff 100644 --- a/platform/telemetry/bundle.js +++ b/platform/telemetry/bundle.js @@ -79,6 +79,7 @@ define([ "key": "telemetry", "implementation": TelemetryCapability, "depends": [ + "openmct", "$injector", "$q", "$log" diff --git a/platform/telemetry/src/TelemetryCapability.js b/platform/telemetry/src/TelemetryCapability.js index 4f3e9e266d..4112b5740b 100644 --- a/platform/telemetry/src/TelemetryCapability.js +++ b/platform/telemetry/src/TelemetryCapability.js @@ -24,8 +24,12 @@ * Module defining TelemetryCapability. Created by vwoeltje on 11/12/14. */ define( - [], - function () { + [ + '../../../src/api/objects/object-utils' + ], + function ( + objectUtils + ) { var ZERO = function () { return 0; @@ -103,7 +107,7 @@ define( * @implements {Capability} * @constructor */ - function TelemetryCapability($injector, $q, $log, domainObject) { + 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 () { @@ -118,7 +122,7 @@ define( } }; - + this.openmct = openmct; this.$q = $q; this.$log = $log; this.domainObject = domainObject; @@ -160,6 +164,20 @@ define( }; + function asSeries(telemetry, defaultDomain, defaultRange) { + return { + getRangeValue: function (index, range) { + return telemetry[index][range || defaultRange]; + }, + getDomainValue: function (index, domain) { + return telemetry[index][domain || defaultDomain]; + }, + getPointCount: function () { + return telemetry.length; + } + }; + } + /** * Request telemetry data for this specific domain object. * @param {TelemetryRequest} [request] parameters for this @@ -169,11 +187,21 @@ define( */ TelemetryCapability.prototype.requestData = function requestTelemetry(request) { // Bring in any defaults from the object model - var fullRequest = this.buildRequest(request || {}), - source = fullRequest.source, - key = fullRequest.key, - telemetryService = this.telemetryService || - this.initializeTelemetryService(); // Lazy initialization + 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] || {}).key; + + var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) === + telemetryAPI.legacyProvider; // Pull out the relevant field from the larger, // structured response. @@ -187,11 +215,17 @@ define( return telemetryService.requestTelemetry([fullRequest]); } - // If a telemetryService is not available, - // getTelemetryService() should reject, and this should - // bubble through subsequent then calls. - return telemetryService && - requestTelemetryFromService().then(getRelevantResponse); + 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); + }); + } }; /** @@ -217,12 +251,26 @@ define( * subscription request */ TelemetryCapability.prototype.subscribe = function subscribe(callback, request) { - var fullRequest = this.buildRequest(request || {}), - telemetryService = this.telemetryService || - this.initializeTelemetryService(); // Lazy initialization + 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] || {}).key; + + var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) === + telemetryAPI.legacyProvider; + + function update(telemetry) { + callback(asSeries([telemetry], defaultDomain, defaultRange)); + } // Unpack the relevant telemetry series - function update(telemetries) { + function updateLegacy(telemetries) { var source = fullRequest.source, key = fullRequest.key, result = ((telemetries || {})[source] || {})[key]; @@ -231,8 +279,13 @@ define( } } - return telemetryService && - telemetryService.subscribe(update, [fullRequest]); + // Avoid a loop here... + if (isLegacyProvider) { + return telemetryService && + telemetryService.subscribe(updateLegacy, [fullRequest]); + } else { + return telemetryAPI.subscribe(domainObject, update, fullRequest); + } }; /** diff --git a/platform/telemetry/test/TelemetryCapabilitySpec.js b/platform/telemetry/test/TelemetryCapabilitySpec.js index 1572bc3e44..04d603d0a2 100644 --- a/platform/telemetry/test/TelemetryCapabilitySpec.js +++ b/platform/telemetry/test/TelemetryCapabilitySpec.js @@ -32,8 +32,9 @@ define( mockTelemetryService, mockReject, mockUnsubscribe, - telemetry; - + telemetry, + mockTelemetryAPI, + mockAPI; function mockPromise(value) { return { @@ -43,6 +44,9 @@ define( }; } + function noop() { + } + beforeEach(function () { mockInjector = jasmine.createSpyObj("$injector", ["get"]); mockQ = jasmine.createSpyObj("$q", ["when", "reject"]); @@ -79,7 +83,25 @@ define( // Bubble up... mockReject.then.andReturn(mockReject); + mockTelemetryAPI = jasmine.createSpyObj("telemetryAPI", [ + "getMetadata", + "subscribe", + "request", + "findRequestProvider", + "findSubscriptionProvider" + ]); + mockTelemetryAPI.getMetadata.andReturn({ + valuesForHints: function () { + return []; + } + }); + + mockAPI = { + telemetry: mockTelemetryAPI + }; + telemetry = new TelemetryCapability( + mockAPI, mockInjector, mockQ, mockLog, @@ -113,7 +135,6 @@ define( key: "testKey", // from model start: 42 // from argument }]); - }); it("provides an empty series when telemetry is missing", function () { @@ -161,6 +182,57 @@ define( expect(mockLog.warn).toHaveBeenCalled(); }); + it("if a new style telemetry source is available, use it", function () { + var mockProvider = {}; + mockTelemetryAPI.findSubscriptionProvider.andReturn(mockProvider); + telemetry.subscribe(noop, {}); + expect(mockTelemetryService.subscribe).not.toHaveBeenCalled(); + expect(mockTelemetryAPI.subscribe).toHaveBeenCalled(); + }); + + it("if a new style telemetry source is not available, revert to old API", function () { + mockTelemetryAPI.findSubscriptionProvider.andReturn(undefined); + telemetry.subscribe(noop, {}); + expect(mockTelemetryAPI.subscribe).not.toHaveBeenCalled(); + expect(mockTelemetryService.subscribe).toHaveBeenCalled(); + }); + + it("Wraps telemetry returned from the new API as a telemetry series", function () { + var returnedTelemetry; + var mockTelemetry = [{ + prop1: "val1", + prop2: "val2", + prop3: "val3" + }, + { + prop1: "val4", + prop2: "val5", + prop3: "val6" + }]; + var mockProvider = {}; + var dunzo = false; + + mockTelemetryAPI.findRequestProvider.andReturn(mockProvider); + mockTelemetryAPI.request.andReturn(Promise.resolve(mockTelemetry)); + + telemetry.requestData({}).then(function (data) { + returnedTelemetry = data; + dunzo = true; + }); + + waitsFor(function () { + return dunzo; + }); + + runs(function () { + expect(returnedTelemetry.getPointCount).toBeDefined(); + expect(returnedTelemetry.getDomainValue).toBeDefined(); + expect(returnedTelemetry.getRangeValue).toBeDefined(); + expect(returnedTelemetry.getPointCount()).toBe(2); + }); + + }); + it("allows subscriptions to updates", function () { var mockCallback = jasmine.createSpy("callback"), subscription = telemetry.subscribe(mockCallback); diff --git a/src/api/telemetry/TelemetryMetadataManager.js b/src/api/telemetry/TelemetryMetadataManager.js index db0e6d3b0a..7780bfcf2f 100644 --- a/src/api/telemetry/TelemetryMetadataManager.js +++ b/src/api/telemetry/TelemetryMetadataManager.js @@ -105,7 +105,6 @@ define([ this.valueMetadatas = this.valueMetadatas.map(applyReasonableDefaults); } - /** * Get value metadata for a single key. */ diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index a974dab2a0..6054278400 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -33,7 +33,6 @@ define([ '../example/export/bundle', '../example/extensions/bundle', '../example/forms/bundle', - '../example/generator/bundle', '../example/identity/bundle', '../example/imagery/bundle', '../example/mobile/bundle', diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index e33fb65f5e..11e5bcfc16 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -21,16 +21,21 @@ *****************************************************************************/ define([ - 'lodash' -], function (_) { + 'lodash', + '../../platform/features/conductor/utcTimeSystem/src/UTCTimeSystem', + '../../example/generator/plugin' +], function ( + _, + UTCTimeSystem, + GeneratorPlugin +) { var bundleMap = { CouchDB: 'platform/persistence/couch', Elasticsearch: 'platform/persistence/elastic', Espresso: 'platform/commonUI/themes/espresso', LocalStorage: 'platform/persistence/local', MyItems: 'platform/features/my-items', - Snow: 'platform/commonUI/themes/snow', - UTCTimeSystem: 'platform/features/conductor/utcTimeSystem' + Snow: 'platform/commonUI/themes/snow' }; var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) { @@ -41,6 +46,15 @@ define([ }; }); + plugins.UTCTimeSystem = function () { + return function (openmct) { + openmct.legacyExtension("timeSystems", { + "implementation": UTCTimeSystem, + "depends": ["$timeout"] + }); + }; + }; + plugins.CouchDB = function (url) { return function (openmct) { if (url) { @@ -85,5 +99,9 @@ define([ }; }; + plugins.Generator = function () { + return GeneratorPlugin; + }; + return plugins; });