From b4fdabf301482240f96d8fdfd9800491dc02e08b Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 12 Sep 2024 09:28:02 -0700 Subject: [PATCH 01/35] adding telemetry collections to condition manager --- package-lock.json | 6 +- src/plugins/condition/ConditionManager.js | 66 ++++++++----------- .../stackedPlot/mixins/objectStyles-mixin.js | 45 ++++++------- 3 files changed, 51 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index b15818b907e..3f2ce454bf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openmct", - "version": "4.0.0-next", + "version": "4.1.0-next", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openmct", - "version": "4.0.0-next", + "version": "4.1.0-next", "license": "Apache-2.0", "workspaces": [ "e2e" @@ -98,7 +98,7 @@ }, "e2e": { "name": "openmct-e2e", - "version": "4.0.0-next", + "version": "4.1.0-next", "license": "Apache-2.0", "devDependencies": { "@axe-core/playwright": "4.8.5", diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 838730883d0..f4889e49602 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -39,7 +39,7 @@ export default class ConditionManager extends EventEmitter { this.shouldEvaluateNewTelemetry = this.shouldEvaluateNewTelemetry.bind(this); this.compositionLoad = this.composition.load(); - this.subscriptions = {}; + this.telemetryCollections = {}; this.telemetryObjects = {}; this.testData = { conditionTestInputs: this.conditionSetDomainObject.configuration.conditionTestData, @@ -48,55 +48,44 @@ export default class ConditionManager extends EventEmitter { this.initialize(); } - async requestLatestValue(endpoint) { - const options = { - size: 1, - strategy: 'latest' - }; - const latestData = await this.openmct.telemetry.request(endpoint, options); + subscribeToTelemetry(telemetryObject) { + const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); - if (!latestData) { - throw new Error('Telemetry request failed by returning a falsy response'); - } - if (latestData.length === 0) { + if (this.telemetryCollections[keyString]) { return; } - this.telemetryReceived(endpoint, latestData[0]); - } - subscribeToTelemetry(endpoint) { - const telemetryKeyString = this.openmct.objects.makeKeyString(endpoint.identifier); - if (this.subscriptions[telemetryKeyString]) { - return; - } + const requestOptions = { + size: 1, + strategy: 'latest' + }; - const metadata = this.openmct.telemetry.getMetadata(endpoint); + this.telemetryCollections[keyString] = this.openmct.telemetry.requestCollection( + telemetryObject, + requestOptions + ); - this.telemetryObjects[telemetryKeyString] = Object.assign({}, endpoint, { - telemetryMetaData: metadata ? metadata.valueMetadatas : [] - }); + const metadata = this.openmct.telemetry.getMetadata(telemetryObject); + const telemetryMetaData = metadata ? metadata.valueMetadatas : []; + const telemetryProcessor = this.telemetryReceived.bind(this, telemetryObject); - // get latest telemetry value (in case subscription is cached and no new data is coming in) - this.requestLatestValue(endpoint); + this.telemetryObjects[keyString] = Object.assign({}, telemetryObject, { telemetryMetaData }); + + this.telemetryCollections[keyString].on('add', telemetryProcessor); + this.telemetryCollections[keyString].load(); - this.subscriptions[telemetryKeyString] = this.openmct.telemetry.subscribe( - endpoint, - this.telemetryReceived.bind(this, endpoint) - ); this.updateConditionTelemetryObjects(); } unsubscribeFromTelemetry(endpointIdentifier) { - const id = this.openmct.objects.makeKeyString(endpointIdentifier); - if (!this.subscriptions[id]) { - console.log('no subscription to remove'); - + const keyString = this.openmct.objects.makeKeyString(endpointIdentifier); + if (!this.telemetryCollections[keyString]) { return; } - this.subscriptions[id](); - delete this.subscriptions[id]; - delete this.telemetryObjects[id]; + this.telemetryCollections[keyString].destroy(); + delete this.telemetryCollections[keyString]; + delete this.telemetryObjects[keyString]; this.removeConditionTelemetryObjects(); //force re-computation of condition set result as we might be in a state where @@ -107,7 +96,7 @@ export default class ConditionManager extends EventEmitter { this.timeSystems, this.openmct.time.getTimeSystem() ); - this.updateConditionResults({ id: id }); + this.updateConditionResults({ id: keyString }); this.updateCurrentCondition(latestTimestamp); if (Object.keys(this.telemetryObjects).length === 0) { @@ -507,8 +496,9 @@ export default class ConditionManager extends EventEmitter { destroy() { this.composition.off('add', this.subscribeToTelemetry, this); this.composition.off('remove', this.unsubscribeFromTelemetry, this); - Object.values(this.subscriptions).forEach((unsubscribe) => unsubscribe()); - delete this.subscriptions; + Object.values(this.telemetryCollections).forEach((telemetryCollection) => + telemetryCollection.destroy() + ); this.conditions.forEach((condition) => { condition.destroy(); diff --git a/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js index f559ea3ced0..30478745d3a 100644 --- a/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js +++ b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js @@ -128,36 +128,31 @@ export default { } }, updateStyle(styleObj) { - let elemToStyle = this.getStyleReceiver(); + const elemToStyle = this.getStyleReceiver(); - if (!styleObj || elemToStyle === undefined) { + if (!styleObj || !elemToStyle) { return; } - let keys = Object.keys(styleObj); - - keys.forEach((key) => { - if (elemToStyle) { - if (typeof styleObj[key] === 'string' && styleObj[key].indexOf('__no_value') > -1) { - if (elemToStyle.style[key]) { - elemToStyle.style[key] = ''; - } - } else { - if ( - !styleObj.isStyleInvisible && - elemToStyle.classList.contains(STYLE_CONSTANTS.isStyleInvisible) - ) { - elemToStyle.classList.remove(STYLE_CONSTANTS.isStyleInvisible); - } else if ( - styleObj.isStyleInvisible && - !elemToStyle.classList.contains(styleObj.isStyleInvisible) - ) { - elemToStyle.classList.add(styleObj.isStyleInvisible); - } - - elemToStyle.style[key] = styleObj[key]; + // handle visibility separately + if (styleObj.isStyleInvisible !== undefined) { + elemToStyle.classList.toggle(STYLE_CONSTANTS.isStyleInvisible, styleObj.isStyleInvisible); + delete styleObj.isStyleInvisible; + } + + // build style string + const styleString = Object.entries(styleObj) + .map(([key, value]) => { + if (typeof value === 'string' && value.includes('__no_value')) { + return `${key}: ;`; // removes the property } - } + return `${key}: ${value};`; + }) + .join(' '); + + // apply styles in one operation + requestAnimationFrame(() => { + elemToStyle.style.cssText += styleString; }); } } From 5bd5ca936516386bba02b0c64a49725cae8a79cf Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 12 Sep 2024 10:02:49 -0700 Subject: [PATCH 02/35] handling telemetry collection data not datum --- src/plugins/condition/ConditionManager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index f4889e49602..f40fb23c2b5 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -399,11 +399,13 @@ export default class ConditionManager extends EventEmitter { return this.openmct.time.getBounds().end >= currentTimestamp; } - telemetryReceived(endpoint, datum) { + telemetryReceived(endpoint, data) { if (!this.isTelemetryUsed(endpoint)) { return; } + const datum = data[0]; + const normalizedDatum = this.createNormalizedDatum(datum, endpoint); const timeSystemKey = this.openmct.time.getTimeSystem().key; let timestamp = {}; From 4ab19e394e2083ebe4e2ff7c4fb1771a12e13743 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 12 Sep 2024 11:53:16 -0700 Subject: [PATCH 03/35] adding from maaster --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f2ce454bf1..b15818b907e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openmct", - "version": "4.1.0-next", + "version": "4.0.0-next", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openmct", - "version": "4.1.0-next", + "version": "4.0.0-next", "license": "Apache-2.0", "workspaces": [ "e2e" @@ -98,7 +98,7 @@ }, "e2e": { "name": "openmct-e2e", - "version": "4.1.0-next", + "version": "4.0.0-next", "license": "Apache-2.0", "devDependencies": { "@axe-core/playwright": "4.8.5", From b467f9ce20135653ee6dddf198586e8f3eaef053 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Mon, 16 Sep 2024 15:37:08 -0700 Subject: [PATCH 04/35] addressing PR comments --- src/plugins/condition/ConditionManager.js | 12 ++++++----- .../stackedPlot/mixins/objectStyles-mixin.js | 21 +++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index f40fb23c2b5..4a18140cf7b 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -67,11 +67,13 @@ export default class ConditionManager extends EventEmitter { const metadata = this.openmct.telemetry.getMetadata(telemetryObject); const telemetryMetaData = metadata ? metadata.valueMetadatas : []; - const telemetryProcessor = this.telemetryReceived.bind(this, telemetryObject); - this.telemetryObjects[keyString] = Object.assign({}, telemetryObject, { telemetryMetaData }); + this.telemetryObjects[keyString] = { ...telemetryObject, telemetryMetaData }; - this.telemetryCollections[keyString].on('add', telemetryProcessor); + this.telemetryCollections[keyString].on( + 'add', + this.telemetryReceived.bind(this, telemetryObject) + ); this.telemetryCollections[keyString].load(); this.updateConditionTelemetryObjects(); @@ -84,8 +86,8 @@ export default class ConditionManager extends EventEmitter { } this.telemetryCollections[keyString].destroy(); - delete this.telemetryCollections[keyString]; - delete this.telemetryObjects[keyString]; + this.telemetryCollections[keyString] = null; + this.telemetryObjects[keyString] = null; this.removeConditionTelemetryObjects(); //force re-computation of condition set result as we might be in a state where diff --git a/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js index 30478745d3a..3cff98cdfd1 100644 --- a/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js +++ b/src/plugins/plot/stackedPlot/mixins/objectStyles-mixin.js @@ -137,22 +137,17 @@ export default { // handle visibility separately if (styleObj.isStyleInvisible !== undefined) { elemToStyle.classList.toggle(STYLE_CONSTANTS.isStyleInvisible, styleObj.isStyleInvisible); - delete styleObj.isStyleInvisible; + styleObj.isStyleInvisible = null; } - // build style string - const styleString = Object.entries(styleObj) - .map(([key, value]) => { - if (typeof value === 'string' && value.includes('__no_value')) { - return `${key}: ;`; // removes the property - } - return `${key}: ${value};`; - }) - .join(' '); - - // apply styles in one operation requestAnimationFrame(() => { - elemToStyle.style.cssText += styleString; + Object.entries(styleObj).forEach(([key, value]) => { + if (typeof value !== 'string' || !value.includes('__no_value')) { + elemToStyle.style[key] = value; + } else { + elemToStyle.style[key] = ''; // remove the property + } + }); }); } } From 64821359cc9fd842c959c5dcc3d9cf113e4376ee Mon Sep 17 00:00:00 2001 From: Jamie V Date: Mon, 16 Sep 2024 17:33:09 -0700 Subject: [PATCH 05/35] update unit test to work with telemetry collections --- src/plugins/condition/pluginSpec.js | 108 ++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index d995d235da5..f71c6c4585f 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -721,30 +721,43 @@ describe('the plugin', function () { }); it('should evaluate as old when telemetry is not received in the allotted time', (done) => { + const mockTelemetryCollection = { + load: jasmine.createSpy('load'), + on: jasmine.createSpy('on'), + off: jasmine.createSpy('off'), + getAll: jasmine.createSpy('getAll').and.returnValue([]) + }; + openmct.telemetry = jasmine.createSpyObj('telemetry', [ - 'subscribe', 'getMetadata', - 'request', 'getValueFormatter', - 'abortAllRequests' + 'abortAllRequests', + 'requestCollection' ]); openmct.telemetry.getMetadata.and.returnValue({ ...testTelemetryObject.telemetry, valueMetadatas: [] }); - openmct.telemetry.request.and.returnValue(Promise.resolve([])); + openmct.telemetry.requestCollection.and.returnValue(mockTelemetryCollection); openmct.telemetry.getValueFormatter.and.returnValue({ parse: function (value) { return value; } }); + let conditionMgr = new ConditionManager(conditionSetDomainObject, openmct); conditionMgr.on('conditionSetResultUpdated', mockListener); conditionMgr.telemetryObjects = { 'test-object': testTelemetryObject }; conditionMgr.updateConditionTelemetryObjects(); + + // Simulate the passage of time and no data received setTimeout(() => { + // Trigger the 'add' event on the mockTelemetryCollection with an empty array + // to simulate no data received + mockTelemetryCollection.on.calls.mostRecent().args[1]([]); + expect(mockListener).toHaveBeenCalledWith({ output: 'Any old telemetry', id: { @@ -758,12 +771,7 @@ describe('the plugin', function () { }, 400); }); - it('should not evaluate as old when telemetry is received in the allotted time', (done) => { - openmct.telemetry.getMetadata = jasmine.createSpy('getMetadata'); - openmct.telemetry.getMetadata.and.returnValue({ - ...testTelemetryObject.telemetry, - valueMetadatas: testTelemetryObject.telemetry.values - }); + it('should not evaluate as old when telemetry is received in the allotted time', async () => { const testDatum = { 'some-key2': '', utc: 1, @@ -771,8 +779,39 @@ describe('the plugin', function () { 'some-key': null, id: 'test-object' }; - openmct.telemetry.request = jasmine.createSpy('request'); - openmct.telemetry.request.and.returnValue(Promise.resolve([testDatum])); + + let onAddResolve; + let onAddCallback; + const onAddCalledPromise = new Promise((resolve) => { + onAddResolve = resolve; + }); + + const mockTelemetryCollection = { + load: jasmine.createSpy('load'), + on: jasmine.createSpy('on').and.callFake((event, callback) => { + if (event === 'add') { + onAddCallback = callback; + onAddResolve(); + } + }) + }; + + openmct.telemetry = jasmine.createSpyObj('telemetry', [ + 'getMetadata', + 'getValueFormatter', + 'requestCollection' + ]); + openmct.telemetry.getMetadata.and.returnValue({ + ...testTelemetryObject.telemetry, + valueMetadatas: testTelemetryObject.telemetry.values + }); + openmct.telemetry.requestCollection.and.returnValue(mockTelemetryCollection); + openmct.telemetry.getValueFormatter.and.returnValue({ + parse: function (value) { + return value; + } + }); + const date = 1; conditionSetDomainObject.configuration.conditionCollection[0].configuration.criteria[0].input = ['0.4']; @@ -782,19 +821,25 @@ describe('the plugin', function () { 'test-object': testTelemetryObject }; conditionMgr.updateConditionTelemetryObjects(); - conditionMgr.telemetryReceived(testTelemetryObject, testDatum); - setTimeout(() => { - expect(mockListener).toHaveBeenCalledWith({ - output: 'Default', - id: { - namespace: '', - key: 'cf4456a9-296a-4e6b-b182-62ed29cd15b9' - }, - conditionId: '2532d90a-e0d6-4935-b546-3123522da2de', - utc: date - }); - done(); - }, 300); + + // Wait for the 'on' callback to be called + await onAddCalledPromise; + + // Simulate receiving telemetry data + onAddCallback([testDatum]); + + // Wait a bit for the condition manager to process the data + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(mockListener).toHaveBeenCalledWith({ + output: 'Default', + id: { + namespace: '', + key: 'cf4456a9-296a-4e6b-b182-62ed29cd15b9' + }, + conditionId: '2532d90a-e0d6-4935-b546-3123522da2de', + utc: date + }); }); }); @@ -1008,20 +1053,23 @@ describe('the plugin', function () { // ); openmct.telemetry = jasmine.createSpyObj('telemetry', [ 'isTelemetryObject', - 'subscribe', 'getMetadata', 'getValueFormatter', - 'request' + 'requestCollection' ]); openmct.telemetry.isTelemetryObject.and.returnValue(true); - openmct.telemetry.subscribe.and.returnValue(function () {}); + openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry); openmct.telemetry.getValueFormatter.and.returnValue({ parse: function (value) { return value; } }); - openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry); - openmct.telemetry.request.and.returnValue(Promise.resolve([])); + openmct.telemetry.requestCollection.and.returnValue({ + load: jasmine.createSpy('load'), + on: jasmine.createSpy('on'), + off: jasmine.createSpy('off'), + getAll: jasmine.createSpy('getAll').and.returnValue([]) + }); const styleRuleManger = new StyleRuleManager(stylesObject, openmct, null, true); spyOn(styleRuleManger, 'subscribeToConditionSet'); From 54c90e02b925658eb32c6c40bac9166b8d7c20d0 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 17 Sep 2024 14:59:21 -0700 Subject: [PATCH 06/35] fixing tests --- src/plugins/condition/pluginSpec.js | 87 +++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index f71c6c4585f..f576ae87543 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -33,7 +33,7 @@ import StyleRuleManager from './StyleRuleManager.js'; import { IS_OLD_KEY } from './utils/constants.js'; import { getApplicableStylesForItem } from './utils/styleUtils.js'; -describe('the plugin', function () { +fdescribe('the plugin', function () { let conditionSetDefinition; let mockConditionSetDomainObject; let mockListener; @@ -720,23 +720,39 @@ describe('the plugin', function () { }; }); - it('should evaluate as old when telemetry is not received in the allotted time', (done) => { + it('should evaluate as old when telemetry is not received in the allotted time', async () => { + let onAddResolve; + const onAddCalledPromise = new Promise((resolve) => { + onAddResolve = resolve; + }); const mockTelemetryCollection = { load: jasmine.createSpy('load'), - on: jasmine.createSpy('on'), - off: jasmine.createSpy('off'), - getAll: jasmine.createSpy('getAll').and.returnValue([]) + on: jasmine.createSpy('on').and.callFake((event, callback) => { + if (event === 'add') { + onAddResolve(); + } + }) }; openmct.telemetry = jasmine.createSpyObj('telemetry', [ 'getMetadata', + 'request', + 'subscribe', 'getValueFormatter', 'abortAllRequests', 'requestCollection' ]); + openmct.telemetry.subscribe.and.returnValue(function () {}); + openmct.telemetry.request.and.returnValue(Promise.resolve([])); openmct.telemetry.getMetadata.and.returnValue({ ...testTelemetryObject.telemetry, - valueMetadatas: [] + valueMetadatas: testTelemetryObject.telemetry.values, + valuesForHints: jasmine + .createSpy('valuesForHints') + .and.returnValue(testTelemetryObject.telemetry.values), + value: jasmine.createSpy('value').and.callFake((key) => { + return testTelemetryObject.telemetry.values.find((value) => value.key === key); + }) }); openmct.telemetry.requestCollection.and.returnValue(mockTelemetryCollection); openmct.telemetry.getValueFormatter.and.returnValue({ @@ -751,13 +767,11 @@ describe('the plugin', function () { 'test-object': testTelemetryObject }; conditionMgr.updateConditionTelemetryObjects(); + // Wait for the 'on' callback to be called + await onAddCalledPromise; // Simulate the passage of time and no data received setTimeout(() => { - // Trigger the 'add' event on the mockTelemetryCollection with an empty array - // to simulate no data received - mockTelemetryCollection.on.calls.mostRecent().args[1]([]); - expect(mockListener).toHaveBeenCalledWith({ output: 'Any old telemetry', id: { @@ -767,7 +781,6 @@ describe('the plugin', function () { conditionId: '39584410-cbf9-499e-96dc-76f27e69885d', utc: undefined }); - done(); }, 400); }); @@ -799,11 +812,21 @@ describe('the plugin', function () { openmct.telemetry = jasmine.createSpyObj('telemetry', [ 'getMetadata', 'getValueFormatter', + 'request', + 'subscribe', 'requestCollection' ]); + openmct.telemetry.subscribe.and.returnValue(function () {}); + openmct.telemetry.request.and.returnValue(Promise.resolve([testDatum])); openmct.telemetry.getMetadata.and.returnValue({ ...testTelemetryObject.telemetry, - valueMetadatas: testTelemetryObject.telemetry.values + valueMetadatas: testTelemetryObject.telemetry.values, + valuesForHints: jasmine + .createSpy('valuesForHints') + .and.returnValue(testTelemetryObject.telemetry.values), + value: jasmine.createSpy('value').and.callFake((key) => { + return testTelemetryObject.telemetry.values.find((value) => value.key === key); + }) }); openmct.telemetry.requestCollection.and.returnValue(mockTelemetryCollection); openmct.telemetry.getValueFormatter.and.returnValue({ @@ -947,17 +970,25 @@ describe('the plugin', function () { openmct.telemetry.getMetadata = jasmine.createSpy('getMetadata'); openmct.telemetry.getMetadata.and.returnValue({ ...testTelemetryObject.telemetry, - valueMetadatas: [] + valueMetadatas: testTelemetryObject.telemetry.values, + valuesForHints: jasmine + .createSpy('valuesForHints') + .and.returnValue(testTelemetryObject.telemetry.values), + value: jasmine.createSpy('value').and.callFake((key) => { + return testTelemetryObject.telemetry.values.find((value) => value.key === key); + }) }); conditionMgr.on('conditionSetResultUpdated', mockListener); conditionMgr.telemetryObjects = { 'test-object': testTelemetryObject }; conditionMgr.updateConditionTelemetryObjects(); - conditionMgr.telemetryReceived(testTelemetryObject, { - 'some-key': 2, - utc: date - }); + conditionMgr.telemetryReceived(testTelemetryObject, [ + { + 'some-key': 2, + utc: date + } + ]); let result = conditionMgr.conditions.map((condition) => condition.result); expect(result[2]).toBeUndefined(); }); @@ -1047,18 +1078,28 @@ describe('the plugin', function () { } }; openmct.$injector = jasmine.createSpyObj('$injector', ['get']); - // const mockTransactionService = jasmine.createSpyObj( - // 'transactionService', - // ['commit'] - // ); openmct.telemetry = jasmine.createSpyObj('telemetry', [ 'isTelemetryObject', + 'request', + 'subscribe', 'getMetadata', 'getValueFormatter', 'requestCollection' ]); + openmct.telemetry.subscribe.and.returnValue(function () {}); + openmct.telemetry.request.and.returnValue(Promise.resolve([])); openmct.telemetry.isTelemetryObject.and.returnValue(true); openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry); + openmct.telemetry.getMetadata.and.returnValue({ + ...testTelemetryObject.telemetry, + valueMetadatas: testTelemetryObject.telemetry.values, + valuesForHints: jasmine + .createSpy('valuesForHints') + .and.returnValue(testTelemetryObject.telemetry.values), + value: jasmine.createSpy('value').and.callFake((key) => { + return testTelemetryObject.telemetry.values.find((value) => value.key === key); + }) + }); openmct.telemetry.getValueFormatter.and.returnValue({ parse: function (value) { return value; @@ -1066,9 +1107,7 @@ describe('the plugin', function () { }); openmct.telemetry.requestCollection.and.returnValue({ load: jasmine.createSpy('load'), - on: jasmine.createSpy('on'), - off: jasmine.createSpy('off'), - getAll: jasmine.createSpy('getAll').and.returnValue([]) + on: jasmine.createSpy('on') }); const styleRuleManger = new StyleRuleManager(stylesObject, openmct, null, true); From 94a4ff3cf73c1e410b52f358e1940007f0bcbebf Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 17 Sep 2024 15:02:13 -0700 Subject: [PATCH 07/35] removing unnecessary addition --- src/plugins/condition/pluginSpec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index f576ae87543..af92d838f88 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -720,7 +720,7 @@ fdescribe('the plugin', function () { }; }); - it('should evaluate as old when telemetry is not received in the allotted time', async () => { + fit('should evaluate as old when telemetry is not received in the allotted time', async () => { let onAddResolve; const onAddCalledPromise = new Promise((resolve) => { onAddResolve = resolve; @@ -737,12 +737,10 @@ fdescribe('the plugin', function () { openmct.telemetry = jasmine.createSpyObj('telemetry', [ 'getMetadata', 'request', - 'subscribe', 'getValueFormatter', 'abortAllRequests', 'requestCollection' ]); - openmct.telemetry.subscribe.and.returnValue(function () {}); openmct.telemetry.request.and.returnValue(Promise.resolve([])); openmct.telemetry.getMetadata.and.returnValue({ ...testTelemetryObject.telemetry, From 7cba09b9203b7e2ab58cd058e30ca2c5c1e1f745 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 17 Sep 2024 15:02:46 -0700 Subject: [PATCH 08/35] removing focused describe --- src/plugins/condition/pluginSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index af92d838f88..951c13bb86d 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -33,7 +33,7 @@ import StyleRuleManager from './StyleRuleManager.js'; import { IS_OLD_KEY } from './utils/constants.js'; import { getApplicableStylesForItem } from './utils/styleUtils.js'; -fdescribe('the plugin', function () { +describe('the plugin', function () { let conditionSetDefinition; let mockConditionSetDomainObject; let mockListener; From 405b4971357913cf1607f715b51a9007fbe83d66 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 17 Sep 2024 15:03:20 -0700 Subject: [PATCH 09/35] removing focused it --- src/plugins/condition/pluginSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index 951c13bb86d..b20918708e3 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -720,7 +720,7 @@ describe('the plugin', function () { }; }); - fit('should evaluate as old when telemetry is not received in the allotted time', async () => { + it('should evaluate as old when telemetry is not received in the allotted time', async () => { let onAddResolve; const onAddCalledPromise = new Promise((resolve) => { onAddResolve = resolve; From 5b385ea7a65fadad7f6952608ca6add467c96681 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 17 Sep 2024 16:22:55 -0700 Subject: [PATCH 10/35] fix weird test bleed --- src/plugins/condition/pluginSpec.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index b20918708e3..233ee9ad060 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -769,17 +769,17 @@ describe('the plugin', function () { await onAddCalledPromise; // Simulate the passage of time and no data received - setTimeout(() => { - expect(mockListener).toHaveBeenCalledWith({ - output: 'Any old telemetry', - id: { - namespace: '', - key: 'cf4456a9-296a-4e6b-b182-62ed29cd15b9' - }, - conditionId: '39584410-cbf9-499e-96dc-76f27e69885d', - utc: undefined - }); - }, 400); + await new Promise((resolve) => setTimeout(resolve, 400)); + + expect(mockListener).toHaveBeenCalledWith({ + output: 'Any old telemetry', + id: { + namespace: '', + key: 'cf4456a9-296a-4e6b-b182-62ed29cd15b9' + }, + conditionId: '39584410-cbf9-499e-96dc-76f27e69885d', + utc: undefined + }); }); it('should not evaluate as old when telemetry is received in the allotted time', async () => { From 23cf829fdc4b37fad8ebc19ba2d9229216643f17 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 19 Sep 2024 14:36:57 -0500 Subject: [PATCH 11/35] Add realtime output of telemetry data in conditionals and add support for historical conditional telemetry queries to allow for plotting --- src/plugins/condition/ConditionManager.js | 108 ++++++- .../ConditionSetTelemetryProvider.js | 3 +- .../components/ConditionCollection.vue | 18 +- .../condition/components/ConditionItem.vue | 96 +++++- .../condition/components/CriterionItem.vue | 2 +- src/plugins/condition/components/TestData.vue | 25 +- .../condition/historicalTelemetryProvider.js | 274 ++++++++++++++++++ .../plot/stackedPlot/StackedPlotItem.vue | 1 + src/plugins/plot/tickUtils.js | 4 +- 9 files changed, 510 insertions(+), 21 deletions(-) create mode 100644 src/plugins/condition/historicalTelemetryProvider.js diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 4a18140cf7b..f18eb068cc8 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -24,6 +24,7 @@ import { EventEmitter } from 'eventemitter3'; import { v4 as uuid } from 'uuid'; import Condition from './Condition.js'; +import HistoricalTelemetryProvider from './historicalTelemetryProvider.js'; import { getLatestTimestamp } from './utils/time.js'; export default class ConditionManager extends EventEmitter { @@ -46,6 +47,8 @@ export default class ConditionManager extends EventEmitter { applied: false }; this.initialize(); + this.telemetryBuffer = []; + this.isProcessing = false; } subscribeToTelemetry(telemetryObject) { @@ -320,6 +323,17 @@ export default class ConditionManager extends EventEmitter { return currentCondition; } + getHistoricalData() { + const historicalTelemetry = new HistoricalTelemetryProvider( + this.openmct, + this.telemetryObjects, + this.compositionLoad, + this.conditions, + this.conditionSetDomainObject + ); + return historicalTelemetry.getHistoricalData(); + } + getCurrentConditionLAD(conditionResults) { const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection; let currentCondition = conditionCollection[conditionCollection.length - 1]; @@ -375,8 +389,26 @@ export default class ConditionManager extends EventEmitter { } const currentCondition = this.getCurrentConditionLAD(conditionResults); + let output = currentCondition?.configuration?.output; + if (output === 'telemetry value') { + const { outputTelemetry, outputMetadata } = currentCondition.configuration; + const outputTelemetryObject = await this.openmct.objects.get(outputTelemetry); + const telemetryOptions = { + size: 1, + strategy: 'latest', + timeContext: this.openmct.time.getContextForView([]) + }; + const latestData = await this.openmct.telemetry.request( + outputTelemetryObject, + telemetryOptions + ); + if (latestData?.[0]?.[outputMetadata]) { + output = latestData?.[0]?.[outputMetadata]; + } + } + const currentOutput = { - output: currentCondition.configuration.output, + output: output, id: this.conditionSetDomainObject.identifier, conditionId: currentCondition.id, ...latestTimestamp @@ -394,6 +426,18 @@ export default class ConditionManager extends EventEmitter { } } + const conditionTelemetries = []; + const conditions = this.conditionSetDomainObject.configuration.conditionCollection; + conditions.forEach((condition) => { + if (condition?.configuration?.outputTelemetry) { + conditionTelemetries.push(condition?.configuration?.outputTelemetry); + } + }); + + if (conditionTelemetries.includes(id)) { + return true; + } + return false; } @@ -415,7 +459,7 @@ export default class ConditionManager extends EventEmitter { timestamp[timeSystemKey] = currentTimestamp; if (this.shouldEvaluateNewTelemetry(currentTimestamp)) { this.updateConditionResults(normalizedDatum); - this.updateCurrentCondition(timestamp); + this.updateCurrentCondition(timestamp, endpoint, datum); } } @@ -428,14 +472,12 @@ export default class ConditionManager extends EventEmitter { }); } - updateCurrentCondition(timestamp) { - const currentCondition = this.getCurrentCondition(); - + emitConditionSetResult(currentCondition, timestamp, outputValue) { this.emit( 'conditionSetResultUpdated', Object.assign( { - output: currentCondition.configuration.output, + output: outputValue, id: this.conditionSetDomainObject.identifier, conditionId: currentCondition.id }, @@ -444,6 +486,60 @@ export default class ConditionManager extends EventEmitter { ); } + updateCurrentCondition(timestamp, telemetryObject, telemetryData) { + this.telemetryBuffer.push({ timestamp, telemetryObject, telemetryData }); + + if (!this.isProcessing) { + this.processBuffer(); + } + } + + async processBuffer() { + this.isProcessing = true; + + while (this.telemetryBuffer.length > 0) { + const { timestamp, telemetryObject, telemetryData } = this.telemetryBuffer.shift(); + await this.processCondition(timestamp, telemetryObject, telemetryData); + } + + this.isProcessing = false; + } + + async processCondition(timestamp, telemetryObject, telemetryData) { + const currentCondition = this.getCurrentCondition(); + let telemetryValue = currentCondition.configuration.output; + if (currentCondition?.configuration?.outputTelemetry) { + const selectedOutputIdentifier = currentCondition?.configuration?.outputTelemetry; + const outputMetadata = currentCondition?.configuration?.outputMetadata; + const telemetryKeystring = this.openmct.objects.makeKeyString(telemetryObject.identifier); + + if (selectedOutputIdentifier === telemetryKeystring) { + telemetryValue = telemetryData[outputMetadata]; + } else { + const outputTelemetryObject = await this.openmct.objects.get(selectedOutputIdentifier); + const telemetryOptions = { + size: 1, + strategy: 'latest', + start: timestamp?.utc - 1000, + end: timestamp?.utc + 1000 + }; + const outputTelemetryData = await this.openmct.telemetry.request( + outputTelemetryObject, + telemetryOptions + ); + const outputTelemetryValue = + outputTelemetryData?.length > 0 ? outputTelemetryData.slice(-1)[0] : null; + if (outputTelemetryData.length && outputTelemetryValue?.[outputMetadata]) { + telemetryValue = outputTelemetryValue?.[outputMetadata]; + } else { + telemetryValue = undefined; + } + } + } + + this.emitConditionSetResult(currentCondition, timestamp, telemetryValue); + } + getTestData(metadatum) { let data = undefined; if (this.testData.applied) { diff --git a/src/plugins/condition/ConditionSetTelemetryProvider.js b/src/plugins/condition/ConditionSetTelemetryProvider.js index 38e7d4cb838..c45b13c22d6 100644 --- a/src/plugins/condition/ConditionSetTelemetryProvider.js +++ b/src/plugins/condition/ConditionSetTelemetryProvider.js @@ -42,8 +42,9 @@ export default class ConditionSetTelemetryProvider { async request(domainObject, options) { let conditionManager = this.getConditionManager(domainObject); + const formattedHistoricalData = await conditionManager.getHistoricalData(); let latestOutput = await conditionManager.requestLADConditionSetOutput(options); - return latestOutput; + return [...formattedHistoricalData, ...latestOutput]; } subscribe(domainObject, callback) { diff --git a/src/plugins/condition/components/ConditionCollection.vue b/src/plugins/condition/components/ConditionCollection.vue index 84b5aff0084..146955002fb 100644 --- a/src/plugins/condition/components/ConditionCollection.vue +++ b/src/plugins/condition/components/ConditionCollection.vue @@ -235,10 +235,11 @@ export default { return arr; }, - addTelemetryObject(domainObject) { + async addTelemetryObject(domainObject) { const keyString = this.openmct.objects.makeKeyString(domainObject.identifier); + const telemetryPath = await this.getFullTelemetryPath(domainObject); - this.telemetryObjs.push(domainObject); + this.telemetryObjs.push({ ...domainObject, path: telemetryPath }); this.$emit('telemetry-updated', this.telemetryObjs); this.subscribeToStaleness(domainObject, (stalenessResponse) => { @@ -248,6 +249,19 @@ export default { }); }); }, + async getFullTelemetryPath(telemetry) { + const keyString = this.openmct.objects.makeKeyString(telemetry.identifier); + const originalPathObjects = await this.openmct.objects.getOriginalPath(keyString, []); + + const telemetryPath = originalPathObjects.reverse().map((pathObject) => { + if (pathObject.type !== 'root') { + return pathObject.name; + } + return undefined; + }); + + return telemetryPath.join('/'); + }, removeTelemetryObject(identifier) { const keyString = this.openmct.objects.makeKeyString(identifier); const index = this.telemetryObjs.findIndex((obj) => { diff --git a/src/plugins/condition/components/ConditionItem.vue b/src/plugins/condition/components/ConditionItem.vue index dd42a583163..250c517154f 100644 --- a/src/plugins/condition/components/ConditionItem.vue +++ b/src/plugins/condition/components/ConditionItem.vue @@ -99,13 +99,13 @@ @change="setOutputValue" > + + + + + + -
Match @@ -181,7 +214,12 @@ {{ condition.configuration.name }} - Output: {{ condition.configuration.output }} + + Output: + {{ + condition.configuration.output === undefined ? 'none' : condition.configuration.output + }} +
@@ -250,10 +288,11 @@ export default { expanded: true, trigger: 'all', selectedOutputSelection: '', - outputOptions: ['false', 'true', 'string'], + outputOptions: ['none', 'false', 'true', 'string', 'telemetry value'], criterionIndex: 0, draggingOver: false, - isDefault: this.condition.isDefault + isDefault: this.condition.isDefault, + telemetryMetadataOptions: {} }; }, computed: { @@ -287,26 +326,51 @@ export default { return false; } }, + watch: { + condition: { + handler() { + if (this.condition.configuration.output !== 'telemetry value') { + this.condition.configuration.outputTelemetry = null; + this.condition.configuration.outputMetadata = null; + } + }, + deep: true + }, + isEditing(newValue, oldValue) { + if (newValue === true) { + this.initializeMetadata(); + } + } + }, unmounted() { this.destroy(); }, mounted() { this.setOutputSelection(); + this.initializeMetadata(); }, methods: { setOutputSelection() { let conditionOutput = this.condition.configuration.output; if (conditionOutput) { - if (conditionOutput !== 'false' && conditionOutput !== 'true') { + if ( + conditionOutput !== 'false' && + conditionOutput !== 'true' && + conditionOutput !== 'telemetry value' + ) { this.selectedOutputSelection = 'string'; } else { this.selectedOutputSelection = conditionOutput; } + } else if (conditionOutput === undefined) { + this.selectedOutputSelection = 'none'; } }, setOutputValue() { if (this.selectedOutputSelection === 'string') { this.condition.configuration.output = ''; + } else if (this.selectedOutputSelection === 'none') { + this.condition.configuration.output = undefined; } else { this.condition.configuration.output = this.selectedOutputSelection; } @@ -401,6 +465,24 @@ export default { }, initCap(str) { return str.charAt(0).toUpperCase() + str.slice(1); + }, + initializeMetadata() { + this.telemetry.forEach((telemetryObject) => { + const id = this.openmct.objects.makeKeyString(telemetryObject.identifier); + let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject); + if (telemetryMetadata) { + this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice(); + } else { + this.telemetryMetadataOptions[id] = []; + } + }); + }, + getId(identifier) { + if (identifier) { + return this.openmct.objects.makeKeyString(identifier); + } + + return []; } } }; diff --git a/src/plugins/condition/components/CriterionItem.vue b/src/plugins/condition/components/CriterionItem.vue index a632d675a1a..4d0b94e094d 100644 --- a/src/plugins/condition/components/CriterionItem.vue +++ b/src/plugins/condition/components/CriterionItem.vue @@ -40,7 +40,7 @@ :key="telemetryOption.identifier.key" :value="telemetryOption.identifier" > - {{ telemetryOption.name }} + {{ telemetryOption.path }} diff --git a/src/plugins/condition/components/TestData.vue b/src/plugins/condition/components/TestData.vue index 7d25680703e..1e6f695cf0e 100644 --- a/src/plugins/condition/components/TestData.vue +++ b/src/plugins/condition/components/TestData.vue @@ -63,7 +63,7 @@ :key="index" :value="telemetryOption.identifier" > - {{ telemetryOption.name }} + {{ telemetryPaths[index] || telemetryOption.name }} @@ -147,7 +147,8 @@ export default { expanded: true, isApplied: false, testInputs: [], - telemetryMetadataOptions: {} + telemetryMetadataOptions: {}, + telemetryPaths: [] }; }, watch: { @@ -200,6 +201,10 @@ export default { this.telemetryMetadataOptions[id] = []; } }); + this.telemetry.forEach(async (telemetryOption, index) => { + const telemetryPath = await this.getFullTelemetryPath(telemetryOption); + this.telemetryPaths[index] = telemetryPath; + }); }, addTestInput(testInput) { this.testInputs.push( @@ -244,6 +249,22 @@ export default { applied: this.isApplied, conditionTestInputs: this.testInputs }); + }, + async getFullTelemetryPath(telemetry) { + const keyStringForObject = this.openmct.objects.makeKeyString(telemetry.identifier); + const originalPathObjects = await this.openmct.objects.getOriginalPath( + keyStringForObject, + [] + ); + + const telemetryPath = originalPathObjects.reverse().map((pathObject) => { + if (pathObject.type !== 'root') { + return pathObject.name; + } + return undefined; + }); + + return telemetryPath.join('/'); } } }; diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/historicalTelemetryProvider.js new file mode 100644 index 00000000000..f5377d82316 --- /dev/null +++ b/src/plugins/condition/historicalTelemetryProvider.js @@ -0,0 +1,274 @@ +export default class HistoricalTelemetryProvider { + constructor(openmct, telemetryObjects, compositionLoad, conditions, conditionSetDomainObject) { + this.openmct = openmct; + this.telemetryObjects = telemetryObjects; + this.compositionLoad = compositionLoad; + this.bounds = { start: null, end: null }; + this.telemetryList = []; + this.conditions = conditions; + this.conditionSetDomainObject = conditionSetDomainObject; + this.historicalTelemetryPoolMap = new Map(); + this.historicalTelemetryDateMap = new Map(); + this.index = 0; + } + + setTimeBounds(bounds) { + this.bounds = bounds; + } + + refreshAllHistoricalTelemetries() { + const refreshPromises = []; + for (const [, value] of Object.entries(this.telemetryObjects)) { + refreshPromises.push(this.refreshHistoricalTelemetry(value)); + } + return Promise.all(refreshPromises); + } + + async refreshHistoricalTelemetry(domainObject, identifier) { + console.log('refreshHistoricalTelemetry'); + if (!domainObject && identifier) { + domainObject = await this.openmct.objects.get(identifier); + } + const id = this.openmct.objects.makeKeyString(domainObject.identifier); + const telemetryOptions = { ...this.bounds }; + const historicalTelemetry = await this.openmct.telemetry.request( + domainObject, + telemetryOptions + ); + this.historicalTelemetryPoolMap.set(id, { domainObject, historicalTelemetry }); + return { domainObject, historicalTelemetry }; + } + + evaluateTrueCondition(historicalDateMap, timestamp, condition, conditionCollectionMap) { + const telemetryData = historicalDateMap.get(timestamp); + const conditionConfiguration = conditionCollectionMap.get(condition.id)?.configuration; + const { outputTelemetry, outputMetadata } = conditionConfiguration; + let output = {}; + if (outputTelemetry) { + const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); + const outputTelemetryData = telemetryData.get(outputTelemetryID); + output.telemetry = outputTelemetryData; + output.value = outputTelemetryData[outputMetadata]; + output.condition = condition; + } else if (conditionConfiguration?.output) { + output.telemetry = null; + output.value = conditionConfiguration?.output; + output.condition = condition; + } + return output; + } + + async getAllTelemetries(conditionCollection) { + const conditionCollectionMap = new Map(); + const inputTelemetries = []; + const outputTelemetries = []; + const historicalTelemetryPoolPromises = []; + + conditionCollection.forEach((condition, index) => { + console.log('-------------------------'); + console.log(condition); + const { id } = condition; + const { criteria, output, outputTelemetry, outputMetadata } = condition.configuration; + const inputTelemetry = criteria?.[0]?.telemetry; + console.log(id); + console.log(criteria); + console.log(output); + console.log(outputMetadata); + console.log('inputTelemetry', inputTelemetry); + console.log('outputTelemetry', outputTelemetry); + conditionCollectionMap.set(condition?.id, condition); + if (inputTelemetry) { + const inputTelemetryId = this.openmct.objects.makeKeyString(inputTelemetry); + if (![...inputTelemetries, ...outputTelemetries].includes(inputTelemetryId)) { + historicalTelemetryPoolPromises.push( + this.refreshHistoricalTelemetry(null, inputTelemetry) + ); + } + inputTelemetries.push(inputTelemetryId); + } else { + inputTelemetries.push(null); + } + if (outputTelemetry) { + if (![...inputTelemetries, ...outputTelemetries].includes(outputTelemetry)) { + historicalTelemetryPoolPromises.push( + this.refreshHistoricalTelemetry(null, outputTelemetry) + ); + } + outputTelemetries.push(outputTelemetry); + } else { + outputTelemetries.push(null); + } + }); + + const historicalTelemetriesPool = await Promise.all(historicalTelemetryPoolPromises); + return { + historicalTelemetriesPool, + inputTelemetries, + outputTelemetries, + conditionCollectionMap + }; + } + + sortTelemetriesByDate(historicalTelemetriesPool) { + const historicalTelemetryDateMap = new Map(); + historicalTelemetriesPool.forEach((historicalTelemetryList) => { + const { historicalTelemetry, domainObject } = historicalTelemetryList; + const { identifier } = domainObject; + const telemetryIdentifier = this.openmct.objects.makeKeyString(identifier); + historicalTelemetry.forEach((historicalTelemetryItem) => { + if (!historicalTelemetryDateMap.get(historicalTelemetryItem.utc)) { + const telemetryMap = new Map(); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + } else { + const telemetryMap = historicalTelemetryDateMap.get(historicalTelemetryItem.utc); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + } + }); + }); + return historicalTelemetryDateMap; + } + + evaluateConditionsByDate(historicalTelemetryDateMap, conditionCollectionMap) { + const outputTelemetryDateMap = new Map(); + historicalTelemetryDateMap.forEach((historicalTelemetryMap, timestamp) => { + let isConditionValid = false; + const evaluatedConditions = []; + this.conditions.forEach((condition) => { + if (isConditionValid) { + return; + } + const { id } = condition; + const conditionMetadata = { condition }; + const conditionCriteria = condition.criteria[0]; + let result; + if (conditionCriteria?.telemetry) { + const conditionInputTelemetryId = this.openmct.objects.makeKeyString( + conditionCriteria.telemetry + ); + const inputTelemetry = historicalTelemetryMap.get(conditionInputTelemetryId); + conditionMetadata.inputTelemetry = inputTelemetry; + result = conditionCriteria.computeResult({ + id, + ...inputTelemetry + }); + } else if (!conditionCriteria) { + const conditionDetails = conditionCollectionMap.get(id); + const { isDefault } = conditionDetails; + const conditionConfiguration = conditionDetails?.configuration; + const { outputTelemetry, outputMetadata, output } = conditionConfiguration; + if (isDefault) { + const conditionOutput = { + telemetry: null, + value: output, + condition + }; + outputTelemetryDateMap.set(timestamp, conditionOutput); + } + } + conditionMetadata.result = result; + evaluatedConditions.push(conditionMetadata); + if (result === true) { + isConditionValid = true; + const conditionOutput = this.evaluateTrueCondition( + historicalTelemetryDateMap, + timestamp, + condition, + conditionCollectionMap + ); + console.log(conditionOutput.value); + outputTelemetryDateMap.set(timestamp, conditionOutput); + } + }); + }); + return outputTelemetryDateMap; + } + + async getHistoricalInputsByDate() { + console.log('getHistoricalInputsByDate'); + console.log(this.conditions); + const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection; + + const { + historicalTelemetriesPool, + inputTelemetries, + outputTelemetries, + conditionCollectionMap + } = await this.getAllTelemetries(conditionCollection); + + const historicalTelemetryDateMap = this.sortTelemetriesByDate(historicalTelemetriesPool); + const outputTelemetryDateMap = this.evaluateConditionsByDate( + historicalTelemetryDateMap, + conditionCollectionMap + ); + + console.log('*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣'); + console.log(historicalTelemetriesPool); + console.log(this.historicalTelemetryPoolMap); + console.log(inputTelemetries); + console.log(outputTelemetries); + console.log(historicalTelemetryDateMap); + console.log(outputTelemetryDateMap); + + return outputTelemetryDateMap; + } + + addItemToHistoricalTelemetryMap(telemetryMap, item, type, index) { + if (type === 'input') { + telemetryMap.set(); + } + } + + async getHistoricalData() { + console.log('getHistoricalData'); + await this.compositionLoad; + this.setTimeBounds(this.openmct.time.getBounds()); + const outputTelemetryMap = await this.getHistoricalInputsByDate(); + const formattedOutputTelemetry = this.formatOutputData(outputTelemetryMap); + // const firstObjectKey = this.historicalTelemetryPoolMap.keys().next().value; + // const firstObjectValue = this.historicalTelemetryPoolMap.values().next().value; + // const formattedHistoricalData = this.formatHistoricalData(firstObjectKey, firstObjectValue); + console.log(formattedOutputTelemetry); + // console.log(formattedHistoricalData); + return formattedOutputTelemetry; + } + + formatOutputData(outputTelemetryMap) { + const outputTelemetryList = []; + const domainObject = this.conditionSetDomainObject; + outputTelemetryMap.forEach((outputMetadata, timestamp) => { + const { condition, telemetry, value } = outputMetadata; + outputTelemetryList.push({ + conditionId: condition.id, + id: domainObject.identifier, + output: value, + utc: timestamp + }); + }); + return outputTelemetryList; + } + + simpleTelemetryList(outputTelemetryMap) { + const outputTelemetryList = []; + outputTelemetryMap.forEach((outputMetadata, timestamp) => { + const { value } = outputMetadata; + outputTelemetryList.push(value); + }); + return outputTelemetryList; + } + + formatHistoricalData(historicalDataKey, telemetryDetails) { + const formattedData = []; + const { domainObject, historicalTelemetry } = telemetryDetails; + historicalTelemetry.forEach((value) => { + formattedData.push({ + id: domainObject.identifier, + output: value.sin, + conditionId: historicalDataKey, + utc: value.utc + }); + }); + return formattedData; + } +} diff --git a/src/plugins/plot/stackedPlot/StackedPlotItem.vue b/src/plugins/plot/stackedPlot/StackedPlotItem.vue index 1541b8c9806..4a9c7e28ada 100644 --- a/src/plugins/plot/stackedPlot/StackedPlotItem.vue +++ b/src/plugins/plot/stackedPlot/StackedPlotItem.vue @@ -243,6 +243,7 @@ export default { domainObject: { ...this.childObject, configuration: { + ...this.childObject.configuration, series: [ { identifier: this.childObject.identifier, diff --git a/src/plugins/plot/tickUtils.js b/src/plugins/plot/tickUtils.js index fff48cb546b..80e19b5dce2 100644 --- a/src/plugins/plot/tickUtils.js +++ b/src/plugins/plot/tickUtils.js @@ -94,7 +94,7 @@ export function ticks(start, stop, count) { } export function commonPrefix(a, b) { - const maxLen = Math.min(a.length, b.length); + const maxLen = Math.min(a.length, b?.length); let breakpoint = 0; for (let i = 0; i < maxLen; i++) { if (a[i] !== b[i]) { @@ -110,7 +110,7 @@ export function commonPrefix(a, b) { } export function commonSuffix(a, b) { - const maxLen = Math.min(a.length, b.length); + const maxLen = Math.min(a.length, b?.length); let breakpoint = 0; for (let i = 0; i <= maxLen; i++) { if (a[a.length - i] !== b[b.length - i]) { From 21e94fd7ff7ed5ba381e5e786b7631b6456b3e16 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Sat, 21 Sep 2024 08:52:20 -0500 Subject: [PATCH 12/35] Cleanup and add missing files --- .webpack/webpack.common.mjs | 3 +- karma.conf.cjs | 4 + .../ConditionInspectorViewProvider.js | 53 ++++++++++++ src/plugins/condition/ConditionManager.js | 4 +- .../ConditionInspectorConfigView.vue | 62 ++++++++++++++ .../condition/historicalTelemetryProvider.js | 81 +++++++++++-------- .../condition/historicalTelemetryWorker.js | 33 ++++++++ src/plugins/condition/plugin.js | 3 + 8 files changed, 207 insertions(+), 36 deletions(-) create mode 100644 src/plugins/condition/ConditionInspectorViewProvider.js create mode 100644 src/plugins/condition/components/ConditionInspectorConfigView.vue create mode 100644 src/plugins/condition/historicalTelemetryWorker.js diff --git a/.webpack/webpack.common.mjs b/.webpack/webpack.common.mjs index 7290ce999e5..f4862099ef9 100644 --- a/.webpack/webpack.common.mjs +++ b/.webpack/webpack.common.mjs @@ -50,7 +50,8 @@ const config = { inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js', espressoTheme: './src/plugins/themes/espresso-theme.scss', snowTheme: './src/plugins/themes/snow-theme.scss', - darkmatterTheme: './src/plugins/themes/darkmatter-theme.scss' + darkmatterTheme: './src/plugins/themes/darkmatter-theme.scss', + historicalTelemetryWorker: './src/plugins/condition/historicalTelemetryWorker.js', }, output: { globalObject: 'this', diff --git a/karma.conf.cjs b/karma.conf.cjs index 1fc2256bfe0..b3e4d336326 100644 --- a/karma.conf.cjs +++ b/karma.conf.cjs @@ -66,6 +66,10 @@ module.exports = async (config) => { { pattern: 'dist/generatorWorker.js*', included: false + }, + { + pattern: 'dist/historicalTelemetryWorker.js*', + included: false } ], port: 9876, diff --git a/src/plugins/condition/ConditionInspectorViewProvider.js b/src/plugins/condition/ConditionInspectorViewProvider.js new file mode 100644 index 00000000000..fc972f60ea8 --- /dev/null +++ b/src/plugins/condition/ConditionInspectorViewProvider.js @@ -0,0 +1,53 @@ +// src/plugins/condition/ConditionInspectorView.js + +import mount from 'utils/mount'; + +import ConditionConfigView from './components/ConditionInspectorConfigView.vue'; + +export default function ConditionInspectorView(openmct) { + return { + key: 'condition-config', + name: 'Config', + canView: function (selection) { + return selection.length > 0 && selection[0][0].context.item.type === 'conditionSet'; + }, + view: function (selection) { + let _destroy = null; + const domainObject = selection[0][0].context.item; + + return { + show: function (element) { + const { destroy } = mount( + { + el: element, + components: { + ConditionConfigView: ConditionConfigView + }, + provide: { + openmct, + domainObject + }, + template: '' + }, + { + app: openmct.app, + element + } + ); + _destroy = destroy; + }, + showTab: function (isEditing) { + return isEditing; + }, + priority: function () { + return 1; + }, + destroy: function () { + if (_destroy) { + _destroy(); + } + } + }; + } + }; +} diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index f18eb068cc8..859bf943e87 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -324,10 +324,12 @@ export default class ConditionManager extends EventEmitter { } getHistoricalData() { + if (!this.conditionSetDomainObject.configuration.shouldFetchHistorical) { + return []; + } const historicalTelemetry = new HistoricalTelemetryProvider( this.openmct, this.telemetryObjects, - this.compositionLoad, this.conditions, this.conditionSetDomainObject ); diff --git a/src/plugins/condition/components/ConditionInspectorConfigView.vue b/src/plugins/condition/components/ConditionInspectorConfigView.vue new file mode 100644 index 00000000000..02c0539ec0b --- /dev/null +++ b/src/plugins/condition/components/ConditionInspectorConfigView.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/historicalTelemetryProvider.js index f5377d82316..d98b762e5bd 100644 --- a/src/plugins/condition/historicalTelemetryProvider.js +++ b/src/plugins/condition/historicalTelemetryProvider.js @@ -1,8 +1,7 @@ export default class HistoricalTelemetryProvider { - constructor(openmct, telemetryObjects, compositionLoad, conditions, conditionSetDomainObject) { + constructor(openmct, telemetryObjects, conditions, conditionSetDomainObject) { this.openmct = openmct; this.telemetryObjects = telemetryObjects; - this.compositionLoad = compositionLoad; this.bounds = { start: null, end: null }; this.telemetryList = []; this.conditions = conditions; @@ -130,6 +129,50 @@ export default class HistoricalTelemetryProvider { return historicalTelemetryDateMap; } + async sortTelemetriesInWorker(historicalTelemetriesPool) { + const sortedTelemetries = await this.startWorker('sortTelemetries', { + historicalTelemetriesPool + }); + return sortedTelemetries; + } + + async startWorker(type, data) { + // eslint-disable-next-line no-undef + const workerUrl = `${this.openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}historicalTelemetryWorker.js`; + const worker = new Worker(workerUrl); + + try { + const result = await this.getDataFromWorker(worker, type, data); + return result; + } catch (error) { + console.error('Error in condition manager getHistoricalData:', error); + throw error; + } finally { + worker.terminate(); + } + } + + getDataFromWorker(worker, type, data) { + return new Promise((resolve, reject) => { + worker.onmessage = (e) => { + if (e.data.type === 'result') { + resolve(e.data.data); + } else if (e.data.type === 'error') { + reject(new Error(e.data.error)); + } + }; + + worker.onerror = (error) => { + reject(error); + }; + + worker.postMessage({ + type, + data + }); + }); + } + evaluateConditionsByDate(historicalTelemetryDateMap, conditionCollectionMap) { const outputTelemetryDateMap = new Map(); historicalTelemetryDateMap.forEach((historicalTelemetryMap, timestamp) => { @@ -177,7 +220,6 @@ export default class HistoricalTelemetryProvider { condition, conditionCollectionMap ); - console.log(conditionOutput.value); outputTelemetryDateMap.set(timestamp, conditionOutput); } }); @@ -186,8 +228,6 @@ export default class HistoricalTelemetryProvider { } async getHistoricalInputsByDate() { - console.log('getHistoricalInputsByDate'); - console.log(this.conditions); const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection; const { @@ -197,7 +237,8 @@ export default class HistoricalTelemetryProvider { conditionCollectionMap } = await this.getAllTelemetries(conditionCollection); - const historicalTelemetryDateMap = this.sortTelemetriesByDate(historicalTelemetriesPool); + const historicalTelemetryDateMap = + await this.sortTelemetriesInWorker(historicalTelemetriesPool); const outputTelemetryDateMap = this.evaluateConditionsByDate( historicalTelemetryDateMap, conditionCollectionMap @@ -222,15 +263,10 @@ export default class HistoricalTelemetryProvider { async getHistoricalData() { console.log('getHistoricalData'); - await this.compositionLoad; this.setTimeBounds(this.openmct.time.getBounds()); const outputTelemetryMap = await this.getHistoricalInputsByDate(); const formattedOutputTelemetry = this.formatOutputData(outputTelemetryMap); - // const firstObjectKey = this.historicalTelemetryPoolMap.keys().next().value; - // const firstObjectValue = this.historicalTelemetryPoolMap.values().next().value; - // const formattedHistoricalData = this.formatHistoricalData(firstObjectKey, firstObjectValue); console.log(formattedOutputTelemetry); - // console.log(formattedHistoricalData); return formattedOutputTelemetry; } @@ -248,27 +284,4 @@ export default class HistoricalTelemetryProvider { }); return outputTelemetryList; } - - simpleTelemetryList(outputTelemetryMap) { - const outputTelemetryList = []; - outputTelemetryMap.forEach((outputMetadata, timestamp) => { - const { value } = outputMetadata; - outputTelemetryList.push(value); - }); - return outputTelemetryList; - } - - formatHistoricalData(historicalDataKey, telemetryDetails) { - const formattedData = []; - const { domainObject, historicalTelemetry } = telemetryDetails; - historicalTelemetry.forEach((value) => { - formattedData.push({ - id: domainObject.identifier, - output: value.sin, - conditionId: historicalDataKey, - utc: value.utc - }); - }); - return formattedData; - } } diff --git a/src/plugins/condition/historicalTelemetryWorker.js b/src/plugins/condition/historicalTelemetryWorker.js new file mode 100644 index 00000000000..017e9daa26b --- /dev/null +++ b/src/plugins/condition/historicalTelemetryWorker.js @@ -0,0 +1,33 @@ +import { makeKeyString } from '../../api/objects/object-utils.js'; + +function sortTelemetriesByDate(historicalTelemetriesPool) { + const historicalTelemetryDateMap = new Map(); + historicalTelemetriesPool.forEach((historicalTelemetryList) => { + const { historicalTelemetry, domainObject } = historicalTelemetryList; + const { identifier } = domainObject; + const telemetryIdentifier = makeKeyString(identifier); + historicalTelemetry.forEach((historicalTelemetryItem) => { + if (!historicalTelemetryDateMap.get(historicalTelemetryItem.utc)) { + const telemetryMap = new Map(); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + } else { + const telemetryMap = historicalTelemetryDateMap.get(historicalTelemetryItem.utc); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + } + }); + }); + return historicalTelemetryDateMap; +} + +self.onmessage = function (e) { + const { type, data } = e.data; + + if (type === 'sortTelemetries') { + const sortedTelemetries = sortTelemetriesByDate(data.historicalTelemetriesPool); + self.postMessage({ type: 'result', data: sortedTelemetries }); + } else { + self.postMessage({ type: 'error', error: 'Unknown message type' }); + } +}; diff --git a/src/plugins/condition/plugin.js b/src/plugins/condition/plugin.js index 365386ef68d..5540746206c 100644 --- a/src/plugins/condition/plugin.js +++ b/src/plugins/condition/plugin.js @@ -21,6 +21,7 @@ *****************************************************************************/ import { v4 as uuid } from 'uuid'; +import ConditionInspectorViewProvider from './ConditionInspectorViewProvider.js'; import ConditionSetCompositionPolicy from './ConditionSetCompositionPolicy.js'; import ConditionSetMetadataProvider from './ConditionSetMetadataProvider.js'; import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider.js'; @@ -37,6 +38,7 @@ export default function ConditionPlugin() { cssClass: 'icon-conditional', initialize: function (domainObject) { domainObject.configuration = { + shouldFetchHistorical: false, conditionTestData: [], conditionCollection: [ { @@ -61,5 +63,6 @@ export default function ConditionPlugin() { openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct)); openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct)); openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct)); + openmct.inspectorViews.addProvider(new ConditionInspectorViewProvider(openmct)); }; } From 101baa58e09ffdda7ccd16780b7e8e518b16ae2f Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Mon, 30 Sep 2024 00:20:48 -0500 Subject: [PATCH 13/35] Fix issue with missing data --- src/plugins/condition/historicalTelemetryWorker.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/plugins/condition/historicalTelemetryWorker.js b/src/plugins/condition/historicalTelemetryWorker.js index 017e9daa26b..fae6a5b9132 100644 --- a/src/plugins/condition/historicalTelemetryWorker.js +++ b/src/plugins/condition/historicalTelemetryWorker.js @@ -7,14 +7,18 @@ function sortTelemetriesByDate(historicalTelemetriesPool) { const { identifier } = domainObject; const telemetryIdentifier = makeKeyString(identifier); historicalTelemetry.forEach((historicalTelemetryItem) => { - if (!historicalTelemetryDateMap.get(historicalTelemetryItem.utc)) { + let telemetryTimestamp = historicalTelemetryItem.utc; + if (historicalTelemetryItem.timestamp) { + telemetryTimestamp = new Date(historicalTelemetryItem.timestamp)?.getTime(); + } + if (!historicalTelemetryDateMap.get(telemetryTimestamp)) { const telemetryMap = new Map(); telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); - historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); } else { - const telemetryMap = historicalTelemetryDateMap.get(historicalTelemetryItem.utc); + const telemetryMap = historicalTelemetryDateMap.get(telemetryTimestamp); telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); - historicalTelemetryDateMap.set(historicalTelemetryItem.utc, telemetryMap); + historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); } }); }); From 7896f36748cfcdf6e68b0a09a45a7e623a250e79 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Mon, 30 Sep 2024 09:22:36 -0500 Subject: [PATCH 14/35] Update emitted values --- e2e/appActions.js | 17 ++++++++++ .../conditionSet/conditionSet.e2e.spec.js | 31 ++++++++++++++++++- src/plugins/condition/ConditionManager.js | 13 +++++--- .../condition/ConditionSetMetadataProvider.js | 23 +++++++++++--- .../condition/historicalTelemetryProvider.js | 11 +++++-- 5 files changed, 82 insertions(+), 13 deletions(-) diff --git a/e2e/appActions.js b/e2e/appActions.js index f1602bc3cd4..14586353009 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -116,6 +116,22 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine }; } +/** + * Retrieves the properties of an OpenMCT domain object by its identifier. + * + * @param {import('@playwright/test').Page} page - The Playwright page object. + * @param {string | identifier - The identifier or UUID of the domain object. + * @returns {Promise} An object containing the properties of the domain object. + */ +async function getDomainObject(page, identifier) { + const domainObject = await page.evaluate(async (objIdentifier) => { + const object = await window.openmct.objects.get(objIdentifier); + return object; + }, identifier); + + return domainObject; +} + /** * Generate a notification with the given options. * @param {import('@playwright/test').Page} page @@ -636,6 +652,7 @@ export { createPlanFromJSON, expandEntireTree, getCanvasPixels, + getDomainObject, navigateToObjectWithFixedTimeBounds, navigateToObjectWithRealTime, setEndOffset, diff --git a/e2e/tests/functional/plugins/conditionSet/conditionSet.e2e.spec.js b/e2e/tests/functional/plugins/conditionSet/conditionSet.e2e.spec.js index 6b76088b395..9ebe193431b 100644 --- a/e2e/tests/functional/plugins/conditionSet/conditionSet.e2e.spec.js +++ b/e2e/tests/functional/plugins/conditionSet/conditionSet.e2e.spec.js @@ -29,7 +29,8 @@ import { fileURLToPath } from 'url'; import { createDomainObjectWithDefaults, - createExampleTelemetryObject + createExampleTelemetryObject, + getDomainObject } from '../../../../appActions.js'; import { expect, test } from '../../../../pluginFixtures.js'; @@ -468,6 +469,34 @@ test.describe('Basic Condition Set Use', () => { description: 'https://github.com/nasa/openmct/issues/7484' }); }); + + test('should toggle shouldFetchHistorical property in inspector', async ({ page }) => { + await page.goto(conditionSet.url); + await page.getByLabel('Edit Object').click(); + await page.getByRole('tab', { name: 'Config' }).click(); + let toggleSwitch = page.getByLabel('condition-historical-toggle'); + const initialState = await toggleSwitch.isChecked(); + expect(initialState).toBe(false); + + await toggleSwitch.click(); + let toggledState = await toggleSwitch.isChecked(); + expect(toggledState).toBe(true); + await page.click('button[title="Save"]'); + await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + let conditionSetObject = await getDomainObject(page, conditionSet.uuid); + expect(conditionSetObject.configuration.shouldFetchHistorical).toBe(true); + + await page.getByLabel('Edit Object').click(); + await page.getByRole('tab', { name: 'Config' }).click(); + toggleSwitch = page.getByLabel('condition-historical-toggle'); + await toggleSwitch.click(); + toggledState = await toggleSwitch.isChecked(); + expect(toggledState).toBe(false); + await page.click('button[title="Save"]'); + await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + conditionSetObject = await getDomainObject(page, conditionSet.uuid); + expect(conditionSetObject.configuration.shouldFetchHistorical).toBe(false); + }); }); test.describe('Condition Set Composition', () => { diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 859bf943e87..44818ed3491 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -474,14 +474,15 @@ export default class ConditionManager extends EventEmitter { }); } - emitConditionSetResult(currentCondition, timestamp, outputValue) { + emitConditionSetResult(currentCondition, timestamp, outputValue, result) { this.emit( 'conditionSetResultUpdated', Object.assign( { - output: outputValue, id: this.conditionSetDomainObject.identifier, - conditionId: currentCondition.id + conditionId: currentCondition.id, + output: outputValue, + result }, timestamp ) @@ -509,6 +510,10 @@ export default class ConditionManager extends EventEmitter { async processCondition(timestamp, telemetryObject, telemetryData) { const currentCondition = this.getCurrentCondition(); + const conditionDetails = this.conditions.filter( + (condition) => condition.id === currentCondition.id + )?.[0]; + const conditionResult = currentCondition?.isDefault ? false : conditionDetails?.result; let telemetryValue = currentCondition.configuration.output; if (currentCondition?.configuration?.outputTelemetry) { const selectedOutputIdentifier = currentCondition?.configuration?.outputTelemetry; @@ -539,7 +544,7 @@ export default class ConditionManager extends EventEmitter { } } - this.emitConditionSetResult(currentCondition, timestamp, telemetryValue); + this.emitConditionSetResult(currentCondition, timestamp, telemetryValue, conditionResult); } getTestData(metadatum) { diff --git a/src/plugins/condition/ConditionSetMetadataProvider.js b/src/plugins/condition/ConditionSetMetadataProvider.js index 0d3190d49b2..d49f9937a2b 100644 --- a/src/plugins/condition/ConditionSetMetadataProvider.js +++ b/src/plugins/condition/ConditionSetMetadataProvider.js @@ -50,12 +50,23 @@ export default class ConditionSetMetadataProvider { }; }); + const resultEnum = [ + { + string: 'true', + value: true + }, + { + string: 'false', + value: false + } + ]; + return { values: this.getDomains().concat([ { - key: 'state', + key: 'output', source: 'output', - name: 'State', + name: 'Value', format: 'enum', enumerations: enumerations, hints: { @@ -63,9 +74,11 @@ export default class ConditionSetMetadataProvider { } }, { - key: 'output', - name: 'Value', - format: 'string', + key: 'result', + source: 'result', + name: 'Result', + format: 'enum', + enumerations: resultEnum, hints: { range: 2 } diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/historicalTelemetryProvider.js index d98b762e5bd..a2a1abb058b 100644 --- a/src/plugins/condition/historicalTelemetryProvider.js +++ b/src/plugins/condition/historicalTelemetryProvider.js @@ -43,6 +43,7 @@ export default class HistoricalTelemetryProvider { const conditionConfiguration = conditionCollectionMap.get(condition.id)?.configuration; const { outputTelemetry, outputMetadata } = conditionConfiguration; let output = {}; + output.result = true; if (outputTelemetry) { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); @@ -203,9 +204,11 @@ export default class HistoricalTelemetryProvider { const { outputTelemetry, outputMetadata, output } = conditionConfiguration; if (isDefault) { const conditionOutput = { + condition, telemetry: null, value: output, - condition + result: false, + isDefault: true }; outputTelemetryDateMap.set(timestamp, conditionOutput); } @@ -274,12 +277,14 @@ export default class HistoricalTelemetryProvider { const outputTelemetryList = []; const domainObject = this.conditionSetDomainObject; outputTelemetryMap.forEach((outputMetadata, timestamp) => { - const { condition, telemetry, value } = outputMetadata; + const { condition, telemetry, value, result, isDefault } = outputMetadata; outputTelemetryList.push({ conditionId: condition.id, id: domainObject.identifier, output: value, - utc: timestamp + utc: timestamp, + result, + isDefault }); }); return outputTelemetryList; From 4b39ea232a215f20c8a99eb2b065d66daff6e159 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Mon, 30 Sep 2024 18:53:08 -0500 Subject: [PATCH 15/35] Addressing feedback --- src/plugins/condition/ConditionManager.js | 2 +- .../condition/components/ConditionItem.vue | 16 +++++----------- src/plugins/condition/components/TestData.vue | 6 +----- .../condition/historicalTelemetryProvider.js | 8 -------- 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 44818ed3491..41bba5c4e91 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -24,7 +24,7 @@ import { EventEmitter } from 'eventemitter3'; import { v4 as uuid } from 'uuid'; import Condition from './Condition.js'; -import HistoricalTelemetryProvider from './historicalTelemetryProvider.js'; +import HistoricalTelemetryProvider from './HistoricalTelemetryProvider.js'; import { getLatestTimestamp } from './utils/time.js'; export default class ConditionManager extends EventEmitter { diff --git a/src/plugins/condition/components/ConditionItem.vue b/src/plugins/condition/components/ConditionItem.vue index 250c517154f..f33495bd08a 100644 --- a/src/plugins/condition/components/ConditionItem.vue +++ b/src/plugins/condition/components/ConditionItem.vue @@ -352,18 +352,12 @@ export default { methods: { setOutputSelection() { let conditionOutput = this.condition.configuration.output; - if (conditionOutput) { - if ( - conditionOutput !== 'false' && - conditionOutput !== 'true' && - conditionOutput !== 'telemetry value' - ) { - this.selectedOutputSelection = 'string'; - } else { - this.selectedOutputSelection = conditionOutput; - } - } else if (conditionOutput === undefined) { + if (conditionOutput === undefined) { this.selectedOutputSelection = 'none'; + } else if (['false', 'true', 'telemetry value'].includes(conditionOutput)) { + this.selectedOutputSelection = conditionOutput; + } else { + this.selectedOutputSelection = 'string'; } }, setOutputValue() { diff --git a/src/plugins/condition/components/TestData.vue b/src/plugins/condition/components/TestData.vue index 1e6f695cf0e..d087d948e75 100644 --- a/src/plugins/condition/components/TestData.vue +++ b/src/plugins/condition/components/TestData.vue @@ -63,7 +63,7 @@ :key="index" :value="telemetryOption.identifier" > - {{ telemetryPaths[index] || telemetryOption.name }} + {{ telemetryOption.path || telemetryOption.name }} @@ -201,10 +201,6 @@ export default { this.telemetryMetadataOptions[id] = []; } }); - this.telemetry.forEach(async (telemetryOption, index) => { - const telemetryPath = await this.getFullTelemetryPath(telemetryOption); - this.telemetryPaths[index] = telemetryPath; - }); }, addTestInput(testInput) { this.testInputs.push( diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/historicalTelemetryProvider.js index a2a1abb058b..78cb79fce04 100644 --- a/src/plugins/condition/historicalTelemetryProvider.js +++ b/src/plugins/condition/historicalTelemetryProvider.js @@ -15,14 +15,6 @@ export default class HistoricalTelemetryProvider { this.bounds = bounds; } - refreshAllHistoricalTelemetries() { - const refreshPromises = []; - for (const [, value] of Object.entries(this.telemetryObjects)) { - refreshPromises.push(this.refreshHistoricalTelemetry(value)); - } - return Promise.all(refreshPromises); - } - async refreshHistoricalTelemetry(domainObject, identifier) { console.log('refreshHistoricalTelemetry'); if (!domainObject && identifier) { From b97555445e8ceedeaa94e3064200f2133aea8a20 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Mon, 30 Sep 2024 19:10:24 -0500 Subject: [PATCH 16/35] Creating a const for telemetry value --- src/plugins/condition/ConditionManager.js | 3 ++- src/plugins/condition/components/ConditionItem.vue | 9 +++++---- src/plugins/condition/utils/constants.js | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 41bba5c4e91..97969be7779 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -25,6 +25,7 @@ import { v4 as uuid } from 'uuid'; import Condition from './Condition.js'; import HistoricalTelemetryProvider from './HistoricalTelemetryProvider.js'; +import { TELEMETRY_VALUE } from './utils/constants.js'; import { getLatestTimestamp } from './utils/time.js'; export default class ConditionManager extends EventEmitter { @@ -392,7 +393,7 @@ export default class ConditionManager extends EventEmitter { const currentCondition = this.getCurrentConditionLAD(conditionResults); let output = currentCondition?.configuration?.output; - if (output === 'telemetry value') { + if (output === TELEMETRY_VALUE) { const { outputTelemetry, outputMetadata } = currentCondition.configuration; const outputTelemetryObject = await this.openmct.objects.get(outputTelemetry); const telemetryOptions = { diff --git a/src/plugins/condition/components/ConditionItem.vue b/src/plugins/condition/components/ConditionItem.vue index f33495bd08a..a7481a10fc3 100644 --- a/src/plugins/condition/components/ConditionItem.vue +++ b/src/plugins/condition/components/ConditionItem.vue @@ -113,7 +113,7 @@ @change="persist" /> - + Date: Thu, 3 Oct 2024 01:56:28 -0500 Subject: [PATCH 21/35] Add back initialize on mount --- src/plugins/condition/components/ConditionItem.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/condition/components/ConditionItem.vue b/src/plugins/condition/components/ConditionItem.vue index 3ce3f2d21c7..c47969b3c43 100644 --- a/src/plugins/condition/components/ConditionItem.vue +++ b/src/plugins/condition/components/ConditionItem.vue @@ -350,6 +350,7 @@ export default { }, mounted() { this.setOutputSelection(); + this.initializeMetadata(); }, methods: { setOutputSelection() { From 4a3b0b97f6e73b3f78139f4f939358035c99cb39 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 3 Oct 2024 10:40:00 -0500 Subject: [PATCH 22/35] Compensate for missing data at certain timestamps --- src/plugins/condition/historicalTelemetryProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/historicalTelemetryProvider.js index c78ec58e037..fb375975d79 100644 --- a/src/plugins/condition/historicalTelemetryProvider.js +++ b/src/plugins/condition/historicalTelemetryProvider.js @@ -41,7 +41,7 @@ export default class HistoricalTelemetryProvider { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); output.telemetry = outputTelemetryData; - output.value = outputTelemetryData[outputMetadata]; + output.value = outputTelemetryData?.[outputMetadata] || undefined; output.condition = condition; } else if (conditionConfiguration?.output) { output.telemetry = null; From a739d61c88ca6044a8a1babe7595b85f1ac0c4c4 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 3 Oct 2024 14:04:11 -0500 Subject: [PATCH 23/35] Rename file --- .../{historicalTelemetryProvider.js => HistoricalTelemetry.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/plugins/condition/{historicalTelemetryProvider.js => HistoricalTelemetry.js} (100%) diff --git a/src/plugins/condition/historicalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetry.js similarity index 100% rename from src/plugins/condition/historicalTelemetryProvider.js rename to src/plugins/condition/HistoricalTelemetry.js From 488178ad43efd73b35f959a5ef5c67d664f366f9 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 3 Oct 2024 14:04:37 -0500 Subject: [PATCH 24/35] Rename file --- .../{HistoricalTelemetry.js => HistoricalTelemetryProvider.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/plugins/condition/{HistoricalTelemetry.js => HistoricalTelemetryProvider.js} (100%) diff --git a/src/plugins/condition/HistoricalTelemetry.js b/src/plugins/condition/HistoricalTelemetryProvider.js similarity index 100% rename from src/plugins/condition/HistoricalTelemetry.js rename to src/plugins/condition/HistoricalTelemetryProvider.js From f239d4bb6dc5e32b45a77a4c92d8cda635d959e8 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Fri, 4 Oct 2024 12:23:57 -0500 Subject: [PATCH 25/35] Update metadata provider --- src/plugins/condition/ConditionSetMetadataProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/ConditionSetMetadataProvider.js b/src/plugins/condition/ConditionSetMetadataProvider.js index d49f9937a2b..52d6d3ceb76 100644 --- a/src/plugins/condition/ConditionSetMetadataProvider.js +++ b/src/plugins/condition/ConditionSetMetadataProvider.js @@ -64,7 +64,7 @@ export default class ConditionSetMetadataProvider { return { values: this.getDomains().concat([ { - key: 'output', + key: 'value', source: 'output', name: 'Value', format: 'enum', From f4c2b79fdb20d2610aded401f2d890f09b85a7bb Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 10 Oct 2024 20:12:52 -0500 Subject: [PATCH 26/35] Update condition set metadata --- .../condition/ConditionSetMetadataProvider.js | 15 ++++++----- .../condition/components/ConditionItem.vue | 27 ++++++++++++++++--- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/plugins/condition/ConditionSetMetadataProvider.js b/src/plugins/condition/ConditionSetMetadataProvider.js index 52d6d3ceb76..21e5c1bb025 100644 --- a/src/plugins/condition/ConditionSetMetadataProvider.js +++ b/src/plugins/condition/ConditionSetMetadataProvider.js @@ -43,11 +43,13 @@ export default class ConditionSetMetadataProvider { } getMetadata(domainObject) { - const enumerations = domainObject.configuration.conditionCollection.map((condition, index) => { - return { - string: condition.configuration.output, - value: index - }; + const format = { formatString: '%0.2f' }; + domainObject.configuration.conditionCollection.forEach((condition, index) => { + if (condition?.configuration?.valueMetadata?.enumerations) { + delete format.formatString; + format.format = 'enum'; + format.enumerations = condition?.configuration?.valueMetadata?.enumerations; + } }); const resultEnum = [ @@ -67,8 +69,7 @@ export default class ConditionSetMetadataProvider { key: 'value', source: 'output', name: 'Value', - format: 'enum', - enumerations: enumerations, + ...format, hints: { range: 1 } diff --git a/src/plugins/condition/components/ConditionItem.vue b/src/plugins/condition/components/ConditionItem.vue index c47969b3c43..9df75b79768 100644 --- a/src/plugins/condition/components/ConditionItem.vue +++ b/src/plugins/condition/components/ConditionItem.vue @@ -294,7 +294,8 @@ export default { criterionIndex: 0, draggingOver: false, isDefault: this.condition.isDefault, - telemetryMetadataOptions: {} + telemetryMetadataOptions: {}, + telemetryFormats: new Map() }; }, computed: { @@ -331,9 +332,10 @@ export default { watch: { condition: { handler() { - if (this.condition.configuration.output !== TELEMETRY_VALUE) { - this.condition.configuration.outputTelemetry = null; - this.condition.configuration.outputMetadata = null; + const config = this.condition?.configuration; + if (config?.output !== TELEMETRY_VALUE) { + config.outputTelemetry = null; + config.outputMetadata = null; } }, deep: true @@ -374,6 +376,16 @@ export default { this.persist(); }, + getOutputMetadata() { + const config = this.condition.configuration; + let valueMetadata; + if (config?.outputTelemetry && config?.outputMetadata) { + valueMetadata = this.telemetryFormats.get( + `${config?.outputTelemetry}_${config?.outputMetadata}` + ); + } + return valueMetadata; + }, addCriteria() { const criteriaObject = { id: uuid(), @@ -456,6 +468,10 @@ export default { this.persist(); }, persist() { + const valueMetadata = this.getOutputMetadata(); + if (valueMetadata) { + this.condition.configuration.valueMetadata = valueMetadata; + } this.$emit('update-condition', { condition: this.condition }); @@ -469,6 +485,9 @@ export default { let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject); if (telemetryMetadata) { this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice(); + telemetryMetadata.values().forEach((telemetryValue) => { + this.telemetryFormats.set(`${id}_${telemetryValue.key}`, telemetryValue); + }); } else { this.telemetryMetadataOptions[id] = []; } From bd3f00c2c6fa7d1afaded9dc5ea32ada7c0b7a37 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Thu, 10 Oct 2024 20:35:22 -0500 Subject: [PATCH 27/35] Fix zero issue --- .../condition/HistoricalTelemetryProvider.js | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index fb375975d79..1b9d4b46c02 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -41,7 +41,7 @@ export default class HistoricalTelemetryProvider { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); output.telemetry = outputTelemetryData; - output.value = outputTelemetryData?.[outputMetadata] || undefined; + output.value = outputTelemetryData?.[outputMetadata]; output.condition = condition; } else if (conditionConfiguration?.output) { output.telemetry = null; @@ -58,17 +58,8 @@ export default class HistoricalTelemetryProvider { const historicalTelemetryPoolPromises = []; conditionCollection.forEach((condition, index) => { - console.log('-------------------------'); - console.log(condition); - const { id } = condition; - const { criteria, output, outputTelemetry, outputMetadata } = condition.configuration; + const { criteria, outputTelemetry } = condition.configuration; const inputTelemetry = criteria?.[0]?.telemetry; - console.log(id); - console.log(criteria); - console.log(output); - console.log(outputMetadata); - console.log('inputTelemetry', inputTelemetry); - console.log('outputTelemetry', outputTelemetry); conditionCollectionMap.set(condition?.id, condition); if (inputTelemetry) { const inputTelemetryId = this.openmct.objects.makeKeyString(inputTelemetry); @@ -241,12 +232,12 @@ export default class HistoricalTelemetryProvider { ); console.log('*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣'); - console.log(historicalTelemetriesPool); - console.log(this.historicalTelemetryPoolMap); - console.log(inputTelemetries); - console.log(outputTelemetries); - console.log(historicalTelemetryDateMap); - console.log(outputTelemetryDateMap); + console.log('historicalTelemetriesPool', historicalTelemetriesPool); + console.log('historicalTelemetryPoolMap', this.historicalTelemetryPoolMap); + console.log('inputTelemetries', inputTelemetries); + console.log('outputTelemetries', outputTelemetries); + console.log('historicalTelemetryDateMap', historicalTelemetryDateMap); + console.log('outputTelemetryDateMap', outputTelemetryDateMap); return outputTelemetryDateMap; } @@ -258,7 +249,7 @@ export default class HistoricalTelemetryProvider { } async getHistoricalData() { - console.log('getHistoricalData'); + console.log('🌟🌟🌟🌟🌟🌟🌟🌟🌟 getHistoricalData 🌟🌟🌟🌟🌟🌟🌟🌟🌟'); this.setTimeBounds(this.openmct.time.getBounds()); const outputTelemetryMap = await this.getHistoricalInputsByDate(); const formattedOutputTelemetry = this.formatOutputData(outputTelemetryMap); From cba2056f72a3bb8618da3252802a5af15f3017c0 Mon Sep 17 00:00:00 2001 From: Khalid Adil Date: Fri, 11 Oct 2024 00:57:54 -0500 Subject: [PATCH 28/35] Try removing the default format for better data inspection --- src/plugins/condition/ConditionSetMetadataProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/ConditionSetMetadataProvider.js b/src/plugins/condition/ConditionSetMetadataProvider.js index 21e5c1bb025..dbaadfff12b 100644 --- a/src/plugins/condition/ConditionSetMetadataProvider.js +++ b/src/plugins/condition/ConditionSetMetadataProvider.js @@ -43,7 +43,7 @@ export default class ConditionSetMetadataProvider { } getMetadata(domainObject) { - const format = { formatString: '%0.2f' }; + const format = {}; domainObject.configuration.conditionCollection.forEach((condition, index) => { if (condition?.configuration?.valueMetadata?.enumerations) { delete format.formatString; From 7702e8f3f7f5ff421d06bb78ef4045ecbc8c8362 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 26 Aug 2025 13:08:27 -0700 Subject: [PATCH 29/35] pulled over changes that were made after copying whole repo in a dump --- src/plugins/condition/ConditionManager.js | 39 ++++---- .../condition/historicalTelemetryWorker.js | 88 ++++++++++++------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index d0c51a62b42..53fdaa98cf2 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -419,11 +419,14 @@ export default class ConditionManager extends EventEmitter { } } + let result = currentCondition?.isDefault ? false : conditionResults[currentCondition.id]; const currentOutput = { - output: output, - id: this.conditionSetDomainObject.identifier, conditionId: currentCondition.id, - ...latestTimestamp + id: this.conditionSetDomainObject.identifier, + output: output, + ...latestTimestamp, + result, + isDefault: currentCondition?.isDefault }; return [currentOutput]; @@ -477,19 +480,15 @@ export default class ConditionManager extends EventEmitter { return matchingCondition; } - emitConditionSetResult(currentCondition, timestamp, outputValue, result) { - this.emit( - 'conditionSetResultUpdated', - Object.assign( - { - id: this.conditionSetDomainObject.identifier, - conditionId: currentCondition.id, - output: outputValue, - result - }, - timestamp - ) - ); + emitConditionSetResult(currentCondition, timestamp, outputValue, result, isDefault) { + this.emit('conditionSetResultUpdated', { + conditionId: currentCondition.id, + id: this.conditionSetDomainObject.identifier, + output: outputValue, + ...timestamp, + result, + isDefault + }); } updateCurrentCondition(timestamp, telemetryObject, telemetryData) { @@ -547,7 +546,13 @@ export default class ConditionManager extends EventEmitter { } } - this.emitConditionSetResult(currentCondition, timestamp, telemetryValue, conditionResult); + this.emitConditionSetResult( + currentCondition, + timestamp, + telemetryValue, + conditionResult, + currentCondition?.isDefault + ); } getTestData(metadatum, identifier) { diff --git a/src/plugins/condition/historicalTelemetryWorker.js b/src/plugins/condition/historicalTelemetryWorker.js index fae6a5b9132..71b6dda2434 100644 --- a/src/plugins/condition/historicalTelemetryWorker.js +++ b/src/plugins/condition/historicalTelemetryWorker.js @@ -1,37 +1,61 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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 { makeKeyString } from '../../api/objects/object-utils.js'; -function sortTelemetriesByDate(historicalTelemetriesPool) { - const historicalTelemetryDateMap = new Map(); - historicalTelemetriesPool.forEach((historicalTelemetryList) => { - const { historicalTelemetry, domainObject } = historicalTelemetryList; - const { identifier } = domainObject; - const telemetryIdentifier = makeKeyString(identifier); - historicalTelemetry.forEach((historicalTelemetryItem) => { - let telemetryTimestamp = historicalTelemetryItem.utc; - if (historicalTelemetryItem.timestamp) { - telemetryTimestamp = new Date(historicalTelemetryItem.timestamp)?.getTime(); - } - if (!historicalTelemetryDateMap.get(telemetryTimestamp)) { - const telemetryMap = new Map(); - telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); - historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); - } else { - const telemetryMap = historicalTelemetryDateMap.get(telemetryTimestamp); - telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); - historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); - } +(function () { + function sortTelemetriesByDate(historicalTelemetriesPool) { + const historicalTelemetryDateMap = new Map(); + historicalTelemetriesPool.forEach((historicalTelemetryList) => { + const { historicalTelemetry, domainObject } = historicalTelemetryList; + const { identifier } = domainObject; + const telemetryIdentifier = makeKeyString(identifier); + historicalTelemetry.forEach((historicalTelemetryItem) => { + let telemetryTimestamp = historicalTelemetryItem.utc; + if (historicalTelemetryItem.timestamp) { + telemetryTimestamp = new Date(historicalTelemetryItem.timestamp)?.getTime(); + } + if (!historicalTelemetryDateMap.get(telemetryTimestamp)) { + const telemetryMap = new Map(); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); + } else { + const telemetryMap = historicalTelemetryDateMap.get(telemetryTimestamp); + telemetryMap.set(telemetryIdentifier, historicalTelemetryItem); + historicalTelemetryDateMap.set(telemetryTimestamp, telemetryMap); + } + }); }); - }); - return historicalTelemetryDateMap; -} + return historicalTelemetryDateMap; + } -self.onmessage = function (e) { - const { type, data } = e.data; + self.onmessage = function (e) { + const { type, data } = e.data; - if (type === 'sortTelemetries') { - const sortedTelemetries = sortTelemetriesByDate(data.historicalTelemetriesPool); - self.postMessage({ type: 'result', data: sortedTelemetries }); - } else { - self.postMessage({ type: 'error', error: 'Unknown message type' }); - } -}; + if (type === 'sortTelemetries') { + const sortedTelemetries = sortTelemetriesByDate(data.historicalTelemetriesPool); + self.postMessage({ type: 'result', data: sortedTelemetries }); + } else { + self.postMessage({ type: 'error', error: 'Unknown message type' }); + } + }; +})(); From a88fa39a89a677c2ba06b6b309e76c0331db755e Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 26 Aug 2025 13:44:19 -0700 Subject: [PATCH 30/35] pulling over from file dump --- .../condition/HistoricalTelemetryProvider.js | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index 1b9d4b46c02..3b8d8a10034 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -17,7 +17,6 @@ export default class HistoricalTelemetryProvider { } async refreshHistoricalTelemetry(domainObject, identifier) { - console.log('refreshHistoricalTelemetry'); if (!domainObject && identifier) { domainObject = await this.openmct.objects.get(identifier); } @@ -31,12 +30,11 @@ export default class HistoricalTelemetryProvider { return { domainObject, historicalTelemetry }; } - evaluateTrueCondition(historicalDateMap, timestamp, condition, conditionCollectionMap) { + evaluateCondition(historicalDateMap, timestamp, condition, conditionCollectionMap) { const telemetryData = historicalDateMap.get(timestamp); const conditionConfiguration = conditionCollectionMap.get(condition.id)?.configuration; const { outputTelemetry, outputMetadata } = conditionConfiguration; let output = {}; - output.result = true; if (outputTelemetry) { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); @@ -184,16 +182,15 @@ export default class HistoricalTelemetryProvider { } else if (!conditionCriteria) { const conditionDetails = conditionCollectionMap.get(id); const { isDefault } = conditionDetails; - const conditionConfiguration = conditionDetails?.configuration; - const { output } = conditionConfiguration; if (isDefault) { - const conditionOutput = { + const conditionOutput = this.evaluateCondition( + historicalTelemetryDateMap, + timestamp, condition, - telemetry: null, - value: output, - result: false, - isDefault: true - }; + conditionCollectionMap + ); + conditionOutput.result = false; + conditionOutput.isDefault = true; outputTelemetryDateMap.set(timestamp, conditionOutput); } } @@ -201,12 +198,13 @@ export default class HistoricalTelemetryProvider { evaluatedConditions.push(conditionMetadata); if (result === true) { isConditionValid = true; - const conditionOutput = this.evaluateTrueCondition( + const conditionOutput = this.evaluateCondition( historicalTelemetryDateMap, timestamp, condition, conditionCollectionMap ); + conditionOutput.result = true; outputTelemetryDateMap.set(timestamp, conditionOutput); } }); @@ -219,8 +217,8 @@ export default class HistoricalTelemetryProvider { const { historicalTelemetriesPool, - inputTelemetries, - outputTelemetries, + // inputTelemetries, + // outputTelemetries, conditionCollectionMap } = await this.getAllTelemetries(conditionCollection); @@ -231,14 +229,6 @@ export default class HistoricalTelemetryProvider { conditionCollectionMap ); - console.log('*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣*️⃣'); - console.log('historicalTelemetriesPool', historicalTelemetriesPool); - console.log('historicalTelemetryPoolMap', this.historicalTelemetryPoolMap); - console.log('inputTelemetries', inputTelemetries); - console.log('outputTelemetries', outputTelemetries); - console.log('historicalTelemetryDateMap', historicalTelemetryDateMap); - console.log('outputTelemetryDateMap', outputTelemetryDateMap); - return outputTelemetryDateMap; } @@ -253,7 +243,7 @@ export default class HistoricalTelemetryProvider { this.setTimeBounds(this.openmct.time.getBounds()); const outputTelemetryMap = await this.getHistoricalInputsByDate(); const formattedOutputTelemetry = this.formatOutputData(outputTelemetryMap); - console.log(formattedOutputTelemetry); + return formattedOutputTelemetry; } From 4611faa56c966920a5ba2dcc83cdb5dec8d62751 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 26 Aug 2025 13:51:00 -0700 Subject: [PATCH 31/35] remove debug --- src/plugins/condition/HistoricalTelemetryProvider.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index 3b8d8a10034..bb2f87b6164 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -239,7 +239,6 @@ export default class HistoricalTelemetryProvider { } async getHistoricalData() { - console.log('🌟🌟🌟🌟🌟🌟🌟🌟🌟 getHistoricalData 🌟🌟🌟🌟🌟🌟🌟🌟🌟'); this.setTimeBounds(this.openmct.time.getBounds()); const outputTelemetryMap = await this.getHistoricalInputsByDate(); const formattedOutputTelemetry = this.formatOutputData(outputTelemetryMap); From f0576026adc655db955e52685d0aca59e8829abf Mon Sep 17 00:00:00 2001 From: Jamie V Date: Tue, 26 Aug 2025 17:45:06 -0700 Subject: [PATCH 32/35] handle "none" output situations --- .../condition/HistoricalTelemetryProvider.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index bb2f87b6164..dfa8bf78dcb 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -35,16 +35,17 @@ export default class HistoricalTelemetryProvider { const conditionConfiguration = conditionCollectionMap.get(condition.id)?.configuration; const { outputTelemetry, outputMetadata } = conditionConfiguration; let output = {}; + output.condition = condition; if (outputTelemetry) { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); output.telemetry = outputTelemetryData; output.value = outputTelemetryData?.[outputMetadata]; - output.condition = condition; } else if (conditionConfiguration?.output) { output.telemetry = null; output.value = conditionConfiguration?.output; - output.condition = condition; + } else { + output.value = undefined; } return output; } @@ -209,18 +210,15 @@ export default class HistoricalTelemetryProvider { } }); }); + return outputTelemetryDateMap; } async getHistoricalInputsByDate() { const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection; - const { - historicalTelemetriesPool, - // inputTelemetries, - // outputTelemetries, - conditionCollectionMap - } = await this.getAllTelemetries(conditionCollection); + const { historicalTelemetriesPool, conditionCollectionMap } = + await this.getAllTelemetries(conditionCollection); const historicalTelemetryDateMap = await this.sortTelemetriesInWorker(historicalTelemetriesPool); From a992d32c1ed0c58da9e3865d41b873687149557e Mon Sep 17 00:00:00 2001 From: Jamie V Date: Wed, 27 Aug 2025 13:35:47 -0700 Subject: [PATCH 33/35] removing telemetry when output condition is "none" --- src/plugins/condition/ConditionManager.js | 23 +++++++------ .../ConditionSetTelemetryProvider.js | 1 + .../condition/HistoricalTelemetryProvider.js | 32 ++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/plugins/condition/ConditionManager.js b/src/plugins/condition/ConditionManager.js index 53fdaa98cf2..c2a75914e1f 100644 --- a/src/plugins/condition/ConditionManager.js +++ b/src/plugins/condition/ConditionManager.js @@ -343,6 +343,7 @@ export default class ConditionManager extends EventEmitter { ); const historicalData = historicalTelemetry.getHistoricalData(); historicalTelemetry = null; + return historicalData; } @@ -402,6 +403,7 @@ export default class ConditionManager extends EventEmitter { const currentCondition = this.getCurrentConditionLAD(conditionResults); let output = currentCondition?.configuration?.output; + if (output === TELEMETRY_VALUE) { const { outputTelemetry, outputMetadata } = currentCondition.configuration; const outputTelemetryObject = await this.openmct.objects.get(outputTelemetry); @@ -429,7 +431,7 @@ export default class ConditionManager extends EventEmitter { isDefault: currentCondition?.isDefault }; - return [currentOutput]; + return output !== undefined ? [currentOutput] : []; } isTelemetryUsed(endpoint) { @@ -517,7 +519,8 @@ export default class ConditionManager extends EventEmitter { )?.[0]; const conditionResult = currentCondition?.isDefault ? false : conditionDetails?.result; let telemetryValue = currentCondition.configuration.output; - if (currentCondition?.configuration?.outputTelemetry) { + + if (telemetryValue !== undefined && currentCondition?.configuration?.outputTelemetry) { const selectedOutputIdentifier = currentCondition?.configuration?.outputTelemetry; const outputMetadata = currentCondition?.configuration?.outputMetadata; const telemetryKeystring = this.openmct.objects.makeKeyString(telemetryObject.identifier); @@ -544,15 +547,15 @@ export default class ConditionManager extends EventEmitter { telemetryValue = undefined; } } - } - this.emitConditionSetResult( - currentCondition, - timestamp, - telemetryValue, - conditionResult, - currentCondition?.isDefault - ); + this.emitConditionSetResult( + currentCondition, + timestamp, + telemetryValue, + conditionResult, + currentCondition?.isDefault + ); + } } getTestData(metadatum, identifier) { diff --git a/src/plugins/condition/ConditionSetTelemetryProvider.js b/src/plugins/condition/ConditionSetTelemetryProvider.js index dc3e3a7240c..df9ddc53d68 100644 --- a/src/plugins/condition/ConditionSetTelemetryProvider.js +++ b/src/plugins/condition/ConditionSetTelemetryProvider.js @@ -44,6 +44,7 @@ export default class ConditionSetTelemetryProvider { let conditionManager = this.getConditionManager(domainObject); const formattedHistoricalData = await conditionManager.getHistoricalData(options); let latestOutput = await conditionManager.requestLADConditionSetOutput(options); + return [...formattedHistoricalData, ...latestOutput]; } diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index dfa8bf78dcb..cb9b0f178a4 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -34,19 +34,20 @@ export default class HistoricalTelemetryProvider { const telemetryData = historicalDateMap.get(timestamp); const conditionConfiguration = conditionCollectionMap.get(condition.id)?.configuration; const { outputTelemetry, outputMetadata } = conditionConfiguration; - let output = {}; - output.condition = condition; + let output = outputTelemetry || conditionConfiguration?.output ? {} : undefined; + if (outputTelemetry) { const outputTelemetryID = this.openmct.objects.makeKeyString(outputTelemetry); const outputTelemetryData = telemetryData.get(outputTelemetryID); + output.condition = condition; output.telemetry = outputTelemetryData; output.value = outputTelemetryData?.[outputMetadata]; } else if (conditionConfiguration?.output) { + output.condition = condition; output.telemetry = null; output.value = conditionConfiguration?.output; - } else { - output.value = undefined; } + return output; } @@ -159,31 +160,29 @@ export default class HistoricalTelemetryProvider { evaluateConditionsByDate(historicalTelemetryDateMap, conditionCollectionMap) { const outputTelemetryDateMap = new Map(); + historicalTelemetryDateMap.forEach((historicalTelemetryMap, timestamp) => { let isConditionValid = false; - const evaluatedConditions = []; + this.conditions.forEach((condition) => { if (isConditionValid) { return; } - const { id } = condition; - const conditionMetadata = { condition }; + const conditionCriteria = condition.criteria[0]; let result; + if (conditionCriteria?.telemetry) { const conditionInputTelemetryId = this.openmct.objects.makeKeyString( conditionCriteria.telemetry ); const inputTelemetry = historicalTelemetryMap.get(conditionInputTelemetryId); - conditionMetadata.inputTelemetry = inputTelemetry; - result = conditionCriteria.computeResult({ - id, - ...inputTelemetry - }); + result = conditionCriteria.computeResult(inputTelemetry); } else if (!conditionCriteria) { - const conditionDetails = conditionCollectionMap.get(id); + const conditionDetails = conditionCollectionMap.get(condition.id); const { isDefault } = conditionDetails; - if (isDefault) { + + if (isDefault && result !== undefined) { const conditionOutput = this.evaluateCondition( historicalTelemetryDateMap, timestamp, @@ -195,8 +194,7 @@ export default class HistoricalTelemetryProvider { outputTelemetryDateMap.set(timestamp, conditionOutput); } } - conditionMetadata.result = result; - evaluatedConditions.push(conditionMetadata); + if (result === true) { isConditionValid = true; const conditionOutput = this.evaluateCondition( @@ -216,10 +214,8 @@ export default class HistoricalTelemetryProvider { async getHistoricalInputsByDate() { const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection; - const { historicalTelemetriesPool, conditionCollectionMap } = await this.getAllTelemetries(conditionCollection); - const historicalTelemetryDateMap = await this.sortTelemetriesInWorker(historicalTelemetriesPool); const outputTelemetryDateMap = this.evaluateConditionsByDate( From 3d4eb3f1bcae914ca7ccb4f6e6b0781bd86eb27b Mon Sep 17 00:00:00 2001 From: Jamie V Date: Wed, 27 Aug 2025 13:45:50 -0700 Subject: [PATCH 34/35] adding license info --- .../ConditionInspectorViewProvider.js | 22 ++++++++++++++++++- .../condition/HistoricalTelemetryProvider.js | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/plugins/condition/ConditionInspectorViewProvider.js b/src/plugins/condition/ConditionInspectorViewProvider.js index fc972f60ea8..5f7f0dbec08 100644 --- a/src/plugins/condition/ConditionInspectorViewProvider.js +++ b/src/plugins/condition/ConditionInspectorViewProvider.js @@ -1,4 +1,24 @@ -// src/plugins/condition/ConditionInspectorView.js +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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 mount from 'utils/mount'; diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index cb9b0f178a4..04823516656 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -1,3 +1,25 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ + export default class HistoricalTelemetryProvider { constructor(openmct, telemetryObjects, conditions, conditionSetDomainObject, options) { this.openmct = openmct; From 2b4d785fd0dc703621c0604e8ed03a896e947c95 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Wed, 27 Aug 2025 16:37:30 -0700 Subject: [PATCH 35/35] one last tweak to get none to work correctly without messin up other stuff --- src/plugins/condition/HistoricalTelemetryProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/condition/HistoricalTelemetryProvider.js b/src/plugins/condition/HistoricalTelemetryProvider.js index 04823516656..eb832ae2077 100644 --- a/src/plugins/condition/HistoricalTelemetryProvider.js +++ b/src/plugins/condition/HistoricalTelemetryProvider.js @@ -202,9 +202,9 @@ export default class HistoricalTelemetryProvider { result = conditionCriteria.computeResult(inputTelemetry); } else if (!conditionCriteria) { const conditionDetails = conditionCollectionMap.get(condition.id); - const { isDefault } = conditionDetails; + const { isDefault, outputTelemetry } = conditionDetails; - if (isDefault && result !== undefined) { + if (isDefault && (outputTelemetry || condition.configuration.output)) { const conditionOutput = this.evaluateCondition( historicalTelemetryDateMap, timestamp,