Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discrete Event Visualization in Timeline #7967

Open
wants to merge 76 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
3159de0
initial structure
scottbell Dec 9, 2024
680b095
more scaffolding
scottbell Dec 9, 2024
7b22cf3
going to try with YAMCS data
scottbell Dec 10, 2024
7af3996
fix events not being removed
scottbell Dec 10, 2024
944634d
adding inspector
scottbell Dec 10, 2024
55bed6a
Merge remote-tracking branch 'origin/master' into 7936-add-discrete-e…
scottbell Dec 11, 2024
2776cc8
inspector works
scottbell Dec 11, 2024
5b006b6
inspector and colors work
scottbell Dec 11, 2024
8b5e2f4
remove undefined
scottbell Dec 11, 2024
3b236cc
pass event bus
scottbell Dec 11, 2024
0db301d
add facility to send action to mounted component regarding extending …
scottbell Dec 12, 2024
e9f120a
button works
scottbell Dec 12, 2024
d046ad1
extended events
scottbell Dec 12, 2024
052129b
ensure colored lines work
scottbell Dec 13, 2024
6f26add
works per swimlane now
scottbell Dec 13, 2024
aaec052
add title
scottbell Dec 13, 2024
96d8870
watch for left offset changes
scottbell Dec 13, 2024
3829295
Closes #7960
charlesh88 Dec 13, 2024
3d3f093
Closes #7960
charlesh88 Dec 14, 2024
64bd625
remove debugging code and extraneous classes
scottbell Dec 16, 2024
781d834
add hovered effect for extended lines
scottbell Dec 16, 2024
d97f7c3
resolve conflicts
scottbell Dec 16, 2024
cda7cc9
fix extended lines
scottbell Dec 16, 2024
d048af1
add tooltip
scottbell Dec 16, 2024
62b4975
add selection class
scottbell Dec 16, 2024
20247bb
only add left margin to container
scottbell Dec 16, 2024
20426fe
add tooltip class and only offset swimlane
scottbell Dec 16, 2024
6bda108
Closes #7936
charlesh88 Dec 17, 2024
aaa2e43
Closes #7936
charlesh88 Dec 17, 2024
2ba6bc9
ensure metadata exists on events
scottbell Dec 17, 2024
3af9083
add a random severity
scottbell Dec 17, 2024
36d3197
bump priority for our timeline view
scottbell Dec 17, 2024
72ff0bc
start e2e testing
scottbell Dec 17, 2024
f4ec532
add tests
scottbell Dec 17, 2024
49a106b
Closes #7936
charlesh88 Dec 17, 2024
cba7c7f
remove is selected, add hover event for extended liens
scottbell Dec 17, 2024
2ae1fe1
resolve conflicts
scottbell Dec 17, 2024
b865d8c
Closes #7936
charlesh88 Dec 18, 2024
27af030
Mods to Event Generator and limit provider
charlesh88 Dec 18, 2024
099153b
Closes #7936
charlesh88 Dec 18, 2024
546714b
Closes #7936
charlesh88 Dec 18, 2024
51d9654
fix selection issue
scottbell Dec 18, 2024
68fc317
Merge branch '7936-add-discrete-event-visualization' of github.com:na…
scottbell Dec 18, 2024
531ef3e
good job tests
scottbell Dec 18, 2024
638b03c
spelling
scottbell Dec 18, 2024
601fc33
trigger off selection for extended line hilight
scottbell Dec 18, 2024
8c72e4a
Closes #7936
charlesh88 Dec 18, 2024
65b1f02
Closes #7936
charlesh88 Dec 19, 2024
3c24205
Merge remote-tracking branch 'origin/7936-add-discrete-event-visualiz…
charlesh88 Dec 19, 2024
5312458
Closes #7936
charlesh88 Dec 19, 2024
bb4fea7
Closes #7936
charlesh88 Dec 19, 2024
5b28086
Closes #7936
charlesh88 Dec 19, 2024
9522040
Closes #7936
charlesh88 Dec 19, 2024
6cafa7a
Closes #7936
charlesh88 Dec 19, 2024
cfa2129
Closes #7936
charlesh88 Dec 19, 2024
e6cb940
Closes #7936
charlesh88 Dec 19, 2024
f163034
Closes #7936
charlesh88 Dec 20, 2024
0933d27
Closes #7936
charlesh88 Dec 20, 2024
15b674f
Closes #7936
charlesh88 Dec 20, 2024
0e940b2
lint and simplify playwright locator
scottbell Jan 6, 2025
32a0e15
handle case where we only have events in timeline
scottbell Jan 6, 2025
9e68514
Removed commented out code
shefalijoshi Mar 5, 2025
1ced12d
Remove the priority for imagerytimestripviewprovider and reduce the p…
shefalijoshi Mar 5, 2025
3ad64f0
Refactor code to
shefalijoshi Mar 5, 2025
aa5fa46
Use the tooltips mixin
shefalijoshi Mar 5, 2025
74a5d7e
Merge branch 'master' of https://github.com/nasa/openmct into 7936-ad…
shefalijoshi Mar 6, 2025
157bde8
Fix linting issues
shefalijoshi Mar 6, 2025
8be969f
Increase priority of event timeline view provider
shefalijoshi Mar 6, 2025
14a5c47
Increase the priority of EventtimelineView
shefalijoshi Mar 6, 2025
88b4331
Handle right offset alignment for event timeline view
shefalijoshi Mar 7, 2025
b171f82
Ensure now marker shows up correctly even when there are no plots to …
shefalijoshi Mar 7, 2025
af6f327
remove extraneous class
scottbell Mar 7, 2025
bbae9c6
use this.el instead of hardcoded layout ref
scottbell Mar 7, 2025
55d15ca
add documentation as to why we need the label hint for EventTimelineView
scottbell Mar 7, 2025
8545dde
remove eventemitter and use native custom events
scottbell Mar 7, 2025
2e35212
Decode object path
akhenry Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/tests/functional/planning/ganttChart.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ test.describe('Gantt Chart', () => {
await page.goto(ganttChart.url);

// Assert that the Plan's status is displayed as draft
expect(await page.locator('.u-contents.c-swimlane.is-status--draft').count()).toBe(
expect(await page.locator('.c-swimlane.is-status--draft').count()).toBe(
Object.keys(testPlan1).length
);
});
Expand Down
110 changes: 110 additions & 0 deletions e2e/tests/functional/plugins/event/eventTimelineView.e2e.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*****************************************************************************
* 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.
*****************************************************************************/

import { createDomainObjectWithDefaults, setTimeConductorBounds } from '../../../../appActions.js';
import { expect, test } from '../../../../pluginFixtures.js';

test.describe('Event Timeline View', () => {
let eventTimelineView;
let eventGenerator1;

test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });

eventTimelineView = await createDomainObjectWithDefaults(page, {
type: 'Time Strip'
});

await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
parent: eventTimelineView.uuid
});

