Compare commits
47 Commits
compass-ro
...
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) {
|
||||
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;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const { TelemetryCollection } = require("./TelemetryCollection");
|
||||
|
||||
define([
|
||||
'../../plugins/displayLayout/CustomStringFormatter',
|
||||
'./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.
|
||||
* 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([
|
||||
'EventEmitter',
|
||||
'lodash',
|
||||
'./collections/BoundedTableRowCollection',
|
||||
'./collections/SortedTableRowCollection',
|
||||
'./collections/FilteredTableRowCollection',
|
||||
'./TelemetryTableNameColumn',
|
||||
'./TelemetryTableRow',
|
||||
@@ -33,7 +33,7 @@ define([
|
||||
], function (
|
||||
EventEmitter,
|
||||
_,
|
||||
BoundedTableRowCollection,
|
||||
SortedTableRowCollection,
|
||||
FilteredTableRowCollection,
|
||||
TelemetryTableNameColumn,
|
||||
TelemetryTableRow,
|
||||
@@ -48,20 +48,21 @@ define([
|
||||
this.domainObject = domainObject;
|
||||
this.openmct = openmct;
|
||||
this.rowCount = 100;
|
||||
this.subscriptions = {};
|
||||
this.tableComposition = undefined;
|
||||
this.telemetryObjects = [];
|
||||
this.datumCache = [];
|
||||
this.removeDatumCache = [];
|
||||
this.outstandingRequests = 0;
|
||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||
this.paused = false;
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.telemetryCollections = {};
|
||||
|
||||
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
||||
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
|
||||
this.isTelemetryObject = this.isTelemetryObject.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.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
|
||||
|
||||
@@ -101,8 +102,8 @@ define([
|
||||
}
|
||||
|
||||
createTableRowCollections() {
|
||||
this.boundedRows = new BoundedTableRowCollection(this.openmct);
|
||||
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
|
||||
this.sortedRows = new SortedTableRowCollection(this.openmct);
|
||||
this.filteredRows = new FilteredTableRowCollection(this.sortedRows);
|
||||
|
||||
//Fetch any persisted default sort
|
||||
let sortOptions = this.configuration.getConfiguration().sortOptions;
|
||||
@@ -115,24 +116,23 @@ define([
|
||||
this.filteredRows.sortBy(sortOptions);
|
||||
}
|
||||
|
||||
loadComposition() {
|
||||
async loadComposition() {
|
||||
this.tableComposition = this.openmct.composition.get(this.domainObject);
|
||||
|
||||
if (this.tableComposition !== undefined) {
|
||||
this.tableComposition.load().then((composition) => {
|
||||
let composition = await this.tableComposition.load();
|
||||
|
||||
composition = composition.filter(this.isTelemetryObject);
|
||||
composition.forEach(this.addTelemetryObject);
|
||||
composition = composition.filter(this.isTelemetryObject);
|
||||
composition.forEach(this.addTelemetryObject);
|
||||
|
||||
this.tableComposition.on('add', this.addTelemetryObject);
|
||||
this.tableComposition.on('remove', this.removeTelemetryObject);
|
||||
});
|
||||
this.tableComposition.on('add', this.addTelemetryObject);
|
||||
this.tableComposition.on('remove', this.removeTelemetryObject);
|
||||
}
|
||||
}
|
||||
|
||||
addTelemetryObject(telemetryObject) {
|
||||
this.addColumnsForObject(telemetryObject, true);
|
||||
this.requestDataFor(telemetryObject);
|
||||
this.subscribeTo(telemetryObject);
|
||||
this.requestTelemetryCollectionFor(telemetryObject);
|
||||
this.telemetryObjects.push(telemetryObject);
|
||||
|
||||
this.emit('object-added', telemetryObject);
|
||||
@@ -140,46 +140,107 @@ define([
|
||||
|
||||
updateFilters() {
|
||||
this.filteredRows.clear();
|
||||
this.boundedRows.clear();
|
||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
||||
this.sortedRows.clear();
|
||||
|
||||
this.telemetryObjects.forEach(this.requestDataFor.bind(this));
|
||||
this.telemetryObjects.forEach(this.subscribeTo.bind(this));
|
||||
this.telemetryObjects.forEach(this.requestTelemetryCollectionFor.bind(this));
|
||||
}
|
||||
|
||||
removeTelemetryObject(objectIdentifier) {
|
||||
this.configuration.removeColumnsForObject(objectIdentifier, true);
|
||||
let keyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||
this.boundedRows.removeAllRowsForObject(keyString);
|
||||
this.unsubscribe(keyString);
|
||||
this.sortedRows.removeAllRowsForObject(keyString);
|
||||
this.removeTelemetryCollection(keyString);
|
||||
this.telemetryObjects = this.telemetryObjects.filter((object) => !_.eq(objectIdentifier, object.identifier));
|
||||
|
||||
this.emit('object-removed', objectIdentifier);
|
||||
}
|
||||
|
||||
requestDataFor(telemetryObject) {
|
||||
requestTelemetryCollectionFor(telemetryObject) {
|
||||
this.incrementOutstandingRequests();
|
||||
|
||||
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)
|
||||
.then(telemetryData => {
|
||||
//Check that telemetry object has not been removed since telemetry was requested.
|
||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||
return;
|
||||
}
|
||||
this.removeTelemetryCollection(keyString);
|
||||
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let columnMap = this.getColumnMapForObject(keyString);
|
||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
this.processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator);
|
||||
}).finally(() => {
|
||||
this.decrementOutstandingRequests();
|
||||
});
|
||||
this.telemetryCollections[keyString] = this.openmct.telemetry
|
||||
.requestTelemetryCollection(telemetryObject, requestOptions);
|
||||
|
||||
this.telemetryCollections[keyString].on('remove', telemetryRemover);
|
||||
this.telemetryCollections[keyString].on('add', telemetryProcessor);
|
||||
this.telemetryCollections[keyString].load();
|
||||
|
||||
this.decrementOutstandingRequests();
|
||||
}
|
||||
|
||||
processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator) {
|
||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows.add(telemetryRows);
|
||||
removeTelemetryCollection(keyString) {
|
||||
if (this.telemetryCollections[keyString]) {
|
||||
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) {
|
||||
if (!isTick && this.outstandingRequests === 0) {
|
||||
this.filteredRows.clear();
|
||||
this.boundedRows.clear();
|
||||
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
|
||||
this.telemetryObjects.forEach(this.requestDataFor);
|
||||
this.sortedRows.clear();
|
||||
this.sortedRows.sortByTimeSystem(this.openmct.time.timeSystem());
|
||||
this.telemetryObjects.forEach(this.requestTelemetryCollectionFor);
|
||||
}
|
||||
}
|
||||
|
||||
clearData() {
|
||||
this.filteredRows.clear();
|
||||
this.boundedRows.clear();
|
||||
this.sortedRows.clear();
|
||||
this.emit('refresh');
|
||||
}
|
||||
|
||||
@@ -256,44 +317,6 @@ define([
|
||||
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) {
|
||||
return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry');
|
||||
}
|
||||
@@ -307,11 +330,6 @@ define([
|
||||
return {filters} || {};
|
||||
}
|
||||
|
||||
unsubscribe(keyString) {
|
||||
this.subscriptions[keyString]();
|
||||
delete this.subscriptions[keyString];
|
||||
}
|
||||
|
||||
sortBy(sortOptions) {
|
||||
this.filteredRows.sortBy(sortOptions);
|
||||
|
||||
@@ -324,19 +342,18 @@ define([
|
||||
|
||||
pause() {
|
||||
this.paused = true;
|
||||
this.boundedRows.unsubscribeFromBounds();
|
||||
}
|
||||
|
||||
unpause() {
|
||||
this.paused = false;
|
||||
this.processDatumCache();
|
||||
this.boundedRows.subscribeToBounds();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.boundedRows.destroy();
|
||||
let keystrings = Object.keys(this.telemetryCollections);
|
||||
keystrings.forEach(this.removeTelemetryCollection);
|
||||
|
||||
this.filteredRows.destroy();
|
||||
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.off('timeSystem', this.refreshData);
|
||||
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
|
||||
define([], function () {
|
||||
class TelemetryTableRow {
|
||||
constructor(datum, columns, objectKeyString, limitEvaluator) {
|
||||
constructor(datum, columns, objectKeyString, limitEvaluator, timeKey) {
|
||||
this.columns = columns;
|
||||
|
||||
this.datum = createNormalizedDatum(datum, columns);
|
||||
this.fullDatum = datum;
|
||||
this.limitEvaluator = limitEvaluator;
|
||||
this.objectKeyString = objectKeyString;
|
||||
this.rowId = objectKeyString + this.getParsedValue(timeKey);
|
||||
}
|
||||
|
||||
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 {
|
||||
constructor(masterCollection) {
|
||||
super();
|
||||
super(masterCollection.openmct);
|
||||
|
||||
this.masterCollection = masterCollection;
|
||||
this.columnFilters = {};
|
||||
|
||||
@@ -37,12 +37,17 @@ define(
|
||||
* @constructor
|
||||
*/
|
||||
class SortedTableRowCollection extends EventEmitter {
|
||||
constructor() {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
|
||||
this.openmct = openmct;
|
||||
|
||||
this.dupeCheck = false;
|
||||
this.rows = [];
|
||||
|
||||
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
|
||||
this.sortByTimeSystem(openmct.time.timeSystem());
|
||||
|
||||
this.add = this.add.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
|
||||
* specifier. Subsequent inserts are sorted to maintain specified sport
|
||||
@@ -227,8 +245,22 @@ define(
|
||||
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) {
|
||||
return row.getParsedValue(this.sortOptions.key);
|
||||
return this.parseTime(row.datum[this.sortOptions.key]);
|
||||
}
|
||||
|
||||
remove(removedRows) {
|
||||
@@ -243,6 +275,10 @@ define(
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
getRowsLength() {
|
||||
return this.rows.length;
|
||||
}
|
||||
|
||||
clear() {
|
||||
let removedRows = this.rows;
|
||||
this.rows = [];
|
||||
|
||||
@@ -471,7 +471,6 @@ export default {
|
||||
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
||||
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
||||
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
||||
|
||||
this.table.configuration.on('change', this.updateConfiguration);
|
||||
|
||||
this.calculateTableSize();
|
||||
@@ -655,7 +654,7 @@ export default {
|
||||
* Calculates height based on total number of rows, and sets table height.
|
||||
*/
|
||||
setHeight() {
|
||||
let filteredRowsLength = this.table.filteredRows.getRows().length;
|
||||
let filteredRowsLength = this.table.filteredRows.getRowsLength();
|
||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||
// 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.
|
||||
|
||||
@@ -48,6 +48,7 @@ describe("the plugin", () => {
|
||||
let tablePlugin;
|
||||
let element;
|
||||
let child;
|
||||
let historicalProvider;
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
@@ -57,7 +58,12 @@ describe("the plugin", () => {
|
||||
tablePlugin = new 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');
|
||||
child = document.createElement('div');
|
||||
@@ -166,11 +172,12 @@ describe("the plugin", () => {
|
||||
let telemetryPromise = new Promise((resolve) => {
|
||||
telemetryPromiseResolve = resolve;
|
||||
});
|
||||
openmct.telemetry.request.and.callFake(() => {
|
||||
|
||||
historicalProvider.request = () => {
|
||||
telemetryPromiseResolve(testTelemetry);
|
||||
|
||||
return telemetryPromise;
|
||||
});
|
||||
};
|
||||
|
||||
openmct.router.path = [testTelemetryObject];
|
||||
|
||||
|
||||
@@ -35,10 +35,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import moment from 'moment';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
|
||||
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
|
||||
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 {
|
||||
inject: ['openmct', 'configuration'],
|
||||
@@ -135,10 +141,20 @@ export default {
|
||||
methods: {
|
||||
getHistoryMenuItems() {
|
||||
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 {
|
||||
cssClass: 'icon-history',
|
||||
name: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
||||
description: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
||||
name,
|
||||
description,
|
||||
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() {
|
||||
const localStorageHistory = localStorage.getItem(this.storageKey);
|
||||
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
|
||||
|
||||
Reference in New Issue
Block a user