openmct/src/plugins/comps/CompsMathWorker.js
2024-09-03 16:48:17 +02:00

91 lines
3.3 KiB
JavaScript

import { evaluate } from 'mathjs';
// eslint-disable-next-line no-undef
onconnect = function (e) {
const port = e.ports[0];
console.debug('🧮 Comps Math Worker connected');
port.onmessage = function (event) {
console.debug('🧮 Comps Math Worker message:', event);
const { type, callbackID, telemetryForComps, expression, parameters } = event.data;
let responseType = 'unknown';
let error = null;
let result = [];
try {
if (type === 'calculateRequest') {
responseType = 'calculationRequestResult';
result = calculateRequest(telemetryForComps, parameters, expression);
} else if (type === 'calculateSubscription') {
responseType = 'calculationSubscriptionResult';
result = calculateSubscription(telemetryForComps, parameters, expression);
} else if (type === 'init') {
port.postMessage({ type: 'ready' });
return;
} else {
throw new Error('Invalid message type');
}
} catch (errorInCalculation) {
error = errorInCalculation;
console.error('🧮 Comps Math Worker error:', errorInCalculation);
}
port.postMessage({ type: responseType, callbackID, result, error });
};
};
function getFullDataFrame(telemetryForComps, parameters) {
const dataFrame = {};
Object.keys(telemetryForComps).forEach((key) => {
const parameter = parameters.find((p) => p.keyString === key);
const dataSet = telemetryForComps[key];
const telemetryMap = new Map(dataSet.map((item) => [item[parameter.timeKey], item]));
dataFrame[key] = telemetryMap;
});
return dataFrame;
}
function calculateSubscription(telemetryForComps, parameters, expression) {
const dataFrame = getFullDataFrame(telemetryForComps, parameters);
return calculate(dataFrame, parameters, expression);
}
function calculateRequest(telemetryForComps, parameters, expression) {
const dataFrame = getFullDataFrame(telemetryForComps, parameters);
return calculate(dataFrame, parameters, expression);
}
function calculate(dataFrame, parameters, expression) {
const sumResults = [];
// ensure all parameter keyStrings have corresponding telemetry data
if (!expression) {
return sumResults;
}
// take the first parameter keyString as the reference
const referenceParameter = parameters[0];
const otherParameters = parameters.slice(1);
// iterate over the reference telemetry data
const referenceTelemetry = dataFrame[referenceParameter.keyString];
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 compsOutput = evaluate(expression, scope);
sumResults.push({ [referenceParameter.timeKey]: referenceTime, compsOutput });
});
return sumResults;
}