[Tables] #707 Supporting realtime telemetry tables

Added real-time table type
This commit is contained in:
Henry 2016-03-08 13:29:16 -08:00
parent 20f1dcef45
commit a4eb9d6a94
6 changed files with 94 additions and 69 deletions

View File

@ -36,8 +36,7 @@ define(
function EventTelemetryProvider($q, $timeout) { function EventTelemetryProvider($q, $timeout) {
var var
subscriptions = [], subscriptions = [],
genInterval = 1000, genInterval = 1000;
startTime = Date.now();
// //
function matchesSource(request) { function matchesSource(request) {
@ -80,7 +79,6 @@ define(
function startGenerating() { function startGenerating() {
$timeout(function () { $timeout(function () {
//console.log("startGenerating... " + Date.now());
handleSubscriptions(); handleSubscriptions();
if (subscriptions.length > 0) { if (subscriptions.length > 0) {
startGenerating(); startGenerating();
@ -93,7 +91,8 @@ define(
callback: callback, callback: callback,
requests: requests requests: requests
}; };
console.log("subscribe... " + Date.now() / 1000 + " request:" +
" " + requests[0].id);
function unsubscribe() { function unsubscribe() {
subscriptions = subscriptions.filter(function (s) { subscriptions = subscriptions.filter(function (s) {
return s !== subscription; return s !== subscription;
@ -101,7 +100,6 @@ define(
} }
subscriptions.push(subscription); subscriptions.push(subscription);
if (subscriptions.length === 1) { if (subscriptions.length === 1) {
startGenerating(); startGenerating();
} }

View File

@ -23,7 +23,7 @@
define([ define([
"./src/directives/MCTTable", "./src/directives/MCTTable",
"./src/controllers/RTTableController", "./src/controllers/RTTelemetryTableController",
"./src/controllers/TelemetryTableController", "./src/controllers/TelemetryTableController",
"./src/controllers/TableOptionsController", "./src/controllers/TableOptionsController",
'../../commonUI/regions/src/Region', '../../commonUI/regions/src/Region',
@ -31,8 +31,8 @@ define([
"legacyRegistry" "legacyRegistry"
], function ( ], function (
MCTTable, MCTTable,
RTTelemetryTableController,
TelemetryTableController, TelemetryTableController,
RTTableController,
TableOptionsController, TableOptionsController,
Region, Region,
InspectorRegion, InspectorRegion,
@ -61,7 +61,7 @@ define([
"types": [ "types": [
{ {
"key": "table", "key": "table",
"name": "Table", "name": "Historical Telemetry Table",
"glyph": "\ue605", "glyph": "\ue605",
"description": "A table for displaying telemetry data", "description": "A table for displaying telemetry data",
"features": "creation", "features": "creation",
@ -80,6 +80,29 @@ define([
"views": [ "views": [
"table" "table"
] ]
},
{
"key": "rttable",
"name": "Real-time Telemetry Table",
"glyph": "\ue605",
"description": "A table for displaying realtime telemetry" +
" data",
"features": "creation",
"delegates": [
"telemetry"
],
"inspector": tableInspector,
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"views": [
"realtime"
]
} }
], ],
"controllers": [ "controllers": [
@ -89,8 +112,8 @@ define([
"depends": ["$scope", "telemetryHandler", "telemetryFormatter"] "depends": ["$scope", "telemetryHandler", "telemetryFormatter"]
}, },
{ {
"key": "RTTableController", "key": "RTTelemetryTableController",
"implementation": RTTableController, "implementation": RTTelemetryTableController,
"depends": ["$scope", "telemetryHandler", "telemetryFormatter"] "depends": ["$scope", "telemetryHandler", "telemetryFormatter"]
}, },
{ {
@ -102,7 +125,7 @@ define([
], ],
"views": [ "views": [
{ {
"name": "Table", "name": "Historical Table",
"key": "table", "key": "table",
"glyph": "\ue605", "glyph": "\ue605",
"templateUrl": "templates/table.html", "templateUrl": "templates/table.html",
@ -113,7 +136,7 @@ define([
"editable": true "editable": true
}, },
{ {
"name": "Scrolling", "name": "Real-time Table",
"key": "realtime", "key": "realtime",
"glyph": "\ue605", "glyph": "\ue605",
"templateUrl": "templates/rt-table.html", "templateUrl": "templates/rt-table.html",

View File

@ -1,4 +1,4 @@
<div ng-controller="RTTableController"> <div ng-controller="RTTelemetryTableController">
<mct-table <mct-table
headers="headers" headers="headers"
rows="rows" rows="rows"

View File

@ -12,7 +12,7 @@ define(
this.element = element; this.element = element;
this.$timeout = $timeout; this.$timeout = $timeout;
this.maxDisplayRows = 50; this.maxDisplayRows = 50;
element.find('div').on('scroll', this.onScroll.bind(this)); element.find('div').on('scroll', this.setVisibleRows.bind(this));
this.scrollable = element.find('div')[0]; this.scrollable = element.find('div')[0];
$scope.visibleRows = []; $scope.visibleRows = [];
@ -66,11 +66,10 @@ define(
} }
/** /**
* On scroll, calculate which rows indexes are visible and * Re-synchronize between data rows and visible rows, based on array
* ensure that an equal number of rows are preloaded for * content and scroll state.
* scrolling in either direction.
*/ */
MCTTableController.prototype.onScroll = function (event) { MCTTableController.prototype.setVisibleRows = function () {
var self = this, var self = this,
target = this.scrollable, target = this.scrollable,
topScroll = target.scrollTop, topScroll = target.scrollTop,
@ -82,40 +81,53 @@ define(
start, start,
end; end;
//No need to scroll
if (this.$scope.displayRows.length < this.maxDisplayRows) { if (this.$scope.displayRows.length < this.maxDisplayRows) {
return; //Check whether need to resynchronize visible with display
} // rows (if data added)
if (this.$scope.visibleRows.length != this.$scope.displayRows.length){
if (topScroll < this.$scope.headerHeight) { start = 0;
firstVisible = 0; end = this.$scope.displayRows.length-1;
} else {
//Data is in sync, and no need to calculate scroll,
// so do nothing.
return;
}
} else { } else {
firstVisible = Math.floor( //rows has exceeded display maximum, so may be necessary to
(topScroll - this.$scope.headerHeight) / this.$scope.rowHeight // scroll
if (topScroll < this.$scope.headerHeight) {
firstVisible = 0;
} else {
firstVisible = Math.floor(
(topScroll - this.$scope.headerHeight) / this.$scope.rowHeight
);
}
lastVisible = Math.ceil(
(bottomScroll - this.$scope.headerHeight) / this.$scope.rowHeight
); );
totalVisible = lastVisible - firstVisible;
numberOffscreen = this.maxDisplayRows - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
//end = this.$scope.visibleRows.length - 1;
end = Math.min(this.maxDisplayRows, this.$scope.displayRows.length) - 1;
} else if (end >= this.$scope.displayRows.length) {
end = this.$scope.displayRows.length - 1;
start = end - this.maxDisplayRows + 1;
}
if (this.$scope.visibleRows[0].rowIndex === start &&
this.$scope.visibleRows[this.$scope.visibleRows.length - 1]
.rowIndex === end) {
return; // don't update if no changes are required.
}
} }
lastVisible = Math.ceil( //Set visible rows from display rows, based on calculated offset.
(bottomScroll - this.$scope.headerHeight) / this.$scope.rowHeight
);
totalVisible = lastVisible - firstVisible;
numberOffscreen = this.maxDisplayRows - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = this.$scope.visibleRows.length - 1;
} else if (end >= this.$scope.displayRows.length) {
end = this.$scope.displayRows.length - 1;
start = end - this.maxDisplayRows + 1;
}
if (this.$scope.visibleRows[0].rowIndex === start &&
this.$scope.visibleRows[this.$scope.visibleRows.length-1]
.rowIndex === end) {
return; // don't update if no changes are required.
}
this.$scope.visibleRows = this.$scope.displayRows.slice(start, end) this.$scope.visibleRows = this.$scope.displayRows.slice(start, end)
.map(function(row, i) { .map(function(row, i) {
return { return {
@ -176,16 +188,7 @@ define(
this.$scope.headerHeight = headerHeight; this.$scope.headerHeight = headerHeight;
this.$scope.rowHeight = rowHeight; this.$scope.rowHeight = rowHeight;
this.$scope.totalHeight = overallHeight; this.$scope.totalHeight = overallHeight;
this.setVisibleRows();
this.$scope.visibleRows = this.$scope.displayRows.slice(0, this.maxDisplayRows).map(function(row, i) {
return {
rowIndex: i,
offsetY: (i * self.$scope.rowHeight) + self.$scope.headerHeight,
contents: row
};
});
this.onScroll();
this.$scope.overrideRowPositioning = true; this.$scope.overrideRowPositioning = true;
}; };
@ -316,13 +319,13 @@ define(
* will be sorted before display. * will be sorted before display.
*/ */
MCTTableController.prototype.updateRows = function (newRows) { MCTTableController.prototype.updateRows = function (newRows) {
console.log('updateRows');
this.$scope.visibleRows = []; this.$scope.visibleRows = [];
this.$scope.overrideRowPositioning = false; this.$scope.overrideRowPositioning = false;
if (!this.$scope.displayHeaders) { if (!this.$scope.displayHeaders) {
return; return;
} }
this.filterAndSort(newRows || []); this.filterAndSort(newRows || []);
this.resize(); this.resize();
}; };

View File

@ -27,8 +27,8 @@
*/ */
define( define(
[ [
'./TableController', './TelemetryTableController',
'../Table', '../TableConfiguration',
'../NameColumn' '../NameColumn'
], ],
function (TableController, Table, NameColumn) { function (TableController, Table, NameColumn) {
@ -44,16 +44,17 @@ define(
* @param telemetryFormatter * @param telemetryFormatter
* @constructor * @constructor
*/ */
function RTTableController($scope, telemetryHandler, telemetryFormatter) { function RTTelemetryTableController($scope, telemetryHandler, telemetryFormatter) {
TableController.call(this, $scope, telemetryHandler, telemetryFormatter); TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
} }
RTTableController.prototype = Object.create(TableController.prototype); RTTelemetryTableController.prototype = Object.create(TableController.prototype);
/** /**
Create a new subscription. This is called when Create a new telemetry subscription.
*/ */
RTTableController.prototype.subscribe = function() { RTTelemetryTableController.prototype.subscribe = function() {
console.trace();
var self = this; var self = this;
if (this.handle) { if (this.handle) {
@ -90,10 +91,10 @@ define(
* be composed of multiple objects) * be composed of multiple objects)
* @param datum The data received from the telemetry source * @param datum The data received from the telemetry source
*/ */
RTTableController.prototype.updateRows = function (object, datum) { RTTelemetryTableController.prototype.updateRows = function (object, datum) {
this.$scope.$broadcast('newRow', this.table.getRowValues(object, datum)); this.$scope.$broadcast('newRow', this.table.getRowValues(object, datum));
}; };
return RTTableController; return RTTelemetryTableController;
} }
); );

View File

@ -82,7 +82,7 @@ define(
this.changeListeners = []; this.changeListeners = [];
// 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('domainObject.getModel().composition', this.subscribe.bind(this))); //this.changeListeners.push(this.$scope.$watchCollection('domainObject.getModel().composition', this.subscribe.bind(this)));
//Change of bounds in time conductor //Change of bounds in time conductor
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds', this.subscribe.bind(this))); this.changeListeners.push(this.$scope.$on('telemetry:display:bounds', this.subscribe.bind(this)));
@ -103,7 +103,7 @@ define(
Create a new subscription. This is called when Create a new subscription. This is called when
*/ */
TelemetryTableController.prototype.subscribe = function() { TelemetryTableController.prototype.subscribe = function() {
console.trace();
if (this.handle) { if (this.handle) {
this.handle.unsubscribe(); this.handle.unsubscribe();
} }