Skip to content

Commit

Permalink
Add realtime output of telemetry data in conditionals and add support…
Browse files Browse the repository at this point in the history
… for historical conditional telemetry queries to allow for plotting
  • Loading branch information
khalidadil committed Sep 19, 2024
1 parent 73489cd commit 23cf829
Show file tree
Hide file tree
Showing 9 changed files with 510 additions and 21 deletions.
108 changes: 102 additions & 6 deletions src/plugins/condition/ConditionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -46,6 +47,8 @@ export default class ConditionManager extends EventEmitter {
applied: false
};
this.initialize();
this.telemetryBuffer = [];
this.isProcessing = false;
}

subscribeToTelemetry(telemetryObject) {
Expand Down Expand Up @@ -320,6 +323,17 @@ export default class ConditionManager extends EventEmitter {
return currentCondition;
}

getHistoricalData() {
const historicalTelemetry = new HistoricalTelemetryProvider(

Check warning on line 327 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L327

Added line #L327 was not covered by tests
this.openmct,
this.telemetryObjects,
this.compositionLoad,
this.conditions,
this.conditionSetDomainObject
);
return historicalTelemetry.getHistoricalData();

Check warning on line 334 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L334

Added line #L334 was not covered by tests
}

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

Check warning on line 396 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L392-L396

Added lines #L392 - L396 were not covered by tests
size: 1,
strategy: 'latest',
timeContext: this.openmct.time.getContextForView([])
};
const latestData = await this.openmct.telemetry.request(

Check warning on line 401 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L401

Added line #L401 was not covered by tests
outputTelemetryObject,
telemetryOptions
);
if (latestData?.[0]?.[outputMetadata]) {
output = latestData?.[0]?.[outputMetadata];

Check warning on line 406 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L405-L406

Added lines #L405 - L406 were not covered by tests
}
}

const currentOutput = {
output: currentCondition.configuration.output,
output: output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id,
...latestTimestamp
Expand All @@ -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);

Check warning on line 433 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L429-L433

Added lines #L429 - L433 were not covered by tests
}
});

if (conditionTelemetries.includes(id)) {
return true;

Check warning on line 438 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L437-L438

Added lines #L437 - L438 were not covered by tests
}

return false;
}

Expand All @@ -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);
}
}

Expand All @@ -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
},
Expand All @@ -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);

Check warning on line 514 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L512-L514

Added lines #L512 - L514 were not covered by tests

if (selectedOutputIdentifier === telemetryKeystring) {
telemetryValue = telemetryData[outputMetadata];

Check warning on line 517 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L516-L517

Added lines #L516 - L517 were not covered by tests
} else {
const outputTelemetryObject = await this.openmct.objects.get(selectedOutputIdentifier);
const telemetryOptions = {

Check warning on line 520 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L519-L520

Added lines #L519 - L520 were not covered by tests
size: 1,
strategy: 'latest',
start: timestamp?.utc - 1000,
end: timestamp?.utc + 1000
};
const outputTelemetryData = await this.openmct.telemetry.request(

Check warning on line 526 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L526

Added line #L526 was not covered by tests
outputTelemetryObject,
telemetryOptions
);
const outputTelemetryValue =
outputTelemetryData?.length > 0 ? outputTelemetryData.slice(-1)[0] : null;
if (outputTelemetryData.length && outputTelemetryValue?.[outputMetadata]) {
telemetryValue = outputTelemetryValue?.[outputMetadata];

Check warning on line 533 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L531-L533

Added lines #L531 - L533 were not covered by tests
} else {
telemetryValue = undefined;

Check warning on line 535 in src/plugins/condition/ConditionManager.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionManager.js#L535

Added line #L535 was not covered by tests
}
}
}

this.emitConditionSetResult(currentCondition, timestamp, telemetryValue);
}

