diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js index 23dd342cd2..7eb4f819bb 100644 --- a/src/plugins/condition/Condition.js +++ b/src/plugins/condition/Condition.js @@ -92,6 +92,7 @@ export default class ConditionClass extends EventEmitter { return { id: uuid(), telemetry: criterionConfiguration.telemetry || '', + telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)], operation: criterionConfiguration.operation || '', input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input, metadata: criterionConfiguration.metadata || '' @@ -109,6 +110,12 @@ export default class ConditionClass extends EventEmitter { this.createCriteria(criterionConfigurations); } + updateTelemetry() { + this.criteria.forEach((criterion) => { + criterion.updateTelemetry(this.conditionManager.telemetryObjects); + }); + } + /** * adds criterion to the condition. */ @@ -209,7 +216,7 @@ export default class ConditionClass extends EventEmitter { requestLADConditionResult() { const criteriaResults = this.criteria - .map(criterion => criterion.requestLAD()); + .map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects})); return Promise.all(criteriaResults) .then(results => { diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index c401e6610d..b3eb1632bc 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -36,6 +36,7 @@ export default class ConditionManager extends EventEmitter { this.composition.on('remove', this.unsubscribeFromTelemetry, this); this.compositionLoad = this.composition.load(); this.subscriptions = {}; + this.telemetryObjects = {}; this.initialize(); this.stopObservingForChanges = this.openmct.objects.observe(this.conditionSetDomainObject, '*', (newDomainObject) => { @@ -49,11 +50,12 @@ export default class ConditionManager extends EventEmitter { console.log('subscription already exists'); return; } - + this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas}); this.subscriptions[id] = this.openmct.telemetry.subscribe( endpoint, this.broadcastTelemetry.bind(this, id) ); + this.updateConditionTelemetry(); } unsubscribeFromTelemetry(endpointIdentifier) { @@ -65,6 +67,7 @@ export default class ConditionManager extends EventEmitter { this.subscriptions[id](); delete this.subscriptions[id]; + delete this.telemetryObjects[id]; } initialize() { @@ -77,6 +80,10 @@ export default class ConditionManager extends EventEmitter { } } + updateConditionTelemetry() { + this.conditionClassCollection.forEach((condition) => condition.updateTelemetry()); + } + updateCondition(conditionConfiguration, index) { let condition = this.conditionClassCollection[index]; condition.update(conditionConfiguration); @@ -255,7 +262,15 @@ export default class ConditionManager extends EventEmitter { } broadcastTelemetry(id, datum) { - this.emit(`broadcastTelemetry`, Object.assign({}, datum, {id: id})); + this.emit(`broadcastTelemetry`, Object.assign({}, this.createNormalizedDatum(datum, id), {id: id})); + } + + createNormalizedDatum(telemetryDatum, id) { + return Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((normalizedDatum, metadatum) => { + const formatter = this.openmct.telemetry.getValueFormatter(metadatum); + normalizedDatum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]); + return normalizedDatum; + }, {}); } persistConditions() { diff --git a/src/plugins/condition/ConditionSpec.js b/src/plugins/condition/ConditionSpec.js index d3375eadba..3f7ad07332 100644 --- a/src/plugins/condition/ConditionSpec.js +++ b/src/plugins/condition/ConditionSpec.js @@ -62,6 +62,9 @@ describe("The condition", function () { }] } }; + conditionManager.telemetryObjects = { + "test-object": testTelemetryObject + }; openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) { resolve(testTelemetryObject); diff --git a/src/plugins/condition/StyleRuleManager.js b/src/plugins/condition/StyleRuleManager.js index 9bfb357e36..c09a291090 100644 --- a/src/plugins/condition/StyleRuleManager.js +++ b/src/plugins/condition/StyleRuleManager.js @@ -48,6 +48,8 @@ export default class StyleRuleManager extends EventEmitter { this.stopProvidingTelemetry(); } this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => { + this.openmct.telemetry.request(conditionSetDomainObject) + .then(output => this.handleConditionSetResultUpdated(output)); this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output)); }); } @@ -115,6 +117,7 @@ export default class StyleRuleManager extends EventEmitter { if (this.stopProvidingTelemetry) { this.stopProvidingTelemetry(); } + delete this.stopProvidingTelemetry; this.conditionSetIdentifier = undefined; } diff --git a/src/plugins/condition/components/ConditionDescription.vue b/src/plugins/condition/components/ConditionDescription.vue index 377435fd5d..f1ca8ba20f 100644 --- a/src/plugins/condition/components/ConditionDescription.vue +++ b/src/plugins/condition/components/ConditionDescription.vue @@ -98,14 +98,23 @@ export default { 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 && metadataObj.name) { - metadataValue = metadataObj.name; + 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, criterion.input)}`; + let description = `${telemetryObject.name} ${metadataValue} ${this.getOperatorText(criterion.operation, inputValue)}`; if (this.criterionDescriptions[index]) { this.criterionDescriptions[index] = description; } else { diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js index bfe6954728..d41f7a76c4 100644 --- a/src/plugins/condition/criterion/TelemetryCriterion.js +++ b/src/plugins/condition/criterion/TelemetryCriterion.js @@ -44,28 +44,25 @@ export default class TelemetryCriterion extends EventEmitter { this.operation = telemetryDomainObjectDefinition.operation; this.input = telemetryDomainObjectDefinition.input; this.metadata = telemetryDomainObjectDefinition.metadata; - this.telemetryObjectIdAsString = undefined; - this.objectAPI.get(this.objectAPI.makeKeyString(this.telemetry)).then((obj) => this.initialize(obj)); - } - - initialize(obj) { - this.telemetryObject = obj; - this.telemetryMetaData = this.openmct.telemetry.getMetadata(obj).valueMetadatas; - this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(this.telemetry); + this.telemetryObject = telemetryDomainObjectDefinition.telemetryObject; + this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(telemetryDomainObjectDefinition.telemetry); this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription); this.emitEvent('criterionUpdated', this); } - formatData(data) { - const normalizedDatum = this.createNormalizedDatum(data); - const datum = { - result: this.computeResult(normalizedDatum) - } + updateTelemetry(telemetryObjects) { + this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString]; + } - if (normalizedDatum) { + formatData(data) { + const datum = { + result: this.computeResult(data) + }; + + if (data) { // TODO check back to see if we should format times here this.timeAPI.getAllTimeSystems().forEach(timeSystem => { - datum[timeSystem.key] = normalizedDatum[timeSystem.key] + datum[timeSystem.key] = data[timeSystem.key] }); } return datum; @@ -77,13 +74,6 @@ export default class TelemetryCriterion extends EventEmitter { } } - createNormalizedDatum(telemetryDatum) { - return Object.values(this.telemetryMetaData).reduce((normalizedDatum, metadatum) => { - normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source]; - return normalizedDatum; - }, {}); - } - findOperation(operation) { for (let i=0, ii=OPERATIONS.length; i < ii; i++) { if (operation === OPERATIONS[i].name) { diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js index a1f6bd1501..7d313140e2 100644 --- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -24,7 +24,6 @@ import TelemetryCriterion from "./TelemetryCriterion"; let openmct = {}, mockListener, - mockListener2, testCriterionDefinition, testTelemetryObject, telemetryCriterion; @@ -60,9 +59,6 @@ describe("The telemetry criterion", function () { } }; openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); - openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) { - resolve(testTelemetryObject); - })); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata"]); openmct.telemetry.isTelemetryObject.and.returnValue(true); @@ -80,13 +76,11 @@ describe("The telemetry criterion", function () { id: 'test-criterion-id', telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier), operation: 'lessThan', - metadata: 'sin' + metadata: 'sin', + telemetryObject: testTelemetryObject }; mockListener = jasmine.createSpy('listener'); - mockListener2 = jasmine.createSpy('updatedListener', (data) => { - console.log(data); - }); telemetryCriterion = new TelemetryCriterion( testCriterionDefinition, @@ -94,18 +88,14 @@ describe("The telemetry criterion", function () { ); telemetryCriterion.on('criterionResultUpdated', mockListener); - telemetryCriterion.on('criterionUpdated', mockListener2); }); it("initializes with a telemetry objectId as string", function () { - telemetryCriterion.initialize(testTelemetryObject); expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key); - expect(mockListener2).toHaveBeenCalled(); }); it("updates and emits event on new data from telemetry providers", function () { - telemetryCriterion.initialize(testTelemetryObject); spyOn(telemetryCriterion, 'emitEvent').and.callThrough(); telemetryCriterion.handleSubscription({ value: 'Hello', diff --git a/src/plugins/condition/utils/operations.js b/src/plugins/condition/utils/operations.js index ca83bb67b4..6249dc16bb 100644 --- a/src/plugins/condition/utils/operations.js +++ b/src/plugins/condition/utils/operations.js @@ -1,3 +1,5 @@ +import _ from 'lodash'; + export const OPERATIONS = [ { name: 'equalTo', @@ -202,6 +204,39 @@ export const OPERATIONS = [ getDescription: function (values) { return ' is not ' + values.join(', '); } + }, + { + name: 'valueIs', + operation: function (input) { + if (input[1]) { + const values = input[1].split(','); + return values.find((value) => input[0].toString() === _.trim(value.toString())); + } + return false; + }, + text: 'is one of', + appliesTo: ["string", "number"], + inputCount: 1, + getDescription: function (values) { + return ' is one of ' + values[0]; + } + }, + { + name: 'valueIsNot', + operation: function (input) { + if (input[1]) { + const values = input[1].split(','); + const found = values.find((value) => input[0].toString() === _.trim(value.toString())); + return !found; + } + return false; + }, + text: 'is not one of', + appliesTo: ["string", "number"], + inputCount: 1, + getDescription: function (values) { + return ' is not one of ' + values[0]; + } } ]; diff --git a/src/plugins/condition/utils/operationsSpec.js b/src/plugins/condition/utils/operationsSpec.js new file mode 100644 index 0000000000..0f82adca92 --- /dev/null +++ b/src/plugins/condition/utils/operationsSpec.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * 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 { OPERATIONS } from "./operations"; +let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIs'); +let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot'); + +describe('Is one of and is not one of operations', function () { + + it('should evaluate isOneOf to true for number inputs', () => { + const inputs = [45, "5,6,45,8"]; + expect(!!isOneOfOperation.operation(inputs)).toBeTrue(); + }); + + it('should evaluate isOneOf to true for string inputs', () => { + const inputs = ["45", " 45, 645, 4,8 "]; + expect(!!isOneOfOperation.operation(inputs)).toBeTrue(); + }); + + it('should evaluate isNotOneOf to true for number inputs', () => { + const inputs = [45, "5,6,4,8"]; + expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue(); + }); + + it('should evaluate isNotOneOf to true for string inputs', () => { + const inputs = ["45", " 5,645, 4,8 "]; + expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue(); + }); + + it('should evaluate isOneOf to false for number inputs', () => { + const inputs = [4, "5, 6, 7, 8"]; + expect(!!isOneOfOperation.operation(inputs)).toBeFalse(); + }); + + it('should evaluate isOneOf to false for string inputs', () => { + const inputs = ["4", "5,645 ,7,8"]; + expect(!!isOneOfOperation.operation(inputs)).toBeFalse(); + }); + + it('should evaluate isNotOneOf to false for number inputs', () => { + const inputs = [4, "5,4, 7,8"]; + expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse(); + }); + + it('should evaluate isNotOneOf to false for string inputs', () => { + const inputs = ["4", "5,46,4,8"]; + expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse(); + }); +}); diff --git a/src/plugins/displayLayout/components/TelemetryView.vue b/src/plugins/displayLayout/components/TelemetryView.vue index 9f67577958..0a9ea7b856 100644 --- a/src/plugins/displayLayout/components/TelemetryView.vue +++ b/src/plugins/displayLayout/components/TelemetryView.vue @@ -268,15 +268,17 @@ export default { this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS); }, initObjectStyles() { - this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.objectStyles, this.openmct, this.updateStyle.bind(this)); + if (this.domainObject.configuration) { + this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.objectStyles, this.openmct, this.updateStyle.bind(this)); - if (this.unlistenStyles) { - this.unlistenStyles(); + if (this.unlistenStyles) { + this.unlistenStyles(); + } + this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => { + //Updating object styles in the inspector view will trigger this so that the changes are reflected immediately + this.styleRuleManager.updateObjectStyleConfig(newObjectStyle); + }); } - this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => { - //Updating object styles in the inspector view will trigger this so that the changes are reflected immediately - this.styleRuleManager.updateObjectStyleConfig(newObjectStyle); - }); }, updateStyle(styleObj) { let keys = Object.keys(styleObj);