can take arbitrary expressions

This commit is contained in:
Scott Bell 2024-08-13 16:21:08 -05:00
parent 0326e38f5d
commit 413338d3e2
4 changed files with 94 additions and 54 deletions

View File

@ -29,10 +29,27 @@ export default class CompsManager extends EventEmitter {
this.persist(); this.persist();
} }
getDomainObjectForParameter(keyString) { getParameters() {
const parameters = this.#domainObject.configuration.comps.parameters;
const parametersWithTimeKey = parameters.map((parameter) => {
return {
...parameter,
timeKey: this.#telemetryCollections[parameter.keyString]?.timeKey
};
});
return parametersWithTimeKey;
}
getTelemetryObjectForParameter(keyString) {
return this.#telemetryObjects[keyString]; return this.#telemetryObjects[keyString];
} }
getMetaDataValuesForParameter(keyString) {
const telemetryObject = this.getTelemetryObjectForParameter(keyString);
const metaData = this.#openmct.telemetry.getMetadata(telemetryObject);
return metaData.valueMetadatas;
}
deleteParameter(keyString) { deleteParameter(keyString) {
this.#domainObject.configuration.comps.parameters = this.#domainObject.configuration.comps.parameters =
this.#domainObject.configuration.comps.parameters.filter( this.#domainObject.configuration.comps.parameters.filter(
@ -54,6 +71,10 @@ export default class CompsManager extends EventEmitter {
'configuration.comps', 'configuration.comps',
this.#domainObject.configuration.comps this.#domainObject.configuration.comps
); );
console.debug(
`📦 CompsManager: persisted domain object`,
this.#domainObject.configuration.comps
);
} }
async load() { async load() {
@ -170,7 +191,7 @@ export default class CompsManager extends EventEmitter {
} }
getExpression() { getExpression() {
return this.#domainObject.configuration.expression; return this.#domainObject.configuration.comps.expression;
} }
#waitForDebounce() { #waitForDebounce() {

View File

@ -8,12 +8,12 @@ onconnect = function (e) {
port.onmessage = function (event) { port.onmessage = function (event) {
console.debug('🧮 Comps Math Worker message:', event); console.debug('🧮 Comps Math Worker message:', event);
try { try {
const { type, callbackID, telemetryForComps, expression } = event.data; const { type, callbackID, telemetryForComps, expression, parameters } = event.data;
if (type === 'calculateRequest') { if (type === 'calculateRequest') {
const result = calculateRequest(telemetryForComps, expression); const result = calculateRequest(telemetryForComps, parameters, expression);
port.postMessage({ type: 'calculationRequestResult', callbackID, result }); port.postMessage({ type: 'calculationRequestResult', callbackID, result });
} else if (type === 'calculateSubscription') { } else if (type === 'calculateSubscription') {
const result = calculateSubscription(telemetryForComps, expression); const result = calculateSubscription(telemetryForComps, parameters, expression);
if (result.length) { if (result.length) {
port.postMessage({ type: 'calculationSubscriptionResult', callbackID, result }); port.postMessage({ type: 'calculationSubscriptionResult', callbackID, result });
} }
@ -38,31 +38,48 @@ function getFullDataFrame(telemetryForComps) {
return dataFrame; return dataFrame;
} }
function calculateSubscription(telemetryForComps, expression) { function calculateSubscription(telemetryForComps, parameters, expression) {
const dataFrame = getFullDataFrame(telemetryForComps); const dataFrame = getFullDataFrame(telemetryForComps);
return calculate(dataFrame, expression); return calculate(dataFrame, parameters, expression);
} }
function calculateRequest(telemetryForComps, expression) { function calculateRequest(telemetryForComps, parameters, expression) {
const dataFrame = getFullDataFrame(telemetryForComps); const dataFrame = getFullDataFrame(telemetryForComps);
return calculate(dataFrame, expression); return calculate(dataFrame, parameters, expression);
} }
function calculate(dataFrame, expression) { function calculate(dataFrame, parameters, expression) {
const sumResults = []; const sumResults = [];
// Iterate over the first dataset and check for matching utc in the other dataset // ensure all parameter keyStrings have corresponding telemetry data
const firstDataSet = Object.values(dataFrame)[0]; if (!expression) {
const secondDataSet = Object.values(dataFrame)[1];
if (!firstDataSet || !secondDataSet) {
return sumResults; return sumResults;
} }
// take the first parameter keyString as the reference
for (const [utc, item1] of firstDataSet.entries()) { const referenceParameter = parameters[0];
if (secondDataSet.has(utc)) { const otherParameters = parameters.slice(1);
const item2 = secondDataSet.get(utc); // iterate over the reference telemetry data
const output = evaluate(expression, { a: item1.sin, b: item2.sin }); const referenceTelemetry = dataFrame[referenceParameter.keyString];
sumResults.push({ utc, output }); referenceTelemetry.forEach((referenceTelemetryItem) => {
const scope = {
[referenceParameter.name]: referenceTelemetryItem[referenceParameter.valueToUse]
};
const referenceTime = referenceTelemetryItem[referenceParameter.timeKey];
// iterate over the other parameters to set the scope
let missingData = false;
otherParameters.forEach((parameter) => {
const otherDataFrame = dataFrame[parameter.keyString];
const otherTelemetry = otherDataFrame.get(referenceTime);
if (!otherTelemetry) {
missingData = true;
return;
}
scope[parameter.name] = otherTelemetry[parameter.valueToUse];
});
if (missingData) {
return;
} }
} const output = evaluate(expression, scope);
sumResults.push({ [referenceParameter.timeKey]: referenceTime, output });
});
return sumResults; return sumResults;
} }

View File

@ -59,21 +59,16 @@ export default class CompsTelemetryProvider {
this.#compsManagerPool this.#compsManagerPool
); );
specificCompsManager.load().then(() => { specificCompsManager.load().then(() => {
console.debug('📚 specific comp is ready');
const callbackID = this.#getCallbackID(); const callbackID = this.#getCallbackID();
const telemetryForComps = specificCompsManager.requestUnderlyingTelemetry(); const telemetryForComps = specificCompsManager.requestUnderlyingTelemetry();
const expression = specificCompsManager.getExpression(); const expression = specificCompsManager.getExpression();
console.debug('🏟️ 1 Telemetry for comps:', telemetryForComps); const parameters = specificCompsManager.getParameters();
console.debug(
'🏟️ 2 Telemetry for comps:',
specificCompsManager.requestUnderlyingTelemetry()
);
console.debug('🏟️ expression:', expression);
this.#requestPromises[callbackID] = { resolve, reject }; this.#requestPromises[callbackID] = { resolve, reject };
this.#sharedWorker.port.postMessage({ this.#sharedWorker.port.postMessage({
type: 'calculateRequest', type: 'calculateRequest',
telemetryForComps, telemetryForComps,
expression, expression,
parameters,
callbackID callbackID
}); });
}); });
@ -83,11 +78,12 @@ export default class CompsTelemetryProvider {
#computeOnNewTelemetry(specificCompsManager, newTelemetry, callbackID) { #computeOnNewTelemetry(specificCompsManager, newTelemetry, callbackID) {
const expression = specificCompsManager.getExpression(); const expression = specificCompsManager.getExpression();
const telemetryForComps = specificCompsManager.getFullDataFrame(newTelemetry); const telemetryForComps = specificCompsManager.getFullDataFrame(newTelemetry);
// console.debug('🏟️ created new Data frame:', telemetryForComps); const parameters = specificCompsManager.getParameters();
this.#sharedWorker.port.postMessage({ this.#sharedWorker.port.postMessage({
type: 'calculateSubscription', type: 'calculateSubscription',
telemetryForComps, telemetryForComps,
expression, expression,
parameters,
callbackID callbackID
}); });
} }
@ -120,7 +116,6 @@ export default class CompsTelemetryProvider {
this.#sharedWorker.port.onmessageerror = this.onSharedWorkerMessageError.bind(this); this.#sharedWorker.port.onmessageerror = this.onSharedWorkerMessageError.bind(this);
this.#sharedWorker.port.start(); this.#sharedWorker.port.start();
// send an initial message to the worker
this.#sharedWorker.port.postMessage({ type: 'init' }); this.#sharedWorker.port.postMessage({ type: 'init' });
this.#openmct.on('destroy', () => { this.#openmct.on('destroy', () => {
@ -131,10 +126,10 @@ export default class CompsTelemetryProvider {
onSharedWorkerMessage(event) { onSharedWorkerMessage(event) {
const { type, result, callbackID } = event.data; const { type, result, callbackID } = event.data;
if (type === 'calculationSubscriptionResult' && this.#subscriptionCallbacks[callbackID]) { if (type === 'calculationSubscriptionResult' && this.#subscriptionCallbacks[callbackID]) {
console.log('📝 Shared worker subscription message:', event.data); console.debug('📝 Shared worker subscription message:', event.data);
this.#subscriptionCallbacks[callbackID](result); this.#subscriptionCallbacks[callbackID](result);
} else if (type === 'calculationRequestResult' && this.#requestPromises[callbackID]) { } else if (type === 'calculationRequestResult' && this.#requestPromises[callbackID]) {
console.log('📝 Shared worker request message:', event.data); console.debug('📝 Shared worker request message:', event.data);
this.#requestPromises[callbackID].resolve(result); this.#requestPromises[callbackID].resolve(result);
delete this.#requestPromises[callbackID]; delete this.#requestPromises[callbackID];
} else if (type === 'error') { } else if (type === 'error') {

View File

@ -55,30 +55,26 @@
:class="{ 's-status-icon-warning-lo': !domainObject.configuration.comps.parameters }" :class="{ 's-status-icon-warning-lo': !domainObject.configuration.comps.parameters }"
> >
<div v-for="parameter in parameters" :key="parameter.name"> <div v-for="parameter in parameters" :key="parameter.name">
<div class="c-cs__telemetry-reference"> <div>
<input Reference
v-model="parameter.name" <input v-model="parameter.name" @change="compsManager.persist" />
class="c-cs__telemetry-reference__label"
@change="compsManager.persist"
/>
<ObjectPath <ObjectPath
:domain-object="compsManager.getDomainObjectForParameter(parameter.keyString)" :domain-object="compsManager.getTelemetryObjectForParameter(parameter.keyString)"
/> />
{{ compsManager.getDomainObjectForParameter(parameter.keyString).name }} {{ compsManager.getTelemetryObjectForParameter(parameter.keyString).name }}
<!-- drop down to select value from telemetry --> <!-- drop down to select value from telemetry -->
<select <select v-model="parameter.valueToUse" @change="persistParameters">
v-model="parameter.value"
class="c-cs__telemetry-reference__value"
@change="compsManager.persist"
>
<option <option
v-for="parameterValueOption in compsManager.getDomainObjectForParameter( v-for="parameterValueOption in compsManager.getMetaDataValuesForParameter(
parameter.keyString parameter.keyString
)" )"
:key="parameterValueOption" :key="parameterValueOption.key"
:value="parameterValueOption" :value="parameterValueOption.key"
/> >
{{ parameterValueOption.name }}
</option>
</select> </select>
<input v-model="parameter.testValue" @change="compsManager.persist" />
</div> </div>
</div> </div>
<template v-if="!domainObject.configuration.comps.parameters" <template v-if="!domainObject.configuration.comps.parameters"
@ -92,13 +88,12 @@
<div class="c-cs__header-label c-section__label">Expression</div> <div class="c-cs__header-label c-section__label">Expression</div>
</div> </div>
<div class="c-cs__content"> <div class="c-cs__content">
<div v-if="!isEditing">{{ domainObject.configuration.comps.expression }}</div> <div>
<div v-else>
<textarea <textarea
v-model="domainObject.configuration.comps.expression" v-model="expression"
class="c-cs__expression__input" class="c-cs__expression__input"
placeholder="Enter an expression" placeholder="Enter an expression"
@change="compsManager.persist" @change="persistExpression"
></textarea> ></textarea>
</div> </div>
</div> </div>
@ -119,6 +114,7 @@ const compsManager = CompsManager.getCompsManager(domainObject, openmct, compsMa
const currentCompOutput = ref(null); const currentCompOutput = ref(null);
const testDataApplied = ref(false); const testDataApplied = ref(false);
const parameters = ref(null); const parameters = ref(null);
const expression = ref(null);
let outputTelemetryCollection; let outputTelemetryCollection;
@ -135,9 +131,20 @@ onBeforeMount(async () => {
outputTelemetryCollection.on('clear', clearData); outputTelemetryCollection.on('clear', clearData);
await outputTelemetryCollection.load(); await outputTelemetryCollection.load();
await compsManager.load(); await compsManager.load();
parameters.value = domainObject.configuration.comps.parameters; parameters.value = compsManager.getParameters();
expression.value = compsManager.getExpression();
}); });
function persistParameters() {
domainObject.configuration.comps.parameters = parameters.value;
compsManager.persist();
}
function persistExpression() {
domainObject.configuration.comps.expression = expression.value;
compsManager.persist();
}
function applyTestData() {} function applyTestData() {}
function telemetryProcessor(data) { function telemetryProcessor(data) {