From 50f303bbdc020c842731465696df1929b465168a Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 15 Jan 2017 10:59:28 -0800 Subject: [PATCH] [Tables] limit digests to increase performance --- .../table/res/templates/mct-table.html | 8 +- .../table/res/templates/telemetry-table.html | 5 +- .../src/controllers/MCTTableController.js | 78 ++++++++++--------- .../controllers/TelemetryTableController.js | 44 ++++++----- 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/platform/features/table/res/templates/mct-table.html b/platform/features/table/res/templates/mct-table.html index 3a805bf4e0..7e24be2c43 100644 --- a/platform/features/table/res/templates/mct-table.html +++ b/platform/features/table/res/templates/mct-table.html @@ -5,7 +5,7 @@ Export -
+
@@ -32,7 +32,8 @@ enableSort ? 'sortable' : '', sortColumn === header ? 'sort' : '', sortDirection || '' - ].join(' ')"> + ].join(' ')" + ng-click="toggleSort(header)"> {{ header }} @@ -58,7 +59,8 @@ + ng-style="{ top: visibleRow.offsetY + 'px' }" + ng-click="table.onRowClick($event, visibleRow.rowIndex) ">
+ Request time: {{tableController.lastRequestTime | date : 'HH:mm:ss:sss'}} diff --git a/platform/features/table/src/controllers/MCTTableController.js b/platform/features/table/src/controllers/MCTTableController.js index e9c18400ee..aef6e68240 100644 --- a/platform/features/table/src/controllers/MCTTableController.js +++ b/platform/features/table/src/controllers/MCTTableController.js @@ -163,8 +163,6 @@ define( } }.bind(this)); - console.log('constructed'); - $scope.$on('$destroy', function() { this.scrollable.off('scroll', this.onScroll); this.destroyConductorListeners(); @@ -193,15 +191,7 @@ define( * @private */ MCTTableController.prototype.scrollToBottom = function () { - var self = this; - - //Use timeout to defer execution until next digest when any - // pending UI changes have completed, eg. a new row in the table. - if (this.$scope.autoScroll) { - this.digest(function () { - self.scrollable[0].scrollTop = self.scrollable[0].scrollHeight; - }); - } + this.scrollable[0].scrollTop = this.scrollable[0].scrollHeight; }; /** @@ -219,8 +209,12 @@ define( //Resize the columns , then update the rows visible in the table this.resize([this.$scope.sizingRow, row]) - .then(this.setVisibleRows.bind(this)) - .then(this.scrollToBottom.bind(this)); + .then(this.setVisibleRows) + .then(function () { + if (this.$scope.autoScroll) { + this.scrollToBottom(); + } + }.bind(this)); var toi = this.conductor.timeOfInterest(); if (toi !== -1) { @@ -250,16 +244,24 @@ define( * @private */ MCTTableController.prototype.onScroll = function (event) { - //If user scrolls away from bottom, disable auto-scroll. - // Auto-scroll will be re-enabled if user scrolls to bottom again. - if (this.scrollable[0].scrollTop < - (this.scrollable[0].scrollHeight - this.scrollable[0].offsetHeight)) { - this.$scope.autoScroll = false; - } else { - this.$scope.autoScroll = true; - } - this.setVisibleRows(); - this.$scope.$digest(); + if (!this.scrolling) { + this.scrolling = true; + + requestAnimationFrame(function () { + this.setVisibleRows(); + this.digest(); + + // If user scrolls away from bottom, disable auto-scroll. + // Auto-scroll will be re-enabled if user scrolls to bottom again. + if (this.scrollable[0].scrollTop < + (this.scrollable[0].scrollHeight - this.scrollable[0].offsetHeight) - 20) { + this.$scope.autoScroll = false; + } else { + this.$scope.autoScroll = true; + } + this.scrolling = false; + }.bind(this)); + } }; /** @@ -338,7 +340,7 @@ define( this.$scope.visibleRows[this.$scope.visibleRows.length - 1] .rowIndex === end) { - return; // don't update if no changes are required. + return Promise.resolve(); // don't update if no changes are required. } } //Set visible rows from display rows, based on calculated offset. @@ -351,6 +353,7 @@ define( contents: row }; }); + return this.digest(); }; /** @@ -566,25 +569,25 @@ define( return largestRow; }; - MCTTableController.prototype.digest = function (callback) { + // Will effectively cap digests at 60Hz + // Also turns digest into a promise allowing code to force digest, then + // schedule something to happen afterwards + MCTTableController.prototype.digest = function () { var scope = this.$scope; - var callbacks = this.callbacks; + var self = this; var requestAnimationFrame = this.$window.requestAnimationFrame; - var promise = callbacks[callback]; - - if (!promise){ - promise = new Promise(function (resolve) { + if (!this.digestPromise) { + this.digestPromise = new Promise(function (resolve) { requestAnimationFrame(function() { scope.$digest(); - delete callbacks[callback]; - resolve(callback && callback()); + delete self.digestPromise; + resolve(); }); }); - callbacks[callback] = promise; } - return promise; + return this.digestPromise; }; /** @@ -598,7 +601,7 @@ define( */ MCTTableController.prototype.resize = function (rows) { this.$scope.sizingRow = this.buildLargestRow(rows); - return this.digest(this.setElementSizes); + return this.digest().then(this.setElementSizes); }; /** @@ -631,7 +634,6 @@ define( .then(this.setVisibleRows) //Timeout following setVisibleRows to allow digest to // perform DOM changes, otherwise scrollTo won't work. - .then(this.digest) .then(function () { //If TOI specified, scroll to it var timeOfInterest = this.conductor.timeOfInterest(); @@ -732,7 +734,9 @@ define( */ MCTTableController.prototype.changeBounds = function (bounds) { this.setTimeOfInterestRow(this.conductor.timeOfInterest()); - this.scrollToRow(this.$scope.toiRowIndex); + if (this.$scope.toiRowIndex !== -1) { + this.scrollToRow(this.$scope.toiRowIndex); + } }; /** diff --git a/platform/features/table/src/controllers/TelemetryTableController.js b/platform/features/table/src/controllers/TelemetryTableController.js index 5aa89e8f1e..f015d11cab 100644 --- a/platform/features/table/src/controllers/TelemetryTableController.js +++ b/platform/features/table/src/controllers/TelemetryTableController.js @@ -76,15 +76,23 @@ define( 'loadColumns', 'getHistoricalData', 'subscribeToNewData', - 'changeBounds' + 'changeBounds', + 'setScroll' ]); this.getData(); this.registerChangeListeners(); + this.openmct.conductor.on('follow', this.setScroll); + this.setScroll(this.openmct.conductor.follow()); + this.$scope.$on("$destroy", this.destroy); } + TelemetryTableController.prototype.setScroll = function (scroll){ + this.$scope.autoScroll = scroll; + }; + /** * Based on the selected time system, find a matching domain column * to sort by. By default will just match on key. @@ -171,6 +179,7 @@ define( this.openmct.conductor.off('timeSystem', this.sortByTimeSystem); this.openmct.conductor.off('bounds', this.changeBounds); + this.openmct.conductor.off('follow', this.setScroll); this.subscriptions.forEach(function (subscription) { subscription(); @@ -229,20 +238,18 @@ define( var openmct = this.openmct; var bounds = openmct.conductor.bounds(); var scope = this.$scope; + var rowData = []; var processedObjects = 0; var requestTime = this.lastRequestTime = Date.now(); return new Promise(function (resolve, reject){ - console.log('Created promise'); function finishProcessing(tableRows){ - scope.rows = tableRows; + scope.rows = tableRows.concat(scope.rows); scope.loading = false; - console.log('Resolved promise'); resolve(tableRows); } - function processData(historicalData, index, rowData, limitEvaluator){ - console.log("Processing batch"); + function processData(historicalData, index, limitEvaluator){ if (index >= historicalData.length) { processedObjects++; @@ -250,13 +257,14 @@ define( finishProcessing(rowData); } } else { - rowData = rowData.concat(historicalData.slice(index, index + this.batchSize) - .map(this.table.getRowValues.bind(this.table, limitEvaluator))); + rowData = rowData.concat( + historicalData.slice(index, index + this.batchSize).map( + this.table.getRowValues.bind(this.table, limitEvaluator)) + ); this.timeoutHandle = this.$timeout(processData.bind( this, historicalData, index + this.batchSize, - rowData, limitEvaluator )); } @@ -265,12 +273,10 @@ define( function makeTableRows(object, historicalData) { // Only process one request at a time if (requestTime === this.lastRequestTime) { - console.log('Processing request'); var limitEvaluator = openmct.telemetry.limitEvaluator(object); - processData.call(this, historicalData, 0, [], limitEvaluator); + processData.call(this, historicalData, 0, limitEvaluator); } else { - console.log('Ignoring returned data because of staleness'); - resolve([]); + resolve(rowData); } } @@ -287,7 +293,6 @@ define( objects.forEach(requestData.bind(this)); } else { scope.loading = false; - console.log('Resolved promise'); resolve([]); } }.bind(this)); @@ -317,16 +322,15 @@ define( this.$scope.$broadcast('remove:row', 0); this.$scope.rows.shift(); } - - this.$scope.$broadcast('add:row', - this.$scope.rows.length - 1); - + if (!this.$scope.loading) { + this.$scope.$broadcast('add:row', + this.$scope.rows.length - 1); + } } objects.forEach(function (object){ this.subscriptions.push( telemetryApi.subscribe(object, newData.bind(this, object), {})); - console.log('subscribed'); }.bind(this)); return objects; @@ -374,7 +378,7 @@ define( getDomainObjects() .then(filterForTelemetry) .then(this.loadColumns) - //.then(this.subscribeToNewData) + .then(this.subscribeToNewData) .then(this.getHistoricalData) .catch(error) };