fix plots

This commit is contained in:
Scott Bell 2024-08-12 16:34:09 -05:00
parent a94c752616
commit 0113ec08db
5 changed files with 164 additions and 43 deletions

View File

@ -90,10 +90,12 @@ export default class TelemetryCollection extends EventEmitter {
this._watchTimeSystem(); this._watchTimeSystem();
this._watchTimeModeChange(); this._watchTimeModeChange();
this._requestHistoricalTelemetry(); const historicalTelemetryLoadedPromise = this._requestHistoricalTelemetry();
this._initiateSubscriptionTelemetry(); this._initiateSubscriptionTelemetry();
this.loaded = true; this.loaded = true;
return historicalTelemetryLoadedPromise;
} }
/** /**
@ -155,6 +157,7 @@ export default class TelemetryCollection extends EventEmitter {
options options
); );
historicalData = await historicalProvider.request(this.domainObject, modifiedOptions); historicalData = await historicalProvider.request(this.domainObject, modifiedOptions);
console.debug('👴 Requesting historical telemetry data...', historicalData);
} catch (error) { } catch (error) {
if (error.name !== 'AbortError') { if (error.name !== 'AbortError') {
console.error('Error requesting telemetry data...'); console.error('Error requesting telemetry data...');

View File

@ -6,20 +6,26 @@ export default class CompsManager extends EventEmitter {
#composition; #composition;
#telemetryObjects = {}; #telemetryObjects = {};
#telemetryCollections = {}; #telemetryCollections = {};
#managerLoadedPromise;
#dataFrame = {}; #dataFrame = {};
#batchedNewData = {};
#batchPromises = {};
#BATCH_DEBOUNCE_MS = 100;
#telemetryLoadedPromises = [];
constructor(openmct, domainObject) { constructor(openmct, domainObject) {
super(); super();
this.#openmct = openmct; this.#openmct = openmct;
this.#domainObject = domainObject; this.#domainObject = domainObject;
// Load the composition
this.#managerLoadedPromise = this.#loadComposition();
} }
load() { async load() {
return this.#managerLoadedPromise; await this.#loadComposition();
await Promise.all(this.#telemetryLoadedPromises);
this.#telemetryLoadedPromises = [];
}
getTelemetryObjects() {
return this.#telemetryObjects;
} }
async #loadComposition() { async #loadComposition() {
@ -28,6 +34,7 @@ export default class CompsManager extends EventEmitter {
this.#composition.on('add', this.#addTelemetryObject); this.#composition.on('add', this.#addTelemetryObject);
this.#composition.on('remove', this.#removeTelemetryObject); this.#composition.on('remove', this.#removeTelemetryObject);
await this.#composition.load(); await this.#composition.load();
console.debug('📚 Composition loaded');
} }
} }
@ -84,9 +91,40 @@ export default class CompsManager extends EventEmitter {
return underlyingTelemetry; return underlyingTelemetry;
} }
#telemetryProcessor = (newTelemetry, keyString) => { #clearBatch(keyString) {
console.debug(`🎉 new data for ${keyString}!`, newTelemetry); this.#batchedNewData[keyString] = [];
this.emit('underlyingTelemetryUpdated', { [keyString]: newTelemetry }); this.#batchPromises[keyString] = null;
}
#deferredTelemetryProcessor(newTelemetry, keyString) {
// We until the next event loop cycle to "collect" all of the get
// requests triggered in this iteration of the event loop
if (!this.#batchedNewData[keyString]) {
this.#batchedNewData[keyString] = [];
}
this.#batchedNewData[keyString].push(newTelemetry[0]);
if (!this.#batchPromises[keyString]) {
this.#batchPromises[keyString] = [];
this.#batchPromises[keyString] = this.#batchedTelemetryProcessor(
this.#batchedNewData[keyString],
keyString
);
}
}
#batchedTelemetryProcessor = async (newTelemetry, keyString) => {
await this.#waitForDebounce();
const specificBatchedNewData = this.#batchedNewData[keyString];
// clear it
this.#clearBatch(keyString);
console.debug(`🎉 new data for ${keyString}!`, specificBatchedNewData);
this.emit('underlyingTelemetryUpdated', { [keyString]: specificBatchedNewData });
}; };
#clearData() { #clearData() {
@ -94,21 +132,33 @@ export default class CompsManager extends EventEmitter {
} }
getExpression() { getExpression() {
return 'a * b '; return 'a + b ';
} }
#addTelemetryObject = async (telemetryObject) => { #waitForDebounce() {
console.debug('📢 CompsManager: #addTelemetryObject', telemetryObject); let timeoutID;
clearTimeout(timeoutID);
return new Promise((resolve) => {
timeoutID = setTimeout(() => {
resolve();
}, this.#BATCH_DEBOUNCE_MS);
});
}
#addTelemetryObject = (telemetryObject) => {
const keyString = this.#openmct.objects.makeKeyString(telemetryObject.identifier); const keyString = this.#openmct.objects.makeKeyString(telemetryObject.identifier);
this.#telemetryObjects[keyString] = telemetryObject; this.#telemetryObjects[keyString] = telemetryObject;
this.#telemetryCollections[keyString] = this.#telemetryCollections[keyString] =
this.#openmct.telemetry.requestCollection(telemetryObject); this.#openmct.telemetry.requestCollection(telemetryObject);
this.#telemetryCollections[keyString].on('add', (data) => { this.#telemetryCollections[keyString].on('add', (data) => {
this.#telemetryProcessor(data, keyString); this.#deferredTelemetryProcessor(data, keyString);
}); });
this.#telemetryCollections[keyString].on('clear', this.#clearData); this.#telemetryCollections[keyString].on('clear', this.#clearData);
await this.#telemetryCollections[keyString].load(); const telemetryLoadedPromise = this.#telemetryCollections[keyString].load();
this.#telemetryLoadedPromises.push(telemetryLoadedPromise);
console.debug('📢 CompsManager: loaded telemetry collection', keyString);
}; };
static getCompsManager(domainObject, openmct, compsManagerPool) { static getCompsManager(domainObject, openmct, compsManagerPool) {

View File

@ -51,21 +51,23 @@ export default class CompsTelemetryProvider {
return this.#lastUniqueID++; return this.#lastUniqueID++;
} }
// eslint-disable-next-line require-await request(domainObject, options) {
async request(domainObject, options) { return new Promise((resolve, reject) => {
const specificCompsManager = CompsManager.getCompsManager( const specificCompsManager = CompsManager.getCompsManager(
domainObject, domainObject,
this.#openmct, this.#openmct,
this.#compsManagerPool this.#compsManagerPool
); );
await specificCompsManager.load(); specificCompsManager.load().then(() => {
return new Promise((resolve, reject) => { 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();
// need to create callbackID with a promise for future execution
console.debug('🏟️ 1 Telemetry for comps:', telemetryForComps); console.debug('🏟️ 1 Telemetry for comps:', telemetryForComps);
console.debug('🏟️ 2 Telemetry for comps:', specificCompsManager.requestUnderlyingTelemetry()); console.debug(
'🏟️ 2 Telemetry for comps:',
specificCompsManager.requestUnderlyingTelemetry()
);
this.#requestPromises[callbackID] = { resolve, reject }; this.#requestPromises[callbackID] = { resolve, reject };
this.#sharedWorker.port.postMessage({ this.#sharedWorker.port.postMessage({
type: 'calculateRequest', type: 'calculateRequest',
@ -74,12 +76,13 @@ export default class CompsTelemetryProvider {
callbackID callbackID
}); });
}); });
});
} }
#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); // console.debug('🏟️ created new Data frame:', telemetryForComps);
this.#sharedWorker.port.postMessage({ this.#sharedWorker.port.postMessage({
type: 'calculateSubscription', type: 'calculateSubscription',
telemetryForComps, telemetryForComps,
@ -125,11 +128,12 @@ export default class CompsTelemetryProvider {
} }
onSharedWorkerMessage(event) { onSharedWorkerMessage(event) {
console.log('📝 Shared worker message:', event.data);
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);
this.#subscriptionCallbacks[callbackID](result); this.#subscriptionCallbacks[callbackID](result);
} else if (type === 'calculationRequestResult') { } else if (type === 'calculationRequestResult' && this.#requestPromises[callbackID]) {
console.log('📝 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

@ -33,11 +33,45 @@
</span> </span>
</div> </div>
</section> </section>
<section id="telemetryReferenceSection" aria-label="Derived Telemetry References">
<div class="c-cs__header c-section__header">
<div class="c-cs__header-label c-section__label">Telemetry References</div>
</div>
<div
:class="[
'c-cs__test-data__controls c-cdef__controls',
{ disabled: !telemetryObjectLength }
]"
>
<label class="c-toggle-switch">
<input type="checkbox" :checked="testDataApplied" @change="applyTestData" />
<span class="c-toggle-switch__slider" aria-label="Apply Test Data"></span>
<span class="c-toggle-switch__label">Apply Test Values</span>
</label>
</div>
<div class="c-cs__content">
<div
v-show="isEditing"
class="hint"
:class="{ 's-status-icon-warning-lo': !telemetryObjectLength }"
>
<template v-if="!telemetryObjectLength"
>Drag telemetry into Telemetry References to add variables for an expression</template
>
</div>
</div>
</section>
<section id="expressionSection" aria-label="Derived Telemetry Expression">
<div class="c-cs__header c-section__header">
<div class="c-cs__header-label c-section__label">Expression</div>
</div>
<div class="c-cs__content"></div>
</section>
</div> </div>
</template> </template>
<script setup> <script setup>
import { inject, onBeforeUnmount, onMounted } from 'vue'; import { inject, onBeforeUnmount, onMounted, ref } from 'vue';
import CompsManager from '../CompsManager'; import CompsManager from '../CompsManager';
@ -45,10 +79,40 @@ const openmct = inject('openmct');
const domainObject = inject('domainObject'); const domainObject = inject('domainObject');
const compsManagerPool = inject('compsManagerPool'); const compsManagerPool = inject('compsManagerPool');
const compsManager = CompsManager.getCompsManager(domainObject, openmct, compsManagerPool); const compsManager = CompsManager.getCompsManager(domainObject, openmct, compsManagerPool);
const currentCompOutput = ref(null);
const telemetryObjectLength = ref(0);
const testDataApplied = ref(false);
onMounted(() => { let outputTelemetryCollection;
console.debug('🚀 CompsView: onMounted with compsManager', compsManager);
defineProps({
isEditing: { type: Boolean, required: true }
}); });
onBeforeUnmount(() => {}); onMounted(async () => {
console.debug('🚀 CompsView: onMounted with compsManager', compsManager);
outputTelemetryCollection = openmct.telemetry.requestCollection(domainObject);
outputTelemetryCollection.on('add', (data) => {
telemetryProcessor(data);
});
outputTelemetryCollection.on('clear', clearData);
await outputTelemetryCollection.load();
await compsManager.load();
telemetryObjectLength.value = Object.keys(compsManager.getTelemetryObjects()).length;
});
function applyTestData() {}
function telemetryProcessor(data) {
// new data will come in as array, so just take the last element
currentCompOutput.value = data[data.length - 1]?.output;
}
function clearData() {
currentCompOutput.value = null;
}
onBeforeUnmount(() => {
outputTelemetryCollection.destroy();
});
</script> </script>

View File

@ -28,7 +28,7 @@ export default function CompsPlugin() {
return function install(openmct) { return function install(openmct) {
openmct.types.addType('comps', { openmct.types.addType('comps', {
name: 'Comps', name: 'Derived Telemetry',
key: 'comps', key: 'comps',
description: description:
'Add one or more telemetry objects, apply a mathematical operation to them, and republish the result as a new telemetry object.', 'Add one or more telemetry objects, apply a mathematical operation to them, and republish the result as a new telemetry object.',