eventGenerator1 = await createDomainObjectWithDefaults(page, {
type: 'Event Message Generator',
parent: eventTimelineView.uuid
});

await createDomainObjectWithDefaults(page, {
type: 'Event Message Generator with Acknowledge',
parent: eventTimelineView.uuid
});

await setTimeConductorBounds(page, {
startDate: '2024-01-01',
endDate: '2024-01-01',
startTime: '01:01:00',
endTime: '01:04:00'
});
});

test('Ensure we can build a Time Strip with event', async ({ page }) => {
await page.goto(eventTimelineView.url);

// click on an event
await page
.getByLabel(eventTimelineView.name)
.getByLabel(/PROGRAM ALARM/)
.click();

// click on the event inspector tab
await page.getByRole('tab', { name: 'Event' }).click();

// ensure the event inspector has the the same event
await expect(page.getByText(/PROGRAM ALARM/)).toBeVisible();

// count the event lines
const eventWrappersContainer = page.locator('.c-events-tsv__container');
const eventWrappers = eventWrappersContainer.locator('.c-events-tsv__event-line');
const expectedEventWrappersCount = 25;
await expect(eventWrappers).toHaveCount(expectedEventWrappersCount);

// click on another event
await page
.getByLabel(eventTimelineView.name)
.getByLabel(/pegged/)
.click();

// ensure the tooltip shows up
await expect(
page.getByRole('tooltip').getByText(/pegged on horizontal velocity/)
).toBeVisible();

// and that event appears in the inspector
await expect(
page.getByLabel('Inspector Views').getByText(/pegged on horizontal velocity/)
).toBeVisible();

// turn on extended lines
await page
.getByRole('button', {
name: `Toggle extended event lines overlay for ${eventGenerator1.name}`
})
.click();

// count the extended lines
const overlayLinesContainer = page.locator('.c-timeline__overlay-lines');
const extendedLines = overlayLinesContainer.locator('.c-timeline__event-line--extended');
const expectedCount = 25;
await expect(extendedLines).toHaveCount(expectedCount);
});
});
88 changes: 88 additions & 0 deletions example/eventGenerator/EventLimitProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*****************************************************************************
* 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.
*****************************************************************************/