getTestData(metadatum) {
let data = undefined;
if (this.testData.applied) {
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/condition/ConditionSetTelemetryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ export default class ConditionSetTelemetryProvider {

async request(domainObject, options) {
let conditionManager = this.getConditionManager(domainObject);
const formattedHistoricalData = await conditionManager.getHistoricalData();

Check warning on line 45 in src/plugins/condition/ConditionSetTelemetryProvider.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionSetTelemetryProvider.js#L45

Added line #L45 was not covered by tests
let latestOutput = await conditionManager.requestLADConditionSetOutput(options);
return latestOutput;
return [...formattedHistoricalData, ...latestOutput];

Check warning on line 47 in src/plugins/condition/ConditionSetTelemetryProvider.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/ConditionSetTelemetryProvider.js#L47

Added line #L47 was not covered by tests
}

subscribe(domainObject, callback) {
Expand Down
18 changes: 16 additions & 2 deletions src/plugins/condition/components/ConditionCollection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Check warning on line 240 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L240

Added line #L240 was not covered by tests
this.telemetryObjs.push(domainObject);
this.telemetryObjs.push({ ...domainObject, path: telemetryPath });

Check warning on line 242 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L242

Added line #L242 was not covered by tests
this.$emit('telemetry-updated', this.telemetryObjs);
this.subscribeToStaleness(domainObject, (stalenessResponse) => {
Expand All @@ -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, []);

Check warning on line 254 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L253-L254

Added lines #L253 - L254 were not covered by tests
const telemetryPath = originalPathObjects.reverse().map((pathObject) => {

Check warning on line 256 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L256

Added line #L256 was not covered by tests
if (pathObject.type !== 'root') {
return pathObject.name;

Check warning on line 258 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L258

Added line #L258 was not covered by tests
}
return undefined;

Check warning on line 260 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L260

Added line #L260 was not covered by tests
});
return telemetryPath.join('/');

Check warning on line 263 in src/plugins/condition/components/ConditionCollection.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionCollection.vue#L263

Added line #L263 was not covered by tests
},
removeTelemetryObject(identifier) {
const keyString = this.openmct.objects.makeKeyString(identifier);
const index = this.telemetryObjs.findIndex((obj) => {
Expand Down
96 changes: 89 additions & 7 deletions src/plugins/condition/components/ConditionItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,55 @@
@change="setOutputValue"
>
<option v-for="option in outputOptions" :key="option" :value="option">
{{ initCap(option) }}
{{ option }}
</option>
</select>
</span>
<span class="c-cdef__control">
<input
v-if="selectedOutputSelection === outputOptions[2]"
v-if="selectedOutputSelection === outputOptions[3]"
v-model="condition.configuration.output"
aria-label="Condition Output String"
class="t-condition-name-input"
type="text"
@change="persist"
/>
</span>
<span v-if="selectedOutputSelection === 'telemetry value'" class="c-cdef__control">
<select
v-model="condition.configuration.outputTelemetry"
aria-label="Output Telemetry Selection"
@change="persist"
>
<option value="">- Select Telemetry -</option>
<option
v-for="telemetryOption in telemetry"
:key="openmct.objects.makeKeyString(telemetryOption.identifier)"
:value="openmct.objects.makeKeyString(telemetryOption.identifier)"
>
{{ telemetryOption.path }}
</option>
</select>
</span>
<span v-if="condition.configuration.outputTelemetry" class="c-cdef__control">
<select
v-model="condition.configuration.outputMetadata"
aria-label="Output Telemetry Metadata Selection"
@change="persist"
>
<option value="">- Select Field -</option>
<option
v-for="(option, index) in telemetryMetadataOptions[
condition.configuration.outputTelemetry
]"
:key="index"
:value="option.key"
>
{{ option.name }}
</option>
</select>
</span>
</span>

<div v-if="!condition.isDefault" class="c-cdef__match-and-criteria">
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Match</span>
Expand Down Expand Up @@ -181,7 +214,12 @@
<span class="c-condition__name">
{{ condition.configuration.name }}
</span>
<span class="c-condition__output"> Output: {{ condition.configuration.output }} </span>
<span class="c-condition__output">
Output:
{{
condition.configuration.output === undefined ? 'none' : condition.configuration.output
}}
</span>
</div>
<div class="c-condition__summary">
<ConditionDescription :show-label="false" :condition="condition" />
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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;

Check warning on line 334 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L333-L334

Added lines #L333 - L334 were not covered by tests
}
},
deep: true
},
isEditing(newValue, oldValue) {
if (newValue === true) {
this.initializeMetadata();

Check warning on line 341 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L341

Added line #L341 was not covered by tests
}
}
},
unmounted() {
this.destroy();
},
mounted() {
this.setOutputSelection();
this.initializeMetadata();

Check warning on line 350 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L350

Added line #L350 was not covered by tests
},
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';

Check warning on line 366 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L366

Added line #L366 was not covered by tests
}
},
setOutputValue() {
if (this.selectedOutputSelection === 'string') {
this.condition.configuration.output = '';
} else if (this.selectedOutputSelection === 'none') {
this.condition.configuration.output = undefined;

Check warning on line 373 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L373

Added line #L373 was not covered by tests
} else {
this.condition.configuration.output = this.selectedOutputSelection;
}
Expand Down Expand Up @@ -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);

Check warning on line 472 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L470-L472

Added lines #L470 - L472 were not covered by tests
if (telemetryMetadata) {
this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();

Check warning on line 474 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L474

Added line #L474 was not covered by tests
} else {
this.telemetryMetadataOptions[id] = [];

Check warning on line 476 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L476

Added line #L476 was not covered by tests
}
});
},
getId(identifier) {
if (identifier) {
return this.openmct.objects.makeKeyString(identifier);

Check warning on line 482 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L482

Added line #L482 was not covered by tests
}
return [];

Check warning on line 485 in src/plugins/condition/components/ConditionItem.vue

View check run for this annotation

Codecov / codecov/patch

src/plugins/condition/components/ConditionItem.vue#L485

Added line #L485 was not covered by tests
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/condition/components/CriterionItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
:key="telemetryOption.identifier.key"
:value="telemetryOption.identifier"
>
{{ telemetryOption.name }}
{{ telemetryOption.path }}
</option>
</select>
</span>
Expand Down
Loading

0 comments on commit 23cf829

Please sign in to comment.