diff --git a/example/generator/bundle.js b/example/generator/bundle.js
index 259c5cff15..f1c0f83224 100644
--- a/example/generator/bundle.js
+++ b/example/generator/bundle.js
@@ -75,8 +75,7 @@ define([
},
{
"key": "delta",
- "name": "Delta",
- "format": "example.delta"
+ "name": "Delta"
}
],
"priority": -1
@@ -103,11 +102,13 @@ define([
"domains": [
{
"key": "utc",
- "name": "Time"
+ "name": "Time",
+ "format": "utc"
},
{
"key": "yesterday",
- "name": "Yesterday"
+ "name": "Yesterday",
+ "format": "utc"
},
{
"key": "delta",
diff --git a/example/generator/src/generatorWorker.js b/example/generator/src/generatorWorker.js
index bb4e55ca4b..091297e185 100644
--- a/example/generator/src/generatorWorker.js
+++ b/example/generator/src/generatorWorker.js
@@ -24,6 +24,7 @@
(function () {
+ var FIFTEEN_MINUTES = 15 * 60 * 1000;
var handlers = {
subscribe: onSubscribe,
@@ -82,8 +83,11 @@
function onRequest(message) {
var data = message.data;
- if (!data.start || !data.end) {
- throw new Error('missing start and end!');
+ if (data.end == undefined) {
+ data.end = Date.now();
+ }
+ if (data.start == undefined){
+ data.start = data.end - FIFTEEN_MINUTES;
}
var now = Date.now();
diff --git a/platform/features/table/bundle.js b/platform/features/table/bundle.js
index 02b78f847f..c034677a02 100644
--- a/platform/features/table/bundle.js
+++ b/platform/features/table/bundle.js
@@ -22,25 +22,21 @@
define([
"./src/directives/MCTTable",
- "./src/controllers/RealtimeTableController",
- "./src/controllers/HistoricalTableController",
+ "./src/controllers/TelemetryTableController",
"./src/controllers/TableOptionsController",
'../../commonUI/regions/src/Region',
'../../commonUI/browse/src/InspectorRegion',
"text!./res/templates/table-options-edit.html",
- "text!./res/templates/rt-table.html",
- "text!./res/templates/historical-table.html",
+ "text!./res/templates/telemetry-table.html",
"legacyRegistry"
], function (
MCTTable,
- RealtimeTableController,
- HistoricalTableController,
+ TelemetryTableController,
TableOptionsController,
Region,
InspectorRegion,
tableOptionsEditTemplate,
- rtTableTemplate,
- historicalTableTemplate,
+ telemetryTableTemplate,
legacyRegistry
) {
/**
@@ -65,9 +61,9 @@ define([
"types": [
{
"key": "table",
- "name": "Historical Telemetry Table",
- "cssclass": "icon-tabular",
- "description": "A static table of all values over time for all included telemetry elements. Rows are timestamped data values for each telemetry element; columns are data fields. The number of rows is based on the range of your query. New incoming data must be manually re-queried for.",
+ "name": "Telemetry Table",
+ "cssclass": "icon-tabular-realtime",
+ "description": "A table of values over a given time period. The table will be automatically updated with new values as they become available",
"priority": 861,
"features": "creation",
"delegates": [
@@ -85,42 +81,13 @@ define([
"views": [
"table"
]
- },
- {
- "key": "rttable",
- "name": "Real-time Telemetry Table",
- "cssclass": "icon-tabular-realtime",
- "description": "A scrolling table of latest values for all included telemetry elements. Rows are timestamped data values for each telemetry element; columns are data fields. New incoming data is automatically added to the view.",
- "priority": 860,
- "features": "creation",
- "delegates": [
- "telemetry"
- ],
- "inspector": tableInspector,
- "contains": [
- {
- "has": "telemetry"
- }
- ],
- "model": {
- "composition": []
- },
- "views": [
- "rt-table",
- "scrolling-table"
- ]
}
],
"controllers": [
{
- "key": "HistoricalTableController",
- "implementation": HistoricalTableController,
- "depends": ["$scope", "telemetryHandler", "telemetryFormatter", "$timeout", "openmct"]
- },
- {
- "key": "RealtimeTableController",
- "implementation": RealtimeTableController,
- "depends": ["$scope", "telemetryHandler", "telemetryFormatter", "openmct"]
+ "key": "TelemetryTableController",
+ "implementation": TelemetryTableController,
+ "depends": ["$scope", "openmct"]
},
{
"key": "TableOptionsController",
@@ -131,21 +98,10 @@ define([
],
"views": [
{
- "name": "Historical Table",
+ "name": "Telemetry Table",
"key": "table",
- "template": historicalTableTemplate,
- "cssclass": "icon-tabular",
- "needs": [
- "telemetry"
- ],
- "delegation": true,
- "editable": false
- },
- {
- "name": "Real-time Table",
- "key": "rt-table",
"cssclass": "icon-tabular-realtime",
- "template": rtTableTemplate,
+ "template": telemetryTableTemplate,
"needs": [
"telemetry"
],
diff --git a/platform/features/table/res/templates/rt-table.html b/platform/features/table/res/templates/rt-table.html
deleted file mode 100644
index da08b0ee8e..0000000000
--- a/platform/features/table/res/templates/rt-table.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/platform/features/table/res/templates/historical-table.html b/platform/features/table/res/templates/telemetry-table.html
similarity index 82%
rename from platform/features/table/res/templates/historical-table.html
rename to platform/features/table/res/templates/telemetry-table.html
index c2abbf5708..6dae139263 100644
--- a/platform/features/table/res/templates/historical-table.html
+++ b/platform/features/table/res/templates/telemetry-table.html
@@ -1,4 +1,4 @@
-
0) {
- self.addColumn(new NameColumn(), 0);
- }
}
return this;
};
@@ -99,9 +102,8 @@ define(
* @returns {Array} The titles of the columns
*/
TableConfiguration.prototype.getHeaders = function () {
- var self = this;
return this.columns.map(function (column, i) {
- return self.getColumnTitle(column) || 'Column ' + (i + 1);
+ return column.getTitle()|| 'Column ' + (i + 1);
});
};
@@ -113,11 +115,11 @@ define(
* @returns {Object} Key value pairs where the key is the column
* title, and the value is the formatted value from the provided datum.
*/
- TableConfiguration.prototype.getRowValues = function (telemetryObject, datum) {
+ TableConfiguration.prototype.getRowValues = function (limitEvaluator, datum) {
var self = this;
return this.columns.reduce(function (rowObject, column, i) {
var columnTitle = self.getColumnTitle(column) || 'Column ' + (i + 1),
- columnValue = column.getValue(telemetryObject, datum);
+ columnValue = column.getValue(datum, limitEvaluator);
if (columnValue !== undefined && columnValue.text === undefined) {
columnValue.text = '';
diff --git a/platform/features/table/src/controllers/HistoricalTableController.js b/platform/features/table/src/controllers/HistoricalTableController.js
deleted file mode 100644
index 0f56f6b4ee..0000000000
--- a/platform/features/table/src/controllers/HistoricalTableController.js
+++ /dev/null
@@ -1,141 +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.
- *****************************************************************************/
-
-define(
- [
- './TelemetryTableController'
- ],
- function (TableController) {
- var BATCH_SIZE = 1000;
-
- /**
- * Extends TelemetryTableController and adds real-time streaming
- * support.
- * @memberof platform/features/table
- * @param $scope
- * @param telemetryHandler
- * @param telemetryFormatter
- * @constructor
- */
- function HistoricalTableController($scope, telemetryHandler, telemetryFormatter, $timeout, openmct) {
- 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, openmct);
- }
-
- HistoricalTableController.prototype = Object.create(TableController.prototype);
-
- /**
- * Set provided row data on scope, and cancel loading spinner
- * @private
- */
- HistoricalTableController.prototype.doneProcessing = function (rowData) {
- this.$scope.rows = rowData;
- this.$scope.loading = false;
- };
-
- /**
- * @private
- */
- HistoricalTableController.prototype.registerChangeListeners = function () {
- TableController.prototype.registerChangeListeners.call(this);
- //Change of bounds in time conductor
- this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
- this.boundsChange.bind(this))
- );
- };
-
- /**
- * @private
- */
- HistoricalTableController.prototype.boundsChange = function (event, bounds, follow) {
- // If in follow mode, don't bother re-subscribing, data will be
- // received from existing subscription.
- if (follow !== true) {
- this.subscribe();
- }
- };
-
- /**
- * 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;
- }
-);
diff --git a/platform/features/table/src/controllers/RealtimeTableController.js b/platform/features/table/src/controllers/RealtimeTableController.js
deleted file mode 100644
index c6ff7b8aee..0000000000
--- a/platform/features/table/src/controllers/RealtimeTableController.js
+++ /dev/null
@@ -1,76 +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.
- *****************************************************************************/
-
-define(
- [
- './TelemetryTableController'
- ],
- function (TableController) {
-
- /**
- * Extends TelemetryTableController and adds real-time streaming
- * support.
- * @memberof platform/features/table
- * @param $scope
- * @param telemetryHandler
- * @param telemetryFormatter
- * @constructor
- */
- function RealtimeTableController($scope, telemetryHandler, telemetryFormatter, openmct) {
- TableController.call(this, $scope, telemetryHandler, telemetryFormatter, openmct);
-
- this.maxRows = 100000;
- }
-
- RealtimeTableController.prototype = Object.create(TableController.prototype);
-
- /**
- * Overrides method on TelemetryTableController providing handling
- * for realtime data.
- */
- RealtimeTableController.prototype.addRealtimeData = function () {
- var self = this,
- datum,
- row;
- this.handle.getTelemetryObjects().forEach(function (telemetryObject) {
- datum = self.handle.getDatum(telemetryObject);
- if (datum) {
- //Populate row values from telemetry datum
- row = self.table.getRowValues(telemetryObject, datum);
- self.$scope.rows.push(row);
-
- //Inform table that a new row has been added
- if (self.$scope.rows.length > self.maxRows) {
- self.$scope.$broadcast('remove:row', 0);
- self.$scope.rows.shift();
- }
-
- self.$scope.$broadcast('add:row',
- self.$scope.rows.length - 1);
- }
- });
- this.$scope.loading = false;
- };
-
- return RealtimeTableController;
- }
-);
diff --git a/platform/features/table/src/controllers/TelemetryTableController.js b/platform/features/table/src/controllers/TelemetryTableController.js
index d845c13d2f..8eea6887dc 100644
--- a/platform/features/table/src/controllers/TelemetryTableController.js
+++ b/platform/features/table/src/controllers/TelemetryTableController.js
@@ -38,14 +38,10 @@ define(
* configuration, and telemetry subscriptions.
* @memberof platform/features/table
* @param $scope
- * @param telemetryHandler
- * @param telemetryFormatter
* @constructor
*/
function TelemetryTableController(
$scope,
- telemetryHandler,
- telemetryFormatter,
openmct
) {
var self = this;
@@ -53,9 +49,8 @@ define(
this.$scope = $scope;
this.columns = {}; //Range and Domain columns
this.handle = undefined;
- this.telemetryHandler = telemetryHandler;
this.table = new TableConfiguration($scope.domainObject,
- telemetryFormatter);
+ openmct);
this.changeListeners = [];
this.conductor = openmct.conductor;
this.openmct = openmct;
@@ -68,6 +63,9 @@ define(
self.subscribe();
self.registerChangeListeners();
});
+ this.mutationListener = openmct.objects.observe(this.newObject, "*", function (domainObject){
+ self.newObject = domainObject;
+ });
this.destroy = this.destroy.bind(this);
@@ -79,6 +77,8 @@ define(
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
this.conductor.on('timeSystem', this.sortByTimeSystem);
this.conductor.off('timeSystem', this.sortByTimeSystem);
+
+ this.subscriptions = [];
}
/**
@@ -130,29 +130,12 @@ define(
* Release the current subscription (called when scope is destroyed)
*/
TelemetryTableController.prototype.destroy = function () {
- if (this.handle) {
- this.handle.unsubscribe();
- this.handle = undefined;
- }
+ this.subscriptions.forEach(function (subscription) {
+ subscription()
+ });
+ this.mutationListener();
};
- /**
- * Function for handling realtime data when it is available. This
- * will be called by the telemetry framework when new data is
- * available.
- *
- * Method should be overridden by specializing class.
- */
- TelemetryTableController.prototype.addRealtimeData = function () {
- };
-
- /**
- * Function for handling historical data. Will be called by
- * telemetry framework when requested historical data is available.
- * Should be overridden by specializing class.
- */
- TelemetryTableController.prototype.addHistoricalData = function () {
- };
/**
Create a new subscription. This can be overridden by children to
@@ -160,94 +143,123 @@ define(
only).
*/
TelemetryTableController.prototype.subscribe = function () {
+ var self = this;
var telemetryApi = this.openmct.telemetry;
+ var compositionApi = this.openmct.composition;
+ var subscriptions = this.subscriptions;
+ var tableConfiguration = this.table;
+ var scope = this.$scope;
+ var maxRows = 100000;
+ var conductor = this.conductor;
+ var newObject = this.newObject;
- if (this.handle) {
- this.handle.unsubscribe();
- }
this.$scope.loading = true;
- function map(func){
- return function (objects) {
- return Promise.all(objects.map(func));
+ function makeTableRows(object, historicalData){
+ var limitEvaluator = telemetryApi.limitEvaluator(object);
+ return historicalData.map(tableConfiguration.getRowValues.bind(tableConfiguration, limitEvaluator));
+ }
+
+ function requestData(objects) {
+ var bounds = conductor.bounds();
+
+ return Promise.all(
+ objects.map(function (object) {
+ return telemetryApi.request(object, {
+ start: bounds.start,
+ end: bounds.end
+ }).then(
+ makeTableRows.bind(this, object)
+ );
+ })
+ );
+ }
+
+ function addHistoricalData(historicalData){
+ scope.rows = Array.prototype.concat.apply([], historicalData);
+ scope.loading = false;
+ }
+
+ function newData(domainObject, datum) {
+ scope.rows.push(tableConfiguration.getRowValues(datum, telemetryApi.limitEvaluator(domainObject)));
+
+ //Inform table that a new row has been added
+ if (scope.rows.length > maxRows) {
+ scope.$broadcast('remove:row', 0);
+ scope.rows.shift();
}
+
+ scope.$broadcast('add:row',
+ scope.rows.length - 1);
+
}
- function add(object){
- return function (objects) {
- objects.unshift(object);
- return objects;
- }
+ function subscribe(objects) {
+ objects.forEach(function (object){
+ subscriptions.push(telemetryApi.subscribe(object, newData.bind(this, object), {}));
+ });
+ return objects;
}
- function subscribeTo(object) {
- return telemetryApi.request(object, {});
+ function error(e) {
+ throw e;
}
- function error() {
- console.log("Unable to subscribe");
+ function loadColumns(objects) {
+ var metadatas = objects.map(telemetryApi.getMetadata.bind(telemetryApi));
+ var allColumns = telemetryApi.commonValuesForHints(metadatas, []);
+
+ tableConfiguration.populateColumns(allColumns);
+
+ this.timeColumns = telemetryApi.commonValuesForHints(metadatas, ['x']).map(function (metadatum){
+ return metadatum.name;
+ });
+
+ self.filterColumns();
+
+ return Promise.resolve(objects);
}
- this.openmct.composition.get(this.newObject)
- .load()
- .then(add(this.newObject))
- .then(map(subscribeTo))
- .then(function (telemetry) {
- console.log(telemetry.length);
- }).catch(error);
-
- this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
- this.$scope.domainObject,
- this.addRealtimeData.bind(this),
- true // Lossless
- );
-
- this.handle.request({}).then(this.addHistoricalData.bind(this));
-
- this.setup();
- };
-
- TelemetryTableController.prototype.populateColumns = function (telemetryMetadata) {
- this.table.populateColumns(telemetryMetadata);
-
- //Identify time columns
- telemetryMetadata.forEach(function (metadatum) {
- //Push domains first
- (metadatum.domains || []).forEach(function (domainMetadata) {
- this.timeColumns.push(domainMetadata.name);
- }.bind(this));
- }.bind(this));
-
- var timeSystem = this.conductor.timeSystem();
- if (timeSystem) {
- this.sortByTimeSystem(timeSystem);
+ function filterForTelemetry(objects){
+ return objects.filter(telemetryApi.canProvideTelemetry.bind(telemetryApi));
}
- };
- /**
- * Setup table columns based on domain object metadata
- */
- TelemetryTableController.prototype.setup = function () {
- var handle = this.handle,
- self = this;
+ function getDomainObjects() {
+ return new Promise(function (resolve, reject){
+ var objects = [newObject];
+ var composition = compositionApi.get(newObject);
- if (handle) {
- this.timeColumns = [];
- handle.promiseTelemetryObjects().then(function () {
- self.$scope.headers = [];
- self.$scope.rows = [];
-
- self.populateColumns(handle.getMetadata());
- self.filterColumns();
-
- // When table column configuration changes, (due to being
- // selected or deselected), filter columns appropriately.
- self.changeListeners.push(self.$scope.$watchCollection(
- 'domainObject.getModel().configuration.table.columns',
- self.filterColumns.bind(self)
- ));
+ if (composition) {
+ composition
+ .load()
+ .then(function (children) {
+ return objects.concat(children);
+ })
+ .then(resolve)
+ .catch(reject);
+ } else {
+ return resolve(objects);
+ }
});
}
+
+ scope.headers = [];
+ scope.rows = [];
+
+ getDomainObjects()
+ .then(filterForTelemetry)
+ .catch(error)
+ .then(function (objects){
+ if (objects.length > 0){
+ return loadColumns(objects)
+ .then(subscribe)
+ .then(requestData)
+ .then(addHistoricalData)
+ .catch(error);
+ } else {
+ scope.loading = false;
+ }
+ })
};
/**