mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 21:27:52 +00:00
Merge remote-tracking branch 'origin/open972'
This commit is contained in:
commit
8ac6ac97f0
@ -109,7 +109,7 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "HistoricalTableController",
|
"key": "HistoricalTableController",
|
||||||
"implementation": HistoricalTableController,
|
"implementation": HistoricalTableController,
|
||||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter"]
|
"depends": ["$scope", "telemetryHandler", "telemetryFormatter", "$timeout"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "RealtimeTableController",
|
"key": "RealtimeTableController",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div ng-controller="HistoricalTableController">
|
<div ng-controller="HistoricalTableController" ng-class="{'loading': loading}">
|
||||||
<mct-table
|
<mct-table
|
||||||
headers="headers"
|
headers="headers"
|
||||||
rows="rows"
|
rows="rows"
|
||||||
|
@ -25,6 +25,7 @@ define(
|
|||||||
'./TelemetryTableController'
|
'./TelemetryTableController'
|
||||||
],
|
],
|
||||||
function (TableController) {
|
function (TableController) {
|
||||||
|
var BATCH_SIZE = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends TelemetryTableController and adds real-time streaming
|
* Extends TelemetryTableController and adds real-time streaming
|
||||||
@ -35,32 +36,82 @@ define(
|
|||||||
* @param telemetryFormatter
|
* @param telemetryFormatter
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter) {
|
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter, $timeout) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.$timeout = $timeout;
|
||||||
|
this.timeoutHandle = undefined;
|
||||||
|
this.batchSize = BATCH_SIZE;
|
||||||
|
|
||||||
|
$scope.$on("$destroy", function () {
|
||||||
|
if (self.timeoutHandle) {
|
||||||
|
self.$timeout.cancel(self.timeoutHandle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoricalTableController.prototype = Object.create(TableController.prototype);
|
HistoricalTableController.prototype = Object.create(TableController.prototype);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates historical data on scope when it becomes available from
|
* Set provided row data on scope, and cancel loading spinner
|
||||||
* the telemetry API
|
* @private
|
||||||
*/
|
*/
|
||||||
HistoricalTableController.prototype.addHistoricalData = function () {
|
HistoricalTableController.prototype.doneProcessing = function (rowData) {
|
||||||
var rowData = [],
|
|
||||||
self = this;
|
|
||||||
|
|
||||||
this.handle.getTelemetryObjects().forEach(function (telemetryObject) {
|
|
||||||
var series = self.handle.getSeries(telemetryObject) || {},
|
|
||||||
pointCount = series.getPointCount ? series.getPointCount() : 0,
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
for (; i < pointCount; i++) {
|
|
||||||
rowData.push(self.table.getRowValues(telemetryObject,
|
|
||||||
self.handle.makeDatum(telemetryObject, series, i)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$scope.rows = rowData;
|
this.$scope.rows = rowData;
|
||||||
|
this.$scope.loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an array of objects, formatting the telemetry available
|
||||||
|
* for them and setting it on scope when done
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
HistoricalTableController.prototype.processTelemetryObjects = function (objects, offset, start, rowData) {
|
||||||
|
var telemetryObject = objects[offset],
|
||||||
|
series,
|
||||||
|
i = start,
|
||||||
|
pointCount,
|
||||||
|
end;
|
||||||
|
|
||||||
|
//No more objects to process
|
||||||
|
if (!telemetryObject) {
|
||||||
|
return this.doneProcessing(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
series = this.handle.getSeries(telemetryObject);
|
||||||
|
|
||||||
|
pointCount = series.getPointCount();
|
||||||
|
end = Math.min(start + this.batchSize, pointCount);
|
||||||
|
|
||||||
|
//Process rows in a batch with size not exceeding a maximum length
|
||||||
|
for (; i < end; i++) {
|
||||||
|
rowData.push(this.table.getRowValues(telemetryObject,
|
||||||
|
this.handle.makeDatum(telemetryObject, series, i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Done processing all rows for this object.
|
||||||
|
if (end >= pointCount) {
|
||||||
|
offset++;
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done processing either a batch or an object, yield process
|
||||||
|
// before continuing processing
|
||||||
|
this.timeoutHandle = this.$timeout(this.processTelemetryObjects.bind(this, objects, offset, end, rowData));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates historical data on scope when it becomes available from
|
||||||
|
* the telemetry API
|
||||||
|
*/
|
||||||
|
HistoricalTableController.prototype.addHistoricalData = function () {
|
||||||
|
if (this.timeoutHandle) {
|
||||||
|
this.$timeout.cancel(this.timeoutHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeoutHandle = this.$timeout(this.processTelemetryObjects.bind(this, this.handle.getTelemetryObjects(), 0, 0, []));
|
||||||
};
|
};
|
||||||
|
|
||||||
return HistoricalTableController;
|
return HistoricalTableController;
|
||||||
|
@ -91,6 +91,7 @@ define(
|
|||||||
self.$scope.rows.length - 1);
|
self.$scope.rows.length - 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.$scope.loading = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return RealtimeTableController;
|
return RealtimeTableController;
|
||||||
|
@ -83,16 +83,24 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
TelemetryTableController.prototype.registerChangeListeners = function () {
|
TelemetryTableController.prototype.registerChangeListeners = function () {
|
||||||
|
var self = this;
|
||||||
this.unregisterChangeListeners();
|
this.unregisterChangeListeners();
|
||||||
|
|
||||||
// When composition changes, re-subscribe to the various
|
// When composition changes, re-subscribe to the various
|
||||||
// telemetry subscriptions
|
// telemetry subscriptions
|
||||||
this.changeListeners.push(this.$scope.$watchCollection(
|
this.changeListeners.push(this.$scope.$watchCollection(
|
||||||
'domainObject.getModel().composition', this.subscribe.bind(this)));
|
'domainObject.getModel().composition',
|
||||||
|
function (newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
self.subscribe();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
//Change of bounds in time conductor
|
//Change of bounds in time conductor
|
||||||
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
|
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
|
||||||
this.subscribe.bind(this)));
|
this.subscribe.bind(this))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,6 +140,7 @@ define(
|
|||||||
if (this.handle) {
|
if (this.handle) {
|
||||||
this.handle.unsubscribe();
|
this.handle.unsubscribe();
|
||||||
}
|
}
|
||||||
|
this.$scope.loading = true;
|
||||||
|
|
||||||
this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
|
this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
|
||||||
this.$scope.domainObject,
|
this.$scope.domainObject,
|
||||||
|
@ -34,6 +34,8 @@ define(
|
|||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockTable,
|
mockTable,
|
||||||
mockConfiguration,
|
mockConfiguration,
|
||||||
|
mockAngularTimeout,
|
||||||
|
mockTimeoutHandle,
|
||||||
watches,
|
watches,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
@ -63,6 +65,11 @@ define(
|
|||||||
watches[expression] = callback;
|
watches[expression] = callback;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mockTimeoutHandle = jasmine.createSpy("timeoutHandle");
|
||||||
|
mockAngularTimeout = jasmine.createSpy("$timeout");
|
||||||
|
mockAngularTimeout.andReturn(mockTimeoutHandle);
|
||||||
|
mockAngularTimeout.cancel = jasmine.createSpy("cancelTimeout");
|
||||||
|
|
||||||
mockConfiguration = {
|
mockConfiguration = {
|
||||||
'range1': true,
|
'range1': true,
|
||||||
'range2': true,
|
'range2': true,
|
||||||
@ -107,7 +114,7 @@ define(
|
|||||||
]);
|
]);
|
||||||
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
||||||
|
|
||||||
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter);
|
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter, mockAngularTimeout);
|
||||||
controller.table = mockTable;
|
controller.table = mockTable;
|
||||||
controller.handle = mockTelemetryHandle;
|
controller.handle = mockTelemetryHandle;
|
||||||
});
|
});
|
||||||
@ -163,6 +170,13 @@ define(
|
|||||||
|
|
||||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||||
|
|
||||||
|
// Angular timeout is called a minumum of twice, regardless
|
||||||
|
// of batch size used.
|
||||||
|
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
expect(mockAngularTimeout.calls.length).toEqual(2);
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
|
||||||
expect(controller.$scope.rows.length).toBe(5);
|
expect(controller.$scope.rows.length).toBe(5);
|
||||||
expect(controller.$scope.rows[0]).toBe(mockRow);
|
expect(controller.$scope.rows[0]).toBe(mockRow);
|
||||||
});
|
});
|
||||||
@ -198,7 +212,7 @@ define(
|
|||||||
' object composition changes', function () {
|
' object composition changes', function () {
|
||||||
controller.registerChangeListeners();
|
controller.registerChangeListeners();
|
||||||
expect(watches['domainObject.getModel().composition']).toBeDefined();
|
expect(watches['domainObject.getModel().composition']).toBeDefined();
|
||||||
watches['domainObject.getModel().composition']();
|
watches['domainObject.getModel().composition']([], []);
|
||||||
expect(controller.subscribe).toHaveBeenCalled();
|
expect(controller.subscribe).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -219,6 +233,78 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
describe('Yields thread', function () {
|
||||||
|
var mockSeries,
|
||||||
|
mockRow;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockSeries = {
|
||||||
|
getPointCount: function () {
|
||||||
|
return 5;
|
||||||
|
},
|
||||||
|
getDomainValue: function () {
|
||||||
|
return 'Domain Value';
|
||||||
|
},
|
||||||
|
getRangeValue: function () {
|
||||||
|
return 'Range Value';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockRow = {'domain': 'Domain Value', 'range': 'Range Value'};
|
||||||
|
|
||||||
|
mockTelemetryHandle.makeDatum.andCallFake(function () {
|
||||||
|
return mockRow;
|
||||||
|
});
|
||||||
|
mockTable.getRowValues.andReturn(mockRow);
|
||||||
|
mockTelemetryHandle.getTelemetryObjects.andReturn([mockDomainObject]);
|
||||||
|
mockTelemetryHandle.getSeries.andReturn(mockSeries);
|
||||||
|
});
|
||||||
|
it('when row count exceeds batch size', function () {
|
||||||
|
controller.batchSize = 3;
|
||||||
|
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||||
|
|
||||||
|
//Timeout is called a minimum of two times
|
||||||
|
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
|
||||||
|
expect(mockAngularTimeout.calls.length).toEqual(2);
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
|
||||||
|
//Because it yields, timeout will have been called a
|
||||||
|
// third time for the batch.
|
||||||
|
expect(mockAngularTimeout.calls.length).toEqual(3);
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
|
||||||
|
expect(controller.$scope.rows.length).toBe(5);
|
||||||
|
expect(controller.$scope.rows[0]).toBe(mockRow);
|
||||||
|
});
|
||||||
|
it('cancelling any outstanding timeouts', function () {
|
||||||
|
controller.batchSize = 3;
|
||||||
|
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||||
|
|
||||||
|
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||||
|
mockAngularTimeout.mostRecentCall.args[0]();
|
||||||
|
|
||||||
|
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||||
|
|
||||||
|
expect(mockAngularTimeout.cancel).toHaveBeenCalledWith(mockTimeoutHandle);
|
||||||
|
});
|
||||||
|
it('cancels timeout on scope destruction', function () {
|
||||||
|
controller.batchSize = 3;
|
||||||
|
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||||
|
|
||||||
|
//Destroy is used by parent class as well, so multiple
|
||||||
|
// calls are made to scope.$on
|
||||||
|
var destroyCalls = mockScope.$on.calls.filter(function (call) {
|
||||||
|
return call.args[0] === '$destroy';
|
||||||
|
});
|
||||||
|
//Call destroy function
|
||||||
|
expect(destroyCalls.length).toEqual(2);
|
||||||
|
|
||||||
|
destroyCalls[0].args[1]();
|
||||||
|
expect(mockAngularTimeout.cancel).toHaveBeenCalledWith(mockTimeoutHandle);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user