export const SEVERITY_CSS = {
WATCH: 'is-event--yellow',
WARNING: 'is-event--yellow',
DISTRESS: 'is-event--red',
CRITICAL: 'is-event--red',
SEVERE: 'is-event--purple'
};

const NOMINAL_SEVERITY = {
cssClass: 'is-event--no-style',
name: 'NOMINAL'
};

/**
* @typedef {Object} EvaluationResult
* @property {string} cssClass CSS class information
* @property {string} name a severity name
*/
export default class EventLimitProvider {
constructor(openmct) {
this.openmct = openmct;
}

getLimitEvaluator(domainObject) {
const self = this;

return {
/**
* Evaluates a telemetry datum for severity.
*
* @param {Datum} datum the telemetry datum from the historical or realtime plugin ({@link Datum})
* @param {object} valueMetadata metadata about the telemetry datum
*
* @returns {EvaluationResult} ({@link EvaluationResult})
*/
evaluate: function (datum, valueMetadata) {
// prevent applying the class to the tr, only to td
if (!valueMetadata) {
return;
}

if (datum.severity in SEVERITY_CSS) {
return self.getSeverity(datum, valueMetadata);
}

return NOMINAL_SEVERITY;
}
};
}
getSeverity(datum, valueMetadata) {
if (!valueMetadata) {
return;

Check warning on line 74 in example/eventGenerator/EventLimitProvider.js

View check run for this annotation

Codecov / codecov/patch

example/eventGenerator/EventLimitProvider.js#L74

Added line #L74 was not covered by tests
}

const severityValue = datum.severity;

return {
cssClass: SEVERITY_CSS[severityValue],
name: severityValue
};
}

supportsLimits(domainObject) {
return domainObject.type === 'eventGenerator';
}
}
5 changes: 4 additions & 1 deletion example/eventGenerator/EventMetadataProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class EventMetadataProvider {
{
key: 'message',
name: 'Message',
format: 'string'
format: 'string',
hints: {
label: 0
}
}
]
}
Expand Down
20 changes: 16 additions & 4 deletions example/eventGenerator/EventTelemetryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,35 @@
* Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
*/

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;
}

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 =
Math.random() > 0.4
? Object.keys(SEVERITY_CSS)[
Math.floor(Math.random() * Object.keys(SEVERITY_CSS).length + 1)
]
: undefined;

return {
name,
utc,
message
message,
severity
};
}

Expand All @@ -53,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;

