[Greedy LAD] Add new functionality for Latest Available Data views (#6432)

* adding greedyLAD logic to telemetry collections
---------

Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Jamie V 2023-03-16 19:12:11 -07:00 committed by GitHub
parent 25c0dab346
commit 39463c515f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 18 deletions

View File

@ -29,6 +29,7 @@ import DefaultMetadataProvider from './DefaultMetadataProvider';
import objectUtils from 'objectUtils';
export default class TelemetryAPI {
#isGreedyLAD;
constructor(openmct) {
this.openmct = openmct;
@ -44,8 +45,8 @@ export default class TelemetryAPI {
this.requestProviders = [];
this.subscriptionProviders = [];
this.valueFormatterCache = new WeakMap();
this.requestInterceptorRegistry = new TelemetryRequestInterceptorRegistry();
this.#isGreedyLAD = true;
}
abortAllRequests() {
@ -226,6 +227,31 @@ export default class TelemetryAPI {
return modifiedRequest;
}
/**
* Get or set greedy LAD. For stategy "latest" telemetry in
* realtime mode the start bound will be ignored if true and
* there is no new data to replace the existing data.
* defaults to true
*
* To turn off greedy LAD:
* openmct.telemetry.greedyLAD(false);
*
* @method greedyLAD
* @returns {boolean} if greedyLAD is active or not
* @memberof module:openmct.TelemetryAPI#
*/
greedyLAD(isGreedy) {
if (arguments.length > 0) {
if (isGreedy !== true && isGreedy !== false) {
throw new Error('Error setting greedyLAD. Greedy LAD only accepts true or false values');
}
this.#isGreedyLAD = isGreedy;
}
return this.#isGreedyLAD;
}
/**
* Request telemetry collection for a domain object.
* The `options` argument allows you to specify filters

View File

@ -30,8 +30,8 @@ export default class TelemetryCollection extends EventEmitter {
/**
* Creates a Telemetry Collection
*
* @param {object} openmct - Openm MCT
* @param {object} domainObject - Domain Object to user for telemetry collection
* @param {OpenMCT} openmct - Open MCT
* @param {module:openmct.DomainObject} domainObject - Domain Object to use for telemetry collection
* @param {object} options - Any options passed in for request/subscribe
*/
constructor(openmct, domainObject, options) {
@ -50,6 +50,7 @@ export default class TelemetryCollection extends EventEmitter {
this.lastBounds = undefined;
this.requestAbort = undefined;
this.isStrategyLatest = this.options.strategy === 'latest';
this.dataOutsideTimeBounds = false;
}
/**
@ -184,6 +185,7 @@ export default class TelemetryCollection extends EventEmitter {
let afterEndOfBounds;
let added = [];
let addedIndices = [];
let hasDataBeforeStartBound = false;
// loop through, sort and dedupe
for (let datum of data) {
@ -191,7 +193,7 @@ export default class TelemetryCollection extends EventEmitter {
beforeStartOfBounds = parsedValue < this.lastBounds.start;
afterEndOfBounds = parsedValue > this.lastBounds.end;
if (!afterEndOfBounds && !beforeStartOfBounds) {
if (!afterEndOfBounds && (!beforeStartOfBounds || (this.isStrategyLatest && this.openmct.telemetry.greedyLAD()))) {
let isDuplicate = false;
let startIndex = this._sortedIndex(datum);
let endIndex = undefined;
@ -218,6 +220,10 @@ export default class TelemetryCollection extends EventEmitter {
this.boundedTelemetry.splice(index, 0, datum);
addedIndices.push(index);
added.push(datum);
if (!hasDataBeforeStartBound && beforeStartOfBounds) {
hasDataBeforeStartBound = true;
}
}
} else if (afterEndOfBounds) {
@ -226,12 +232,18 @@ export default class TelemetryCollection extends EventEmitter {
}
if (added.length) {
// if latest strategy is requested, we need to check if the value is the latest unmitted value
// if latest strategy is requested, we need to check if the value is the latest unemitted value
if (this.isStrategyLatest) {
this.boundedTelemetry = [this.boundedTelemetry[this.boundedTelemetry.length - 1]];
// if true, then this value has yet to be emitted
if (this.boundedTelemetry[0] !== latestBoundedDatum) {
if (hasDataBeforeStartBound) {
this._handleDataOutsideBounds();
} else {
this._handleDataInsideBounds();
}
this.emit('add', this.boundedTelemetry);
}
} else {
@ -294,6 +306,17 @@ export default class TelemetryCollection extends EventEmitter {
let added = [];
let testDatum = {};
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);
}
if (startChanged) {
testDatum[this.timeKey] = bounds.start;
@ -307,20 +330,19 @@ export default class TelemetryCollection extends EventEmitter {
);
discarded = this.boundedTelemetry.splice(0, startIndex);
} else if (this.parseTime(testDatum) > this.parseTime(this.boundedTelemetry[0])) {
discarded = this.boundedTelemetry;
this.boundedTelemetry = [];
}
}
// if greedyLAD is active and there is no new data to replace, don't discard
const isGreedyLAD = this.openmct.telemetry.greedyLAD();
const shouldRemove = (!isGreedyLAD || (isGreedyLAD && added.length > 0));
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);
if (shouldRemove) {
discarded = this.boundedTelemetry;
this.boundedTelemetry = [];
// since it IS strategy latest, we can assume there will be at least 1 datum
// unless no data was returned in the first request, we need to account for that
} else if (this.boundedTelemetry.length === 1) {
this._handleDataOutsideBounds();
}
}
}
if (discarded.length > 0) {
@ -331,6 +353,8 @@ export default class TelemetryCollection extends EventEmitter {
if (!this.isStrategyLatest) {
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
} else {
this._handleDataInsideBounds();
added = [added[added.length - 1]];
this.boundedTelemetry = added;
}
@ -345,6 +369,20 @@ export default class TelemetryCollection extends EventEmitter {
}
_handleDataInsideBounds() {
if (this.dataOutsideTimeBounds) {
this.dataOutsideTimeBounds = false;
this.emit('dataInsideTimeBounds');
}
}
_handleDataOutsideBounds() {
if (!this.dataOutsideTimeBounds) {
this.dataOutsideTimeBounds = true;
this.emit('dataOutsideTimeBounds');
}
}
/**
* whenever the time system is updated need to update related values in
* the Telemetry Collection and reset the telemetry collection