2020-01-06 10:09:47 -08:00
|
|
|
/*****************************************************************************
|
2020-01-16 15:30:49 -08:00
|
|
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
2020-01-06 10:09:47 -08:00
|
|
|
* 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 * as EventEmitter from 'eventemitter3';
|
2020-01-10 10:43:55 -08:00
|
|
|
import uuid from 'uuid';
|
2020-02-27 11:21:40 -08:00
|
|
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
|
|
|
import { TRIGGER } from "./utils/constants";
|
|
|
|
import {computeCondition} from "./utils/evaluator";
|
2020-01-06 10:09:47 -08:00
|
|
|
|
|
|
|
/*
|
2020-02-06 11:53:31 -08:00
|
|
|
* conditionConfiguration = {
|
2020-01-16 15:30:49 -08:00
|
|
|
* identifier: {
|
|
|
|
* key: '',
|
|
|
|
* namespace: ''
|
|
|
|
* },
|
2020-01-06 10:09:47 -08:00
|
|
|
* trigger: 'any'/'all',
|
|
|
|
* criteria: [
|
|
|
|
* {
|
2020-02-12 10:41:43 -08:00
|
|
|
* telemetry: '',
|
2020-01-10 10:43:55 -08:00
|
|
|
* operation: '',
|
|
|
|
* input: '',
|
2020-02-19 12:51:24 -08:00
|
|
|
* metadata: ''
|
2020-01-06 10:09:47 -08:00
|
|
|
* }
|
|
|
|
* ]
|
|
|
|
* }
|
|
|
|
*/
|
2020-01-16 15:30:49 -08:00
|
|
|
export default class ConditionClass extends EventEmitter {
|
2020-01-06 10:09:47 -08:00
|
|
|
|
2020-01-16 15:30:49 -08:00
|
|
|
/**
|
|
|
|
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
|
|
|
|
* @constructor
|
2020-02-06 11:53:31 -08:00
|
|
|
* @param conditionConfiguration: {identifier: {domainObject.identifier},trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
|
2020-01-16 15:30:49 -08:00
|
|
|
* @param openmct
|
|
|
|
*/
|
2020-02-06 11:53:31 -08:00
|
|
|
constructor(conditionConfiguration, openmct) {
|
2020-01-06 10:09:47 -08:00
|
|
|
super();
|
|
|
|
|
|
|
|
this.openmct = openmct;
|
2020-02-06 11:53:31 -08:00
|
|
|
this.id = this.openmct.objects.makeKeyString(conditionConfiguration.identifier);
|
2020-01-22 12:07:14 -08:00
|
|
|
this.criteria = [];
|
2020-01-23 12:08:28 -08:00
|
|
|
this.criteriaResults = {};
|
2020-02-28 11:15:19 -08:00
|
|
|
this.result = undefined;
|
2020-02-06 11:53:31 -08:00
|
|
|
if (conditionConfiguration.configuration.criteria) {
|
|
|
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
2020-01-10 10:43:55 -08:00
|
|
|
}
|
2020-02-06 11:53:31 -08:00
|
|
|
this.trigger = conditionConfiguration.configuration.trigger;
|
2020-01-22 12:07:14 -08:00
|
|
|
this.openmct.objects.get(this.id).then(obj => this.observeForChanges(obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
observeForChanges(conditionDO) {
|
|
|
|
this.stopObservingForChanges = this.openmct.objects.observe(conditionDO, '*', this.update.bind(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
update(newDomainObject) {
|
2020-02-06 11:53:31 -08:00
|
|
|
this.updateTrigger(newDomainObject.configuration.trigger);
|
2020-02-18 16:04:14 -08:00
|
|
|
this.updateCriteria(newDomainObject.configuration.criteria);
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
2020-01-23 12:08:28 -08:00
|
|
|
updateTrigger(trigger) {
|
|
|
|
if (this.trigger !== trigger) {
|
|
|
|
this.trigger = trigger;
|
2020-01-06 10:09:47 -08:00
|
|
|
this.handleConditionUpdated();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:53:31 -08:00
|
|
|
generateCriterion(criterionConfiguration) {
|
2020-01-06 10:09:47 -08:00
|
|
|
return {
|
2020-01-10 10:43:55 -08:00
|
|
|
id: uuid(),
|
2020-02-13 16:13:29 -08:00
|
|
|
telemetry: criterionConfiguration.telemetry || '',
|
2020-02-06 11:53:31 -08:00
|
|
|
operation: criterionConfiguration.operation || '',
|
|
|
|
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
|
2020-02-19 12:51:24 -08:00
|
|
|
metadata: criterionConfiguration.metadata || ''
|
2020-01-06 10:09:47 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:53:31 -08:00
|
|
|
createCriteria(criterionConfigurations) {
|
2020-02-13 16:13:29 -08:00
|
|
|
criterionConfigurations.forEach((criterionConfiguration) => {
|
|
|
|
this.addCriterion(criterionConfiguration);
|
2020-01-06 10:09:47 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:53:31 -08:00
|
|
|
updateCriteria(criterionConfigurations) {
|
2020-01-06 10:09:47 -08:00
|
|
|
this.destroyCriteria();
|
2020-02-06 11:53:31 -08:00
|
|
|
this.createCriteria(criterionConfigurations);
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* adds criterion to the condition.
|
|
|
|
*/
|
2020-02-06 11:53:31 -08:00
|
|
|
addCriterion(criterionConfiguration) {
|
|
|
|
let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
|
|
|
|
let criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
2020-01-16 16:11:23 -08:00
|
|
|
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
2020-01-23 12:08:28 -08:00
|
|
|
criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
2020-01-10 10:43:55 -08:00
|
|
|
if (!this.criteria) {
|
|
|
|
this.criteria = [];
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
2020-01-10 10:43:55 -08:00
|
|
|
this.criteria.push(criterion);
|
2020-02-06 11:53:31 -08:00
|
|
|
return criterionConfigurationWithId.id;
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
findCriterion(id) {
|
2020-01-10 10:43:55 -08:00
|
|
|
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;
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
2020-02-06 11:53:31 -08:00
|
|
|
updateCriterion(id, criterionConfiguration) {
|
2020-01-10 10:43:55 -08:00
|
|
|
let found = this.findCriterion(id);
|
|
|
|
if (found) {
|
2020-02-19 12:51:24 -08:00
|
|
|
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
|
|
|
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
2020-01-23 12:08:28 -08:00
|
|
|
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
|
|
|
newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
|
|
|
2020-01-10 10:43:55 -08:00
|
|
|
let criterion = found.item;
|
|
|
|
criterion.unsubscribe();
|
2020-01-23 12:08:28 -08:00
|
|
|
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
|
|
|
criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
2020-01-10 10:43:55 -08:00
|
|
|
this.criteria.splice(found.index, 1, newCriterion);
|
2020-01-23 12:08:28 -08:00
|
|
|
if (this.criteriaResults[criterion.id] !== undefined) {
|
|
|
|
delete this.criteriaResults[criterion.id];
|
|
|
|
}
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
removeCriterion(id) {
|
|
|
|
if (this.destroyCriterion(id)) {
|
|
|
|
this.handleConditionUpdated();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
destroyCriterion(id) {
|
2020-01-10 10:43:55 -08:00
|
|
|
let found = this.findCriterion(id);
|
|
|
|
if (found) {
|
|
|
|
let criterion = found.item;
|
2020-01-22 12:07:14 -08:00
|
|
|
criterion.destroy();
|
2020-02-28 11:15:19 -08:00
|
|
|
// TODO this is passing the wrong args
|
2020-01-10 10:43:55 -08:00
|
|
|
criterion.off('criterionUpdated', (result) => {
|
|
|
|
this.handleCriterionUpdated(id, result);
|
2020-01-06 10:09:47 -08:00
|
|
|
});
|
2020-01-10 10:43:55 -08:00
|
|
|
this.criteria.splice(found.index, 1);
|
2020-01-23 12:08:28 -08:00
|
|
|
if (this.criteriaResults[criterion.id] !== undefined) {
|
|
|
|
delete this.criteriaResults[criterion.id];
|
|
|
|
}
|
2020-01-06 10:09:47 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-16 15:30:49 -08:00
|
|
|
handleCriterionUpdated(criterion) {
|
|
|
|
let found = this.findCriterion(criterion.id);
|
|
|
|
if (found) {
|
2020-01-22 12:07:14 -08:00
|
|
|
this.criteria[found.index] = criterion.data;
|
2020-02-13 16:13:29 -08:00
|
|
|
this.subscribe();
|
2020-02-28 11:15:19 -08:00
|
|
|
// TODO nothing is listening to this
|
2020-01-16 15:30:49 -08:00
|
|
|
this.emitEvent('conditionUpdated', {
|
|
|
|
trigger: this.trigger,
|
|
|
|
criteria: this.criteria
|
|
|
|
});
|
|
|
|
}
|
2020-01-23 12:08:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
handleCriterionResult(eventData) {
|
|
|
|
let id = eventData.id;
|
2020-02-28 11:15:19 -08:00
|
|
|
if (this.findCriterion(id)) {
|
|
|
|
this.criteriaResults[id] = eventData.data.result;
|
2020-01-23 12:08:28 -08:00
|
|
|
}
|
2020-02-28 11:15:19 -08:00
|
|
|
// change criterion result to be condition result as passed down the line
|
|
|
|
// hacky but trying not to make sweeping changes to current architecture
|
|
|
|
eventData.data.result = computeCondition(this.criteriaResults, this.trigger === TRIGGER.ALL);
|
|
|
|
this.emitEvent('conditionResultUpdated', eventData.data);
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
2020-01-23 12:08:28 -08:00
|
|
|
subscribe() {
|
2020-02-28 11:15:19 -08:00
|
|
|
// TODO it looks like on any single criterion update subscriptions fire for all criteria
|
2020-01-23 12:08:28 -08:00
|
|
|
this.criteria.forEach((criterion) => {
|
2020-02-25 12:29:47 -08:00
|
|
|
criterion.subscribe();
|
2020-01-23 12:08:28 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-06 10:09:47 -08:00
|
|
|
getCriteria() {
|
2020-01-10 10:43:55 -08:00
|
|
|
return this.criteria;
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
destroyCriteria() {
|
|
|
|
let success = true;
|
2020-01-10 10:43:55 -08:00
|
|
|
//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);
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2020-01-16 15:30:49 -08:00
|
|
|
emitEvent(eventName, data) {
|
|
|
|
this.emit(eventName, {
|
|
|
|
id: this.id,
|
|
|
|
data: data
|
2020-01-06 10:09:47 -08:00
|
|
|
});
|
|
|
|
}
|
2020-01-22 12:07:14 -08:00
|
|
|
|
|
|
|
destroy() {
|
|
|
|
if (typeof this.stopObservingForChanges === 'function') {
|
|
|
|
this.stopObservingForChanges();
|
|
|
|
}
|
|
|
|
this.destroyCriteria();
|
|
|
|
}
|
2020-01-06 10:09:47 -08:00
|
|
|
}
|