Skip to content
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
b4fdabf
adding telemetry collections to condition manager
jvigliotta Sep 12, 2024
5bd5ca9
handling telemetry collection data not datum
jvigliotta Sep 12, 2024
4ab19e3
adding from maaster
jvigliotta Sep 12, 2024
b467f9c
addressing PR comments
jvigliotta Sep 16, 2024
3f66140
Merge branch 'master' into conditional-style-performance
jvigliotta Sep 16, 2024
6482135
update unit test to work with telemetry collections
jvigliotta Sep 17, 2024
54c90e0
fixing tests
jvigliotta Sep 17, 2024
94a4ff3
removing unnecessary addition
jvigliotta Sep 17, 2024
7cba09b
removing focused describe
jvigliotta Sep 17, 2024
405b497
removing focused it
jvigliotta Sep 17, 2024
1581216
Merge branch 'master' into conditional-style-performance
jvigliotta Sep 17, 2024
5b385ea
fix weird test bleed
jvigliotta Sep 17, 2024
73489cd
Merge branch 'conditional-style-performance' of https://github.com/na…
jvigliotta Sep 17, 2024
23cf829
Add realtime output of telemetry data in conditionals and add support…
khalidadil Sep 19, 2024
21e94fd
Cleanup and add missing files
khalidadil Sep 21, 2024
101baa5
Fix issue with missing data
khalidadil Sep 30, 2024
7896f36
Update emitted values
khalidadil Sep 30, 2024
4b39ea2
Addressing feedback
khalidadil Sep 30, 2024
b975554
Creating a const for telemetry value
khalidadil Oct 1, 2024
7727a90
Cleanup
khalidadil Oct 1, 2024
f544a1d
Pass through plot options
khalidadil Oct 1, 2024
1180597
Cleanup
khalidadil Oct 2, 2024
39a73cf
Fix problem introduced with const
khalidadil Oct 3, 2024
0f2f71f
Add back initialize on mount
khalidadil Oct 3, 2024
4a3b0b9
Compensate for missing data at certain timestamps
khalidadil Oct 3, 2024
a739d61
Rename file
khalidadil Oct 3, 2024
488178a
Rename file
khalidadil Oct 3, 2024
f239d4b
Update metadata provider
khalidadil Oct 4, 2024
f4c2b79
Update condition set metadata
khalidadil Oct 11, 2024
bd3f00c
Fix zero issue
khalidadil Oct 11, 2024
cba2056
Try removing the default format for better data inspection
khalidadil Oct 11, 2024
848fbb9
merging fork
jvigliotta Aug 26, 2025
7702e8f
pulled over changes that were made after copying whole repo in a dump
jvigliotta Aug 26, 2025
a88fa39
pulling over from file dump
jvigliotta Aug 26, 2025
4611faa
remove debug
jvigliotta Aug 26, 2025
f057602
handle "none" output situations
jvigliotta Aug 27, 2025
a992d32
removing telemetry when output condition is "none"
jvigliotta Aug 27, 2025
3d4eb3f
adding license info
jvigliotta Aug 27, 2025
2b4d785
one last tweak to get none to work correctly without messin up other …
jvigliotta Aug 27, 2025
559eb30
Merge branch 'master' into feature/historical-conditions
jvigliotta Aug 27, 2025
4cba005
Merge branch 'master' into feature/historical-conditions
jvigliotta Oct 6, 2025
d9cc238
Merge branch 'master' into feature/historical-conditions
jvigliotta Oct 6, 2025
1c8cae3
Merge branch 'master' into feature/historical-conditions
jvigliotta Oct 9, 2025
7ca6a94
WIP tests
jvigliotta Oct 14, 2025
2d36f4e
make sure unit exists
jvigliotta Oct 14, 2025
97da1ed
WIP
jvigliotta Oct 16, 2025
d1ab616
WIP
jvigliotta Oct 21, 2025
6a3cc02
adding timesystem accounting
jvigliotta Oct 24, 2025
8776502
WIP
jvigliotta Oct 27, 2025
27451f6
remove unused method
jvigliotta Feb 6, 2026
f9e8487
handling dupes from historical/subscription, correct formatting befor…
jvigliotta Feb 9, 2026
1dc3d29
Merge branch 'master' into feature/historical-conditions
jvigliotta Feb 10, 2026
5a0f449
update tests to new output format
jvigliotta Feb 18, 2026
6f222f3
Merge branch 'master' into feature/historical-conditions
jvigliotta Feb 18, 2026
18600bb
updating to reflect column name change
jvigliotta Feb 19, 2026
024ef1e
update to match new path style for telemetry options
jvigliotta Feb 19, 2026
9579841
more robust handling of new condition inspector view "canView" method
jvigliotta Feb 19, 2026
87ba787
Merge branch 'feature/historical-conditions' of https://github.com/na…
jvigliotta Feb 19, 2026
c2b0d77
update for paths in condition telemetry dropdown, add config tab to i…
jvigliotta Feb 19, 2026
dd91b4f
updating for new paths in telemetry selection dropdwns
jvigliotta Feb 20, 2026
6a32966
lint
jvigliotta Feb 20, 2026
a9daa8c
Merge branch 'master' into feature/historical-conditions
akhenry Mar 4, 2026
b795fdd
remove unused method
jvigliotta Mar 16, 2026
e094a77
add keystring to telem objcts so its easy to grab
jvigliotta Mar 17, 2026
3417af9
dont use index
jvigliotta Mar 17, 2026
84677e7
remove unused method
jvigliotta Mar 17, 2026
0462bfd
removed debug logs
jvigliotta Mar 17, 2026
cd88ab2
use predefined priority constants
jvigliotta Mar 17, 2026
dc424ba
remove debug
jvigliotta Mar 17, 2026
0bc09d0
lint
jvigliotta Mar 17, 2026
104cf7e
fix process buffer and remove fit on test
jvigliotta Mar 17, 2026
7acce70
Merge branch 'master' into feature/historical-conditions
shefalijoshi Mar 19, 2026
1091ec8
Merge branch 'master' into feature/historical-conditions
jvigliotta Mar 20, 2026
07b37e0
added missing await
jvigliotta Mar 23, 2026
2f7fcaf
remove unused arg
jvigliotta Mar 23, 2026
bb195ca
using a set instead of two arrays
jvigliotta Mar 23, 2026
cf40220
using a set instead of two arrays, normalize id
jvigliotta Mar 23, 2026
51bb761
add a guard if unsubscribe path is called with no telemetryobject
jvigliotta Mar 23, 2026
cc4165c
explicitly import isEqual instead of random global use
jvigliotta Mar 24, 2026
2fc4339
Merge branch 'master' into feature/historical-conditions
jvigliotta Mar 24, 2026
a1b7cb9
Merge branch 'feature/historical-conditions' of https://github.com/na…
jvigliotta Mar 26, 2026
a9950ea
Updates to work with new changes introduced for conditionsets, merged…
jvigliotta Mar 26, 2026
ae7fe33
lint
jvigliotta Mar 26, 2026
09a3c4f
Merge branch 'master' into feature/historical-conditions
jvigliotta Mar 26, 2026
9107498
correcting case
jvigliotta Mar 27, 2026
e22fcd4
Merge branch 'master' into feature/historical-conditions
jvigliotta Mar 27, 2026
c5779b9
Merge branch 'master' into feature/historical-conditions
akhenry Mar 27, 2026
bad97a9
handling removing output telemetry when condition set is being viewed…
jvigliotta Apr 2, 2026
60cb632
handle removed telemetry correctly for conditions that use it
jvigliotta Apr 7, 2026
0f1f6b5
deleting instead of null settting to avoid issues on last unsub
jvigliotta Apr 7, 2026
29935e6
lint
jvigliotta Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .webpack/webpack.common.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
17 changes: 17 additions & 0 deletions e2e/appActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ async function createDomainObjectWithDefaults(
};
}

