cherry-pick(#7052): Allow Data Visualization in inspector based on current selection (#7062)

Allow Data Visualization in inspector based on current selection (#7052)

* visualize data in inspector per selection

---------

Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
Andrew Henry 2023-09-18 15:12:18 -07:00 committed by GitHub
parent b923af8705
commit 4f559fdccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 926 additions and 2 deletions

View File

@ -0,0 +1,186 @@
<!--
Open MCT, Copyright (c) 2014-2023, 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.
-->
<template>
<div
class="c-data-visualization-inspect-properties c-inspector__data-pivot c-data-visualization-inspector__flex-column"
>
<div class="c-inspect-properties">
<div class="c-inspect-properties__header">Data Visualization</div>
</div>
<div v-if="isLoading" class="c-inspector__data-pivot-placeholder">Loading...</div>
<div v-else-if="hasDataRanges">
<div
v-if="selectedDataRange !== undefined && hasDescription"
class="c-inspector__data-pivot-coordinates-wrapper"
>
<span
class="c-tree__item__type-icon c-object-label__type-icon"
:class="description.icon"
></span>
<span class="c-inspector__data-pivot-coordinates">
{{ description.text }}
</span>
</div>
<select class="c-inspector__data-pivot-range-selector" v-model="selectedDataRangeIndex">
<option
v-for="(dataRange, index) in descendingDataRanges"
:key="index"
:value="index"
:selected="selectedDataRangeIndex === index"
>
{{ displayDataRange(dataRange) }}
</option>
</select>
</div>
<div
v-else-if="dataRanges && dataRanges.length === 0"
class="c-inspector__data-pivot-placeholder"
>
No data for the current {{ description.name }}
</div>
<div v-else-if="hasPlaceholderText" class="c-inspector__data-pivot-placeholder">
{{ placeholderText }}
</div>
<template v-if="selectedBounds !== undefined">
<NumericData
:bounds="selectedBounds"
:telemetry-keys="plotTelemetryKeys"
:no-numeric-data-text="noNumericDataText"
/>
<Imagery v-if="hasImagery" :bounds="selectedBounds" :telemetry-keys="imageryTelemetryKeys" />
</template>
</div>
</template>
<script>
import NumericData from './NumericData.vue';
import Imagery from './Imagery.vue';
const TIMESTAMP_VIEW_BUFFER = 30 * 1000;
const timestampBufferText = `${TIMESTAMP_VIEW_BUFFER / 1000} seconds`;
export default {
components: {
NumericData,
Imagery
},
inject: ['timeFormatter', 'placeholderText', 'plotOptions', 'imageryOptions'],
props: {
description: {
type: Object,
default: () => {}
},
dataRanges: {
type: Array,
default: () => undefined
},
plotTelemetryKeys: {
type: Array,
default: () => []
},
isLoading: {
type: Boolean,
default: false
}
},
data() {
return {
selectedDataRangeIndex: 0
};
},
computed: {
hasPlaceholderText() {
return this.placeholderText.length > 0;
},
descendingDataRanges() {
return this.dataRanges?.slice().reverse();
},
hasDescription() {
return this.description?.text?.length > 0;
},
hasDataRanges() {
return this.dataRanges?.length > 0;
},
selectedDataRange() {
if (!this.hasDataRanges || this.selectedDataRangeIndex === undefined) {
return;
}
return this.descendingDataRanges[this.selectedDataRangeIndex];
},
selectedBounds() {
if (this.selectedDataRange === undefined) {
return;
}
const { start, end } = this.selectedDataRange.bounds;
if (start === end) {
return {
start: start - TIMESTAMP_VIEW_BUFFER,
end: end + TIMESTAMP_VIEW_BUFFER
};
}
return this.selectedDataRange.bounds;
},
imageryTelemetryKeys() {
return this.imageryOptions?.telemetryKeys;
},
hasImagery() {
return this.imageryTelemetryKeys?.length;
},
noNumericDataText() {
return this.plotOptions?.noNumericDataText;
}
},
methods: {
shortDate(date) {
return date.slice(0, date.indexOf('.')).replace('T', ' ');
},
displayDataRange(dataRange) {
const startTime = dataRange.bounds.start;
const endTime = dataRange.bounds.end;
if (startTime === endTime) {
return `${this.shortDate(this.timeFormatter.format(startTime))} +/- ${timestampBufferText}`;
}
return `${this.shortDate(this.timeFormatter.format(startTime))} - ${this.shortDate(
this.timeFormatter.format(endTime)
)}`;
},
isSelectedDataRange(dataRange, index) {
const selectedDataRange = this.descendingDataRanges[index];
return (
dataRange.bounds.start === selectedDataRange.bounds.start &&
dataRange.bounds.end === selectedDataRange.bounds.end
);
}
}
};
</script>

View File

@ -0,0 +1,127 @@
<!--
Open MCT, Copyright (c) 2014-2023, 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.
-->
<template>
<div
v-if="camerasWithImagesInBounds.length > 0"
class="c-inspect-properties c-inspector__imagery-view"
>
<div class="c-inspect-properties__header">Imagery View</div>
<div
v-for="(camera, index) in camerasWithImagesInBounds"
:key="index"
class="c-imagery-view__camera-image-set"
>
<TelemetryFrame :bounds="bounds" :telemetry-object="camera">
<div class="c-imagery-view__camera-image-list">
<span
v-for="(cameraImage, imageIndex) in camera.imagesInBounds"
:key="imageIndex"
class="c-imagery-view__camera-image"
>
<img :src="cameraImage.value" />
<span class="c-imagery-view__camera-image-timestamp">
{{ cameraImage.timestamp }}
</span>
</span>
</div>
</TelemetryFrame>
</div>
</div>
</template>
<script>
import TelemetryFrame from './TelemetryFrame.vue';
export default {
components: {
TelemetryFrame
},
inject: ['openmct'],
props: {
bounds: {
type: Object,
default: () => {}
},
telemetryKeys: {
type: Array,
required: true
}
},
data() {
return {
camerasWithImagesInBounds: []
};
},
watch: {
bounds() {
this.getCameraImagesInBounds();
}
},
mounted() {
this.getCameraImagesInBounds();
},
methods: {
async getCameraImagesInBounds() {
this.camerasWithImagesInBounds = [];
this.cameraImagesList = [];
const { start, end } = this.bounds;
const cameraObjectPromises = [];
this.telemetryKeys.forEach((telemetryKey) => {
const cameraPromise = this.openmct.objects.get(telemetryKey);
cameraObjectPromises.push(cameraPromise);
});
const cameraObjects = await Promise.all(cameraObjectPromises);
const cameraTelemetryPromises = [];
cameraObjects.forEach((cameraObject) => {
const cameraTelemetryPromise = this.openmct.telemetry.request(cameraObject, {
start,
end
});
cameraTelemetryPromises.push(cameraTelemetryPromise);
});
const cameraImages = await Promise.all(cameraTelemetryPromises);
cameraObjects.forEach((cameraObject, index) => {
cameraObject.images = cameraImages[index];
});
cameraObjects.forEach((cameraObject) => {
if (cameraObject.images.length > 0) {
const imagesInBounds = cameraObject.images.filter((imageDetails) => {
if (!imageDetails.timestamp) {
return false;
}
const timestamp = Date.parse(imageDetails.timestamp);
return timestamp >= start && timestamp <= end;
});
if (imagesInBounds.length > 0) {
cameraObject.imagesInBounds = imagesInBounds;
this.camerasWithImagesInBounds.push(cameraObject);
}
}
});
}
}
};
</script>

View File

@ -0,0 +1,65 @@
<!--
Open MCT, Copyright (c) 2014-2023, 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.
-->
<template>
<div
class="c-inspector__properties c-data-visualization-inspector__properties c-data-visualization-inspector__flex-column"
>
<DataVisualization
:data-ranges="dataRanges"
:plot-telemetry-keys="plotTelemetryKeys"
:description="description"
:is-loading="isLoading"
/>
</div>
</template>
<script>
import DataVisualization from './DataVisualization.vue';
export default {
components: {
DataVisualization
},
inject: ['openmct', 'domainObject'],
props: {
context: {
type: Object,
required: true
}
},
computed: {
dataRanges() {
return this.context.dataRanges;
},
plotTelemetryKeys() {
return this.context.telemetryKeys;
},
description() {
return this.context.description;
},
isLoading() {
return Boolean(this.context.loading);
}
}
};
</script>

View File

@ -0,0 +1,94 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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 mount from 'utils/mount';
import InspectorDataVisualizationComponent from './InspectorDataVisualizationComponent.vue';
export default function InspectorDataVisualizationViewProvider(openmct, configuration) {
const {
type = 'mmgis',
name = 'Data Visualization',
placeholderText = '',
plotOptions,
imageryOptions
} = configuration;
return {
key: 'inspectorDataVisualizationView',
name,
canView(selection) {
const domainObject = selection?.[0]?.[0]?.context?.item;
return domainObject?.type === type;
},
view(selection) {
let _destroy = null;
const context = selection[0][0].context;
const domainObject = context.item;
const dataVisualizationContext = context?.dataVisualization ?? {};
const timeFormatter = openmct.telemetry.getFormatter('iso');
return {
show(element) {
const { destroy } = mount(
{
components: {
InspectorDataVisualization: InspectorDataVisualizationComponent
},
provide: {
openmct,
domainObject,
timeFormatter,
placeholderText,
plotOptions,
imageryOptions
},
data() {
return {
context: dataVisualizationContext
};
},
template: `<InspectorDataVisualization :context="context" />`
},
{
app: openmct.app,
element
}
);
_destroy = destroy;
},
destroy() {
if (_destroy) {
_destroy();
}
},
priority() {
return openmct.priority.HIGH;
}
};
}
};
}

View File

@ -0,0 +1,165 @@
<!--
Open MCT, Copyright (c) 2014-2023, 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.
-->
<template>
<div class="c-inspector__numeric-data">
<div class="c-inspect-properties">
<div class="c-inspect-properties__header">Numeric Data</div>
</div>
<div ref="numericDataView"></div>
<div v-if="!hasNumericData">
{{ noNumericDataText }}
</div>
</div>
</template>
<script>
import mount from 'utils/mount';
import TelemetryFrame from './TelemetryFrame.vue';
import Plot from '../plot/Plot.vue';
export default {
inject: ['openmct', 'domainObject', 'timeFormatter'],
props: {
bounds: {
type: Object,
required: true
},
telemetryKeys: {
type: Array,
default: () => []
},
noNumericDataText: {
type: String,
default: 'No Numeric Data to display.'
}
},
data() {
return {
plotObjects: []
};
},
computed: {
hasNumericData() {
return this.plotObjects.length > 0;
}
},
watch: {
telemetryKeys: {
handler() {
this.renderNumericData();
},
deep: true
},
bounds: {
handler() {
this.renderNumericData();
},
deep: true
}
},
mounted() {
this.renderNumericData();
},
beforeUnmount() {
this.clearPlots();
},
methods: {
renderNumericData() {
this.clearPlots();
this.unregisterTimeContextList = [];
this.elementsList = [];
this.componentsList = [];
this.telemetryKeys.forEach(async (telemetryKey) => {
const plotObject = await this.openmct.objects.get(telemetryKey);
this.plotObjects.push(plotObject);
this.unregisterTimeContextList.push(this.setIndependentTimeContextForComponent(plotObject));
this.renderPlot(plotObject);
});
},
setIndependentTimeContextForComponent(plotObject) {
const keyString = this.openmct.objects.makeKeyString(plotObject.identifier);
// get an independent time context for object
this.openmct.time.getContextForView([plotObject]);
// set the time context of the object to the selected time range
return this.openmct.time.addIndependentContext(keyString, this.bounds);
},
renderPlot(plotObject) {
const { vNode, destroy } = mount(
{
components: {
TelemetryFrame,
Plot
},
provide: {
openmct: this.openmct,
path: [plotObject]
},
data() {
return {
plotObject,
bounds: this.bounds
};
},
template: `<TelemetryFrame
:bounds="bounds"
:telemetry-object="plotObject"
>
<Plot />
</TelemetryFrame>`
},
{
app: this.openmct.app
}
);
this.componentsList.push(destroy);
this.elementsList.push(vNode.el);
this.$refs.numericDataView.append(vNode.el);
},
clearPlots() {
if (this.componentsList?.length) {
this.componentsList.forEach((destroy) => destroy());
delete this.componentsList;
}
if (this.elementsList?.length) {
this.elementsList.forEach((element) => element.remove());
delete this.elementsList;
}
if (this.plotObjects?.length) {
this.plotObjects = [];
}
if (this.unregisterTimeContextList?.length) {
this.unregisterTimeContextList.forEach((unregisterTimeContext) => unregisterTimeContext());
delete this.unregisterTimeContextList;
}
}
}
};
</script>

View File

@ -0,0 +1,124 @@
<!--
Open MCT, Copyright (c) 2014-2023, 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.
-->
<template>
<div class="c-telemetry-frame">
<div class="c-telemetry-frame__title-bar">
<span class="c-telemetry-frame__title">
<span class="c-telemetry-frame__title-icon icon-telemetry"></span>
<span class="title-text">{{ telemetryObject.name }}</span>
</span>
<button
ref="menu-button"
title="More options"
class="l-browse-bar__actions c-icon-button icon-3-dots"
@click="toggleMenu"
></button>
</div>
<div
v-if="showMenu"
class="c-menu c-menu__inspector-telemetry-options"
aria-label="Telemetry Options"
@blur="showMenu = false"
>
<ul>
<li
v-if="telemetryObject.type === 'yamcs.telemetry'"
role="menuitem"
title="View Full Screen"
class="icon-eye-open"
@click="previewTelemetry"
>
View Full Screen
</li>
<li
role="menuitem"
title="Open in a new browser tab"
class="icon-new-window"
@click="openInNewTab"
>
Open In New Tab
</li>
</ul>
</div>
<slot></slot>
</div>
</template>
<script>
export default {
inject: ['openmct'],
provide() {
return {
domainObject: this.telemetryObject
};
},
props: {
bounds: {
type: Object,
default: () => {}
},
telemetryObject: {
type: Object,
default: () => {}
}
},
data() {
return {
showMenu: false
};
},
methods: {
toggleMenu() {
this.showMenu = !this.showMenu;
},
async getTelemetryPath() {
let sourceTelem;
if (this.telemetryObject.type === 'yamcs.telemetry') {
sourceTelem = this.openmct.objects.makeKeyString(this.telemetryObject.identifier);
} else if (this.telemetryObject.type === 'yamcs.image') {
sourceTelem = this.openmct.objects.makeKeyString(this.telemetryObject.identifier);
}
const telemetryPath = await this.openmct.objects.getOriginalPath(sourceTelem);
return telemetryPath;
},
async openInNewTab() {
const telemetryPath = await this.getTelemetryPath();
const sourceTelemObject = telemetryPath[0];
const timeBounds = this.bounds;
const urlParams = {
'tc.startBound': timeBounds?.start,
'tc.endBound': timeBounds?.end,
'tc.mode': 'fixed'
};
const newTabAction = this.openmct.actions.getAction('newTab');
newTabAction.invoke([sourceTelemObject], urlParams);
this.showMenu = false;
},
previewTelemetry() {
const previewAction = this.openmct.actions.getAction('preview');
previewAction.invoke([this.telemetryObject]);
this.showMenu = false;
}
}
};
</script>

View File

@ -0,0 +1,128 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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.
*****************************************************************************/
//InspectorDataVisualizationComponent
.c-data-visualization-inspector__flex-column {
display: flex;
flex-direction: column;
}
.c-data-visualization-inspector__flex-row {
display: flex;
flex-direction: row;
}
.c-data-visualization-inspect-properties + .c-data-visualization-inspect-properties {
margin-top: 10px;
}
// DataVisualization
.c-inspector__data-pivot-placeholder {
margin-top: 8px;
}
.c-inspector__data-pivot-coordinates-wrapper {
display: flex;
align-items: center;
margin-top: 10px;
}
.c-inspector__data-pivot-coordinates {
margin-left: 6px;
text-transform: capitalize;
}
.c-inspector__data-pivot-range-selector {
margin: 10px auto;
height: 25px;
max-width: 100%;
}
.c-inspector__imagery-view {
margin-top: 10px;
}
.c-imagery-view__camera-image-set {
grid-column: 1/3;
}
.c-imagery-view__camera-image-list {
display: grid;
grid-auto-flow: column;
grid-gap: 10px;
grid-auto-columns: min-content;
overflow: auto;
white-space: nowrap;
margin-top: 5px;
}
.c-imagery-view__camera-image {
display: inline-block;
}
.c-imagery-view__camera-image img {
width: 70px;
height: 70px;
}
.c-imagery-view__camera-image-timestamp {
white-space: break-spaces;
}
// Telemetry Frame
.c-telemetry-frame {
margin: 8px 0px;
&__title-bar {
display: flex;
align-items: center;
margin: 6px 0px;
}
&__title {
flex: 1;
font-size: 1.2em;
}
&__title-icon {
margin-right: 4px;
}
}
.c-telemetry-frame .c-menu {
position: absolute;
right: 0px;
}
.c-inspector__data-pivot .c-plot {
position: relative;
min-height: 150px;
max-height: 200px;
}
.c-inspector__data-pivot .c-plot .c-plot--stacked-container {
min-height: 150px;
}
.c-inspector__numeric-data .c-inspect-properties__header {
margin-bottom: 10px;
}

View File

@ -0,0 +1,31 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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 InspectorDataVisualizationViewProvider from './InspectorDataVisualizationViewProvider';
export default function (options) {
return function (openmct) {
openmct.inspectorViews.addProvider(
new InspectorDataVisualizationViewProvider(openmct, options)
);
};
}

View File

@ -83,7 +83,8 @@ define([
'./timelist/plugin',
'./faultManagement/FaultManagementPlugin',
'../../example/exampleTags/plugin',
'./inspectorViews/plugin'
'./inspectorViews/plugin',
'./inspectorDataVisualization/plugin'
], function (
_,
UTCTimeSystem,
@ -147,7 +148,8 @@ define([
TimeList,
FaultManagementPlugin,
ExampleTags,
InspectorViews
InspectorViews,
InspectorDataVisualization
) {
const plugins = {};
@ -232,6 +234,7 @@ define([
plugins.Gauge = GaugePlugin.default;
plugins.Timelist = TimeList.default;
plugins.InspectorViews = InspectorViews.default;
plugins.InspectorDataVisualization = InspectorDataVisualization.default;
return plugins;
});

View File

@ -58,6 +58,7 @@
@import '../plugins/gauge/gauge.scss';
@import '../plugins/faultManagement/fault-manager.scss';
@import '../plugins/operatorStatus/operator-status';
@import '../plugins/inspectorDataVisualization/inspector-data-visualization.scss';
#splash-screen {
display: none;