Expand All @@ -78,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;
Expand Down
3 changes: 3 additions & 0 deletions example/eventGenerator/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EventLimitProvider from './EventLimitProvider.js';
import EventMetadataProvider from './EventMetadataProvider.js';
import EventTelemetryProvider from './EventTelemetryProvider.js';
import EventWithAcknowledgeTelemetryProvider from './EventWithAcknowledgeTelemetryProvider.js';
Expand Down Expand Up @@ -54,5 +55,7 @@ export default function EventGeneratorPlugin(options) {
});

openmct.telemetry.addProvider(new EventWithAcknowledgeTelemetryProvider());

openmct.telemetry.addProvider(new EventLimitProvider(openmct));
};
}
4 changes: 3 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
creatable: true
})
);
openmct.install(openmct.plugins.Timeline());
const timeLinePlugin = openmct.plugins.Timeline();
openmct.install(timeLinePlugin);
openmct.install(openmct.plugins.Hyperlink());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(
Expand Down Expand Up @@ -234,6 +235,7 @@
openmct.install(openmct.plugins.Timelist());
openmct.install(openmct.plugins.BarChart());
openmct.install(openmct.plugins.ScatterPlot());
openmct.install(openmct.plugins.EventTimestripPlugin(timeLinePlugin.extendedLinesBus));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to decide if we want this to be the default plugin in core. Traditionally, we don't add new plugins for e2e tests and run the addInitScript to add the plugin at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, will defer this to @charlesh88 or @akhenry

document.addEventListener('DOMContentLoaded', function () {
openmct.start();
});
Expand Down
8 changes: 5 additions & 3 deletions src/api/tooltips/ToolTip.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ import TooltipComponent from './components/TooltipComponent.vue';

class Tooltip extends EventEmitter {
constructor(
{ toolTipText, toolTipLocation, parentElement } = {
{ toolTipText, toolTipLocation, parentElement, cssClasses } = {
tooltipText: '',
toolTipLocation: 'below',
parentElement: null
parentElement: null,
cssClasses: []
}
) {
super();
Expand All @@ -42,7 +43,8 @@ class Tooltip extends EventEmitter {
provide: {
toolTipText,
toolTipLocation,
parentElement
parentElement,
cssClasses
},
template: '<tooltip-component toolTipText="toolTipText"></tooltip-component>'
});
Expand Down
2 changes: 1 addition & 1 deletion src/api/tooltips/ToolTipAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class TooltipAPI {
*/

/**
* Tooltips take an options object that consists of the string, tooltipLocation, and parentElement
* Tooltips take an options object that consists of the string, tooltipLocation, a parentElement, and an array of cssClasses
* @param {TooltipOptions} options
*/
tooltip(options) {
Expand Down
5 changes: 3 additions & 2 deletions src/api/tooltips/components/TooltipComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ at runtime from the About dialog for additional information.
<template>
<div
ref="tooltip-wrapper"
class="c-menu c-tooltip-wrapper"
class="c-tooltip-wrapper"
:class="cssClasses"
:style="toolTipLocationStyle"
role="tooltip"
aria-labelledby="tooltip-text"
Expand All @@ -36,7 +37,7 @@ at runtime from the About dialog for additional information.

<script>
export default {
inject: ['toolTipText', 'toolTipLocation', 'parentElement'],
inject: ['toolTipText', 'toolTipLocation', 'parentElement', 'cssClasses'],
computed: {
toolTipCoordinates() {
return this.parentElement.getBoundingClientRect();
Expand Down
6 changes: 5 additions & 1 deletion src/api/tooltips/components/tooltip-component.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
.c-tooltip-wrapper {
@include menuOuter();
max-width: 200px;
height: auto;
width: auto;
padding: $interiorMargin;
padding: $interiorMargin $interiorMarginLg;
overflow-wrap: break-word;
pointer-events: none;
position: absolute;
z-index: 100;
}

.c-tooltip {
Expand Down
Loading
Loading