Compare commits
47 Commits
enable-dis
...
time-condu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05764fbf35 | ||
|
|
bdc50564f2 | ||
|
|
d349315944 | ||
|
|
c5034beb9c | ||
|
|
5b97d96b2e | ||
|
|
6a586635c3 | ||
|
|
fc8021c72c | ||
|
|
7fe19d424f | ||
|
|
515c18278b | ||
|
|
9b8518e6d2 | ||
|
|
4639a1eabc | ||
|
|
26f6407283 | ||
|
|
b9fc3d0499 | ||
|
|
5db71db974 | ||
|
|
0b9bdb0c74 | ||
|
|
cdedea4bed | ||
|
|
82096ea887 | ||
|
|
43488646c0 | ||
|
|
71bbb67d78 | ||
|
|
ad96e5ce66 | ||
|
|
1dc245c9ee | ||
|
|
c6c432005f | ||
|
|
8a1a2bbf84 | ||
|
|
0cbb0e7ae2 | ||
|
|
993576a471 | ||
|
|
d69056361f | ||
|
|
30da83eb0a | ||
|
|
e2b4f0a3fa | ||
|
|
6f952789ed | ||
|
|
2bf06dbf23 | ||
|
|
b02bd08a12 | ||
|
|
a9b70c6f9a | ||
|
|
c09fc6110b | ||
|
|
84e45e2543 | ||
|
|
8f50425043 | ||
|
|
f7f1b0d97e | ||
|
|
cb08a82a5d | ||
|
|
d9caa734d3 | ||
|
|
72848849dd | ||
|
|
66130ba542 | ||
|
|
895bdc164f | ||
|
|
c9728144a5 | ||
|
|
76fec7f3bc | ||
|
|
1b4717065a | ||
|
|
e24542c1a6 | ||
|
|
b08f3106ed | ||
|
|
700bc7616d |
@@ -63,7 +63,7 @@ define([
|
|||||||
|
|
||||||
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||||
var start = options.start;
|
var start = options.start;
|
||||||
var end = options.end;
|
var end = Math.min(Date.now(), options.end); // no future values
|
||||||
var duration = domainObject.telemetry.duration * 1000;
|
var duration = domainObject.telemetry.duration * 1000;
|
||||||
if (options.strategy === 'latest' || options.size === 1) {
|
if (options.strategy === 'latest' || options.size === 1) {
|
||||||
start = end;
|
start = end;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
const { TelemetryCollection } = require("./TelemetryCollection");
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../../plugins/displayLayout/CustomStringFormatter',
|
'../../plugins/displayLayout/CustomStringFormatter',
|
||||||
'./TelemetryMetadataManager',
|
'./TelemetryMetadataManager',
|
||||||
@@ -273,6 +275,28 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request telemetry collection for a domain object.
|
||||||
|
* The `options` argument allows you to specify filters
|
||||||
|
* (start, end, etc.), sort order, and strategies for retrieving
|
||||||
|
* telemetry (aggregation, latest available, etc.).
|
||||||
|
*
|
||||||
|
* @method requestTelemetryCollection
|
||||||
|
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||||
|
* @param {module:openmct.DomainObject} domainObject the object
|
||||||
|
* which has associated telemetry
|
||||||
|
* @param {module:openmct.TelemetryAPI~TelemetryRequest} options
|
||||||
|
* options for this telemetry collection request
|
||||||
|
* @returns {TelemetryCollection} a TelemetryCollection instance
|
||||||
|
*/
|
||||||
|
TelemetryAPI.prototype.requestTelemetryCollection = function (domainObject, options = {}) {
|
||||||
|
return new TelemetryCollection(
|
||||||
|
this.openmct,
|
||||||
|
domainObject,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request historical telemetry for a domain object.
|
* Request historical telemetry for a domain object.
|
||||||
* The `options` argument allows you to specify filters
|
* The `options` argument allows you to specify filters
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
449
src/api/telemetry/TelemetryCollection.js
Normal file
449
src/api/telemetry/TelemetryCollection.js
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds class methods
|
||||||
|
*/
|
||||||
|
function methods() {
|
||||||
|
return [
|
||||||
|
'load',
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'hasMorePages',
|
||||||
|
'nextPage',
|
||||||
|
'updateOptions',
|
||||||
|
'destroy',
|
||||||
|
'_requestHistoricalTelemetry',
|
||||||
|
'_initiateHistoricalRequests',
|
||||||
|
'_initiateSubscriptionTelemetry',
|
||||||
|
'_addPage',
|
||||||
|
'_processNewTelemetry',
|
||||||
|
'_bounds',
|
||||||
|
'_timeSystem',
|
||||||
|
'_reset',
|
||||||
|
'_emit',
|
||||||
|
'_watchBounds',
|
||||||
|
'_unwatchBounds',
|
||||||
|
'_watchTimeSystem',
|
||||||
|
'_unwatchTimeSystem'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Class representing a Telemetry Collection. */
|
||||||
|
|
||||||
|
export class TelemetryCollection {
|
||||||
|
/**
|
||||||
|
* Creates a Telemetry Collection
|
||||||
|
*
|
||||||
|
* @param {object} openmct - Openm MCT
|
||||||
|
* @param {object} domainObject - Domain Object to user for telemetry collection
|
||||||
|
* @param {object} options - Any options passed in for request/subscribe
|
||||||
|
*/
|
||||||
|
constructor(openmct, domainObject, options) {
|
||||||
|
methods().forEach(method => this[method] = this[method].bind(this));
|
||||||
|
|
||||||
|
this.loaded = false;
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
|
||||||
|
this.boundedTelemetry = [];
|
||||||
|
this.futureBuffer = [];
|
||||||
|
|
||||||
|
this.parseTime = undefined;
|
||||||
|
this.metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||||
|
|
||||||
|
this.unsubscribe = undefined;
|
||||||
|
this.historicalProvider = undefined;
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.collectionState = undefined;
|
||||||
|
|
||||||
|
this.listeners = {
|
||||||
|
add: [],
|
||||||
|
remove: [],
|
||||||
|
timesystem: [],
|
||||||
|
bounds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This will load start the requests for historical and realtime data,
|
||||||
|
* as well as setting up initial values and watchers
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
if (this.loaded) {
|
||||||
|
throw new Error('Telemetry Collection has already been loaded.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._timeSystem(this.openmct.time.timeSystem());
|
||||||
|
this.lastBounds = this.openmct.time.bounds();
|
||||||
|
|
||||||
|
this._watchBounds();
|
||||||
|
this._watchTimeSystem();
|
||||||
|
|
||||||
|
this._initiateHistoricalRequests();
|
||||||
|
this._initiateSubscriptionTelemetry();
|
||||||
|
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns if there is more telemetry within the time bounds
|
||||||
|
* if the provider supports it
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasMorePages() {
|
||||||
|
return this.historicalProvider
|
||||||
|
&& this.historicalProvider.supportsPaging
|
||||||
|
&& this.historicalProvider.supportsPaging()
|
||||||
|
&& this.historicalProvider.hasMorePages
|
||||||
|
&& this.historicalProvider.hasMorePages(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* will trigger the next page for the provider if it supports it,
|
||||||
|
* _addPage will be passed in as a callback to receive the telemetry and updated state
|
||||||
|
*/
|
||||||
|
nextPage() {
|
||||||
|
if (
|
||||||
|
!this.historicalProvider
|
||||||
|
|| !this.historicalProvider.supportsPaging()
|
||||||
|
|| !this.historicalProvider.nextPage
|
||||||
|
) {
|
||||||
|
throw new Error('Provider does not support paging');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.historicalProvider.nextPage(this._addPage, this.collectionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* provides a way to update the options for the telemetry request and subscription
|
||||||
|
* doing so will trigger a lite reset (no re-reqeustiong data yet) and then a
|
||||||
|
* re-initialization of request and subscription
|
||||||
|
*
|
||||||
|
* @param {Object} options - options to send into request/subscription providers
|
||||||
|
*/
|
||||||
|
updateOptions(options) {
|
||||||
|
const SKIP_RESET_REQUEST = true;
|
||||||
|
|
||||||
|
this._reset(SKIP_RESET_REQUEST);
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
// will update options and providers if necesarry
|
||||||
|
this._initiateHistoricalRequests();
|
||||||
|
this._initiateSubscriptionTelemetry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} event - add, remove
|
||||||
|
* @param {requestCallback} callback - callback to be executed when event happens,
|
||||||
|
* should accept an array of added telemetry data
|
||||||
|
* @param {object} [context] - optional context to use
|
||||||
|
*/
|
||||||
|
on(event, callback, context) {
|
||||||
|
if (!this.listeners[event]) {
|
||||||
|
throw new Error('Event not supported by Telemetry Collections: ' + event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.listeners[event].includes(callback)) {
|
||||||
|
throw new Error('Tried to add a listener that is already registered');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listeners[event].push({
|
||||||
|
callback,
|
||||||
|
context
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} event - add, remove
|
||||||
|
* @param {requestCallback} callback - callback to be executed when event happens,
|
||||||
|
* should accept an array of removed
|
||||||
|
* telemetry data
|
||||||
|
*/
|
||||||
|
off(event, callback) {
|
||||||
|
if (!this.listeners[event]) {
|
||||||
|
throw new Error('Event not supported by Telemetry Collections: ' + event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.listeners[event].includes(callback)) {
|
||||||
|
throw new Error('Tried to remove a listener that does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listeners[event].remove(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* can/should be called by the requester of the telemetry collection
|
||||||
|
* to remove any listeners
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this._unwatchBounds();
|
||||||
|
this._unwatchTimeSystem();
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the telemetry collection for historical requests,
|
||||||
|
* this uses the "standardizeRequestOptions" from Telemetry API
|
||||||
|
*/
|
||||||
|
_initiateHistoricalRequests() {
|
||||||
|
this.openmct.telemetry.standardizeRequestOptions(this.options);
|
||||||
|
this.historicalProvider = this.openmct.telemetry.
|
||||||
|
findRequestProvider(this.domainObject, this.options);
|
||||||
|
|
||||||
|
this._requestHistoricalTelemetry();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* If a historical provider exists, then historical requests will be made
|
||||||
|
*/
|
||||||
|
async _requestHistoricalTelemetry() {
|
||||||
|
if (!this.historicalProvider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let historicalData = await this.historicalProvider.request(this.domainObject, this.options)
|
||||||
|
.catch((rejected) => {
|
||||||
|
this.openmct.notifications.error('Error requesting telemetry data, see console for details');
|
||||||
|
console.error(rejected);
|
||||||
|
|
||||||
|
return Promise.reject(rejected);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Array.isArray(historicalData)) {
|
||||||
|
this._processNewTelemetry(historicalData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This uses the built in subscription function from Telemetry API
|
||||||
|
*/
|
||||||
|
_initiateSubscriptionTelemetry() {
|
||||||
|
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unsubscribe = this.openmct.telemetry
|
||||||
|
.subscribe(this.domainObject, (datum) => {
|
||||||
|
this._processNewTelemetry(datum);
|
||||||
|
}, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* call back for telemetry provider to add more data as well as
|
||||||
|
* pass in the current state of the telemetry collection
|
||||||
|
* (which the telemetry collection will hold)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {Object[]} telemetryData - array of telemetry data objects
|
||||||
|
* @param {*} [collectionState] - providers can pass a collectionState that
|
||||||
|
* will be used for tracking between collection and provider
|
||||||
|
*/
|
||||||
|
_addPage(telemetryData, collectionState) {
|
||||||
|
this.collectionState = collectionState;
|
||||||
|
this._processNewTelemetry(telemetryData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter any new telemetry (add/page, historical, subscription) based on
|
||||||
|
* time bounds
|
||||||
|
*
|
||||||
|
* @param {(Object|Object[])} telemetryData - telemetry data object or
|
||||||
|
* array of telemetry data objects
|
||||||
|
*/
|
||||||
|
_processNewTelemetry(telemetryData) {
|
||||||
|
let data = Array.isArray(telemetryData) ? telemetryData : [telemetryData];
|
||||||
|
let parsedValue;
|
||||||
|
let beforeStartOfBounds;
|
||||||
|
let afterEndOfBounds;
|
||||||
|
let added = [];
|
||||||
|
|
||||||
|
for (let datum of data) {
|
||||||
|
parsedValue = this.parseTime(datum);
|
||||||
|
beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
||||||
|
afterEndOfBounds = parsedValue > this.lastBounds.end;
|
||||||
|
|
||||||
|
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
||||||
|
if (!this.boundedTelemetry.includes(datum)) {
|
||||||
|
this.boundedTelemetry.push(datum);
|
||||||
|
added.push(datum);
|
||||||
|
}
|
||||||
|
} else if (afterEndOfBounds) {
|
||||||
|
this.futureBuffer.push(datum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added.length) {
|
||||||
|
this._emit('add', added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when the start time, end time, or both have been updated.
|
||||||
|
* data could be added OR removed here we update the current
|
||||||
|
* bounded telemetry and emit the results
|
||||||
|
*
|
||||||
|
* @param {TimeConductorBounds} bounds The newly updated bounds
|
||||||
|
* @param {boolean} [tick] `true` if the bounds update was due to
|
||||||
|
* a "tick" event (ie. was an automatic update), false otherwise.
|
||||||
|
*/
|
||||||
|
_bounds(bounds, isTick) {
|
||||||
|
let startChanged = this.lastBounds.start !== bounds.start;
|
||||||
|
let endChanged = this.lastBounds.end !== bounds.end;
|
||||||
|
|
||||||
|
this.lastBounds = bounds;
|
||||||
|
|
||||||
|
this._emit('bounds', ...arguments);
|
||||||
|
|
||||||
|
if (isTick) {
|
||||||
|
// need to check futureBuffer and need to check
|
||||||
|
// if anything has fallen out of bounds
|
||||||
|
let startIndex = 0;
|
||||||
|
let endIndex = 0;
|
||||||
|
|
||||||
|
let discarded = [];
|
||||||
|
let added = [];
|
||||||
|
let testDatum = {};
|
||||||
|
|
||||||
|
if (startChanged) {
|
||||||
|
testDatum[this.timeKey] = bounds.start;
|
||||||
|
// Calculate the new index of the first item within the bounds
|
||||||
|
startIndex = _.sortedIndexBy(this.boundedTelemetry, testDatum, datum => this.parseTime(datum));
|
||||||
|
discarded = this.boundedTelemetry.splice(0, startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endChanged) {
|
||||||
|
testDatum[this.timeKey] = bounds.end;
|
||||||
|
// Calculate the new index of the last item in bounds
|
||||||
|
endIndex = _.sortedLastIndexBy(this.futureBuffer, testDatum, datum => this.parseTime(datum));
|
||||||
|
added = this.futureBuffer.splice(0, endIndex);
|
||||||
|
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discarded.length > 0) {
|
||||||
|
this._emit('remove', discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added.length > 0) {
|
||||||
|
this._emit('add', added);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// user bounds change, reset
|
||||||
|
this._reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whenever the time system is updated need to update related values in
|
||||||
|
* the Telemetry Collection and reset the telemetry collection
|
||||||
|
*
|
||||||
|
* @param {TimeSystem} timeSystem - the value of the currently applied
|
||||||
|
* Time System
|
||||||
|
*/
|
||||||
|
_timeSystem(timeSystem) {
|
||||||
|
this.timeKey = timeSystem.key;
|
||||||
|
let metadataValue = this.metadata.value(this.timeKey) || { format: this.timeKey };
|
||||||
|
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||||
|
|
||||||
|
this.parseTime = (datum) => {
|
||||||
|
return valueFormatter.parse(datum);
|
||||||
|
};
|
||||||
|
|
||||||
|
this._emit('timesystem', timeSystem);
|
||||||
|
this._reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the telemetry data of the collection, and re-request
|
||||||
|
* historical telemetry
|
||||||
|
*
|
||||||
|
* @param {boolean} skipRequest - skip requesting history, default false
|
||||||
|
*
|
||||||
|
* @todo handle subscriptions more granually
|
||||||
|
*/
|
||||||
|
_reset(skipRequest = false) {
|
||||||
|
this.boundedTelemetry = [];
|
||||||
|
this.futureBuffer = [];
|
||||||
|
|
||||||
|
if (skipRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._requestHistoricalTelemetry();
|
||||||
|
// possible unsubscribe/resubscribe...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* will call all the listeners for the event type and pass in the args
|
||||||
|
*
|
||||||
|
* @param {string} event event type, 'add' or 'remove'
|
||||||
|
* @param {arguments} args arguments to pass to the cvent callback
|
||||||
|
*/
|
||||||
|
_emit(event, ...args) {
|
||||||
|
if (!this.listeners[event].length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listeners[event].forEach((listener) => {
|
||||||
|
if (listener.context) {
|
||||||
|
listener.callback.apply(listener.context, args);
|
||||||
|
} else {
|
||||||
|
listener.callback(...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds the _bounds callback to the 'bounds' timeAPI listener
|
||||||
|
*/
|
||||||
|
_watchBounds() {
|
||||||
|
this.openmct.time.on('bounds', this._bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes the _bounds callback from the 'bounds' timeAPI listener
|
||||||
|
*/
|
||||||
|
_unwatchBounds() {
|
||||||
|
this.openmct.time.off('bounds', this._bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds the _timeSystem callback to the 'timeSystem' timeAPI listener
|
||||||
|
*/
|
||||||
|
_watchTimeSystem() {
|
||||||
|
this.openmct.time.on('timeSystem', this._timeSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes the _timeSystem callback from the 'timeSystem' timeAPI listener
|
||||||
|
*/
|
||||||
|
_unwatchTimeSystem() {
|
||||||
|
this.openmct.time.off('timeSystem', this._timeSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
define([
|
define([
|
||||||
'EventEmitter',
|
'EventEmitter',
|
||||||
'lodash',
|
'lodash',
|
||||||
'./collections/BoundedTableRowCollection',
|
'./collections/SortedTableRowCollection',
|
||||||
'./collections/FilteredTableRowCollection',
|
'./collections/FilteredTableRowCollection',
|
||||||
'./TelemetryTableNameColumn',
|
'./TelemetryTableNameColumn',
|
||||||
'./TelemetryTableRow',
|
'./TelemetryTableRow',
|
||||||
@@ -33,7 +33,7 @@ define([
|
|||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
_,
|
_,
|
||||||
BoundedTableRowCollection,
|
SortedTableRowCollection,
|
||||||
FilteredTableRowCollection,
|
FilteredTableRowCollection,
|
||||||
TelemetryTableNameColumn,
|
TelemetryTableNameColumn,
|
||||||
TelemetryTableRow,
|
TelemetryTableRow,
|
||||||
@@ -48,20 +48,21 @@ define([
|
|||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.rowCount = 100;
|
this.rowCount = 100;
|
||||||
this.subscriptions = {};
|
|
||||||
this.tableComposition = undefined;
|
this.tableComposition = undefined;
|
||||||
this.telemetryObjects = [];
|
this.telemetryObjects = [];
|
||||||
this.datumCache = [];
|
this.datumCache = [];
|
||||||
|
this.removeDatumCache = [];
|
||||||
this.outstandingRequests = 0;
|
this.outstandingRequests = 0;
|
||||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
this.telemetryCollections = {};
|
||||||
|
|
||||||
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
||||||
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
|
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
|
||||||
this.isTelemetryObject = this.isTelemetryObject.bind(this);
|
this.isTelemetryObject = this.isTelemetryObject.bind(this);
|
||||||
this.refreshData = this.refreshData.bind(this);
|
this.refreshData = this.refreshData.bind(this);
|
||||||
this.requestDataFor = this.requestDataFor.bind(this);
|
this.requestTelemetryCollectionFor = this.requestTelemetryCollectionFor.bind(this);
|
||||||
this.updateFilters = this.updateFilters.bind(this);
|
this.updateFilters = this.updateFilters.bind(this);
|
||||||
this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
|
this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
|
||||||
|
|
||||||
@@ -101,8 +102,8 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
createTableRowCollections() {
|
createTableRowCollections() {
|
||||||
this.boundedRows = new BoundedTableRowCollection(this.openmct);
|
this.sortedRows = new SortedTableRowCollection(this.openmct);
|
||||||
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
|
this.filteredRows = new FilteredTableRowCollection(this.sortedRows);
|
||||||
|
|
||||||
//Fetch any persisted default sort
|
//Fetch any persisted default sort
|
||||||
let sortOptions = this.configuration.getConfiguration().sortOptions;
|
let sortOptions = this.configuration.getConfiguration().sortOptions;
|
||||||
@@ -115,24 +116,23 @@ define([
|
|||||||
this.filteredRows.sortBy(sortOptions);
|
this.filteredRows.sortBy(sortOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadComposition() {
|
async loadComposition() {
|
||||||
this.tableComposition = this.openmct.composition.get(this.domainObject);
|
this.tableComposition = this.openmct.composition.get(this.domainObject);
|
||||||
|
|
||||||
if (this.tableComposition !== undefined) {
|
if (this.tableComposition !== undefined) {
|
||||||
this.tableComposition.load().then((composition) => {
|
let composition = await this.tableComposition.load();
|
||||||
|
|
||||||
composition = composition.filter(this.isTelemetryObject);
|
composition = composition.filter(this.isTelemetryObject);
|
||||||
composition.forEach(this.addTelemetryObject);
|
composition.forEach(this.addTelemetryObject);
|
||||||
|
|
||||||
this.tableComposition.on('add', this.addTelemetryObject);
|
this.tableComposition.on('add', this.addTelemetryObject);
|
||||||
this.tableComposition.on('remove', this.removeTelemetryObject);
|
this.tableComposition.on('remove', this.removeTelemetryObject);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addTelemetryObject(telemetryObject) {
|
addTelemetryObject(telemetryObject) {
|
||||||
this.addColumnsForObject(telemetryObject, true);
|
this.addColumnsForObject(telemetryObject, true);
|
||||||
this.requestDataFor(telemetryObject);
|
this.requestTelemetryCollectionFor(telemetryObject);
|
||||||
this.subscribeTo(telemetryObject);
|
|
||||||
this.telemetryObjects.push(telemetryObject);
|
this.telemetryObjects.push(telemetryObject);
|
||||||
|
|
||||||
this.emit('object-added', telemetryObject);
|
this.emit('object-added', telemetryObject);
|
||||||
@@ -140,46 +140,107 @@ define([
|
|||||||
|
|
||||||
updateFilters() {
|
updateFilters() {
|
||||||
this.filteredRows.clear();
|
this.filteredRows.clear();
|
||||||
this.boundedRows.clear();
|
this.sortedRows.clear();
|
||||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
|
||||||
|
|
||||||
this.telemetryObjects.forEach(this.requestDataFor.bind(this));
|
this.telemetryObjects.forEach(this.requestTelemetryCollectionFor.bind(this));
|
||||||
this.telemetryObjects.forEach(this.subscribeTo.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTelemetryObject(objectIdentifier) {
|
removeTelemetryObject(objectIdentifier) {
|
||||||
this.configuration.removeColumnsForObject(objectIdentifier, true);
|
this.configuration.removeColumnsForObject(objectIdentifier, true);
|
||||||
let keyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
let keyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||||
this.boundedRows.removeAllRowsForObject(keyString);
|
this.sortedRows.removeAllRowsForObject(keyString);
|
||||||
this.unsubscribe(keyString);
|
this.removeTelemetryCollection(keyString);
|
||||||
this.telemetryObjects = this.telemetryObjects.filter((object) => !_.eq(objectIdentifier, object.identifier));
|
this.telemetryObjects = this.telemetryObjects.filter((object) => !_.eq(objectIdentifier, object.identifier));
|
||||||
|
|
||||||
this.emit('object-removed', objectIdentifier);
|
this.emit('object-removed', objectIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestDataFor(telemetryObject) {
|
requestTelemetryCollectionFor(telemetryObject) {
|
||||||
this.incrementOutstandingRequests();
|
this.incrementOutstandingRequests();
|
||||||
|
|
||||||
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
|
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
|
||||||
|
const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
|
const telemetryProcessor = this.getTelemetryProcessor(telemetryObject, keyString);
|
||||||
|
const telemetryRemover = this.getTelemetryRemover(keyString);
|
||||||
|
|
||||||
return this.openmct.telemetry.request(telemetryObject, requestOptions)
|
this.removeTelemetryCollection(keyString);
|
||||||
.then(telemetryData => {
|
|
||||||
//Check that telemetry object has not been removed since telemetry was requested.
|
|
||||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
this.telemetryCollections[keyString] = this.openmct.telemetry
|
||||||
let columnMap = this.getColumnMapForObject(keyString);
|
.requestTelemetryCollection(telemetryObject, requestOptions);
|
||||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
|
||||||
this.processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator);
|
this.telemetryCollections[keyString].on('remove', telemetryRemover);
|
||||||
}).finally(() => {
|
this.telemetryCollections[keyString].on('add', telemetryProcessor);
|
||||||
this.decrementOutstandingRequests();
|
this.telemetryCollections[keyString].load();
|
||||||
});
|
|
||||||
|
this.decrementOutstandingRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator) {
|
removeTelemetryCollection(keyString) {
|
||||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
if (this.telemetryCollections[keyString]) {
|
||||||
this.boundedRows.add(telemetryRows);
|
this.telemetryCollections[keyString].destroy();
|
||||||
|
this.telemetryCollections[keyString] = undefined;
|
||||||
|
delete this.telemetryCollections[keyString];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTelemetryProcessor(telemetryObject, keyString) {
|
||||||
|
let columnMap = this.getColumnMapForObject(keyString);
|
||||||
|
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||||
|
|
||||||
|
return (telemetry) => {
|
||||||
|
//Check that telemetry object has not been removed since telemetry was requested.
|
||||||
|
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only cache realtime
|
||||||
|
if (this.paused) {
|
||||||
|
let cacheData = {
|
||||||
|
telemetry,
|
||||||
|
columnMap,
|
||||||
|
keyString,
|
||||||
|
limitEvaluator
|
||||||
|
};
|
||||||
|
|
||||||
|
this.datumCache.push(cacheData);
|
||||||
|
} else {
|
||||||
|
this.processTelemetryData(telemetry, columnMap, keyString, limitEvaluator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTelemetryRemover(keyString) {
|
||||||
|
return (telemetry) => {
|
||||||
|
// only cache realtime
|
||||||
|
if (this.paused) {
|
||||||
|
let cacheData = {
|
||||||
|
telemetry,
|
||||||
|
keyString
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeDatumCache.push(cacheData);
|
||||||
|
} else {
|
||||||
|
this.sortedRows.removeRowsFor(telemetry, keyString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
processTelemetryData(telemetryData, columnMap, keyString, limitEvaluator) {
|
||||||
|
let timeKey = this.openmct.time.timeSystem().key;
|
||||||
|
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator, timeKey));
|
||||||
|
this.sortedRows.add(telemetryRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
processDatumCache() {
|
||||||
|
this.datumCache.forEach(cachedDatum => {
|
||||||
|
this.processTelemetryData(cachedDatum.telemetry, cachedDatum.columnMap, cachedDatum.keyString, cachedDatum.limitEvaluator);
|
||||||
|
});
|
||||||
|
this.datumCache = [];
|
||||||
|
|
||||||
|
this.removeDatumCache.forEach(cachedDatum => {
|
||||||
|
this.sortedRows.removeRowsFor(cachedDatum.telemetry, cachedDatum.keyString);
|
||||||
|
});
|
||||||
|
this.removeDatumCache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,15 +268,15 @@ define([
|
|||||||
refreshData(bounds, isTick) {
|
refreshData(bounds, isTick) {
|
||||||
if (!isTick && this.outstandingRequests === 0) {
|
if (!isTick && this.outstandingRequests === 0) {
|
||||||
this.filteredRows.clear();
|
this.filteredRows.clear();
|
||||||
this.boundedRows.clear();
|
this.sortedRows.clear();
|
||||||
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
|
this.sortedRows.sortByTimeSystem(this.openmct.time.timeSystem());
|
||||||
this.telemetryObjects.forEach(this.requestDataFor);
|
this.telemetryObjects.forEach(this.requestTelemetryCollectionFor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearData() {
|
clearData() {
|
||||||
this.filteredRows.clear();
|
this.filteredRows.clear();
|
||||||
this.boundedRows.clear();
|
this.sortedRows.clear();
|
||||||
this.emit('refresh');
|
this.emit('refresh');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,44 +317,6 @@ define([
|
|||||||
return new TelemetryTableUnitColumn(this.openmct, metadatum);
|
return new TelemetryTableUnitColumn(this.openmct, metadatum);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeTo(telemetryObject) {
|
|
||||||
let subscribeOptions = this.buildOptionsFromConfiguration(telemetryObject);
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
|
||||||
let columnMap = this.getColumnMapForObject(keyString);
|
|
||||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
|
||||||
|
|
||||||
this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
|
|
||||||
//Check that telemetry object has not been removed since telemetry was requested.
|
|
||||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.paused) {
|
|
||||||
let realtimeDatum = {
|
|
||||||
datum,
|
|
||||||
columnMap,
|
|
||||||
keyString,
|
|
||||||
limitEvaluator
|
|
||||||
};
|
|
||||||
|
|
||||||
this.datumCache.push(realtimeDatum);
|
|
||||||
} else {
|
|
||||||
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
|
|
||||||
}
|
|
||||||
}, subscribeOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
processDatumCache() {
|
|
||||||
this.datumCache.forEach(cachedDatum => {
|
|
||||||
this.processRealtimeDatum(cachedDatum.datum, cachedDatum.columnMap, cachedDatum.keyString, cachedDatum.limitEvaluator);
|
|
||||||
});
|
|
||||||
this.datumCache = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
processRealtimeDatum(datum, columnMap, keyString, limitEvaluator) {
|
|
||||||
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
|
||||||
}
|
|
||||||
|
|
||||||
isTelemetryObject(domainObject) {
|
isTelemetryObject(domainObject) {
|
||||||
return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry');
|
return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry');
|
||||||
}
|
}
|
||||||
@@ -307,11 +330,6 @@ define([
|
|||||||
return {filters} || {};
|
return {filters} || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(keyString) {
|
|
||||||
this.subscriptions[keyString]();
|
|
||||||
delete this.subscriptions[keyString];
|
|
||||||
}
|
|
||||||
|
|
||||||
sortBy(sortOptions) {
|
sortBy(sortOptions) {
|
||||||
this.filteredRows.sortBy(sortOptions);
|
this.filteredRows.sortBy(sortOptions);
|
||||||
|
|
||||||
@@ -324,19 +342,18 @@ define([
|
|||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
this.paused = true;
|
this.paused = true;
|
||||||
this.boundedRows.unsubscribeFromBounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unpause() {
|
unpause() {
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
this.processDatumCache();
|
this.processDatumCache();
|
||||||
this.boundedRows.subscribeToBounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.boundedRows.destroy();
|
let keystrings = Object.keys(this.telemetryCollections);
|
||||||
|
keystrings.forEach(this.removeTelemetryCollection);
|
||||||
|
|
||||||
this.filteredRows.destroy();
|
this.filteredRows.destroy();
|
||||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
|
||||||
this.openmct.time.off('bounds', this.refreshData);
|
this.openmct.time.off('bounds', this.refreshData);
|
||||||
this.openmct.time.off('timeSystem', this.refreshData);
|
this.openmct.time.off('timeSystem', this.refreshData);
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,14 @@
|
|||||||
|
|
||||||
define([], function () {
|
define([], function () {
|
||||||
class TelemetryTableRow {
|
class TelemetryTableRow {
|
||||||
constructor(datum, columns, objectKeyString, limitEvaluator) {
|
constructor(datum, columns, objectKeyString, limitEvaluator, timeKey) {
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
|
|
||||||
this.datum = createNormalizedDatum(datum, columns);
|
this.datum = createNormalizedDatum(datum, columns);
|
||||||
this.fullDatum = datum;
|
this.fullDatum = datum;
|
||||||
this.limitEvaluator = limitEvaluator;
|
this.limitEvaluator = limitEvaluator;
|
||||||
this.objectKeyString = objectKeyString;
|
this.objectKeyString = objectKeyString;
|
||||||
|
this.rowId = objectKeyString + this.getParsedValue(timeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormattedDatum(headers) {
|
getFormattedDatum(headers) {
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2021, 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(
|
|
||||||
[
|
|
||||||
'lodash',
|
|
||||||
'./SortedTableRowCollection'
|
|
||||||
],
|
|
||||||
function (
|
|
||||||
_,
|
|
||||||
SortedTableRowCollection
|
|
||||||
) {
|
|
||||||
|
|
||||||
class BoundedTableRowCollection extends SortedTableRowCollection {
|
|
||||||
constructor(openmct) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.futureBuffer = new SortedTableRowCollection();
|
|
||||||
this.openmct = openmct;
|
|
||||||
|
|
||||||
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
|
|
||||||
this.bounds = this.bounds.bind(this);
|
|
||||||
|
|
||||||
this.sortByTimeSystem(openmct.time.timeSystem());
|
|
||||||
|
|
||||||
this.lastBounds = openmct.time.bounds();
|
|
||||||
|
|
||||||
this.subscribeToBounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
addOne(item) {
|
|
||||||
let parsedValue = this.getValueForSortColumn(item);
|
|
||||||
// Insert into either in-bounds array, or the future buffer.
|
|
||||||
// Data in the future buffer will be re-evaluated for possible
|
|
||||||
// insertion on next bounds change
|
|
||||||
let beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
|
||||||
let afterEndOfBounds = parsedValue > this.lastBounds.end;
|
|
||||||
|
|
||||||
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
|
||||||
return super.addOne(item);
|
|
||||||
} else if (afterEndOfBounds) {
|
|
||||||
this.futureBuffer.addOne(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortByTimeSystem(timeSystem) {
|
|
||||||
this.sortBy({
|
|
||||||
key: timeSystem.key,
|
|
||||||
direction: 'asc'
|
|
||||||
});
|
|
||||||
let formatter = this.openmct.telemetry.getValueFormatter({
|
|
||||||
key: timeSystem.key,
|
|
||||||
source: timeSystem.key,
|
|
||||||
format: timeSystem.timeFormat
|
|
||||||
});
|
|
||||||
this.parseTime = formatter.parse.bind(formatter);
|
|
||||||
this.futureBuffer.sortBy({
|
|
||||||
key: timeSystem.key,
|
|
||||||
direction: 'asc'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is optimized for ticking - it assumes that start and end
|
|
||||||
* bounds will only increase and as such this cannot be used for decreasing
|
|
||||||
* bounds changes.
|
|
||||||
*
|
|
||||||
* An implication of this is that data will not be discarded that exceeds
|
|
||||||
* the given end bounds. For arbitrary bounds changes, it's assumed that
|
|
||||||
* a telemetry requery is performed anyway, and the collection is cleared
|
|
||||||
* and repopulated.
|
|
||||||
*
|
|
||||||
* @fires TelemetryCollection#added
|
|
||||||
* @fires TelemetryCollection#discarded
|
|
||||||
* @param bounds
|
|
||||||
*/
|
|
||||||
bounds(bounds) {
|
|
||||||
let startChanged = this.lastBounds.start !== bounds.start;
|
|
||||||
let endChanged = this.lastBounds.end !== bounds.end;
|
|
||||||
|
|
||||||
let startIndex = 0;
|
|
||||||
let endIndex = 0;
|
|
||||||
|
|
||||||
let discarded = [];
|
|
||||||
let added = [];
|
|
||||||
let testValue = {
|
|
||||||
datum: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.lastBounds = bounds;
|
|
||||||
|
|
||||||
if (startChanged) {
|
|
||||||
testValue.datum[this.sortOptions.key] = bounds.start;
|
|
||||||
// Calculate the new index of the first item within the bounds
|
|
||||||
startIndex = this.sortedIndex(this.rows, testValue);
|
|
||||||
discarded = this.rows.splice(0, startIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endChanged) {
|
|
||||||
testValue.datum[this.sortOptions.key] = bounds.end;
|
|
||||||
// Calculate the new index of the last item in bounds
|
|
||||||
endIndex = this.sortedLastIndex(this.futureBuffer.rows, testValue);
|
|
||||||
added = this.futureBuffer.rows.splice(0, endIndex);
|
|
||||||
added.forEach((datum) => this.rows.push(datum));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (discarded && discarded.length > 0) {
|
|
||||||
/**
|
|
||||||
* A `discarded` event is emitted when telemetry data fall out of
|
|
||||||
* bounds due to a bounds change event
|
|
||||||
* @type {object[]} discarded the telemetry data
|
|
||||||
* discarded as a result of the bounds change
|
|
||||||
*/
|
|
||||||
this.emit('remove', discarded);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (added && added.length > 0) {
|
|
||||||
/**
|
|
||||||
* An `added` event is emitted when a bounds change results in
|
|
||||||
* received telemetry falling within the new bounds.
|
|
||||||
* @type {object[]} added the telemetry data that is now within bounds
|
|
||||||
*/
|
|
||||||
this.emit('add', added);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getValueForSortColumn(row) {
|
|
||||||
return this.parseTime(row.datum[this.sortOptions.key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsubscribeFromBounds() {
|
|
||||||
this.openmct.time.off('bounds', this.bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribeToBounds() {
|
|
||||||
this.openmct.time.on('bounds', this.bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.unsubscribeFromBounds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return BoundedTableRowCollection;
|
|
||||||
});
|
|
||||||
@@ -29,7 +29,7 @@ define(
|
|||||||
) {
|
) {
|
||||||
class FilteredTableRowCollection extends SortedTableRowCollection {
|
class FilteredTableRowCollection extends SortedTableRowCollection {
|
||||||
constructor(masterCollection) {
|
constructor(masterCollection) {
|
||||||
super();
|
super(masterCollection.openmct);
|
||||||
|
|
||||||
this.masterCollection = masterCollection;
|
this.masterCollection = masterCollection;
|
||||||
this.columnFilters = {};
|
this.columnFilters = {};
|
||||||
|
|||||||
@@ -37,12 +37,17 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
class SortedTableRowCollection extends EventEmitter {
|
class SortedTableRowCollection extends EventEmitter {
|
||||||
constructor() {
|
constructor(openmct) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
this.dupeCheck = false;
|
this.dupeCheck = false;
|
||||||
this.rows = [];
|
this.rows = [];
|
||||||
|
|
||||||
|
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
|
||||||
|
this.sortByTimeSystem(openmct.time.timeSystem());
|
||||||
|
|
||||||
this.add = this.add.bind(this);
|
this.add = this.add.bind(this);
|
||||||
this.remove = this.remove.bind(this);
|
this.remove = this.remove.bind(this);
|
||||||
}
|
}
|
||||||
@@ -162,6 +167,19 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortByTimeSystem(timeSystem) {
|
||||||
|
this.sortBy({
|
||||||
|
key: timeSystem.key,
|
||||||
|
direction: 'asc'
|
||||||
|
});
|
||||||
|
let formatter = this.openmct.telemetry.getValueFormatter({
|
||||||
|
key: timeSystem.key,
|
||||||
|
source: timeSystem.key,
|
||||||
|
format: timeSystem.timeFormat
|
||||||
|
});
|
||||||
|
this.parseTime = formatter.parse.bind(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the telemetry collection based on the provided sort field
|
* Sorts the telemetry collection based on the provided sort field
|
||||||
* specifier. Subsequent inserts are sorted to maintain specified sport
|
* specifier. Subsequent inserts are sorted to maintain specified sport
|
||||||
@@ -227,8 +245,22 @@ define(
|
|||||||
this.emit('remove', removed);
|
this.emit('remove', removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeRowsFor(telemetryData, keystring) {
|
||||||
|
let discarded = [];
|
||||||
|
const rowIdMap = telemetryData.map((datum) => {
|
||||||
|
return keystring + this.parseTime(datum);
|
||||||
|
});
|
||||||
|
|
||||||
|
rowIdMap.forEach(id => {
|
||||||
|
const index = this.rows.findIndex(row => row.rowId === id);
|
||||||
|
discarded = [...discarded, ...this.rows.splice(index, 1)];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit('remove', discarded);
|
||||||
|
}
|
||||||
|
|
||||||
getValueForSortColumn(row) {
|
getValueForSortColumn(row) {
|
||||||
return row.getParsedValue(this.sortOptions.key);
|
return this.parseTime(row.datum[this.sortOptions.key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(removedRows) {
|
remove(removedRows) {
|
||||||
@@ -243,6 +275,10 @@ define(
|
|||||||
return this.rows;
|
return this.rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRowsLength() {
|
||||||
|
return this.rows.length;
|
||||||
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
let removedRows = this.rows;
|
let removedRows = this.rows;
|
||||||
this.rows = [];
|
this.rows = [];
|
||||||
|
|||||||
@@ -471,7 +471,6 @@ export default {
|
|||||||
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
||||||
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
||||||
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
||||||
|
|
||||||
this.table.configuration.on('change', this.updateConfiguration);
|
this.table.configuration.on('change', this.updateConfiguration);
|
||||||
|
|
||||||
this.calculateTableSize();
|
this.calculateTableSize();
|
||||||
@@ -655,7 +654,7 @@ export default {
|
|||||||
* Calculates height based on total number of rows, and sets table height.
|
* Calculates height based on total number of rows, and sets table height.
|
||||||
*/
|
*/
|
||||||
setHeight() {
|
setHeight() {
|
||||||
let filteredRowsLength = this.table.filteredRows.getRows().length;
|
let filteredRowsLength = this.table.filteredRows.getRowsLength();
|
||||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||||
// Set element height directly to avoid having to wait for Vue to update DOM
|
// Set element height directly to avoid having to wait for Vue to update DOM
|
||||||
// which causes subsequent scroll to use an out of date height.
|
// which causes subsequent scroll to use an out of date height.
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ describe("the plugin", () => {
|
|||||||
let tablePlugin;
|
let tablePlugin;
|
||||||
let element;
|
let element;
|
||||||
let child;
|
let child;
|
||||||
|
let historicalProvider;
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
openmct = createOpenMct();
|
openmct = createOpenMct();
|
||||||
@@ -57,7 +58,12 @@ describe("the plugin", () => {
|
|||||||
tablePlugin = new TablePlugin();
|
tablePlugin = new TablePlugin();
|
||||||
openmct.install(tablePlugin);
|
openmct.install(tablePlugin);
|
||||||
|
|
||||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
historicalProvider = {
|
||||||
|
request: () => {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider);
|
||||||
|
|
||||||
element = document.createElement('div');
|
element = document.createElement('div');
|
||||||
child = document.createElement('div');
|
child = document.createElement('div');
|
||||||
@@ -166,11 +172,12 @@ describe("the plugin", () => {
|
|||||||
let telemetryPromise = new Promise((resolve) => {
|
let telemetryPromise = new Promise((resolve) => {
|
||||||
telemetryPromiseResolve = resolve;
|
telemetryPromiseResolve = resolve;
|
||||||
});
|
});
|
||||||
openmct.telemetry.request.and.callFake(() => {
|
|
||||||
|
historicalProvider.request = () => {
|
||||||
telemetryPromiseResolve(testTelemetry);
|
telemetryPromiseResolve(testTelemetry);
|
||||||
|
|
||||||
return telemetryPromise;
|
return telemetryPromise;
|
||||||
});
|
};
|
||||||
|
|
||||||
openmct.router.path = [testTelemetryObject];
|
openmct.router.path = [testTelemetryObject];
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// import moment from 'moment';
|
||||||
|
|
||||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||||
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
|
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
|
||||||
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
|
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
|
||||||
const DEFAULT_RECORDS = 10;
|
const DEFAULT_RECORDS = 10;
|
||||||
|
const ONE_MINUTE = 60 * 1000;
|
||||||
|
const ONE_HOUR = ONE_MINUTE * 60;
|
||||||
|
const EIGHT_HOURS = 8 * ONE_HOUR;
|
||||||
|
const TWENTYFOUR_HOURS = EIGHT_HOURS * 3;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
@@ -135,10 +141,20 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
getHistoryMenuItems() {
|
getHistoryMenuItems() {
|
||||||
const history = this.historyForCurrentTimeSystem.map(timespan => {
|
const history = this.historyForCurrentTimeSystem.map(timespan => {
|
||||||
|
let name;
|
||||||
|
let startTime = this.formatTime(timespan.start);
|
||||||
|
let description = `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`;
|
||||||
|
|
||||||
|
if (this.timeSystem.isUTCBased && !this.openmct.time.clock()) {
|
||||||
|
name = `Starting ${startTime} ${this.getDuration(timespan.end - timespan.start)}`;
|
||||||
|
} else {
|
||||||
|
name = `${startTime} - ${this.formatTime(timespan.end)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cssClass: 'icon-history',
|
cssClass: 'icon-history',
|
||||||
name: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
name,
|
||||||
description: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
description,
|
||||||
callBack: () => this.selectTimespan(timespan)
|
callBack: () => this.selectTimespan(timespan)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -163,6 +179,23 @@ export default {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getDuration(numericDuration) {
|
||||||
|
let result;
|
||||||
|
let age;
|
||||||
|
|
||||||
|
if (numericDuration > TWENTYFOUR_HOURS) {
|
||||||
|
age = (numericDuration / TWENTYFOUR_HOURS).toFixed(2);
|
||||||
|
result = ` for ${age} days`;
|
||||||
|
} else if (numericDuration > ONE_HOUR) {
|
||||||
|
age = (numericDuration / ONE_HOUR).toFixed(2);
|
||||||
|
result = ` for ${age} hours`;
|
||||||
|
} else {
|
||||||
|
age = (numericDuration / ONE_MINUTE).toFixed(2);
|
||||||
|
result = ` for ${age} mins`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
getHistoryFromLocalStorage() {
|
getHistoryFromLocalStorage() {
|
||||||
const localStorageHistory = localStorage.getItem(this.storageKey);
|
const localStorageHistory = localStorage.getItem(this.storageKey);
|
||||||
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
|
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user