mirror of
https://github.com/nasa/openmct.git
synced 2025-03-29 07:06:05 +00:00
* Change the mount utility to use Vue's createApp and defineComponent methods * Fix display layout memory leaks caused by `getSelectionContext` * fix some display layout leaks due to use of slots * Fix imagery memory leak (removed span tag). NOTE: CompassRose svg leaks memory - must test on firefox to see if this is a Chrome leak. * Fix ActionsAPI action collection and applicable actions leak. * Fix flexible layout memory leaks - remove listeners on unmount. NOTE: One type of overlay plot (Rover Yaw) is still leaking. * pass in the el on mount * e2e test config and spec changes * Remove mounting of limit lines. Use components directly * test: remove `.only()` * Fix display layout memory leaks * Enable passing tests * e2e README and appActions should be what master has. * lint: add word to cspell list * lint: fixes * lint:fix * fix: revert `el` change * fix: remove empty span * fix: creating shapes in displayLayout * fix: avoid `splice` as it loses reactivity * test: reduce timeout time * quick fixes * add prod mode and convert the test config to select the correct mode * Fix webpack prod config * Add launch flag for exposing window.gc * never worked * explicit naming * rename * We don't need to destroy view providers * test: increase timeout time * test: unskip all mem tests * fix(vue-loader): disable static hoisting * chore: run `test:perf:memory` * Don't destroy view providers * Move context menu once listener to beforeUnmount instead. * Disconnect all resize observers on unmount * Delete Test vue component * Use beforeUnmount and remove splice(0) in favor of [] for emptying arrays * re-structure * fix: unregister listener in pane.vue * test: tweak timeouts * chore: lint:fix * test: unskip perf tests * fix: unregister events properly * fix: unregister listener * fix: unregister listener * fix: unregister listener * fix: use `unmounted()` * fix: unregister listeners * fix: unregister listener properly * chore: lint:fix * test: fix imagery layer toggle test * test: increase timeout * Don't use anonymous functions for listeners * Destroy objects and event listeners properly * Delete config stores that are created by components * Use the right unmount hook. Destroy mounted view on unmount. * Use unmounted, not beforeUnmounted * Lint fixes * Fix time strip memory leak * Undo unneeded change for memory leaks. * chore: combine common webpack configs --------- Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov> Co-authored-by: John Hill <john.c.hill@nasa.gov>
343 lines
11 KiB
JavaScript
343 lines
11 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT, Copyright (c) 2014-2023, 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 EventEmitter from 'EventEmitter';
|
|
import { v4 as uuid } from 'uuid';
|
|
|
|
import AllTelemetryCriterion from './criterion/AllTelemetryCriterion';
|
|
import TelemetryCriterion from './criterion/TelemetryCriterion';
|
|
import { TRIGGER_CONJUNCTION, TRIGGER_LABEL } from './utils/constants';
|
|
import { evaluateResults } from './utils/evaluator';
|
|
import { getLatestTimestamp } from './utils/time';
|
|
|
|
/*
|
|
* conditionConfiguration = {
|
|
* id: uuid,
|
|
* trigger: 'any'/'all'/'not','xor',
|
|
* criteria: [
|
|
* {
|
|
* telemetry: '',
|
|
* operation: '',
|
|
* input: [],
|
|
* metadata: ''
|
|
* }
|
|
* ]
|
|
* }
|
|
*/
|
|
export default class Condition extends EventEmitter {
|
|
/**
|
|
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
|
|
* @constructor
|
|
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
|
|
* @param openmct
|
|
* @param conditionManager
|
|
*/
|
|
constructor(conditionConfiguration, openmct, conditionManager) {
|
|
super();
|
|
|
|
this.openmct = openmct;
|
|
this.conditionManager = conditionManager;
|
|
this.id = conditionConfiguration.id;
|
|
this.criteria = [];
|
|
this.result = undefined;
|
|
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
|
if (conditionConfiguration.configuration.criteria) {
|
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
|
}
|
|
|
|
this.trigger = conditionConfiguration.configuration.trigger;
|
|
this.summary = '';
|
|
this.handleCriterionUpdated = this.handleCriterionUpdated.bind(this);
|
|
this.handleOldTelemetryCriterion = this.handleOldTelemetryCriterion.bind(this);
|
|
this.handleTelemetryStaleness = this.handleTelemetryStaleness.bind(this);
|
|
}
|
|
|
|
updateResult(datum) {
|
|
if (!datum || !datum.id) {
|
|
console.log('no data received');
|
|
|
|
return;
|
|
}
|
|
|
|
// if all the criteria in this condition have no telemetry, we want to force the condition result to evaluate
|
|
if (this.hasNoTelemetry() || this.isTelemetryUsed(datum.id)) {
|
|
this.criteria.forEach((criterion) => {
|
|
if (this.isAnyOrAllTelemetry(criterion)) {
|
|
criterion.updateResult(datum, this.conditionManager.telemetryObjects);
|
|
} else {
|
|
if (criterion.usesTelemetry(datum.id)) {
|
|
criterion.updateResult(datum);
|
|
}
|
|
}
|
|
});
|
|
|
|
this.result = evaluateResults(
|
|
this.criteria.map((criterion) => criterion.result),
|
|
this.trigger
|
|
);
|
|
}
|
|
}
|
|
|
|
isAnyOrAllTelemetry(criterion) {
|
|
return criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any');
|
|
}
|
|
|
|
hasNoTelemetry() {
|
|
return this.criteria.every((criterion) => {
|
|
return !this.isAnyOrAllTelemetry(criterion) && criterion.telemetry === '';
|
|
});
|
|
}
|
|
|
|
isTelemetryUsed(id) {
|
|
return this.criteria.some((criterion) => {
|
|
return this.isAnyOrAllTelemetry(criterion) || criterion.usesTelemetry(id);
|
|
});
|
|
}
|
|
|
|
update(conditionConfiguration) {
|
|
this.updateTrigger(conditionConfiguration.configuration.trigger);
|
|
this.updateCriteria(conditionConfiguration.configuration.criteria);
|
|
}
|
|
|
|
updateTrigger(trigger) {
|
|
if (this.trigger !== trigger) {
|
|
this.trigger = trigger;
|
|
}
|
|
}
|
|
|
|
generateCriterion(criterionConfiguration) {
|
|
return {
|
|
id: criterionConfiguration.id || uuid(),
|
|
telemetry: criterionConfiguration.telemetry || '',
|
|
telemetryObjects: this.conditionManager.telemetryObjects,
|
|
operation: criterionConfiguration.operation || '',
|
|
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
|
|
metadata: criterionConfiguration.metadata || ''
|
|
};
|
|
}
|
|
|
|
createCriteria(criterionConfigurations) {
|
|
criterionConfigurations.forEach((criterionConfiguration) => {
|
|
this.addCriterion(criterionConfiguration);
|
|
});
|
|
}
|
|
|
|
updateCriteria(criterionConfigurations) {
|
|
this.destroyCriteria();
|
|
this.createCriteria(criterionConfigurations);
|
|
}
|
|
|
|
updateTelemetryObjects() {
|
|
this.criteria.forEach((criterion) => {
|
|
criterion.updateTelemetryObjects(this.conditionManager.telemetryObjects);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* adds criterion to the condition.
|
|
*/
|
|
addCriterion(criterionConfiguration) {
|
|
let criterion;
|
|
let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
|
|
if (
|
|
criterionConfiguration.telemetry &&
|
|
(criterionConfiguration.telemetry === 'any' || criterionConfiguration.telemetry === 'all')
|
|
) {
|
|
criterion = new AllTelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
|
} else {
|
|
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
|
}
|
|
|
|
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
|
criterion.on('telemetryIsOld', (obj) => this.handleOldTelemetryCriterion(obj));
|
|
criterion.on('telemetryStaleness', () => this.handleTelemetryStaleness());
|
|
if (!this.criteria) {
|
|
this.criteria = [];
|
|
}
|
|
|
|
this.criteria.push(criterion);
|
|
|
|
return criterionConfigurationWithId.id;
|
|
}
|
|
|
|
findCriterion(id) {
|
|
let criterion;
|
|
|
|
for (let i = 0, ii = this.criteria.length; i < ii; i++) {
|
|
if (this.criteria[i].id === id) {
|
|
criterion = {
|
|
item: this.criteria[i],
|
|
index: i
|
|
};
|
|
}
|
|
}
|
|
|
|
return criterion;
|
|
}
|
|
|
|
updateCriterion(id, criterionConfiguration) {
|
|
let found = this.findCriterion(id);
|
|
if (found) {
|
|
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
|
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
|
newCriterion.on('criterionUpdated', this.handleCriterionUpdated);
|
|
newCriterion.on('telemetryIsOld', this.handleOldTelemetryCriterion);
|
|
newCriterion.on('telemetryStaleness', this.handleTelemetryStaleness);
|
|
|
|
let criterion = found.item;
|
|
criterion.unsubscribe();
|
|
criterion.off('criterionUpdated', this.handleCriterionUpdated);
|
|
criterion.off('telemetryIsOld', this.handleOldTelemetryCriterion);
|
|
newCriterion.off('telemetryStaleness', this.handleTelemetryStaleness);
|
|
this.criteria.splice(found.index, 1, newCriterion);
|
|
}
|
|
}
|
|
|
|
destroyCriterion(id) {
|
|
let found = this.findCriterion(id);
|
|
if (found) {
|
|
let criterion = found.item;
|
|
criterion.off('criterionUpdated', this.handleCriterionUpdated);
|
|
criterion.off('telemetryIsOld', this.handleOldTelemetryCriterion);
|
|
criterion.off('telemetryStaleness', this.handleTelemetryStaleness);
|
|
criterion.destroy();
|
|
this.criteria.splice(found.index, 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
handleCriterionUpdated(criterion) {
|
|
let found = this.findCriterion(criterion.id);
|
|
if (found) {
|
|
this.criteria[found.index] = criterion.data;
|
|
}
|
|
}
|
|
|
|
handleOldTelemetryCriterion(updatedCriterion) {
|
|
this.result = evaluateResults(
|
|
this.criteria.map((criterion) => criterion.result),
|
|
this.trigger
|
|
);
|
|
let latestTimestamp = {};
|
|
latestTimestamp = getLatestTimestamp(
|
|
latestTimestamp,
|
|
updatedCriterion.data,
|
|
this.timeSystems,
|
|
this.openmct.time.timeSystem()
|
|
);
|
|
this.conditionManager.updateCurrentCondition(latestTimestamp);
|
|
}
|
|
|
|
handleTelemetryStaleness() {
|
|
this.result = evaluateResults(
|
|
this.criteria.map((criterion) => criterion.result),
|
|
this.trigger
|
|
);
|
|
this.conditionManager.updateCurrentCondition();
|
|
}
|
|
|
|
updateDescription() {
|
|
const triggerDescription = this.getTriggerDescription();
|
|
let description = '';
|
|
this.criteria.forEach((criterion, index) => {
|
|
if (!index) {
|
|
description = `Match if ${triggerDescription.prefix}`;
|
|
}
|
|
|
|
description = `${description} ${criterion.getDescription()} ${
|
|
index < this.criteria.length - 1 ? triggerDescription.conjunction : ''
|
|
}`;
|
|
});
|
|
this.summary = description;
|
|
}
|
|
|
|
getTriggerDescription() {
|
|
if (this.trigger) {
|
|
return {
|
|
conjunction: TRIGGER_CONJUNCTION[this.trigger],
|
|
prefix: `${TRIGGER_LABEL[this.trigger]}: `
|
|
};
|
|
} else {
|
|
return {
|
|
conjunction: '',
|
|
prefix: ''
|
|
};
|
|
}
|
|
}
|
|
|
|
requestLADConditionResult(options) {
|
|
let latestTimestamp;
|
|
let criteriaResults = {};
|
|
const criteriaRequests = this.criteria.map((criterion) =>
|
|
criterion.requestLAD(this.conditionManager.telemetryObjects, options)
|
|
);
|
|
|
|
return Promise.all(criteriaRequests).then((results) => {
|
|
results.forEach((resultObj) => {
|
|
const {
|
|
id,
|
|
data,
|
|
data: { result }
|
|
} = resultObj;
|
|
if (this.findCriterion(id)) {
|
|
criteriaResults[id] = Boolean(result);
|
|
}
|
|
|
|
latestTimestamp = getLatestTimestamp(
|
|
latestTimestamp,
|
|
data,
|
|
this.timeSystems,
|
|
this.openmct.time.timeSystem()
|
|
);
|
|
});
|
|
|
|
return {
|
|
id: this.id,
|
|
data: Object.assign({}, latestTimestamp, {
|
|
result: evaluateResults(Object.values(criteriaResults), this.trigger)
|
|
})
|
|
};
|
|
});
|
|
}
|
|
|
|
getCriteria() {
|
|
return this.criteria;
|
|
}
|
|
|
|
destroyCriteria() {
|
|
let success = true;
|
|
//looping through the array backwards since destroyCriterion modifies the criteria array
|
|
for (let i = this.criteria.length - 1; i >= 0; i--) {
|
|
success = success && this.destroyCriterion(this.criteria[i].id);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
destroy() {
|
|
this.destroyCriteria();
|
|
}
|
|
}
|