going to try with YAMCS data

This commit is contained in:
Scott Bell 2024-12-10 11:16:47 +01:00
parent 680b0953b2
commit 7b22cf3371
5 changed files with 252 additions and 63 deletions

View File

@ -21,8 +21,8 @@
-->
<template>
<div ref="events" class="c-event-tsv c-timeline-holder">
<div ref="eventsHolder" class="c-event-tsv__contents u-contents"></div>
<div ref="events" class="c-events-tsv c-timeline-holder">
<div ref="eventsHolder" class="c-events-tsv__contents u-contents"></div>
</div>
</template>
@ -32,20 +32,21 @@ import _ from 'lodash';
import mount from 'utils/mount';
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
import eventData from '../mixins/eventData.js';
const PADDING = 1;
const ROW_HEIGHT = 100;
const EVENT_DETAIL_SIZE = 85;
const CONTAINER_CLASS = 'c-event-tsv-container';
const NO_ITEMS_CLASS = 'c-event-tsv__no-items';
const EVENT_WRAPPER_CLASS = 'c-event-tsv__event-wrapper';
const CONTAINER_CLASS = 'c-events-tsv-container';
const NO_ITEMS_CLASS = 'c-events-tsv__no-items';
const EVENT_WRAPPER_CLASS = 'c-events-tsv__event-wrapper';
const ID_PREFIX = 'wrapper-';
export default {
mixins: [eventData],
inject: ['openmct', 'domainObject', 'objectPath'],
data() {
let timeSystem = this.openmct.time.getTimeSystem();
const timeSystem = this.openmct.time.getTimeSystem();
this.metadata = {};
this.requestCount = 0;
@ -67,8 +68,6 @@ export default {
}
},
mounted() {
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
this.canvas = this.$refs.events.appendChild(document.createElement('canvas'));
this.canvas.height = 0;
this.canvasContext = this.canvas.getContext('2d');
@ -113,18 +112,11 @@ export default {
this.timeContext.off('boundsChanged', this.updateViewBounds);
}
},
expand(eventTimestamp) {
const path = this.objectPath[0];
this.previewAction.invoke([path], {
timestamp: eventTimestamp,
objectPath: this.objectPath
});
},
observeForChanges(mutatedObject) {
this.updateViewBounds();
},
resize() {
let clientWidth = this.getClientWidth();
const clientWidth = this.getClientWidth();
if (clientWidth !== this.width) {
this.setDimensions();
this.updateViewBounds();
@ -135,25 +127,26 @@ export default {
if (!clientWidth) {
//this is a hack - need a better way to find the parent of this component
let parent = this.openmct.layout.$refs.browseObject.$el;
const parent = this.openmct.layout.$refs.browseObject.$el;
if (parent) {
clientWidth = parent.getBoundingClientRect().width;
}
}
console.debug('🇹🇯 Calculated clientWidth:', clientWidth);
return clientWidth;
},
updateViewBounds(bounds, isTick) {
this.viewBounds = this.timeContext.getBounds();
if (this.timeSystem === undefined) {
if (!this.timeSystem) {
this.timeSystem = this.timeContext.getTimeSystem();
}
this.setScaleAndPlotEvents(this.timeSystem, !isTick);
},
setScaleAndPlotEvents(timeSystem, clearAllEvents) {
if (timeSystem !== undefined) {
if (timeSystem) {
this.timeSystem = timeSystem;
this.timeFormatter = this.getFormatter(this.timeSystem.key);
}
@ -164,8 +157,8 @@ export default {
getFormatter(key) {
const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
let metadataValue = metadata.value(key) || { format: key };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
const metadataValue = metadata.value(key) || { format: key };
const valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
return valueFormatter;
},
@ -181,7 +174,7 @@ export default {
noItemsEl.forEach((item) => {
item.remove();
});
let events = this.$el.querySelectorAll(`.${EVENT_WRAPPER_CLASS}`);
const events = this.$el.querySelectorAll(`.${EVENT_WRAPPER_CLASS}`);
events.forEach((eventElm) => {
if (clearAllEvents) {
eventElm.remove();
@ -211,7 +204,7 @@ export default {
return;
}
if (timeSystem === undefined) {
if (!timeSystem) {
timeSystem = this.timeContext.getTimeSystem();
}
@ -229,8 +222,8 @@ export default {
return evenObj.time <= this.viewBounds.end && evenObj.time >= this.viewBounds.start;
},
getEventsContainer() {
let containerHeight = 100;
let containerWidth = this.eventHistory.length ? this.width : 200;
const containerHeight = 100;
const containerWidth = this.eventHistory.length ? this.width : 200;
let eventContainer;
let existingContainer = this.$el.querySelector(`.${CONTAINER_CLASS}`);
@ -254,7 +247,7 @@ export default {
isNested: true
};
},
template: `<swim-lane :is-nested="isNested" :hide-label="true"><template v-slot:object><div class="c-event-tsv-container"></div></template></swim-lane>`
template: `<swim-lane :is-nested="isNested" :hide-label="true"><template v-slot:object><div class="c-events-tsv-container"></div></template></swim-lane>`
},
{
app: this.openmct.app
@ -277,7 +270,7 @@ export default {
let index = 0;
if (this.eventHistory.length) {
this.eventHistory.forEach((currentEventObject) => {
if (this.isEventsInBounds(currentEventObject)) {
if (this.isEventInBounds(currentEventObject)) {
this.plotEvents(currentEventObject, eventContainer, index);
index = index + 1;
}
@ -287,7 +280,7 @@ export default {
}
},
plotNoItems(containerElement) {
let textElement = document.createElement('text');
const textElement = document.createElement('text');
textElement.classList.add(NO_ITEMS_CLASS);
textElement.innerHTML = 'No events within timeframe';
@ -317,58 +310,34 @@ export default {
return this.$el.querySelector(`.c-events-tsv__contents div[id=${id}]`);
},
plotEvents(item, containerElement, index) {
let existingEventWrapper = this.getEventWrapper(item);
const existingEventWrapper = this.getEventWrapper(item);
//eventWrapper wraps the vertical tick and the EVENT
if (existingEventWrapper) {
this.updateExistingEventWrapper(existingEventWrapper, item);
} else {
let eventWrapper = this.createEventWrapper(index, item);
const eventWrapper = this.createEventWrapper(index, item);
containerElement.appendChild(eventWrapper);
}
},
setEventDisplay(eventElement) {
eventElement.style.display = 'block';
console.debug('🧟 Event time:', new Date(item.time).toISOString());
console.debug('🧟 Scaled X position:', this.xScale(item.time));
},
updateExistingEventWrapper(existingEventWrapper, event) {
//Update the x co-ordinates of the event wrapper and the url of event
//this is to avoid tearing down all elements completely and re-drawing them
//Update the x co-ordinates of the event wrapper
existingEventWrapper.style.left = `${this.xScale(event.time)}px`;
let eventElement = existingEventWrapper.querySelector('img');
this.setNSAttributesForElement(eventElement, {
src: event.thumbnailUrl || event.url
});
this.setEventDisplay(eventElement);
},
createEventWrapper(index, event) {
const id = `${ID_PREFIX}${event.time}`;
let eventWrapper = document.createElement('div');
const eventWrapper = document.createElement('div');
eventWrapper.ariaLabel = id;
eventWrapper.classList.add(EVENT_WRAPPER_CLASS);
eventWrapper.style.left = `${this.xScale(event.time)}px`;
//create event vertical tick indicator
let eventTickElement = document.createElement('div');
const eventTickElement = document.createElement('div');
eventTickElement.classList.add('c-events-tsv__event-handle');
eventTickElement.style.width = '2px';
eventTickElement.style.height = `${String(ROW_HEIGHT - 10)}px`;
eventWrapper.appendChild(eventTickElement);
//create placeholder - this will also hold the actual event
let eventPlaceholder = document.createElement('div');
eventPlaceholder.classList.add('c-events-tsv__event-placeholder');
eventPlaceholder.style.width = `${EVENT_DETAIL_SIZE}px`;
eventPlaceholder.style.height = `${EVENT_DETAIL_SIZE}px`;
eventWrapper.appendChild(eventPlaceholder);
//create event element
let eventElement = document.createElement('img');
this.setNSAttributesForElement(eventElement, {
src: event.thumbnailUrl || event.url
});
eventElement.style.width = `${EVENT_DETAIL_SIZE}px`;
eventElement.style.height = `${EVENT_DETAIL_SIZE}px`;
this.setEventDisplay(eventElement);
//handle mousedown event to show the event in a large view
eventWrapper.addEventListener('mousedown', (e) => {
if (e.button === 0) {
@ -376,8 +345,6 @@ export default {
}
});
eventPlaceholder.appendChild(eventElement);
return eventWrapper;
}
}

View File

@ -0,0 +1,34 @@
.c-events-tsv {
div.c-events-tsv__event-wrapper {
cursor: pointer;
position: absolute;
top: 0;
display: flex;
z-index: 1;
margin-top: 5px;
&:hover {
z-index: 2;
[class*='__event-handle'] {
background-color: $colorBodyFg;
}
}
}
&__no-items {
fill: $colorBodyFg !important;
}
&__event-handle {
background-color: rgba($colorBodyFg, 0.5);
}
}
.c-events-canvas {
pointer-events: auto; // This allows the event element to receive a browser-level context click
position: absolute;
left: 0;
top: 0;
z-index: 2;
}

View File

@ -0,0 +1,183 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
const DEFAULT_DURATION_FORMATTER = 'duration';
import { TIME_CONTEXT_EVENTS } from '../../../api/time/constants.js';
export default {
inject: ['openmct', 'domainObject', 'objectPath'],
mounted() {
// listen
this.boundsChanged = this.boundsChanged.bind(this);
this.timeSystemChanged = this.timeSystemChanged.bind(this);
this.setDataTimeContext = this.setDataTimeContext.bind(this);
this.openmct.objectViews.on('clearData', this.dataCleared);
// Get metadata and formatters
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.durationFormatter = this.getFormatter(
this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER
);
// initialize
this.timeKey = this.timeSystem.key;
this.timeFormatter = this.getFormatter(this.timeKey);
this.setDataTimeContext();
this.loadTelemetry();
},
beforeUnmount() {
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
this.stopFollowingDataTimeContext();
this.openmct.objectViews.off('clearData', this.dataCleared);
this.telemetryCollection.off('add', this.dataAdded);
this.telemetryCollection.off('remove', this.dataRemoved);
this.telemetryCollection.off('clear', this.dataCleared);
this.telemetryCollection.destroy();
},
methods: {
dataAdded(addedItems, addedItemIndices) {
const normalizedDataToAdd = addedItems.map((datum) => this.normalizeDatum(datum));
let newEventHistory = this.eventHistory.slice();
normalizedDataToAdd.forEach((datum, index) => {
newEventHistory.splice(addedItemIndices[index] ?? -1, 0, datum);
});
//Assign just once so eventHistory watchers don't get called too often
this.eventHistory = newEventHistory;
},
dataCleared() {
this.eventHistory = [];
},
dataRemoved(removed) {
const removedTimestamps = {};
removed.forEach((_removed) => {
const removedTimestamp = this.parseTime(_removed);
removedTimestamps[removedTimestamp] = true;
});
this.eventHistory = this.eventHistory.filter((event) => {
const eventTimestamp = this.parseTime(event);
return !removedTimestamps[eventTimestamp];
});
},
setDataTimeContext() {
this.stopFollowingDataTimeContext();
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
this.timeContext.on(TIME_CONTEXT_EVENTS.boundsChanged, this.boundsChanged);
this.timeContext.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.timeSystemChanged);
},
stopFollowingDataTimeContext() {
if (this.timeContext) {
this.timeContext.off(TIME_CONTEXT_EVENTS.boundsChanged, this.boundsChanged);
this.timeContext.off(TIME_CONTEXT_EVENTS.timeSystemChanged, this.timeSystemChanged);
}
},
formatEventUrl(datum) {
if (!datum) {
return;
}
return this.eventFormatter.format(datum);
},
formatEventThumbnailUrl(datum) {
if (!datum || !this.eventThumbnailFormatter) {
return;
}
return this.eventThumbnailFormatter.format(datum);
},
formatTime(datum) {
if (!datum) {
return;
}
const dateTimeStr = this.timeFormatter.format(datum);
// Replace ISO "T" with a space to allow wrapping
return dateTimeStr.replace('T', ' ');
},
getEventDownloadName(datum) {
let eventDownloadName = '';
if (datum) {
const key = this.eventDownloadNameMetadataValue.key;
eventDownloadName = datum[key];
}
return eventDownloadName;
},
parseTime(datum) {
if (!datum) {
return;
}
return this.timeFormatter.parse(datum);
},
loadTelemetry() {
this.telemetryCollection = this.openmct.telemetry.requestCollection(this.domainObject, {
timeContext: this.timeContext
});
this.telemetryCollection.on('add', this.dataAdded);
this.telemetryCollection.on('remove', this.dataRemoved);
this.telemetryCollection.on('clear', this.dataCleared);
this.telemetryCollection.load();
},
boundsChanged(bounds, isTick) {
if (isTick) {
return;
}
this.bounds = bounds; // setting bounds for EventryView watcher
},
timeSystemChanged() {
this.timeSystem = this.timeContext.getTimeSystem();
this.timeKey = this.timeSystem.key;
this.timeFormatter = this.getFormatter(this.timeKey);
this.durationFormatter = this.getFormatter(
this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER
);
},
normalizeDatum(datum) {
const formattedTime = this.formatTime(datum);
const time = this.parseTime(formattedTime);
return {
...datum,
formattedTime,
time
};
},
getFormatter(key) {
const metadataValue = this.metadata.value(key) || { format: key };
const valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
return valueFormatter;
}
}
};

View File

@ -145,6 +145,7 @@ export default {
}
}
console.debug('🗺️ Calculated clientWidth:', clientWidth);
return clientWidth;
},
updateViewBounds(bounds, isTick) {
@ -345,6 +346,9 @@ export default {
let imageWrapper = this.createImageWrapper(index, item, showImagePlaceholders);
containerElement.appendChild(imageWrapper);
}
console.debug('🪝 Event time:', new Date(item.time).toISOString());
console.debug('🪝 Scaled X position:', this.xScale(item.time));
},
setImageDisplay(imageElement, showImagePlaceholders) {
if (showImagePlaceholders) {

View File

@ -11,6 +11,7 @@
@import '../plugins/displayLayout/components/layout-frame.scss';
@import '../plugins/displayLayout/components/telemetry-view.scss';
@import '../plugins/displayLayout/components/text-view.scss';
@import '../plugins/events/components/events-view.scss';
@import '../plugins/filters/components/filters-view.scss';
@import '../plugins/filters/components/global-filters.scss';
@import '../plugins/flexibleLayout/components/flexible-layout.scss';