This commit is contained in:
Scott Bell 2024-10-09 09:32:44 +02:00
parent d859322a47
commit 395436a361
7 changed files with 121 additions and 48 deletions

View File

@ -93,9 +93,6 @@ export default class TelemetryCollection extends EventEmitter {
if (this.options.end) {
this.lastBounds.end = this.options.end;
}
console.debug(
`🫙 Bounds for collection are start ${new Date(this.lastBounds.start).toISOString()} and end ${new Date(this.lastBounds.end).toISOString()}`
);
this._watchBounds();
this._watchTimeSystem();
this._watchTimeModeChange();
@ -140,9 +137,6 @@ export default class TelemetryCollection extends EventEmitter {
* @private
*/
async _requestHistoricalTelemetry() {
console.debug(
`🫙 Requesting historical telemetry with start ${new Date(this.lastBounds.start).toISOString()} and end ${new Date(this.lastBounds.end).toISOString()}}`
);
let options = this.openmct.telemetry.standardizeRequestOptions({ ...this.options });
const historicalProvider = this.openmct.telemetry.findRequestProvider(
this.domainObject,
@ -243,19 +237,6 @@ export default class TelemetryCollection extends EventEmitter {
beforeStartOfBounds = parsedValue < boundsToUse.start;
afterEndOfBounds = parsedValue > boundsToUse.end;
if (beforeStartOfBounds) {
console.debug(
`🫙 Datum is BEFORE start of bounds: ${new Date(parsedValue).toISOString()} < ${new Date(this.lastBounds.start).toISOString()}`,
this.options
);
}
if (afterEndOfBounds) {
console.debug(
`🫙 Datum is AFTER start of bounds: ${new Date(parsedValue).toISOString()} < ${new Date(this.lastBounds.start).toISOString()}`,
this.options
);
}
if (
!afterEndOfBounds &&
(!beforeStartOfBounds || (this.isStrategyLatest && this.openmct.telemetry.greedyLAD()))

View File

@ -59,7 +59,8 @@ export default class CompsManager extends EventEmitter {
name: `${this.#getNextAlphabeticalParameterName()}`,
valueToUse,
testValue: 0,
timeMetaData
timeMetaData,
accumulateValues: false
});
this.emit('parameterAdded', this.#domainObject);
}
@ -173,27 +174,45 @@ export default class CompsManager extends EventEmitter {
}
}
getFullDataFrame(newTelemetry) {
const dataFrame = {};
// can assume on data item
const newTelemetryKey = Object.keys(newTelemetry)[0];
const newTelemetryData = newTelemetry[newTelemetryKey];
const otherTelemetryKeys = Object.keys(this.#telemetryCollections).filter(
(keyString) => keyString !== newTelemetryKey
#getParameterForKeyString(keyString) {
return this.#domainObject.configuration.comps.parameters.find(
(parameter) => parameter.keyString === keyString
);
// initialize the data frame with the new telemetry data
dataFrame[newTelemetryKey] = newTelemetryData;
// initialize the other telemetry data
}
getTelemetryForComps(newTelemetry) {
const telemetryForComps = {};
const newTelemetryKey = Object.keys(newTelemetry)[0];
const newTelemetryParameter = this.#getParameterForKeyString(newTelemetryKey);
const newTelemetryData = newTelemetry[newTelemetryKey];
const otherTelemetryKeys = Object.keys(this.#telemetryCollections).slice(0);
if (newTelemetryParameter.accumulateValues) {
telemetryForComps[newTelemetryKey] = this.#telemetryCollections[newTelemetryKey].getAll();
} else {
telemetryForComps[newTelemetryKey] = newTelemetryData;
}
otherTelemetryKeys.forEach((keyString) => {
dataFrame[keyString] = [];
telemetryForComps[keyString] = [];
});
// march through the new telemetry data and add data to the frame from the other telemetry objects
// using LOCF
const otherTelemetryKeysNotAccumulating = otherTelemetryKeys.filter(
(keyString) => !this.#getParameterForKeyString(keyString).accumulateValues
);
const otherTelemetryKeysAccumulating = otherTelemetryKeys.filter(
(keyString) => this.#getParameterForKeyString(keyString).accumulateValues
);
// if we're accumulating, just add all the data
otherTelemetryKeysAccumulating.forEach((keyString) => {
telemetryForComps[keyString] = this.#telemetryCollections[keyString].getAll();
});
// for the others, march through the new telemetry data and add data to the frame from the other telemetry objects
// using LOCF
newTelemetryData.forEach((newDatum) => {
otherTelemetryKeys.forEach((otherKeyString) => {
otherTelemetryKeysNotAccumulating.forEach((otherKeyString) => {
const otherCollection = this.#telemetryCollections[otherKeyString];
// otherwise we need to find the closest datum to the new datum
let insertionPointForNewData = otherCollection._sortedIndex(newDatum);
const otherCollectionData = otherCollection.getAll();
if (insertionPointForNewData && insertionPointForNewData >= otherCollectionData.length) {
@ -202,11 +221,11 @@ export default class CompsManager extends EventEmitter {
// get the closest datum to the new datum
const closestDatum = otherCollectionData[insertionPointForNewData];
if (closestDatum) {
dataFrame[otherKeyString].push(closestDatum);
telemetryForComps[otherKeyString].push(closestDatum);
}
});
});
return dataFrame;
return telemetryForComps;
}
#removeTelemetryObject = (telemetryObjectIdentifier) => {

View File

@ -5,7 +5,8 @@ onconnect = function (e) {
const port = e.ports[0];
port.onmessage = function (event) {
const { type, callbackID, telemetryForComps, expression, parameters } = event.data;
const { type, callbackID, telemetryForComps, expression, parameters, newTelemetry } =
event.data;
let responseType = 'unknown';
let error = null;
let result = [];
@ -15,7 +16,7 @@ onconnect = function (e) {
result = calculateRequest(telemetryForComps, parameters, expression);
} else if (type === 'calculateSubscription') {
responseType = 'calculationSubscriptionResult';
result = calculateSubscription(telemetryForComps, parameters, expression);
result = calculateSubscription(telemetryForComps, newTelemetry, parameters, expression);
} else if (type === 'init') {
port.postMessage({ type: 'ready' });
return;
@ -40,9 +41,16 @@ function getFullDataFrame(telemetryForComps, parameters) {
return dataFrame;
}
function calculateSubscription(telemetryForComps, parameters, expression) {
function calculateSubscription(telemetryForComps, newTelemetry, parameters, expression) {
const dataFrame = getFullDataFrame(telemetryForComps, parameters);
return calculate(dataFrame, parameters, expression);
const calculation = calculate(dataFrame, parameters, expression);
const newTelemetryKey = Object.keys(newTelemetry)[0];
const newTelemetrySize = newTelemetry[newTelemetryKey].length;
let trimmedCalculation = calculation;
if (calculation.length > newTelemetrySize) {
trimmedCalculation = calculation.slice(calculation.length - newTelemetrySize);
}
return trimmedCalculation;
}
function calculateRequest(telemetryForComps, parameters, expression) {
@ -56,14 +64,28 @@ function calculate(dataFrame, parameters, expression) {
if (!expression) {
return sumResults;
}
// set up accumulated data structure
const accumulatedData = {};
parameters.forEach((parameter) => {
if (parameter.accumulateValues) {
accumulatedData[parameter.name] = [];
}
});
// 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) => {
let referenceValue = referenceTelemetryItem[referenceParameter.valueToUse];
if (referenceParameter.accumulateValues) {
accumulatedData[referenceParameter.name].push(referenceValue);
referenceValue = accumulatedData[referenceParameter.name];
}
const scope = {
[referenceParameter.name]: referenceTelemetryItem[referenceParameter.valueToUse]
[referenceParameter.name]: referenceValue
};
const referenceTime = referenceTelemetryItem[referenceParameter.timeKey];
// iterate over the other parameters to set the scope
@ -75,7 +97,12 @@ function calculate(dataFrame, parameters, expression) {
missingData = true;
return;
}
scope[parameter.name] = otherTelemetry[parameter.valueToUse];
let otherValue = otherTelemetry[parameter.valueToUse];
if (parameter.accumulateValues) {
accumulatedData[parameter.name].push(referenceValue);
otherValue = accumulatedData[referenceParameter.name];
}
scope[parameter.name] = otherValue;
});
if (missingData) {
return;

View File

@ -61,6 +61,7 @@ export default class CompsMetadataProvider {
key: 'compsOutput',
source: 'compsOutput',
name: 'Output',
derived: true,
formatString: specificCompsManager.getOutputFormat(),
hints: {
range: 1

View File

@ -95,7 +95,7 @@ export default class CompsTelemetryProvider {
return;
}
const expression = specificCompsManager.getExpression();
const telemetryForComps = specificCompsManager.getFullDataFrame(newTelemetry);
const telemetryForComps = specificCompsManager.getTelemetryForComps(newTelemetry);
const parameters = JSON.parse(JSON.stringify(specificCompsManager.getParameters()));
if (!expression || !parameters) {
return;
@ -103,6 +103,7 @@ export default class CompsTelemetryProvider {
const payload = {
type: 'calculateSubscription',
telemetryForComps,
newTelemetry,
expression,
parameters,
callbackID
@ -134,10 +135,6 @@ export default class CompsTelemetryProvider {
);
return () => {
delete this.#subscriptionCallbacks[callbackID];
console.debug(
`🛑 Stopping subscription for ${domainObject.name} with callback ID ${callbackID}. We now have ${Object.keys(this.#subscriptionCallbacks).length} subscribers`,
this.#subscriptionCallbacks
);
specificCompsManager.stopListeningToUnderlyingTelemetry();
specificCompsManager.off('underlyingTelemetryUpdated', boundComputeOnNewTelemetry);
};

View File

@ -95,6 +95,25 @@
</option>
</select>
<div v-else>{{ parameter.valueToUse }}</div>
<div
:class="[
'c-comps__refs-controls c-cdef__controls',
{ disabled: !parameters?.length }
]"
>
<label v-if="isEditing" class="c-toggle-switch">
<input
v-model="parameter.accumulateValues"
type="checkbox"
@change="updateAccumulateValues(parameter)"
/>
<span
class="c-toggle-switch__slider"
aria-label="Toggle Parameter Accumulation"
></span>
<span class="c-toggle-switch__label">Accumulate Values</span>
</label>
</div>
</span>
<span v-if="isEditing" class="c-test-datum__string">Test value</span>
@ -235,6 +254,15 @@ function updateParameters() {
applyTestData();
}
function updateAccumulateValues(parameter) {
if (parameter.accumulateValues) {
parameter.testValue = [''];
} else {
parameter.testValue = '';
}
updateParameters();
}
function toggleTestData() {
testDataApplied.value = !testDataApplied.value;
if (testDataApplied.value) {
@ -270,6 +298,20 @@ function applyTestData() {
}
return acc;
}, {});
// see which parameters are misconfigured as non-arrays
const misconfiguredParameterNames = parameters.value
.filter((parameter) => {
return parameter.accumulateValues && !Array.isArray(scope[parameter.name]);
})
.map((parameter) => parameter.name);
if (misconfiguredParameterNames.length) {
const misconfiguredParameterNamesString = misconfiguredParameterNames.join(', ');
currentTestOutput.value = null;
expressionOutput.value = `Reference "${misconfiguredParameterNamesString}" set to accumulating, but test values aren't arrays.`;
return;
}
try {
const testOutput = evaluate(expression.value, scope);
const formattedData = getValueFormatter().format(testOutput);

View File

@ -225,7 +225,13 @@ export default class PlotSeries extends Model {
try {
const points = await this.openmct.telemetry.request(this.domainObject, options);
const data = this.getSeriesData();
// if derived, we can't use the old data
let data = this.getSeriesData();
if (this.metadata.value(this.get('yKey')).derived) {
data = [];
}
// eslint-disable-next-line you-dont-need-lodash-underscore/concat
const newPoints = _(data)
.concat(points)