diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js index 9e0a26e041..a102dff575 100644 --- a/src/plugins/condition/Condition.js +++ b/src/plugins/condition/Condition.js @@ -22,9 +22,9 @@ import * as EventEmitter from 'eventemitter3'; import uuid from 'uuid'; -import TelemetryCriterion from "@/plugins/condition/criterion/TelemetryCriterion"; -import { TRIGGER } from "@/plugins/condition/utils/constants"; -import {computeCondition} from "@/plugins/condition/utils/evaluator"; +import TelemetryCriterion from "./criterion/TelemetryCriterion"; +import { TRIGGER } from "./utils/constants"; +import {computeCondition} from "./utils/evaluator"; /* * conditionConfiguration = { @@ -58,11 +58,11 @@ export default class ConditionClass extends EventEmitter { this.id = this.openmct.objects.makeKeyString(conditionConfiguration.identifier); this.criteria = []; this.criteriaResults = {}; + this.result = null; if (conditionConfiguration.configuration.criteria) { this.createCriteria(conditionConfiguration.configuration.criteria); } this.trigger = conditionConfiguration.configuration.trigger; - this.result = null; this.openmct.objects.get(this.id).then(obj => this.observeForChanges(obj)); } @@ -199,9 +199,7 @@ export default class ConditionClass extends EventEmitter { subscribe() { this.criteria.forEach((criterion) => { - if (criterion.isValid()) { - criterion.subscribe(); - } + criterion.subscribe(); }) } diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js new file mode 100644 index 0000000000..9740ca9ba5 --- /dev/null +++ b/src/plugins/condition/ConditionManager.js @@ -0,0 +1,250 @@ +/***************************************************************************** + * 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 Condition from "./Condition"; +import uuid from "uuid"; +import * as EventEmitter from 'eventemitter3'; + +export default class ConditionManager extends EventEmitter { + constructor(domainObject, openmct) { + super(); + this.domainObject = domainObject; + this.openmct = openmct; + this.instantiate = this.openmct.$injector.get('instantiate'); + this.initialize(); + } + + initialize() { + this.conditionResults = {}; + this.openmct.objects.get(this.domainObject.identifier).then((obj) => { + this.observeForChanges(obj); + this.conditionCollection = []; + if (this.domainObject.configuration.conditionCollection.length) { + this.domainObject.configuration.conditionCollection.forEach((conditionConfigurationId, index) => { + this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => { + this.initCondition(conditionConfiguration, index) + }); + }); + } else { + this.addCondition(true); + } + }); + } + + observeForChanges(domainObject) { + //TODO: Observe only the conditionCollection property instead of the whole domainObject + this.stopObservingForChanges = this.openmct.objects.observe(domainObject, '*', this.handleConditionCollectionUpdated.bind(this)); + } + + handleConditionCollectionUpdated(newDomainObject) { + let oldConditionIdentifiers = this.domainObject.configuration.conditionCollection.map((conditionConfigurationId) => { + return this.openmct.objects.makeKeyString(conditionConfigurationId); + }); + let newConditionIdentifiers = newDomainObject.configuration.conditionCollection.map((conditionConfigurationId) => { + return this.openmct.objects.makeKeyString(conditionConfigurationId); + }); + + this.domainObject = newDomainObject; + + //check for removed conditions + oldConditionIdentifiers.forEach((identifier, index) => { + if (newConditionIdentifiers.indexOf(identifier) < 0) { + this.removeCondition(identifier); + } + }); + + let newConditionCount = this.domainObject.configuration.conditionCollection.length - this.conditionCollection.length; + + for (let i = 0; i < newConditionCount; i++) { + let conditionConfigurationId = this.domainObject.configuration.conditionCollection[i]; + this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => { + this.initCondition(conditionConfiguration, i); + }); + } + + } + + initCondition(conditionConfiguration, index) { + let condition = new Condition(conditionConfiguration, this.openmct); + condition.on('conditionResultUpdated', this.handleConditionResult.bind(this)); + if (index !== undefined) { + this.conditionCollection.splice(index + 1, 0, condition); + } else { + this.conditionCollection.unshift(condition); + } + //There are no criteria for a default condition and hence no subscriptions. + //Hence the conditionResult must be manually triggered for it. + if (conditionConfiguration.isDefault) { + this.handleConditionResult(); + } + } + + createConditionDomainObject(isDefault, conditionConfiguration) { + let conditionObj; + if (conditionConfiguration) { + conditionObj = { + ...conditionConfiguration, + name: `Copy of ${conditionConfiguration.name}`, + identifier: { + ...this.domainObject.identifier, + key: uuid() + } + }; + } else { + conditionObj = { + isDefault: isDefault, + type: 'condition', + identifier: { + ...this.domainObject.identifier, + key: uuid() + }, + configuration: { + name: isDefault ? 'Default' : 'Unnamed Condition', + output: 'false', + trigger: 'all', + criteria: isDefault ? [] : [{ + telemetry: '', + operation: '', + input: [], + metadata: '' + }] + }, + summary: '' + }; + } + let conditionDomainObjectKeyString = this.openmct.objects.makeKeyString(conditionObj.identifier); + let newDomainObject = this.instantiate(conditionObj, conditionDomainObjectKeyString); + + return newDomainObject.useCapability('adapter'); + } + + addCondition(isDefault, index) { + this.createAndSaveConditionDomainObject(!!isDefault, index); + } + + cloneCondition(conditionConfigurationId, index) { + this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => { + this.createAndSaveConditionDomainObject(false, index, conditionConfiguration); + }); + } + + createAndSaveConditionDomainObject(isDefault, index, conditionConfiguration) { + let newConditionDomainObject = this.createConditionDomainObject(isDefault, conditionConfiguration); + //persist the condition domain object so that we can do an openmct.objects.get on it and only persist the identifier in the conditionCollection of conditionSet + this.openmct.objects.mutate(newConditionDomainObject, 'created', new Date()); + if (index !== undefined) { + this.domainObject.configuration.conditionCollection.splice(index + 1, 0, newConditionDomainObject.identifier); + } else { + this.domainObject.configuration.conditionCollection.unshift(newConditionDomainObject.identifier); + } + this.persist(); + } + + removeCondition(identifier) { + let found = this.findConditionById(identifier); + if (found) { + let index = found.index; + let condition = this.conditionCollection[index]; + let conditionIdAsString = condition.id; + condition.destroyCriteria(); + condition.off('conditionResultUpdated', this.handleConditionResult.bind(this)); + this.conditionCollection.splice(index, 1); + this.domainObject.configuration.conditionCollection.splice(index, 1); + if (this.conditionResults[conditionIdAsString] !== undefined) { + delete this.conditionResults[conditionIdAsString]; + } + this.persist(); + this.handleConditionResult(); + } + } + + findConditionById(identifier) { + let found; + for (let i=0, ii=this.conditionCollection.length; i < ii; i++) { + if (this.conditionCollection[i].id === this.openmct.objects.makeKeyString(identifier)) { + found = { + item: this.conditionCollection[i], + index: i + }; + break; + } + } + + return found; + } + + //this.$set(this.conditionCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]); + reorderConditions(reorderPlan) { + let oldConditions = Array.from(this.domainObject.configuration.conditionCollection); + let newCollection = []; + reorderPlan.forEach((reorderEvent) => { + let item = oldConditions[reorderEvent.oldIndex]; + newCollection.push(item); + this.domainObject.configuration.conditionCollection = newCollection; + }); + this.persist(); + } + + handleConditionResult(resultObj) { + let conditionCollection = this.domainObject.configuration.conditionCollection; + let currentConditionIdentifier = conditionCollection[conditionCollection.length-1]; + + if (resultObj) { + let idAsString = this.openmct.objects.makeKeyString(resultObj.id); + let found = this.findConditionById(idAsString); + if (found) { + this.conditionResults[idAsString] = resultObj.data.result; + } + } + + for (let i = 0, ii = conditionCollection.length - 1; i < ii; i++) { + let conditionIdAsString = this.openmct.objects.makeKeyString(conditionCollection[i]); + if (this.conditionResults[conditionIdAsString]) { + //first condition to be true wins + currentConditionIdentifier = conditionCollection[i]; + break; + } + } + + this.openmct.objects.get(currentConditionIdentifier).then((obj) => { + this.emit('conditionSetResultUpdated', { + id: this.domainObject.identifier, + output: obj.configuration.output, + conditionId: currentConditionIdentifier + }) + }); + } + + persist() { + this.openmct.objects.mutate(this.domainObject, 'configuration.conditionCollection', this.domainObject.configuration.conditionCollection); + } + + destroy() { + if (typeof this.stopObservingForChanges === 'function') { + this.stopObservingForChanges(); + } + this.conditionCollection.forEach((condition) => { + condition.off('conditionResultUpdated', this.handleConditionResult); + condition.destroy(); + }) + } +} diff --git a/src/plugins/condition/ConditionManagerSpec.js b/src/plugins/condition/ConditionManagerSpec.js new file mode 100644 index 0000000000..c5e50e9694 --- /dev/null +++ b/src/plugins/condition/ConditionManagerSpec.js @@ -0,0 +1,98 @@ +/***************************************************************************** + * 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 ConditionManager from './ConditionManager'; + +describe('ConditionManager', () => { + + let conditionMgr; + let mockListener; + let openmct = {}; + let conditionSetDomainObject = { + identifier: { + namespace: "", + key: "600a7372-8d48-4dc4-98b6-548611b1ff7e" + }, + type: "conditionSet", + location: "mine", + configuration: { + conditionCollection: [] + } + }; + let mockConditionDomainObject = { + isDefault: true, + type: 'condition', + identifier: { + namespace: '', + key: '1234-5678' + } + }; + + function mockAngularComponents() { + let mockInjector = jasmine.createSpyObj('$injector', ['get']); + + let mockInstantiate = jasmine.createSpy('mockInstantiate'); + mockInstantiate.and.returnValue(mockInstantiate); + + let mockDomainObject = { + useCapability: function () { + return mockConditionDomainObject; + } + }; + mockInstantiate.and.callFake(function () { + return mockDomainObject; + }); + mockInjector.get.and.callFake(function (service) { + return { + 'instantiate': mockInstantiate + }[service]; + }); + + openmct.$injector = mockInjector; + } + + beforeAll(function () { + + mockAngularComponents(); + + openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', 'observe', 'mutate']); + openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) { + resolve(conditionSetDomainObject); + }), new Promise(function (resolve, reject) { + resolve(mockConditionDomainObject); + })); + openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key); + openmct.objects.observe.and.returnValue(function () {}); + openmct.objects.mutate.and.returnValue(function () {}); + conditionMgr = new ConditionManager(conditionSetDomainObject, openmct); + mockListener = jasmine.createSpy('mockListener'); + + conditionMgr.on('conditionSetResultUpdated', mockListener); + }); + + it('creates a conditionCollection with a default condition', function () { + expect(conditionMgr.domainObject.configuration.conditionCollection.length).toEqual(1); + let defaultConditionIdentifier = conditionMgr.domainObject.configuration.conditionCollection[0]; + expect(defaultConditionIdentifier).toEqual(mockConditionDomainObject.identifier); + }); + +}); diff --git a/src/plugins/condition/components/Condition.vue b/src/plugins/condition/components/Condition.vue index 937234cabd..fadd54aeb6 100644 --- a/src/plugins/condition/components/Condition.vue +++ b/src/plugins/condition/components/Condition.vue @@ -177,8 +177,7 @@