/**
* 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<Object>} 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
Expand Down Expand Up @@ -716,6 +732,7 @@ export {
createStableStateTelemetry,
expandEntireTree,
getCanvasPixels,
getDomainObject,
linkParameterToObject,
navigateToObjectWithFixedTimeBounds,
navigateToObjectWithRealTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import { fileURLToPath } from 'url';

import {
createDomainObjectWithDefaults,
createExampleTelemetryObject
createExampleTelemetryObject,
getDomainObject
} from '../../../../appActions.js';
import { expect, test } from '../../../../pluginFixtures.js';

Expand Down Expand Up @@ -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', () => {
Expand Down
4 changes: 4 additions & 0 deletions karma.conf.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ module.exports = async (config) => {
{
pattern: 'dist/generatorWorker.js*',
included: false
},
{
pattern: 'dist/historicalTelemetryWorker.js*',
included: false
}
],
port: 9876,
Expand Down
73 changes: 73 additions & 0 deletions src/plugins/condition/ConditionInspectorViewProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*****************************************************************************
* 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';

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: '<condition-config-view></condition-config-view>'
},
{
app: openmct.app,
element
}
);
_destroy = destroy;
},
showTab: function (isEditing) {
return isEditing;
},
priority: function () {
return 1;
},
destroy: function () {
if (_destroy) {
_destroy();
}
}
};
}
};
}
158 changes: 137 additions & 21 deletions src/plugins/condition/ConditionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { EventEmitter } from 'eventemitter3';
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 {
Expand Down Expand Up @@ -52,6 +54,8 @@ export default class ConditionManager extends EventEmitter {
applied: false
};
this.initialize();
this.telemetryBuffer = [];
this.isProcessing = false;
}

subscribeToTelemetry(telemetryObject) {
Expand Down Expand Up @@ -310,6 +314,39 @@ export default class ConditionManager extends EventEmitter {
this.persistConditions();
}

getCurrentCondition() {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length - 1];

for (let i = 0; i < conditionCollection.length - 1; i++) {
const condition = this.findConditionById(conditionCollection[i].id);
if (condition.result) {
//first condition to be true wins
currentCondition = conditionCollection[i];
break;
}
}

return currentCondition;
}

getHistoricalData(options) {
if (!this.conditionSetDomainObject.configuration.shouldFetchHistorical) {
return [];
}
let historicalTelemetry = new HistoricalTelemetryProvider(
this.openmct,
this.telemetryObjects,
this.conditions,
this.conditionSetDomainObject,
options
);
const historicalData = historicalTelemetry.getHistoricalData();
historicalTelemetry = null;

return historicalData;
}

getCurrentConditionLAD(conditionResults) {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length - 1];
Expand Down Expand Up @@ -365,14 +402,36 @@ 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];
}
}

let result = currentCondition?.isDefault ? false : conditionResults[currentCondition.id];
const currentOutput = {
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id,
...latestTimestamp
id: this.conditionSetDomainObject.identifier,
output: output,
...latestTimestamp,
result,
isDefault: currentCondition?.isDefault
};

return [currentOutput];
return output !== undefined ? [currentOutput] : [];
}

isTelemetryUsed(endpoint) {
Expand Down Expand Up @@ -407,8 +466,8 @@ export default class ConditionManager extends EventEmitter {
this.#latestDataTable.set(normalizedDatum.id, normalizedDatum);

if (this.shouldEvaluateNewTelemetry(currentTimestamp)) {
const matchingCondition = this.updateConditionResults(normalizedDatum.id);
this.updateCurrentCondition(timestamp, matchingCondition);
this.updateConditionResults(normalizedDatum);
this.updateCurrentCondition(timestamp, endpoint, datum);
}
}

Expand All @@ -423,23 +482,80 @@ export default class ConditionManager extends EventEmitter {
return matchingCondition;
}

updateCurrentCondition(timestamp, matchingCondition) {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
const defaultCondition = conditionCollection[conditionCollection.length - 1];
emitConditionSetResult(currentCondition, timestamp, outputValue, result, isDefault) {
this.emit('conditionSetResultUpdated', {
conditionId: currentCondition.id,
id: this.conditionSetDomainObject.identifier,
output: outputValue,
...timestamp,
result,
isDefault
});
}

const currentCondition = matchingCondition || defaultCondition;
updateCurrentCondition(timestamp, telemetryObject, telemetryData) {
this.telemetryBuffer.push({ timestamp, telemetryObject, telemetryData });

this.emit(
'conditionSetResultUpdated',
Object.assign(
{
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id
},
timestamp
)
);
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();
const conditionDetails = this.conditions.filter(
(condition) => condition.id === currentCondition.id
)?.[0];
const conditionResult = currentCondition?.isDefault ? false : conditionDetails?.result;
let telemetryValue = currentCondition.configuration.output;

if (telemetryValue !== undefined && 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,
conditionResult,
currentCondition?.isDefault
);
}
}

getTestData(metadatum, identifier) {
Expand Down
Loading