Merge branch '7936-add-discrete-event-visualization' of github.com:nasa/openmct into 7936-add-discrete-event-visualization

This commit is contained in:
Scott Bell 2024-12-18 09:19:57 +01:00
commit 68fc3172a0
12 changed files with 122 additions and 42 deletions

View File

@ -74,7 +74,7 @@ test.describe('Event Timeline View', () => {
// count the event lines
const eventWrappersContainer = page.locator('.c-events-tsv__container');
const eventWrappers = eventWrappersContainer.locator('.c-events-tsv__event-wrapper');
const eventWrappers = eventWrappersContainer.locator('.c-events-tsv__event-line');
const expectedEventWrappersCount = 25;
await expect(eventWrappers).toHaveCount(expectedEventWrappersCount);
@ -104,7 +104,7 @@ test.describe('Event Timeline View', () => {
// count the extended lines
const overlayLinesContainer = page.locator('.c-timeline__overlay-lines');
const extendedLines = overlayLinesContainer.locator('.c-timeline__extended-line');
const extendedLines = overlayLinesContainer.locator('.c-timeline__event-line--extended');
const expectedCount = 25;
await expect(extendedLines).toHaveCount(expectedCount);
});

View File

@ -25,7 +25,7 @@ export const SEVERITY_CSS = {
WARNING: 'is-event--yellow',
DISTRESS: 'is-event--red',
CRITICAL: 'is-event--red',
SEVERE: 'is-event--red'
SEVERE: 'is-event--purple'
};
const NOMINAL_SEVERITY = {

View File

@ -27,6 +27,8 @@
import { SEVERITY_CSS } from './EventLimitProvider.js';
import messages from './transcript.json';
const DUR_MIN = 1000;
const DUR_MAX = 10000;
class EventTelemetryProvider {
constructor() {
this.defaultSize = 25;
@ -34,12 +36,17 @@ class EventTelemetryProvider {
generateData(firstObservedTime, count, startTime, duration, name) {
const millisecondsSinceStart = startTime - firstObservedTime;
const utc = startTime + count * duration;
const randStartDelay = Math.max(DUR_MIN, Math.random() * DUR_MAX);
const utc = startTime + randStartDelay + count * duration;
const ind = count % messages.length;
const message = messages[ind] + ' - [' + millisecondsSinceStart + ']';
// pick a random severity level + 1 for an undefined level so we can do nominal
const severity =
Object.keys(SEVERITY_CSS)[Math.floor(Math.random() * Object.keys(SEVERITY_CSS).length + 1)];
Math.random() > 0.4
? Object.keys(SEVERITY_CSS)[
Math.floor(Math.random() * Object.keys(SEVERITY_CSS).length + 1)
]
: undefined;
return {
name,
@ -58,7 +65,7 @@ class EventTelemetryProvider {
}
subscribe(domainObject, callback) {
const duration = domainObject.telemetry.duration * 1000;
const duration = domainObject.telemetry.duration * DUR_MIN;
const firstObservedTime = Date.now();
let count = 0;
@ -83,7 +90,7 @@ class EventTelemetryProvider {
request(domainObject, options) {
let start = options.start;
const end = Math.min(Date.now(), options.end); // no future values
const duration = domainObject.telemetry.duration * 1000;
const duration = domainObject.telemetry.duration * DUR_MIN;
const size = options.size ? options.size : this.defaultSize;
const data = [];
const firstObservedTime = options.start;

View File

@ -38,7 +38,7 @@ import eventData from '../mixins/eventData.js';
const PADDING = 1;
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 EVENT_WRAPPER_CLASS = 'c-events-tsv__event-line';
const ID_PREFIX = 'wrapper-';
const AXES_PADDING = 20;
@ -403,6 +403,10 @@ export default {
}
this.openmct.selection.select(selection, true);
},
getLimitClass(event) {
const limitEvaluation = this.limitEvaluator.evaluate(event, this.valueMetadata);
return limitEvaluation?.cssClass;
},
createEventWrapper(event) {
const id = `${ID_PREFIX}${event.time}`;
const eventWrapper = document.createElement('div');
@ -413,7 +417,7 @@ export default {
const textToShow = event[this.titleKey];
eventWrapper.ariaLabel = textToShow;
eventWrapper.addEventListener('mouseover', () => {
this.showToolTip(textToShow, eventWrapper);
this.showToolTip(textToShow, eventWrapper, event);
this.extendedLinesBus.updateHoverExtendEventLine(this.keyString, event.time);
});
eventWrapper.addEventListener('mouseleave', () => {
@ -421,8 +425,7 @@ export default {
this.extendedLinesBus.updateHoverExtendEventLine(this.keyString, null);
});
}
const limitEvaluation = this.limitEvaluator.evaluate(event, this.valueMetadata);
const limitClass = limitEvaluation?.cssClass;
const limitClass = this.getLimitClass(event);
if (limitClass) {
eventWrapper.classList.add(limitClass);
event.limitClass = limitClass;
@ -451,12 +454,22 @@ export default {
});
}
},
showToolTip(textToShow, referenceElement) {
showToolTip(textToShow, referenceElement, event) {
const aClasses = ['c-events-tooltip'];
const limitClass = this.getLimitClass(event);
if (limitClass) {
aClasses.push(limitClass);
}
const showToLeft = false; // Temp, stubbed in
if (showToLeft) {
aClasses.push('--left');
}
this.tooltip = this.openmct.tooltips.tooltip({
toolTipText: textToShow,
toolTipLocation: this.openmct.tooltips.TOOLTIP_LOCATIONS.RIGHT,
parentElement: referenceElement,
cssClasses: ['c-timeline-event-tooltip']
cssClasses: [aClasses.join(' ')]
});
}
}

View File

@ -1,3 +1,16 @@
@mixin styleEventLine($colorConst) {
background-color: $colorConst !important;
&:hover,
&[s-selected] {
box-shadow: rgba($colorConst, 0.5) 0 0 0px 4px;
transition: none;
z-index: 2;
}
}
@mixin styleEventLineExtended($colorConst) {
background-color: $colorConst !important;
}
.c-events-tsv {
$m: $interiorMargin;
overflow: hidden;
@ -11,21 +24,37 @@
top: $m; right: 0; bottom: $m; left: 0;
}
&__event-wrapper {
&__event-line {
// Wraps an individual event line
// Also holds the hover flyout element
$c: $colorEventLine;
$lineW: $eventLineW;
$hitAreaW: 7px;
$m: $interiorMarginSm;
background-color: rgba($c, 0.6);
cursor: pointer;
position: absolute;
display: flex;
top: $m; bottom: $m;
transition: box-shadow 250ms ease-out;
width: $lineW;
z-index: 1;
@include styleEventLine($colorEventLine);
&.is-event {
&--purple {
@include styleEventLine($colorEventPurpleLine);
}
&--red {
@include styleEventLine($colorEventRedLine);
}
&--orange {
@include styleEventLine($colorEventOrangeLine);
}
&--yellow {
@include styleEventLine($colorEventYellowLine);
}
}
&:before {
// Extend hit area
content: '';
@ -36,17 +65,6 @@
width: $hitAreaW;
transform: translateX(($hitAreaW - $lineW) * -0.5);
}
&.is-selected, // TODO: temp, remove this once we set selection correctly
&[s-selected],
&:hover {
z-index: 2;
background-color: $c;
&:before {
background-color: rgba($c, 0.4);
}
}
}
&__no-items {
@ -64,3 +82,38 @@
top: 0;
z-index: 2;
}
// Extended event lines
.c-timeline__event-line--extended {
@include abs();
transition: opacity 250ms ease-out;
width: $eventLineW;
opacity: 0.4;
&.--hilite {
opacity: 0.8;
transition: none;
}
@include styleEventLineExtended($colorEventLine);
&.is-event {
&--purple {
@include styleEventLineExtended($colorEventPurpleLine);
}
&--red {
@include styleEventLineExtended($colorEventRedLine);
}
&--orange {
@include styleEventLineExtended($colorEventOrangeLine);
}
&--yellow {
@include styleEventLineExtended($colorEventYellowLine);
}
}
}
.c-events-tooltip {
// Default to right of event line
border-radius: 0 !important;
//transform: translate(0, $interiorMargin);
}

View File

@ -24,18 +24,17 @@
<div
v-for="(lines, key) in extendedLinesPerKey"
:key="key"
class="c-timeline__extended-line-container"
class="c-timeline__event-line--extended-container"
>
<div
v-for="(line, index) in lines"
:id="line.id"
:key="index"
class="c-timeline__extended-line"
class="c-timeline__event-line--extended"
:class="[
line.limitClass,
{
'c-timeline__extended-line-hovered':
hoveredLineId && hoveredKeyString === key && line.id === hoveredLineId
'--hilite': hoveredLineId && hoveredKeyString === key && line.id === hoveredLineId
}
]"
:style="{ left: `${line.x + leftOffset}px`, height: `${height}px` }"

View File

@ -51,10 +51,4 @@
pointer-events: none; // Allows clicks to pass through
z-index: 10; // Ensure it sits atop swimlanes
}
&__extended-line {
position: absolute;
width: $eventLineW;
background-color: $colorEventLineExtended;
}
}

View File

@ -443,6 +443,10 @@ $colorEventPurpleBg: #31204a;
$colorEventRedBg: #3c1616;
$colorEventOrangeBg: #3e2a13;
$colorEventYellowBg: #3e3316;
$colorEventPurpleLine: #9e36ff;
$colorEventRedLine: #ff2525;
$colorEventOrangeLine: #ff8800;
$colorEventYellowLine: #fdce22;
// Bubble colors
$colorInfoBubbleBg: #dddddd;

View File

@ -412,6 +412,10 @@ $colorEventPurpleBg: #31204a;
$colorEventRedBg: #3c1616;
$colorEventOrangeBg: #3e2a13;
$colorEventYellowBg: #3e3316;
$colorEventPurpleLine: #9e36ff;
$colorEventRedLine: #ff2525;
$colorEventOrangeLine: #ff8800;
$colorEventYellowLine: #fdce22;
// Bubble colors
$colorInfoBubbleBg: #dddddd;

View File

@ -428,6 +428,10 @@ $colorEventPurpleBg: #31204a;
$colorEventRedBg: #3c1616;
$colorEventOrangeBg: #3e2a13;
$colorEventYellowBg: #3e3316;
$colorEventPurpleLine: #9e36ff;
$colorEventRedLine: #ff2525;
$colorEventOrangeLine: #ff8800;
$colorEventYellowLine: #fdce22;
// Bubble colors
$colorInfoBubbleBg: #dddddd;

View File

@ -403,14 +403,18 @@ $colorLimitCyanFg: #d3faff;
$colorLimitCyanIc: #1795c0;
// Events
$colorEventPurpleFg: #6433ff;
$colorEventPurpleFg: #6f07ed;
$colorEventRedFg: #aa0000;
$colorEventOrangeFg: #b84900;
$colorEventYellowFg: #867109;
$colorEventYellowFg: #a98c04;
$colorEventPurpleBg: #ebe7fb;
$colorEventRedBg: #fcefef;
$colorEventOrangeBg: #ffece3;
$colorEventYellowBg: #fdf8eb;
$colorEventPurpleLine: $colorEventPurpleFg;
$colorEventRedLine: $colorEventRedFg;
$colorEventOrangeLine: $colorEventOrangeFg;
$colorEventYellowLine: $colorEventYellowFg;
// Bubble colors
$colorInfoBubbleBg: $colorMenuBg;

View File

@ -252,8 +252,6 @@ tr {
background-color: $colorEventYellowBg !important;
color: $colorEventYellowFg !important;
}
&--no-style {
background-color: $colorBodyBg !important;
color: $colorBodyFg !important;
}
}