Addresses review comments for conditionals code (#2978)

Conditionals code refactoring

Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Shefali Joshi 2020-06-17 14:56:36 -07:00 committed by GitHub
parent e9968e3649
commit 38dbf2ccab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 208 additions and 194 deletions

View File

@ -26,6 +26,7 @@ import TelemetryCriterion from "./criterion/TelemetryCriterion";
import { evaluateResults } from './utils/evaluator'; import { evaluateResults } from './utils/evaluator';
import { getLatestTimestamp } from './utils/time'; import { getLatestTimestamp } from './utils/time';
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion"; import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
import {TRIGGER_CONJUNCTION, TRIGGER_LABEL} from "./utils/constants";
/* /*
* conditionConfiguration = { * conditionConfiguration = {
@ -41,13 +42,14 @@ import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
* ] * ]
* } * }
*/ */
export default class ConditionClass extends EventEmitter { export default class Condition extends EventEmitter {
/** /**
* Manages criteria and emits the result of - true or false - based on criteria evaluated. * Manages criteria and emits the result of - true or false - based on criteria evaluated.
* @constructor * @constructor
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} } * @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
* @param openmct * @param openmct
* @param conditionManager
*/ */
constructor(conditionConfiguration, openmct, conditionManager) { constructor(conditionConfiguration, openmct, conditionManager) {
super(); super();
@ -62,6 +64,7 @@ export default class ConditionClass extends EventEmitter {
this.createCriteria(conditionConfiguration.configuration.criteria); this.createCriteria(conditionConfiguration.configuration.criteria);
} }
this.trigger = conditionConfiguration.configuration.trigger; this.trigger = conditionConfiguration.configuration.trigger;
this.description = '';
} }
getResult(datum) { getResult(datum) {
@ -109,7 +112,7 @@ export default class ConditionClass extends EventEmitter {
return { return {
id: criterionConfiguration.id || uuid(), id: criterionConfiguration.id || uuid(),
telemetry: criterionConfiguration.telemetry || '', telemetry: criterionConfiguration.telemetry || '',
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)], telemetryObjects: this.conditionManager.telemetryObjects,
operation: criterionConfiguration.operation || '', operation: criterionConfiguration.operation || '',
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input, input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
metadata: criterionConfiguration.metadata || '' metadata: criterionConfiguration.metadata || ''
@ -120,6 +123,7 @@ export default class ConditionClass extends EventEmitter {
criterionConfigurations.forEach((criterionConfiguration) => { criterionConfigurations.forEach((criterionConfiguration) => {
this.addCriterion(criterionConfiguration); this.addCriterion(criterionConfiguration);
}); });
this.updateDescription();
} }
updateCriteria(criterionConfigurations) { updateCriteria(criterionConfigurations) {
@ -127,10 +131,11 @@ export default class ConditionClass extends EventEmitter {
this.createCriteria(criterionConfigurations); this.createCriteria(criterionConfigurations);
} }
updateTelemetry() { updateTelemetryObjects() {
this.criteria.forEach((criterion) => { this.criteria.forEach((criterion) => {
criterion.updateTelemetry(this.conditionManager.telemetryObjects); criterion.updateTelemetryObjects(this.conditionManager.telemetryObjects);
}); });
this.updateDescription();
} }
/** /**
@ -178,6 +183,7 @@ export default class ConditionClass extends EventEmitter {
criterion.unsubscribe(); criterion.unsubscribe();
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj)); criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
this.criteria.splice(found.index, 1, newCriterion); this.criteria.splice(found.index, 1, newCriterion);
this.updateDescription();
} }
} }
@ -190,6 +196,7 @@ export default class ConditionClass extends EventEmitter {
}); });
criterion.destroy(); criterion.destroy();
this.criteria.splice(found.index, 1); this.criteria.splice(found.index, 1);
this.updateDescription();
return true; return true;
} }
@ -200,9 +207,30 @@ export default class ConditionClass extends EventEmitter {
let found = this.findCriterion(criterion.id); let found = this.findCriterion(criterion.id);
if (found) { if (found) {
this.criteria[found.index] = criterion.data; this.criteria[found.index] = criterion.data;
this.updateDescription();
} }
} }
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.description = description;
this.conditionManager.updateConditionDescription(this);
}
getTriggerDescription() {
return {
conjunction: TRIGGER_CONJUNCTION[this.trigger],
prefix: `${TRIGGER_LABEL[this.trigger]}: `
};
}
requestLADConditionResult() { requestLADConditionResult() {
let latestTimestamp; let latestTimestamp;
let criteriaResults = {}; let criteriaResults = {};

View File

@ -57,7 +57,7 @@ export default class ConditionManager extends EventEmitter {
endpoint, endpoint,
this.telemetryReceived.bind(this, endpoint) this.telemetryReceived.bind(this, endpoint)
); );
this.updateConditionTelemetry(); this.updateConditionTelemetryObjects();
} }
unsubscribeFromTelemetry(endpointIdentifier) { unsubscribeFromTelemetry(endpointIdentifier) {
@ -70,11 +70,11 @@ export default class ConditionManager extends EventEmitter {
this.subscriptions[id](); this.subscriptions[id]();
delete this.subscriptions[id]; delete this.subscriptions[id];
delete this.telemetryObjects[id]; delete this.telemetryObjects[id];
this.removeConditionTelemetry(); this.removeConditionTelemetryObjects();
} }
initialize() { initialize() {
this.conditionClassCollection = []; this.conditions = [];
if (this.conditionSetDomainObject.configuration.conditionCollection.length) { if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => { this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.initCondition(conditionConfiguration, index); this.initCondition(conditionConfiguration, index);
@ -82,13 +82,14 @@ export default class ConditionManager extends EventEmitter {
} }
} }
updateConditionTelemetry() { updateConditionTelemetryObjects() {
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry()); this.conditions.forEach((condition) => condition.updateTelemetryObjects());
} }
removeConditionTelemetry() { removeConditionTelemetryObjects() {
let conditionsChanged = false; let conditionsChanged = false;
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => { this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, conditionIndex) => {
let conditionChanged = false;
conditionConfiguration.configuration.criteria.forEach((criterion, index) => { conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all'); const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
if (!isAnyAllTelemetry) { if (!isAnyAllTelemetry) {
@ -100,10 +101,14 @@ export default class ConditionManager extends EventEmitter {
criterion.metadata = ''; criterion.metadata = '';
criterion.input = []; criterion.input = [];
criterion.operation = ''; criterion.operation = '';
conditionsChanged = true; conditionChanged = true;
} }
} }
}); });
if (conditionChanged) {
this.updateCondition(conditionConfiguration, conditionIndex);
conditionsChanged = true;
}
}); });
if (conditionsChanged) { if (conditionsChanged) {
this.persistConditions(); this.persistConditions();
@ -111,18 +116,24 @@ export default class ConditionManager extends EventEmitter {
} }
updateCondition(conditionConfiguration, index) { updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index]; let condition = this.conditions[index];
condition.update(conditionConfiguration);
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration; this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
condition.update(conditionConfiguration);
this.persistConditions();
}
updateConditionDescription(condition) {
const found = this.conditionSetDomainObject.configuration.conditionCollection.find(conditionConfiguration => (conditionConfiguration.id === condition.id));
found.summary = condition.description;
this.persistConditions(); this.persistConditions();
} }
initCondition(conditionConfiguration, index) { initCondition(conditionConfiguration, index) {
let condition = new Condition(conditionConfiguration, this.openmct, this); let condition = new Condition(conditionConfiguration, this.openmct, this);
if (index !== undefined) { if (index !== undefined) {
this.conditionClassCollection.splice(index + 1, 0, condition); this.conditions.splice(index + 1, 0, condition);
} else { } else {
this.conditionClassCollection.unshift(condition); this.conditions.unshift(condition);
} }
} }
@ -181,15 +192,15 @@ export default class ConditionManager extends EventEmitter {
} }
removeCondition(index) { removeCondition(index) {
let condition = this.conditionClassCollection[index]; let condition = this.conditions[index];
condition.destroy(); condition.destroy();
this.conditionClassCollection.splice(index, 1); this.conditions.splice(index, 1);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1); this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
this.persistConditions(); this.persistConditions();
} }
findConditionById(id) { findConditionById(id) {
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id); return this.conditions.find(condition => condition.id === id);
} }
reorderConditions(reorderPlan) { reorderConditions(reorderPlan) {
@ -234,14 +245,14 @@ export default class ConditionManager extends EventEmitter {
} }
requestLADConditionSetOutput() { requestLADConditionSetOutput() {
if (!this.conditionClassCollection.length) { if (!this.conditions.length) {
return Promise.resolve([]); return Promise.resolve([]);
} }
return this.compositionLoad.then(() => { return this.compositionLoad.then(() => {
let latestTimestamp; let latestTimestamp;
let conditionResults = {}; let conditionResults = {};
const conditionRequests = this.conditionClassCollection const conditionRequests = this.conditions
.map(condition => condition.requestLADConditionResult()); .map(condition => condition.requestLADConditionResult());
return Promise.all(conditionRequests) return Promise.all(conditionRequests)
@ -281,7 +292,7 @@ export default class ConditionManager extends EventEmitter {
isTelemetryUsed(endpoint) { isTelemetryUsed(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier); const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditionClassCollection) { for(const condition of this.conditions) {
if (condition.isTelemetryUsed(id)) { if (condition.isTelemetryUsed(id)) {
return true; return true;
} }
@ -300,7 +311,7 @@ export default class ConditionManager extends EventEmitter {
let timestamp = {}; let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey]; timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
this.conditionClassCollection.forEach(condition => { this.conditions.forEach(condition => {
condition.getResult(normalizedDatum); condition.getResult(normalizedDatum);
}); });
@ -364,7 +375,7 @@ export default class ConditionManager extends EventEmitter {
this.stopObservingForChanges(); this.stopObservingForChanges();
} }
this.conditionClassCollection.forEach((condition) => { this.conditions.forEach((condition) => {
condition.destroy(); condition.destroy();
}) })
} }

View File

@ -126,7 +126,7 @@ describe('ConditionManager', () => {
it('creates a conditionCollection with a default condition', function () { it('creates a conditionCollection with a default condition', function () {
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1); expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionId = conditionMgr.conditionClassCollection[0].id; let defaultConditionId = conditionMgr.conditions[0].id;
expect(defaultConditionId).toEqual(mockCondition.id); expect(defaultConditionId).toEqual(mockCondition.id);
}); });

View File

@ -36,19 +36,20 @@ describe("The condition", function () {
beforeEach (() => { beforeEach (() => {
conditionManager = jasmine.createSpyObj('conditionManager', conditionManager = jasmine.createSpyObj('conditionManager',
['on'] ['on', 'updateConditionDescription']
); );
mockTelemetryReceived = jasmine.createSpy('listener'); mockTelemetryReceived = jasmine.createSpy('listener');
conditionManager.on('telemetryReceived', mockTelemetryReceived); conditionManager.on('telemetryReceived', mockTelemetryReceived);
conditionManager.updateConditionDescription.and.returnValue(function () {});
testTelemetryObject = { testTelemetryObject = {
identifier:{ namespace: "", key: "test-object"}, identifier:{ namespace: "", key: "test-object"},
type: "test-object", type: "test-object",
name: "Test Object", name: "Test Object",
telemetry: { telemetry: {
values: [{ valueMetadatas: [{
key: "value", key: "some-key",
name: "Value", name: "Some attribute",
hints: { hints: {
range: 2 range: 2
} }
@ -78,7 +79,7 @@ describe("The condition", function () {
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']); openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']);
openmct.telemetry.isTelemetryObject.and.returnValue(true); openmct.telemetry.isTelemetryObject.and.returnValue(true);
openmct.telemetry.subscribe.and.returnValue(function () {}); openmct.telemetry.subscribe.and.returnValue(function () {});
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values); openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
mockTimeSystems = { mockTimeSystems = {
key: 'utc' key: 'utc'

View File

@ -50,7 +50,7 @@
<span class="c-condition__name">{{ condition.configuration.name }}</span> <span class="c-condition__name">{{ condition.configuration.name }}</span>
<span class="c-condition__summary"> <span class="c-condition__summary">
<template v-if="!canEvaluateCriteria"> <template v-if="!condition.isDefault && !canEvaluateCriteria">
Define criteria Define criteria
</template> </template>
<span v-else> <span v-else>
@ -250,7 +250,7 @@ export default {
keys.forEach((trigger) => { keys.forEach((trigger) => {
triggerOptions.push({ triggerOptions.push({
value: TRIGGER[trigger], value: TRIGGER[trigger],
label: TRIGGER_LABEL[TRIGGER[trigger]] label: `when ${TRIGGER_LABEL[TRIGGER[trigger]]}`
}); });
}); });
return triggerOptions; return triggerOptions;

View File

@ -152,7 +152,8 @@ export default {
}, },
observeForChanges() { observeForChanges() {
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => { this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
this.conditionCollection = newConditionCollection; //this forces children to re-render
this.conditionCollection = newConditionCollection.map(condition => condition);
this.updateDefaultCondition(); this.updateDefaultCondition();
}); });
}, },

View File

@ -27,20 +27,20 @@
> >
{{ condition.configuration.name }} {{ condition.configuration.name }}
</span> </span>
<span v-for="(criterionDescription, index) in criterionDescriptions" <span class="c-style__condition-desc__text"
:key="criterionDescription" v-if="!condition.isDefault"
class="c-style__condition-desc__text"
> >
<template v-if="!index">When</template> {{ description }}
{{ criterionDescription }} </span>
<template v-if="index < (criterionDescriptions.length-1)">{{ triggerDescription }}</template> <span class="c-style__condition-desc__text"
v-else
>
Match if no other condition is matched
</span> </span>
</div> </div>
</template> </template>
<script> <script>
import { TRIGGER } from "@/plugins/condition/utils/constants";
import { OPERATIONS } from "@/plugins/condition/utils/operations";
export default { export default {
name: 'ConditionDescription', name: 'ConditionDescription',
@ -59,95 +59,9 @@ export default {
} }
} }
}, },
data() { computed: {
return { description() {
criterionDescriptions: [], return this.condition ? this.condition.summary : '';
triggerDescription: ''
}
},
watch: {
condition: {
handler(val) {
this.getConditionDescription();
},
deep: true
}
},
mounted() {
this.getConditionDescription();
},
methods: {
getTriggerDescription(trigger) {
let description = '';
switch(trigger) {
case TRIGGER.ANY:
case TRIGGER.XOR:
description = 'or';
break;
case TRIGGER.ALL:
case TRIGGER.NOT: description = 'and';
break;
}
return description;
},
getConditionDescription() {
if (this.condition) {
this.triggerDescription = this.getTriggerDescription(this.condition.configuration.trigger);
this.criterionDescriptions = [];
this.condition.configuration.criteria.forEach((criterion, index) => {
this.getCriterionDescription(criterion, index);
});
if (this.condition.isDefault) {
this.criterionDescriptions.splice(0, 0, 'all else fails');
}
} else {
this.criterionDescriptions = [];
}
},
getCriterionDescription(criterion, index) {
if (!criterion.telemetry) {
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
this.criterionDescriptions.splice(index, 0, description);
} else if (criterion.telemetry === 'all' || criterion.telemetry === 'any') {
const telemetryDescription = criterion.telemetry === 'all' ? 'All telemetry' : 'Any telemetry';
let description = `${telemetryDescription} ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
this.criterionDescriptions.splice(index, 0, description);
} else {
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
if (telemetryObject.type === 'unknown') {
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
this.criterionDescriptions.splice(index, 0, description);
} else {
let metadataValue = criterion.metadata;
let inputValue = criterion.input;
if (criterion.metadata) {
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
const metadataObj = this.telemetryMetadata.valueMetadatas.find((metadata) => metadata.key === criterion.metadata);
if (metadataObj) {
if (metadataObj.name) {
metadataValue = metadataObj.name;
}
if(metadataObj.enumerations && inputValue.length) {
if (metadataObj.enumerations[inputValue[0]] && metadataObj.enumerations[inputValue[0]].string) {
inputValue = [metadataObj.enumerations[inputValue[0]].string];
}
}
}
}
let description = `${telemetryObject.name} ${metadataValue} ${this.getOperatorText(criterion.operation, inputValue)}`;
if (this.criterionDescriptions[index]) {
this.criterionDescriptions[index] = description;
} else {
this.criterionDescriptions.splice(index, 0, description);
}
}
});
}
},
getOperatorText(operationName, values) {
const found = OPERATIONS.find((operation) => operation.name === operationName);
return found ? found.getDescription(values) : '';
} }
} }
} }

View File

@ -66,22 +66,13 @@ export default {
} }
}, },
getCriterionErrors(criterion, index) { getCriterionErrors(criterion, index) {
if (!criterion.telemetry) { //It is sufficient to check for absence of telemetry here since the condition manager ensures that telemetry for a criterion is set if it exists
const isInvalidTelemetry = !criterion.telemetry && (criterion.telemetry !== 'all' && criterion.telemetry !== 'any');
if (isInvalidTelemetry) {
this.conditionErrors.push({ this.conditionErrors.push({
message: ERROR.TELEMETRY_NOT_FOUND, message: ERROR.TELEMETRY_NOT_FOUND,
additionalInfo: '' additionalInfo: ''
}); });
} else {
if (criterion.telemetry !== 'all' && criterion.telemetry !== 'any') {
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
if (telemetryObject.type === 'unknown') {
this.conditionErrors.push({
message: ERROR.TELEMETRY_NOT_FOUND,
additionalInfo: criterion.telemetry ? `Key: ${this.openmct.objects.makeKeyString(criterion.telemetry)}` : ''
});
}
});
}
} }
} }
} }

View File

@ -79,7 +79,7 @@
<input v-model="criterion.input[inputIndex]" <input v-model="criterion.input[inputIndex]"
class="c-cdef__control__input" class="c-cdef__control__input"
:type="setInputType" :type="setInputType"
@blur="persist" @change="persist"
> >
<span v-if="inputIndex < inputCount-1">and</span> <span v-if="inputIndex < inputCount-1">and</span>
</span> </span>
@ -108,6 +108,7 @@
<script> <script>
import { OPERATIONS } from '../utils/operations'; import { OPERATIONS } from '../utils/operations';
import { INPUT_TYPES } from '../utils/operations'; import { INPUT_TYPES } from '../utils/operations';
import {TRIGGER_CONJUNCTION} from "../utils/constants";
export default { export default {
inject: ['openmct'], inject: ['openmct'],
@ -143,8 +144,8 @@ export default {
}, },
computed: { computed: {
setRowLabel: function () { setRowLabel: function () {
let operator = this.trigger === 'all' ? 'and ': 'or '; let operator = TRIGGER_CONJUNCTION[this.trigger];
return (this.index !== 0 ? operator : '') + 'when'; return (this.index !== 0 ? operator : '') + ' when';
}, },
filteredOps: function () { filteredOps: function () {
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1); return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
@ -178,17 +179,18 @@ export default {
methods: { methods: {
checkTelemetry() { checkTelemetry() {
if(this.criterion.telemetry) { if(this.criterion.telemetry) {
if (this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all') { const isAnyAllTelemetry = this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all';
this.updateMetadataOptions(); const telemetryForCriterionExists = this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier));
} else { if (!isAnyAllTelemetry &&
if (!this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier))) { !telemetryForCriterionExists) {
//telemetry being used was removed. So reset this criterion. //telemetry being used was removed. So reset this criterion.
this.criterion.telemetry = ''; this.criterion.telemetry = '';
this.criterion.metadata = ''; this.criterion.metadata = '';
this.criterion.input = []; this.criterion.input = [];
this.criterion.operation = ''; this.criterion.operation = '';
this.persist(); this.persist();
} } else {
this.updateMetadataOptions();
} }
} }
}, },
@ -221,19 +223,17 @@ export default {
this.persist(); this.persist();
} }
if (this.criterion.telemetry) { if (this.criterion.telemetry) {
const telemetry = (this.criterion.telemetry === 'all' || this.criterion.telemetry === 'any') ? this.telemetry : [{ let telemetryObjects = this.telemetry;
identifier: this.criterion.telemetry if (this.criterion.telemetry !== 'all' && this.criterion.telemetry !== 'any') {
}]; const found = this.telemetry.find(telemetryObj => (this.openmct.objects.areIdsEqual(telemetryObj.identifier, this.criterion.telemetry)));
telemetryObjects = found ? [found] : [];
let telemetryPromises = telemetry.map((telemetryObject) => this.openmct.objects.get(telemetryObject.identifier)); }
Promise.all(telemetryPromises).then(telemetryObjects => {
this.telemetryMetadataOptions = []; this.telemetryMetadataOptions = [];
telemetryObjects.forEach(telemetryObject => { telemetryObjects.forEach(telemetryObject => {
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject); let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
this.addMetaDataOptions(telemetryMetadata.values()); this.addMetaDataOptions(telemetryMetadata.values());
}); });
this.updateOperations(); this.updateOperations();
});
} }
}, },
addMetaDataOptions(options) { addMetaDataOptions(options) {

View File

@ -23,6 +23,7 @@
import TelemetryCriterion from './TelemetryCriterion'; import TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator"; import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time'; import { getLatestTimestamp } from '../utils/time';
import { getOperatorText } from "@/plugins/condition/utils/operations";
export default class AllTelemetryCriterion extends TelemetryCriterion { export default class AllTelemetryCriterion extends TelemetryCriterion {
@ -46,7 +47,7 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation; return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
} }
updateTelemetry(telemetryObjects) { updateTelemetryObjects(telemetryObjects) {
this.telemetryObjects = { ...telemetryObjects }; this.telemetryObjects = { ...telemetryObjects };
this.removeTelemetryDataCache(); this.removeTelemetryDataCache();
} }
@ -159,6 +160,25 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
}); });
} }
getDescription() {
const telemetryDescription = this.telemetry === 'all' ? 'all telemetry' : 'any telemetry';
let metadataValue = this.metadata;
let inputValue = this.input;
if (this.metadata) {
const telemetryObjects = Object.values(this.telemetryObjects);
for (let i=0; i < telemetryObjects.length; i++) {
const telemetryObject = telemetryObjects[i];
const metadataObject = this.getMetaDataObject(telemetryObject, this.metadata);
if (metadataObject) {
metadataValue = this.getMetadataValueFromMetaData(metadataObject) || this.metadata;
inputValue = this.getInputValueFromMetaData(metadataObject, this.input) || this.input;
break;
}
}
}
return `${telemetryDescription} ${metadataValue} ${getOperatorText(this.operation, inputValue)}`;
}
destroy() { destroy() {
delete this.telemetryObjects; delete this.telemetryObjects;
delete this.telemetryDataCache; delete this.telemetryDataCache;

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
import { OPERATIONS } from '../utils/operations'; import { OPERATIONS, getOperatorText } from '../utils/operations';
export default class TelemetryCriterion extends EventEmitter { export default class TelemetryCriterion extends EventEmitter {
@ -49,15 +49,15 @@ export default class TelemetryCriterion extends EventEmitter {
} }
initialize() { initialize() {
this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry); this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
this.updateTelemetryObjects(this.telemetryDomainObjectDefinition.telemetryObjects);
} }
isValid() { isValid() {
return this.telemetryObject && this.metadata && this.operation; return this.telemetryObject && this.metadata && this.operation;
} }
updateTelemetry(telemetryObjects) { updateTelemetryObjects(telemetryObjects) {
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString]; this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
} }
@ -153,6 +153,51 @@ export default class TelemetryCriterion extends EventEmitter {
}); });
} }
getMetaDataObject(telemetryObject, metadata) {
let metadataObject;
if (metadata) {
const telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
metadataObject = telemetryMetadata.valueMetadatas.find((valueMetadata) => valueMetadata.key === metadata);
}
return metadataObject;
}
getInputValueFromMetaData(metadataObject, input) {
let inputValue;
if (metadataObject) {
if(metadataObject.enumerations && input.length) {
const enumeration = metadataObject.enumerations[input[0]];
if (enumeration !== undefined && enumeration.string) {
inputValue = [enumeration.string];
}
}
}
return inputValue;
}
getMetadataValueFromMetaData(metadataObject) {
let metadataValue;
if (metadataObject) {
if (metadataObject.name) {
metadataValue = metadataObject.name;
}
}
return metadataValue;
}
getDescription(criterion, index) {
let description;
if (!this.telemetry || !this.telemetryObject || (this.telemetryObject.type === 'unknown')) {
description = `Unknown ${this.metadata} ${getOperatorText(this.operation, this.input)}`;
} else {
const metadataObject = this.getMetaDataObject(this.telemetryObject, this.metadata);
const metadataValue = this.getMetadataValueFromMetaData(metadataObject) || this.metadata;
const inputValue = this.getInputValueFromMetaData(metadataObject, this.input) || this.input;
description = `${this.telemetryObject.name} ${metadataValue} ${getOperatorText(this.operation, inputValue)}`;
}
return description;
}
destroy() { destroy() {
delete this.telemetryObject; delete this.telemetryObject;

View File

@ -83,7 +83,7 @@ describe("The telemetry criterion", function () {
operation: 'textContains', operation: 'textContains',
metadata: 'value', metadata: 'value',
input: ['Hell'], input: ['Hell'],
telemetryObject: testTelemetryObject telemetryObjects: {[testTelemetryObject.identifier.key]: testTelemetryObject}
}; };
mockListener = jasmine.createSpy('listener'); mockListener = jasmine.createSpy('listener');
@ -109,13 +109,4 @@ describe("The telemetry criterion", function () {
}); });
expect(telemetryCriterion.result).toBeTrue(); expect(telemetryCriterion.result).toBeTrue();
}); });
// it("does not return a result on new data from irrelavant telemetry providers", function () {
// telemetryCriterion.getResult({
// value: 'Hello',
// utc: 'Hi',
// id: '1234'
// });
// expect(telemetryCriterion.result).toBeFalse();
// });
}); });

View File

@ -28,10 +28,17 @@ export const TRIGGER = {
}; };
export const TRIGGER_LABEL = { export const TRIGGER_LABEL = {
'any': 'when any criteria are met', 'any': 'any criteria are met',
'all': 'when all criteria are met', 'all': 'all criteria are met',
'not': 'when no criteria are met', 'not': 'no criteria are met',
'xor': 'when only one criteria is met' 'xor': 'only one criterion is met'
};
export const TRIGGER_CONJUNCTION = {
'any': 'or',
'all': 'and',
'not': 'and',
'xor': 'or'
}; };
export const STYLE_CONSTANTS = { export const STYLE_CONSTANTS = {

View File

@ -35,7 +35,7 @@ export const evaluateResults = (results, trigger) => {
function matchAll(results) { function matchAll(results) {
for (const result of results) { for (const result of results) {
if (!result) { if (result !== true) {
return false; return false;
} }
} }
@ -45,7 +45,7 @@ function matchAll(results) {
function matchAny(results) { function matchAny(results) {
for (const result of results) { for (const result of results) {
if (result) { if (result === true) {
return true; return true;
} }
} }
@ -56,7 +56,7 @@ function matchAny(results) {
function matchExact(results, target) { function matchExact(results, target) {
let matches = 0; let matches = 0;
for (const result of results) { for (const result of results) {
if (result) { if (result === true) {
matches++; matches++;
} }
if (matches > target) { if (matches > target) {

View File

@ -250,12 +250,12 @@ export const OPERATIONS = [
} }
}, },
{ {
name: 'valueIs', name: 'isOneOf',
operation: function (input) { operation: function (input) {
const lhsValue = input[0] !== undefined ? input[0].toString() : ''; const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) { if (input[1]) {
const values = input[1].split(','); const values = input[1].split(',');
return values.find((value) => lhsValue === value.toString().trim()); return values.some((value) => lhsValue === value.toString().trim());
} }
return false; return false;
}, },
@ -267,12 +267,12 @@ export const OPERATIONS = [
} }
}, },
{ {
name: 'valueIsNot', name: 'isNotOneOf',
operation: function (input) { operation: function (input) {
const lhsValue = input[0] !== undefined ? input[0].toString() : ''; const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) { if (input[1]) {
const values = input[1].split(','); const values = input[1].split(',');
const found = values.find((value) => lhsValue === value.toString().trim()); const found = values.some((value) => lhsValue === value.toString().trim());
return !found; return !found;
} }
return false; return false;
@ -290,3 +290,8 @@ export const INPUT_TYPES = {
'string': 'text', 'string': 'text',
'number': 'number' 'number': 'number'
}; };
export const getOperatorText = (operationName, values) => {
const found = OPERATIONS.find((operation) => operation.name === operationName);
return found ? found.getDescription(values) : '';
};

View File

@ -21,8 +21,8 @@
*****************************************************************************/ *****************************************************************************/
import { OPERATIONS } from "./operations"; import { OPERATIONS } from "./operations";
let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIs'); let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'isOneOf');
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot'); let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'isNotOneOf');
let isBetween = OPERATIONS.find((operation) => operation.name === 'between'); let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween'); let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs'); let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs');