Compare commits
22 Commits
karma-debu
...
telemetry-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e69fb17b02 | ||
|
|
72f9a53ba3 | ||
|
|
cf15ff5c07 | ||
|
|
6bbdfcdfbe | ||
|
|
06e93ff520 | ||
|
|
550e7a15e6 | ||
|
|
71c54cd541 | ||
|
|
e81b8e53dc | ||
|
|
84e6928f54 | ||
|
|
ba688fe62c | ||
|
|
c533e10352 | ||
|
|
04f47b3db6 | ||
|
|
717fa5edf4 | ||
|
|
14f5f048fb | ||
|
|
72929500d3 | ||
|
|
471adde923 | ||
|
|
6c5d5f3d00 | ||
|
|
2262fef29b | ||
|
|
bda30f1475 | ||
|
|
bfec434369 | ||
|
|
14df350994 | ||
|
|
80582f5e8d |
@@ -17,7 +17,7 @@
|
||||
"screenfull": "^3.0.0",
|
||||
"node-uuid": "^1.4.7",
|
||||
"comma-separated-values": "^3.6.4",
|
||||
"FileSaver.js": "^0.0.2",
|
||||
"file-saver": "^1.3.3",
|
||||
"zepto": "^1.1.6",
|
||||
"eventemitter3": "^1.2.0",
|
||||
"lodash": "3.10.1",
|
||||
|
||||
@@ -33,7 +33,7 @@ requirejs.config({
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
"saveAs": "bower_components/file-saver/FileSaver.min",
|
||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||
"text": "bower_components/text/text",
|
||||
"uuid": "bower_components/node-uuid/uuid",
|
||||
|
||||
@@ -101,10 +101,15 @@ define(
|
||||
*/
|
||||
EditorCapability.prototype.finish = function () {
|
||||
var domainObject = this.domainObject;
|
||||
return this.transactionService.cancel().then(function () {
|
||||
domainObject.getCapability("status").set("editing", false);
|
||||
return domainObject;
|
||||
});
|
||||
|
||||
if (this.transactionService.isActive()) {
|
||||
return this.transactionService.cancel().then(function () {
|
||||
domainObject.getCapability("status").set("editing", false);
|
||||
return domainObject;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(domainObject);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,6 +62,7 @@ define(
|
||||
);
|
||||
mockTransactionService.commit.andReturn(fastPromise());
|
||||
mockTransactionService.cancel.andReturn(fastPromise());
|
||||
mockTransactionService.isActive = jasmine.createSpy('isActive');
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
@@ -141,6 +142,7 @@ define(
|
||||
|
||||
describe("finish", function () {
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.andReturn(true);
|
||||
capability.edit();
|
||||
capability.finish();
|
||||
});
|
||||
@@ -152,6 +154,23 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
describe("finish", function () {
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.andReturn(false);
|
||||
capability.edit();
|
||||
});
|
||||
|
||||
it("does not cancel transaction when transaction is not active", function () {
|
||||
capability.finish();
|
||||
expect(mockTransactionService.cancel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns a promise", function () {
|
||||
expect(capability.finish() instanceof Promise).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("dirty", function () {
|
||||
var model = {};
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ define([
|
||||
};
|
||||
|
||||
UTCTimeFormat.prototype.parse = function (text) {
|
||||
if (typeof text === 'number') {
|
||||
return text;
|
||||
}
|
||||
return moment.utc(text, DATE_FORMATS).valueOf();
|
||||
};
|
||||
|
||||
|
||||
@@ -255,6 +255,8 @@ define(
|
||||
if (this.nextDatum) {
|
||||
this.updateValues(this.nextDatum);
|
||||
delete this.nextDatum;
|
||||
} else {
|
||||
this.updateValues(this.$scope.imageHistory[this.$scope.imageHistory.length - 1]);
|
||||
}
|
||||
this.autoScroll = true;
|
||||
}
|
||||
|
||||
@@ -183,6 +183,17 @@ define(
|
||||
expect(controller.getImageUrl()).toEqual(newUrl);
|
||||
});
|
||||
|
||||
it("forwards large image view to latest image in history on un-pause", function () {
|
||||
$scope.imageHistory = [
|
||||
{ utc: 1434600258122, url: 'some/url1', selected: false},
|
||||
{ utc: 1434600258123, url: 'some/url2', selected: false}
|
||||
];
|
||||
controller.paused(true);
|
||||
controller.paused(false);
|
||||
|
||||
expect(controller.getImageUrl()).toEqual(controller.getImageUrl($scope.imageHistory[1]));
|
||||
});
|
||||
|
||||
it("subscribes to telemetry", function () {
|
||||
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
|
||||
newDomainObject,
|
||||
@@ -227,7 +238,7 @@ define(
|
||||
expect(controller.updateHistory(mockDatum)).toBe(false);
|
||||
});
|
||||
|
||||
describe("user clicks on imagery thumbnail", function () {
|
||||
describe("when user clicks on imagery thumbnail", function () {
|
||||
var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false};
|
||||
|
||||
it("pauses and adds selected class to imagery thumbnail", function () {
|
||||
@@ -248,6 +259,7 @@ define(
|
||||
expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("initially shows an empty string for date/time", function () {
|
||||
|
||||
@@ -360,22 +360,47 @@ define(
|
||||
*/
|
||||
FixedController.prototype.updateView = function (telemetryObject, datum) {
|
||||
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||
var rangeMetadata = metadata.valuesForHints(['range'])[0];
|
||||
var rangeKey = rangeMetadata.source || rangeMetadata.key;
|
||||
var valueMetadata = metadata.value(rangeKey);
|
||||
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
|
||||
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
|
||||
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
|
||||
var value = datum[valueMetadata.key];
|
||||
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, rangeKey);
|
||||
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
|
||||
|
||||
this.setDisplayedValue(
|
||||
telemetryObject,
|
||||
formatter.format(value),
|
||||
formattedTelemetryValue,
|
||||
alarm && alarm.cssClass
|
||||
);
|
||||
this.digest();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
|
||||
var valueMetadata = metadata.value(telemetryKeyToDisplay);
|
||||
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
|
||||
|
||||
return formatter.format(datum[valueMetadata.key]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
|
||||
// If there is a range value, show that preferentially
|
||||
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
|
||||
|
||||
// If no range is defined, default to the highest priority non time-domain data.
|
||||
if (telemetryKeyToDisplay === undefined) {
|
||||
var valuesOrderedByPriority = metadata.values();
|
||||
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
|
||||
return !(valueMetadata.hints.domain);
|
||||
})[0];
|
||||
}
|
||||
|
||||
return telemetryKeyToDisplay.source;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request the last historical data point for the given domain objects
|
||||
* @param {object[]} objects
|
||||
@@ -388,7 +413,9 @@ define(
|
||||
objects.forEach(function (object) {
|
||||
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
|
||||
.then(function (data) {
|
||||
self.updateView(object, data[data.length - 1]);
|
||||
if (data.length > 0) {
|
||||
self.updateView(object, data[data.length - 1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
return objects;
|
||||
|
||||
@@ -127,9 +127,7 @@ define(
|
||||
if (self.droppedIdToSelectAfterRefresh) {
|
||||
self.select(null, self.droppedIdToSelectAfterRefresh);
|
||||
delete self.droppedIdToSelectAfterRefresh;
|
||||
}
|
||||
|
||||
if (composition.indexOf(self.selectedId) === -1) {
|
||||
} else if (composition.indexOf(self.selectedId) === -1) {
|
||||
self.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ define(
|
||||
Promise.resolve(mockChildren)
|
||||
);
|
||||
|
||||
|
||||
mockScope.model = testModel;
|
||||
mockScope.configuration = testConfiguration;
|
||||
mockScope.selection = jasmine.createSpyObj(
|
||||
@@ -194,7 +193,8 @@ define(
|
||||
|
||||
mockMetadata = jasmine.createSpyObj('mockMetadata', [
|
||||
'valuesForHints',
|
||||
'value'
|
||||
'value',
|
||||
'values'
|
||||
]);
|
||||
mockMetadata.value.andReturn({
|
||||
key: 'value'
|
||||
@@ -653,6 +653,39 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("selects an range value to display, if available", function () {
|
||||
mockMetadata.valuesForHints.andReturn([
|
||||
{
|
||||
key: 'range',
|
||||
source: 'range'
|
||||
}
|
||||
]);
|
||||
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
|
||||
expect(key).toEqual('range');
|
||||
});
|
||||
|
||||
it("selects the first non-domain value to display, if no range available", function () {
|
||||
mockMetadata.valuesForHints.andReturn([]);
|
||||
mockMetadata.values.andReturn([
|
||||
{
|
||||
key: 'domain',
|
||||
source: 'domain',
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
source: 'image',
|
||||
hints: {
|
||||
image: 1
|
||||
}
|
||||
}
|
||||
]);
|
||||
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
|
||||
expect(key).toEqual('image');
|
||||
});
|
||||
|
||||
it("reflects limit status", function () {
|
||||
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
|
||||
controller.updateView(mockTelemetryObject, [{
|
||||
|
||||
@@ -170,6 +170,9 @@ define(
|
||||
* @param rows
|
||||
*/
|
||||
TelemetryTableController.prototype.addRowsToTable = function (rows) {
|
||||
rows.forEach(function (row) {
|
||||
this.$scope.rows.push(row);
|
||||
}, this);
|
||||
this.$scope.$broadcast('add:rows', rows);
|
||||
};
|
||||
|
||||
|
||||
@@ -436,5 +436,28 @@ define(
|
||||
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
|
||||
});
|
||||
|
||||
describe('when telemetry is added', function () {
|
||||
var testRows;
|
||||
var expectedRows;
|
||||
|
||||
beforeEach(function () {
|
||||
testRows = [{ a: 0 }, { a: 1 }, { a: 2 }];
|
||||
mockScope.rows = [{ a: -1 }];
|
||||
expectedRows = mockScope.rows.concat(testRows);
|
||||
|
||||
spyOn(controller.telemetry, "on").andCallThrough();
|
||||
controller.registerChangeListeners();
|
||||
|
||||
controller.telemetry.on.calls.forEach(function (call) {
|
||||
if (call.args[0] === 'added') {
|
||||
call.args[1](testRows);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("adds it to rows in scope", function () {
|
||||
expect(mockScope.rows).toEqual(expectedRows);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ define([
|
||||
"./src/capabilities/CostCapability",
|
||||
"./src/directives/MCTSwimlaneDrop",
|
||||
"./src/directives/MCTSwimlaneDrag",
|
||||
"./src/directives/MCTResourceGraphDrop",
|
||||
"./src/services/ObjectLoader",
|
||||
"./src/chart/MCTTimelineChart",
|
||||
"text!./res/templates/values.html",
|
||||
@@ -69,6 +70,7 @@ define([
|
||||
CostCapability,
|
||||
MCTSwimlaneDrop,
|
||||
MCTSwimlaneDrag,
|
||||
MCTResourceGraphDrop,
|
||||
ObjectLoader,
|
||||
MCTTimelineChart,
|
||||
valuesTemplate,
|
||||
@@ -577,6 +579,13 @@ define([
|
||||
"$interval",
|
||||
"$log"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "mctResourceGraphDrop",
|
||||
"implementation": MCTResourceGraphDrop,
|
||||
"depends": [
|
||||
"dndService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
.l-timeline-pane {
|
||||
@include absPosDefault();
|
||||
|
||||
&.drop-over {
|
||||
background-color: lighten($colorEditAreaBg, 5%);
|
||||
}
|
||||
|
||||
.l-width-control {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
<mct-splitter></mct-splitter>
|
||||
|
||||
<!-- BOTTOM PANE RESOURCE LEGEND -->
|
||||
<div class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
|
||||
<div mct-resource-graph-drop
|
||||
class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
|
||||
<div class="l-title s-title">{{ngModel.title}}Resource Graph Legend</div>
|
||||
<div class="l-legend-items legend">
|
||||
<mct-include key="'timeline-legend-item'"
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./SwimlaneDragConstants'],
|
||||
function (SwimlaneDragConstants) {
|
||||
|
||||
/**
|
||||
* Defines the `mct-resource-graph-drop` directive. When a drop occurs
|
||||
* on an element with this attribute, the swimlane targeted by the drop
|
||||
* will receive the dropped domain object (at which point it can handle
|
||||
* the drop, typically by toggling the swimlane graph.)
|
||||
* @param {DndService} dndService drag-and-drop service
|
||||
*/
|
||||
function MCTResourceGraphDrop(dndService) {
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
// Handle dragover
|
||||
element.on('dragover', function (e) {
|
||||
var swimlane = dndService.getData(
|
||||
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
|
||||
);
|
||||
|
||||
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
|
||||
element.addClass('drop-over');
|
||||
scope.$apply();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
// Handle drops
|
||||
element.on('drop', function (e) {
|
||||
var swimlane = dndService.getData(
|
||||
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
|
||||
);
|
||||
|
||||
element.removeClass('drop-over');
|
||||
|
||||
// Only toggle if the graph isn't already set
|
||||
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
|
||||
swimlane.toggleGraph();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
// Clear highlights when drag leaves this swimlane
|
||||
element.on('dragleave', function (e) {
|
||||
element.removeClass('drop-over');
|
||||
scope.$apply();
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
// Applies to attributes
|
||||
restrict: "A",
|
||||
// Link using above function
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTResourceGraphDrop;
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,159 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['../../src/directives/MCTResourceGraphDrop', '../../src/directives/SwimlaneDragConstants'],
|
||||
function (MCTResourceGraphDrop, SwimlaneDragConstants) {
|
||||
|
||||
describe("The mct-resource-graph-drop directive", function () {
|
||||
var mockDndService,
|
||||
mockScope,
|
||||
mockElement,
|
||||
testAttrs,
|
||||
mockSwimlane,
|
||||
testEvent,
|
||||
handlers,
|
||||
directive;
|
||||
|
||||
beforeEach(function () {
|
||||
handlers = {};
|
||||
|
||||
mockDndService = jasmine.createSpyObj(
|
||||
'dndService',
|
||||
['setData', 'getData', 'removeData']
|
||||
);
|
||||
mockScope = jasmine.createSpyObj('$scope', ['$eval', '$apply']);
|
||||
mockElement = jasmine.createSpyObj('element', ['on', 'addClass', 'removeClass']);
|
||||
testAttrs = { mctSwimlaneDrop: "mockSwimlane" };
|
||||
mockSwimlane = jasmine.createSpyObj(
|
||||
"swimlane",
|
||||
['graph', 'toggleGraph']
|
||||
);
|
||||
|
||||
testEvent = {
|
||||
dataTransfer: { getData: jasmine.createSpy() },
|
||||
preventDefault: jasmine.createSpy(),
|
||||
stopPropagation: jasmine.createSpy()
|
||||
};
|
||||
|
||||
testEvent.dataTransfer.getData.andReturn('abc');
|
||||
mockDndService.getData.andCallFake(function (key) {
|
||||
return key === SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ?
|
||||
mockSwimlane : undefined;
|
||||
});
|
||||
|
||||
mockSwimlane.graph.andReturn(false);
|
||||
|
||||
directive = new MCTResourceGraphDrop(mockDndService);
|
||||
directive.link(mockScope, mockElement, testAttrs);
|
||||
|
||||
mockElement.on.calls.forEach(function (call) {
|
||||
handlers[call.args[0]] = call.args[1];
|
||||
});
|
||||
});
|
||||
|
||||
it("is available as an attribute", function () {
|
||||
expect(directive.restrict).toEqual("A");
|
||||
});
|
||||
|
||||
[false, true].forEach(function (graphing) {
|
||||
describe("when swimlane graph is " + (graphing ? "" : "not ") + "enabled", function () {
|
||||
beforeEach(function () {
|
||||
mockSwimlane.graph.andReturn(graphing);
|
||||
});
|
||||
|
||||
|
||||
describe("on dragover", function () {
|
||||
var prefix = !graphing ? "does" : "does not";
|
||||
|
||||
beforeEach(function () {
|
||||
handlers.dragover(testEvent);
|
||||
});
|
||||
|
||||
it(prefix + " add a drop-over class", function () {
|
||||
var expectAddClass = expect(mockElement.addClass);
|
||||
(!graphing ? expectAddClass : expectAddClass.not)
|
||||
.toHaveBeenCalledWith('drop-over');
|
||||
});
|
||||
|
||||
it(prefix + " call $apply on scope", function () {
|
||||
var expectApply = expect(mockScope.$apply);
|
||||
(!graphing ? expectApply : expectApply.not)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(prefix + " prevent default", function () {
|
||||
var expectPreventDefault = expect(testEvent.preventDefault);
|
||||
(!graphing ? expectPreventDefault : expectPreventDefault.not)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("on drop", function () {
|
||||
var prefix = !graphing ? "does" : "does not";
|
||||
|
||||
beforeEach(function () {
|
||||
handlers.drop(testEvent);
|
||||
});
|
||||
|
||||
it("removes any drop-over class", function () {
|
||||
expect(mockElement.removeClass)
|
||||
.toHaveBeenCalledWith('drop-over');
|
||||
});
|
||||
|
||||
it(prefix + " toggle the swimlane's resource graph", function () {
|
||||
var expectToggle = expect(mockSwimlane.toggleGraph);
|
||||
(!graphing ? expectToggle : expectToggle.not)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(prefix + " prevent default", function () {
|
||||
var expectPreventDefault = expect(testEvent.preventDefault);
|
||||
(!graphing ? expectPreventDefault : expectPreventDefault.not)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("on dragleave", function () {
|
||||
beforeEach(function () {
|
||||
handlers.dragleave(testEvent);
|
||||
});
|
||||
|
||||
it("removes any drop-over class", function () {
|
||||
expect(mockElement.removeClass)
|
||||
.toHaveBeenCalledWith('drop-over');
|
||||
});
|
||||
|
||||
it("calls $apply on scope", function () {
|
||||
expect(mockScope.$apply).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls preventDefault on events", function () {
|
||||
expect(testEvent.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -50,11 +50,18 @@ define([
|
||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fallback provider, this is an internal API for legacy reasons.
|
||||
* @private
|
||||
*/
|
||||
ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) {
|
||||
this.fallbackProvider = p;
|
||||
};
|
||||
|
||||
// Retrieve the provider for a given key.
|
||||
/**
|
||||
* Retrieve the provider for a given identifier.
|
||||
* @private
|
||||
*/
|
||||
ObjectAPI.prototype.getProvider = function (identifier) {
|
||||
if (identifier.key === 'ROOT') {
|
||||
return this.rootProvider;
|
||||
@@ -135,27 +142,28 @@ define([
|
||||
* @returns {Promise} a promise which will resolve when the domain object
|
||||
* has been saved, or be rejected if it cannot be saved
|
||||
*/
|
||||
ObjectAPI.prototype.get = function (identifier) {
|
||||
identifier = utils.parseKeyString(identifier);
|
||||
var provider = this.getProvider(identifier);
|
||||
|
||||
[
|
||||
'save',
|
||||
'delete',
|
||||
'get'
|
||||
].forEach(function (method) {
|
||||
ObjectAPI.prototype[method] = function () {
|
||||
var identifier = arguments[0],
|
||||
provider = this.getProvider(identifier);
|
||||
if (!provider) {
|
||||
throw new Error('No Provider Matched');
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
throw new Error('No Provider Matched');
|
||||
}
|
||||
if (!provider.get) {
|
||||
throw new Error('Provider does not support get!');
|
||||
}
|
||||
|
||||
if (!provider[method]) {
|
||||
throw new Error('Provider does not support [' + method + '].');
|
||||
}
|
||||
return provider.get(identifier);
|
||||
};
|
||||
|
||||
return provider[method].apply(provider, arguments);
|
||||
};
|
||||
});
|
||||
ObjectAPI.prototype.delete = function () {
|
||||
throw new Error('Delete not implemented');
|
||||
};
|
||||
|
||||
ObjectAPI.prototype.save = function () {
|
||||
throw new Error('Save not implemented');
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a root-level object.
|
||||
|
||||
@@ -27,7 +27,8 @@ define([
|
||||
'../../platform/features/autoflow/plugin',
|
||||
'./timeConductor/plugin',
|
||||
'../../example/imagery/plugin',
|
||||
'../../platform/import-export/bundle'
|
||||
'../../platform/import-export/bundle',
|
||||
'./telemetryMean/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
@@ -35,7 +36,8 @@ define([
|
||||
AutoflowPlugin,
|
||||
TimeConductorPlugin,
|
||||
ExampleImagery,
|
||||
ImportExport
|
||||
ImportExport,
|
||||
TelemetryMean
|
||||
) {
|
||||
var bundleMap = {
|
||||
CouchDB: 'platform/persistence/couch',
|
||||
@@ -120,6 +122,7 @@ define([
|
||||
};
|
||||
|
||||
plugins.ExampleImagery = ExampleImagery;
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
76
src/plugins/telemetryMean/plugin.js
Executable file
76
src/plugins/telemetryMean/plugin.js
Executable file
@@ -0,0 +1,76 @@
|
||||
/*****************************************************************************
|
||||
* 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(['./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;
|
||||
});
|
||||
125
src/plugins/telemetryMean/src/MeanTelemetryProvider.js
Normal file
125
src/plugins/telemetryMean/src/MeanTelemetryProvider.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*jshint latedef: nofunc */
|
||||
/*global console */
|
||||
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))
|
||||
.catch(logError);
|
||||
|
||||
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 avgDatum = telemetryAverager.createAverageDatum(datum);
|
||||
|
||||
if (telemetryAverager.sampleCount() === samples) {
|
||||
averageData.push(avgDatum);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return averageData;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MeanTelemetryProvider.prototype.getLinkedObject = function (domainObject) {
|
||||
var objectId = objectUtils.parseKeyString(domainObject.telemetryPoint);
|
||||
return this.objectAPI.get(objectId);
|
||||
};
|
||||
|
||||
function logError(error) {
|
||||
if (error.stack) {
|
||||
console.error(error.stack);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return MeanTelemetryProvider;
|
||||
});
|
||||
460
src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js
Normal file
460
src/plugins/telemetryMean/src/MeanTelemetryProviderSpec.js
Normal file
@@ -0,0 +1,460 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*jshint latedef: nofunc */
|
||||
define([
|
||||
"./MeanTelemetryProvider",
|
||||
"./MockTelemetryApi"
|
||||
], function (
|
||||
MeanTelemetryProvider,
|
||||
MockTelemetryApi
|
||||
) {
|
||||
var RANGE_KEY = 'value';
|
||||
|
||||
describe("The Mean Telemetry Provider", function () {
|
||||
var mockApi;
|
||||
var meanTelemetryProvider;
|
||||
var outstandingPromises = 0;
|
||||
var mockDomainObject;
|
||||
var associatedObject;
|
||||
|
||||
beforeEach(function () {
|
||||
createMockApi();
|
||||
setTimeSystemTo('utc');
|
||||
createMockObjects();
|
||||
meanTelemetryProvider = new MeanTelemetryProvider(mockApi);
|
||||
});
|
||||
|
||||
it("supports telemetry-mean objects only", function () {
|
||||
var mockTelemetryMeanObject = mockObjectWithType('telemetry-mean');
|
||||
var mockOtherObject = mockObjectWithType('other');
|
||||
|
||||
expect(meanTelemetryProvider.canProvideTelemetry(mockTelemetryMeanObject)).toBe(true);
|
||||
expect(meanTelemetryProvider.canProvideTelemetry(mockOtherObject)).toBe(false);
|
||||
});
|
||||
|
||||
describe("the subscribe function", function () {
|
||||
var subscriptionCallback;
|
||||
|
||||
beforeEach(function () {
|
||||
subscriptionCallback = jasmine.createSpy('subscriptionCallback');
|
||||
});
|
||||
|
||||
it("subscribes to telemetry for the associated object", function () {
|
||||
meanTelemetryProvider.subscribe(mockDomainObject);
|
||||
|
||||
expectObjectWasSubscribedTo(associatedObject);
|
||||
});
|
||||
|
||||
it("returns a function that unsubscribes from the associated object", function () {
|
||||
var unsubscribe = meanTelemetryProvider.subscribe(mockDomainObject);
|
||||
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(unsubscribe);
|
||||
|
||||
expectUnsubscribeFrom(associatedObject);
|
||||
});
|
||||
|
||||
it("returns an average only when the sample size is reached", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313}
|
||||
];
|
||||
|
||||
setSampleSize(5);
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectNoAverageForTelemetry(inputTelemetry);
|
||||
});
|
||||
|
||||
it("correctly averages a sample of five values", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231}
|
||||
];
|
||||
var expectedAverages = [{
|
||||
'utc': 5, 'value': 222.44888
|
||||
}];
|
||||
|
||||
setSampleSize(5);
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
|
||||
});
|
||||
|
||||
it("correctly averages a sample of ten values", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231},
|
||||
{'utc': 6, 'defaultRange': 2323.12},
|
||||
{'utc': 7, 'defaultRange': 532.12},
|
||||
{'utc': 8, 'defaultRange': 453.543},
|
||||
{'utc': 9, 'defaultRange': 89.2111},
|
||||
{'utc': 10, 'defaultRange': 0.543}
|
||||
];
|
||||
var expectedAverages = [{
|
||||
'utc': 10, 'value': 451.07815
|
||||
}];
|
||||
|
||||
setSampleSize(10);
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
|
||||
});
|
||||
|
||||
it("only averages values within its sample window", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231},
|
||||
{'utc': 6, 'defaultRange': 2323.12},
|
||||
{'utc': 7, 'defaultRange': 532.12},
|
||||
{'utc': 8, 'defaultRange': 453.543},
|
||||
{'utc': 9, 'defaultRange': 89.2111},
|
||||
{'utc': 10, 'defaultRange': 0.543}
|
||||
];
|
||||
var expectedAverages = [
|
||||
{'utc': 5, 'value': 222.44888},
|
||||
{'utc': 6, 'value': 662.4482599999999},
|
||||
{'utc': 7, 'value': 704.6078},
|
||||
{'utc': 8, 'value': 773.02748},
|
||||
{'utc': 9, 'value': 679.8234399999999},
|
||||
{'utc': 10, 'value': 679.70742}
|
||||
];
|
||||
|
||||
setSampleSize(5);
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(expectedAverages, inputTelemetry);
|
||||
});
|
||||
describe("given telemetry input with range values", function () {
|
||||
var inputTelemetry;
|
||||
|
||||
beforeEach(function () {
|
||||
inputTelemetry = [{
|
||||
'utc': 1,
|
||||
'rangeKey': 5678,
|
||||
'otherKey': 9999
|
||||
}];
|
||||
setSampleSize(1);
|
||||
});
|
||||
it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () {
|
||||
var averageTelemetryForRangeKey = [{
|
||||
'utc': 1,
|
||||
'value': 5678
|
||||
}];
|
||||
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
mockApi.telemetry.setDefaultRangeTo('rangeKey');
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(averageTelemetryForRangeKey, inputTelemetry);
|
||||
});
|
||||
|
||||
it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () {
|
||||
var averageTelemetryForOtherKey = [{
|
||||
'utc': 1,
|
||||
'value': 9999
|
||||
}];
|
||||
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
mockApi.telemetry.setDefaultRangeTo('otherKey');
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(averageTelemetryForOtherKey, inputTelemetry);
|
||||
|
||||
});
|
||||
});
|
||||
describe("given telemetry input with range values", function () {
|
||||
var inputTelemetry;
|
||||
|
||||
beforeEach(function () {
|
||||
inputTelemetry = [{
|
||||
'utc': 1,
|
||||
'rangeKey': 5678,
|
||||
'otherKey': 9999
|
||||
}];
|
||||
setSampleSize(1);
|
||||
});
|
||||
it("uses the 'rangeKey' input range, when it is the default, to calculate the average", function () {
|
||||
var averageTelemetryForRangeKey = [{
|
||||
'utc': 1,
|
||||
'value': 5678
|
||||
}];
|
||||
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
mockApi.telemetry.setDefaultRangeTo('rangeKey');
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(averageTelemetryForRangeKey, inputTelemetry);
|
||||
});
|
||||
|
||||
it("uses the 'otherKey' input range, when it is the default, to calculate the average", function () {
|
||||
var averageTelemetryForOtherKey = [{
|
||||
'utc': 1,
|
||||
'value': 9999
|
||||
}];
|
||||
|
||||
meanTelemetryProvider.subscribe(mockDomainObject, subscriptionCallback);
|
||||
mockApi.telemetry.setDefaultRangeTo('otherKey');
|
||||
feedInputTelemetry(inputTelemetry);
|
||||
|
||||
expectAveragesForTelemetry(averageTelemetryForOtherKey, inputTelemetry);
|
||||
});
|
||||
});
|
||||
|
||||
function feedInputTelemetry(inputTelemetry) {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
inputTelemetry.forEach(mockApi.telemetry.mockReceiveTelemetry);
|
||||
});
|
||||
}
|
||||
|
||||
function expectNoAverageForTelemetry(inputTelemetry) {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
expect(subscriptionCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
function expectAveragesForTelemetry(expectedAverages) {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
expectedAverages.forEach(function (averageDatum) {
|
||||
expect(subscriptionCallback).toHaveBeenCalledWith(averageDatum);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function expectObjectWasSubscribedTo(object) {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
expect(mockApi.telemetry.subscribe).toHaveBeenCalledWith(object, jasmine.any(Function));
|
||||
});
|
||||
}
|
||||
|
||||
function expectUnsubscribeFrom() {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
expect(mockApi.telemetry.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
describe("the request function", function () {
|
||||
|
||||
it("requests telemetry for the associated object", function () {
|
||||
meanTelemetryProvider.request(mockDomainObject);
|
||||
|
||||
expectTelemetryToBeRequestedFor(associatedObject);
|
||||
});
|
||||
|
||||
it("returns an average only when the sample size is reached", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313}
|
||||
];
|
||||
var promiseForAverage;
|
||||
|
||||
setSampleSize(5);
|
||||
whenTelemetryRequestedReturn(inputTelemetry);
|
||||
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
|
||||
|
||||
expectEmptyResponse(promiseForAverage);
|
||||
});
|
||||
|
||||
it("correctly averages a sample of five values", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231}
|
||||
];
|
||||
var promiseForAverage;
|
||||
|
||||
setSampleSize(5);
|
||||
whenTelemetryRequestedReturn(inputTelemetry);
|
||||
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
|
||||
|
||||
expectAverageToBe(222.44888, promiseForAverage);
|
||||
});
|
||||
|
||||
it("correctly averages a sample of ten values", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231},
|
||||
{'utc': 6, 'defaultRange': 2323.12},
|
||||
{'utc': 7, 'defaultRange': 532.12},
|
||||
{'utc': 8, 'defaultRange': 453.543},
|
||||
{'utc': 9, 'defaultRange': 89.2111},
|
||||
{'utc': 10, 'defaultRange': 0.543}
|
||||
];
|
||||
var promiseForAverage;
|
||||
|
||||
setSampleSize(10);
|
||||
whenTelemetryRequestedReturn(inputTelemetry);
|
||||
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
|
||||
|
||||
expectAverageToBe(451.07815, promiseForAverage);
|
||||
});
|
||||
|
||||
it("only averages values within its sample window", function () {
|
||||
var inputTelemetry = [
|
||||
{'utc': 1, 'defaultRange': 123.1231},
|
||||
{'utc': 2, 'defaultRange': 321.3223},
|
||||
{'utc': 3, 'defaultRange': 111.4446},
|
||||
{'utc': 4, 'defaultRange': 555.2313},
|
||||
{'utc': 5, 'defaultRange': 1.1231},
|
||||
{'utc': 6, 'defaultRange': 2323.12},
|
||||
{'utc': 7, 'defaultRange': 532.12},
|
||||
{'utc': 8, 'defaultRange': 453.543},
|
||||
{'utc': 9, 'defaultRange': 89.2111},
|
||||
{'utc': 10, 'defaultRange': 0.543}
|
||||
];
|
||||
var promiseForAverage;
|
||||
|
||||
setSampleSize(5);
|
||||
whenTelemetryRequestedReturn(inputTelemetry);
|
||||
promiseForAverage = meanTelemetryProvider.request(mockDomainObject);
|
||||
|
||||
expectAverageToBe(679.70742, promiseForAverage);
|
||||
});
|
||||
|
||||
function expectAverageToBe(expectedValue, promiseForAverage) {
|
||||
var averageData;
|
||||
|
||||
promiseForAverage.then(function (data) {
|
||||
averageData = data;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return averageData !== undefined;
|
||||
}, 'data to return from request', 1);
|
||||
runs(function () {
|
||||
var averageDatum = averageData[averageData.length - 1];
|
||||
expect(averageDatum[RANGE_KEY]).toBe(expectedValue);
|
||||
});
|
||||
}
|
||||
|
||||
function expectEmptyResponse(promiseForAverage) {
|
||||
var averageData;
|
||||
|
||||
promiseForAverage.then(function (data) {
|
||||
averageData = data;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return averageData !== undefined;
|
||||
}, 'data to return from request', 1);
|
||||
runs(function () {
|
||||
expect(averageData.length).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
function whenTelemetryRequestedReturn(telemetry) {
|
||||
mockApi.telemetry.request.andReturn(resolvePromiseWith(telemetry));
|
||||
}
|
||||
|
||||
function expectTelemetryToBeRequestedFor(object) {
|
||||
waitsFor(allPromisesToBeResolved);
|
||||
runs(function () {
|
||||
expect(mockApi.telemetry.request).toHaveBeenCalledWith(object, undefined);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function createMockObjects() {
|
||||
mockDomainObject = {
|
||||
telemetryPoint: 'someTelemetryPoint'
|
||||
};
|
||||
associatedObject = {};
|
||||
mockApi.objects.get.andReturn(resolvePromiseWith(associatedObject));
|
||||
}
|
||||
|
||||
function setSampleSize(sampleSize) {
|
||||
mockDomainObject.samples = sampleSize;
|
||||
}
|
||||
|
||||
function createMockApi() {
|
||||
mockApi = {
|
||||
telemetry: new MockTelemetryApi(),
|
||||
objects: createMockObjectApi(),
|
||||
time: createMockTimeApi()
|
||||
};
|
||||
}
|
||||
|
||||
function createMockObjectApi() {
|
||||
return jasmine.createSpyObj('ObjectAPI', [
|
||||
'get'
|
||||
]);
|
||||
}
|
||||
|
||||
function mockObjectWithType(type) {
|
||||
return {
|
||||
type: type
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePromiseWith(value) {
|
||||
outstandingPromises++;
|
||||
return Promise.resolve(value).then(function () {
|
||||
outstandingPromises--;
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
function allPromisesToBeResolved() {
|
||||
return outstandingPromises === 0;
|
||||
}
|
||||
|
||||
function createMockTimeApi() {
|
||||
return jasmine.createSpyObj("timeApi", ['timeSystem']);
|
||||
}
|
||||
|
||||
function setTimeSystemTo(timeSystemKey) {
|
||||
mockApi.time.timeSystem.andReturn({
|
||||
key: timeSystemKey
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
106
src/plugins/telemetryMean/src/MockTelemetryApi.js
Normal file
106
src/plugins/telemetryMean/src/MockTelemetryApi.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global jasmine, spyOn */
|
||||
define([], function () {
|
||||
|
||||
function MockTelemetryApi() {
|
||||
this.createSpy('subscribe');
|
||||
this.createSpy('getMetadata');
|
||||
|
||||
this.metadata = this.createMockMetadata();
|
||||
this.setDefaultRangeTo('defaultRange');
|
||||
this.unsubscribe = jasmine.createSpy('unsubscribe');
|
||||
this.mockReceiveTelemetry = this.mockReceiveTelemetry.bind(this);
|
||||
}
|
||||
|
||||
MockTelemetryApi.prototype.subscribe = function () {
|
||||
return this.unsubscribe;
|
||||
};
|
||||
|
||||
MockTelemetryApi.prototype.getMetadata = function (object) {
|
||||
return this.metadata;
|
||||
};
|
||||
|
||||
MockTelemetryApi.prototype.request = jasmine.createSpy('request');
|
||||
|
||||
MockTelemetryApi.prototype.getValueFormatter = function (valueMetadata) {
|
||||
var mockValueFormatter = jasmine.createSpyObj("valueFormatter", [
|
||||
"parse"
|
||||
]);
|
||||
|
||||
mockValueFormatter.parse.andCallFake(function (value) {
|
||||
return value[valueMetadata.key];
|
||||
});
|
||||
|
||||
return mockValueFormatter;
|
||||
};
|
||||
|
||||
MockTelemetryApi.prototype.mockReceiveTelemetry = function (newTelemetryDatum) {
|
||||
var subscriptionCallback = this.subscribe.mostRecentCall.args[1];
|
||||
subscriptionCallback(newTelemetryDatum);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MockTelemetryApi.prototype.onRequestReturn = function (telemetryData) {
|
||||
this.requestTelemetry = telemetryData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MockTelemetryApi.prototype.setDefaultRangeTo = function (rangeKey) {
|
||||
var mockMetadataValue = {
|
||||
key: rangeKey
|
||||
};
|
||||
this.metadata.valuesForHints.andReturn([mockMetadataValue]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MockTelemetryApi.prototype.createMockMetadata = function () {
|
||||
var mockMetadata = jasmine.createSpyObj("metadata", [
|
||||
'value',
|
||||
'valuesForHints'
|
||||
]);
|
||||
|
||||
mockMetadata.value.andCallFake(function (key) {
|
||||
return {
|
||||
key: key
|
||||
};
|
||||
});
|
||||
return mockMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
MockTelemetryApi.prototype.createSpy = function (functionName) {
|
||||
this[functionName] = this[functionName].bind(this);
|
||||
spyOn(this, functionName);
|
||||
this[functionName].andCallThrough();
|
||||
};
|
||||
|
||||
return MockTelemetryApi;
|
||||
});
|
||||
112
src/plugins/telemetryMean/src/TelemetryAverager.js
Normal file
112
src/plugins/telemetryMean/src/TelemetryAverager.js
Normal file
@@ -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;
|
||||
});
|
||||
@@ -59,7 +59,7 @@ requirejs.config({
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
"saveAs": "bower_components/file-saver/FileSaver.min",
|
||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||
"text": "bower_components/text/text",
|
||||
"uuid": "bower_components/node-uuid/uuid",
|
||||
|
||||
Reference in New Issue
Block a user