mirror of
https://github.com/nasa/openmct.git
synced 2025-07-02 21:20:52 +00:00
Compare commits
2 Commits
plotly-imp
...
code-cover
Author | SHA1 | Date | |
---|---|---|---|
1ac9b4981d | |||
f910ce6c59 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,4 +41,3 @@ npm-debug.log
|
||||
report.*.json
|
||||
|
||||
package-lock.json
|
||||
report.*.json
|
||||
|
@ -82,7 +82,7 @@ module.exports = (config) => {
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
thresholds: {
|
||||
global: {
|
||||
lines: 62
|
||||
lines: 63
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2,6 +2,7 @@
|
||||
"name": "openmct",
|
||||
"version": "1.0.0-snapshot",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"angular": ">=1.8.0",
|
||||
"angular-route": "1.4.14",
|
||||
@ -60,7 +61,6 @@
|
||||
"node-bourbon": "^4.2.3",
|
||||
"node-sass": "^4.9.2",
|
||||
"painterro": "^1.0.35",
|
||||
"plotly.js-dist": "^1.54.5",
|
||||
"printj": "^1.2.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"request": "^2.69.0",
|
||||
|
@ -252,7 +252,6 @@ define([
|
||||
// Plugins that are installed by default
|
||||
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.PlotlyPlot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(PreviewPlugin.default());
|
||||
this.install(LegacyIndicatorsPlugin());
|
||||
|
@ -169,7 +169,6 @@ define([
|
||||
.telemetry
|
||||
.request(this.domainObject, options)
|
||||
.then(function (points) {
|
||||
console.log('PlotSeries: total points', points.length);
|
||||
var newPoints = _(this.data)
|
||||
.concat(points)
|
||||
.sortBy(this.getXVal)
|
||||
@ -396,7 +395,6 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
console.log('PlotSeries: this.data.length', this.data.length);
|
||||
},
|
||||
/**
|
||||
* Updates filters, clears the plot series, unsubscribes and resubscribes
|
||||
|
@ -1,72 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2019, 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 PlotlyViewLayout from './components/PlotlyViewLayout.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default function PlotlyViewProvider(openmct) {
|
||||
return {
|
||||
key: 'plotlyPlot',
|
||||
name: 'Plotly Plot',
|
||||
cssClass: 'icon-plot-overlay',
|
||||
canView: function (domainObject) {
|
||||
return domainObject.type === 'plotlyPlot';
|
||||
},
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'plotlyPlot';
|
||||
},
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
return {
|
||||
show: function (element, isEditing) {
|
||||
component = new Vue({
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject
|
||||
},
|
||||
el: element,
|
||||
components: {
|
||||
PlotlyViewLayout
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEditing
|
||||
}
|
||||
},
|
||||
template: '<plotly-view-layout :isEditing="isEditing"></plotly-view-layout>'
|
||||
});
|
||||
},
|
||||
onEditModeChange: function (isEditing) {
|
||||
component.isEditing = isEditing;
|
||||
},
|
||||
destroy: function (element) {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
priority: function () {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,370 +0,0 @@
|
||||
<template>
|
||||
<div :id="plotId"
|
||||
class="l-view-section"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import BoundedTableRowCollection from '../../telemetryTable/collections/BoundedTableRowCollection';
|
||||
import TelemetryTableRow from '../../telemetryTable/TelemetryTableRow';
|
||||
import TelemetryTableColumn from '../../telemetryTable/TelemetryTableColumn';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
data: function () {
|
||||
return {
|
||||
data: [],
|
||||
bounds: this.openmct.time.bounds(),
|
||||
plotData: {},
|
||||
outstandingRequests: 0,
|
||||
subscriptions: {},
|
||||
plotComposition: undefined,
|
||||
timestampKey: this.openmct.time.timeSystem().key,
|
||||
yAxisLabel: '',
|
||||
plotId: this.openmct.objects.makeKeyString(this.domainObject.identifier)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getContainerHeight: function () {
|
||||
return this.plotElement.parentNode.offsetHeight - 5;
|
||||
},
|
||||
getContainerWidth: function () {
|
||||
return this.plotElement.parentNode.offsetWidth - 5;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.plotElement = document.getElementById(this.plotId);
|
||||
this.openmct.time.on('bounds', this.updateDomain);
|
||||
this.openmct.time.on('bounds', this.updateData);
|
||||
|
||||
this.loadComposition();
|
||||
this.createPlot();
|
||||
|
||||
this.boundedRows = {};
|
||||
this.limitEvaluators = {};
|
||||
this.columnMaps = {};
|
||||
this.drawBuffers = {};
|
||||
this.telemetryObjects = [];
|
||||
this.subscriptions = {};
|
||||
this.boundedRowsUnlisteners = {};
|
||||
this.traceIndices = {};
|
||||
|
||||
this.purgeDataOutsideRange = throttle(this.purgeDataOutsideRange, 5000);
|
||||
},
|
||||
destroyed() {
|
||||
Object.values(this.subscriptions)
|
||||
.forEach(subscription => subscription());
|
||||
|
||||
this.openmct.time.off('bounds', this.updateDomain);
|
||||
this.openmct.time.off('bounds', this.updateData);
|
||||
|
||||
Object.values(this.boundedRowsUnlisteners).forEach((unlisteners) => {
|
||||
unlisteners.forEach(unlistener => unlistener());
|
||||
});
|
||||
|
||||
this.plotComposition.off('add', this.addTelemetryObject);
|
||||
this.plotComposition.off('remove', this.removeTelemetryObject);
|
||||
},
|
||||
methods: {
|
||||
loadComposition() {
|
||||
this.plotComposition = this.openmct.composition.get(this.domainObject);
|
||||
this.plotComposition.on('add', this.addTelemetryObject);
|
||||
this.plotComposition.on('remove', this.removeTelemetryObject);
|
||||
this.plotComposition.load()
|
||||
},
|
||||
addTelemetryObject(telemetryObject) {
|
||||
this.telemetryObjects.push(telemetryObject);
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
this.addTraceForObject(telemetryObject);
|
||||
|
||||
this.requestData(telemetryObject);
|
||||
let subscription = this.subscribe(telemetryObject);
|
||||
this.subscriptions[keyString] = subscription;
|
||||
},
|
||||
removeTelemetryObject(identifier) {
|
||||
const keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
const index = this.telemetryObjects.findIndex(object => identifier.key === object.identifier.key);
|
||||
this.unsubscribe(keyString);
|
||||
this.removeTraceForObject(this.telemetryObjects[index]);
|
||||
this.telemetryObjects = this.telemetryObjects.filter(object => !(identifier.key === object.identifier.key));
|
||||
if (!this.telemetryObjects.length) {
|
||||
Plotly.purge(this.plotElement);
|
||||
this.createPlot();
|
||||
}
|
||||
},
|
||||
updateDomain(bounds, isTick) {
|
||||
let newDomain = {
|
||||
'xaxis.range': [
|
||||
bounds.start,
|
||||
bounds.end
|
||||
]
|
||||
};
|
||||
Plotly.relayout(this.plotElement, newDomain);
|
||||
},
|
||||
updateData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.clearData();
|
||||
this.telemetryObjects.forEach(telemetryObject => this.requestData(telemetryObject));
|
||||
}
|
||||
},
|
||||
clearData() {
|
||||
this.telemetryObjects.forEach(telemetryObject => this.resetTraceForObject(telemetryObject));
|
||||
},
|
||||
requestData(telemetryObject) {
|
||||
return this.openmct.telemetry.request(telemetryObject)
|
||||
.then(telemetryData => {
|
||||
const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let columnMap = this.columnMaps[keyString];
|
||||
let limitEvaluator = this.limitEvaluators[keyString];
|
||||
console.log('Plotly: total points', telemetryData.length);
|
||||
const telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows[keyString].add(telemetryRows);
|
||||
});
|
||||
},
|
||||
subscribe(telemetryObject) {
|
||||
const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let columnMap = this.columnMaps[keyString];
|
||||
let limitEvaluator = this.limitEvaluators[keyString];
|
||||
|
||||
return this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
|
||||
let newRow = new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator);
|
||||
this.boundedRows[keyString].add(newRow);
|
||||
});
|
||||
},
|
||||
unsubscribe(keyString) {
|
||||
this.subscriptions[keyString]();
|
||||
delete this.subscriptions[keyString];
|
||||
},
|
||||
createPlot() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let formatMetadata = {
|
||||
key: timeSystem.key,
|
||||
name: timeSystem.name,
|
||||
format: timeSystem.timeFormat
|
||||
}
|
||||
this.timeFormatter = this.openmct.telemetry.getValueFormatter(formatMetadata);
|
||||
let xRange = [
|
||||
bounds.start,
|
||||
bounds.end
|
||||
];
|
||||
|
||||
let layout = {
|
||||
hovermode: 'compare',
|
||||
hoverdistance: -1,
|
||||
autosize: true,
|
||||
showlegend: true,
|
||||
legend: {
|
||||
y: 1.07,
|
||||
"orientation": "h"
|
||||
},
|
||||
height: this.getContainerHeight,
|
||||
font: {
|
||||
family: "'Helvetica Neue', Helvetica, Arial, sans-serif",
|
||||
size: "12px",
|
||||
color: "#aaa"
|
||||
},
|
||||
xaxis: {
|
||||
title: timeSystem.name,
|
||||
type: 'date',
|
||||
zeroline: false,
|
||||
showgrid: false,
|
||||
range: xRange
|
||||
},
|
||||
yaxis: {
|
||||
zeroline: false,
|
||||
showgrid: false,
|
||||
tickwidth: 3,
|
||||
tickcolor: 'transparent',
|
||||
autorange: true,
|
||||
visible: false
|
||||
},
|
||||
margin: {
|
||||
l: 40,
|
||||
r: 5,
|
||||
b: 40,
|
||||
t: 0
|
||||
},
|
||||
paper_bgcolor: 'transparent',
|
||||
plot_bgcolor: 'transparent'
|
||||
};
|
||||
Plotly.newPlot(
|
||||
this.plotElement,
|
||||
this.data,
|
||||
layout,
|
||||
{
|
||||
displayModeBar: true, // turns off hover-activated toolbar
|
||||
staticPlot: false // turns off hover effects on datapoints
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
purgeDataOutsideRange() {
|
||||
const bounds = this.openmct.time.bounds();
|
||||
const pointsPerData = [];
|
||||
this.data.forEach((data, index) => {
|
||||
const startIndex = this.getStartIndex(data, bounds.start);
|
||||
const endIndex = this.getEndIndex(data, bounds.end);
|
||||
data.x = data.x.slice(startIndex, endIndex);
|
||||
data.y = data.y.slice(startIndex, endIndex);
|
||||
this.$set(this.data, index, data);
|
||||
});
|
||||
|
||||
console.log('Plotly: this.data.length', this.data[0].x.length);
|
||||
},
|
||||
getEndIndex(data, time) {
|
||||
let endIndex = data.x.length - 1;
|
||||
let success = false;
|
||||
let index = endIndex;
|
||||
while(!success && index >= 0) {
|
||||
if (index === 0) {
|
||||
success = true;
|
||||
endIndex = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
success = data.x[index] <= time;
|
||||
if (success) {
|
||||
endIndex = index;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
return endIndex;
|
||||
},
|
||||
getStartIndex(data, time) {
|
||||
let startIndex = 0;
|
||||
data.x.some((x, index) => {
|
||||
const success = x >= time;
|
||||
if (success) {
|
||||
startIndex = index;
|
||||
}
|
||||
|
||||
return success;
|
||||
});
|
||||
|
||||
return Math.max(0, startIndex - 3);
|
||||
},
|
||||
resetTraceForObject(telemetryObject) {
|
||||
this.removeTraceForObject(telemetryObject);
|
||||
this.addTraceForObject(telemetryObject);
|
||||
},
|
||||
removeTraceForObject(telemetryObject) {
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let index = this.traceIndices[keyString];
|
||||
Plotly.deleteTraces(this.plotElement, index);
|
||||
|
||||
delete this.traceIndices[keyString];
|
||||
this.recalculateTraceIndices();
|
||||
|
||||
this.boundedRowsUnlisteners[keyString].forEach((unlistener) => unlistener());
|
||||
},
|
||||
addTraceForObject(telemetryObject) {
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let boundedRows = new BoundedTableRowCollection(this.openmct);
|
||||
this.boundedRows[keyString] = boundedRows;
|
||||
|
||||
this.traceIndices[keyString] = Object.keys(this.traceIndices).length;
|
||||
this.recalculateTraceIndices();
|
||||
|
||||
Plotly.addTraces(this.plotElement, {
|
||||
x: [],
|
||||
y: [],
|
||||
name: telemetryObject.name,
|
||||
type: "scattergl",
|
||||
mode: 'lines+markers',
|
||||
marker: {
|
||||
size: 5
|
||||
},
|
||||
line: {
|
||||
shape: 'linear',
|
||||
width: 1.5
|
||||
}
|
||||
});
|
||||
|
||||
const metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
|
||||
let columnMap = metadataValues.reduce((map, metadatum) => {
|
||||
let column = new TelemetryTableColumn(this.openmct, metadatum);
|
||||
map[metadatum.key] = column;
|
||||
return map;
|
||||
}, {});
|
||||
const limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
const valueFormatter = this.openmct.telemetry.getValueFormatter(this.openmct.telemetry.getMetadata(telemetryObject).valuesForHints(['range'])[0]);
|
||||
let layout_update = {
|
||||
yaxis: {title: valueFormatter.valueMetadata.name, visible: true}
|
||||
};
|
||||
Plotly.update(this.plotElement, {}, layout_update)
|
||||
this.columnMaps[keyString] = columnMap;
|
||||
this.limitEvaluators[keyString] = limitEvaluator;
|
||||
|
||||
let timeSystemKey = this.openmct.time.timeSystem().key;
|
||||
let drawBuffer = {
|
||||
keyString,
|
||||
x: [],
|
||||
y: []
|
||||
};
|
||||
|
||||
this.drawBuffers[keyString] = drawBuffer;
|
||||
|
||||
const addRow = (rows) => {
|
||||
if (rows instanceof Array) {
|
||||
rows.forEach(row => {
|
||||
drawBuffer.x.push(row.datum[timeSystemKey]);
|
||||
drawBuffer.y.push(valueFormatter.format(row.datum));
|
||||
})
|
||||
} else {
|
||||
drawBuffer.x.push(rows.datum[timeSystemKey]);
|
||||
drawBuffer.y.push(valueFormatter.format(rows.datum));
|
||||
}
|
||||
this.scheduleDraw();
|
||||
}
|
||||
|
||||
boundedRows.on('add', addRow);
|
||||
this.boundedRowsUnlisteners[keyString] = [];
|
||||
|
||||
this.boundedRowsUnlisteners[keyString].push(() => {
|
||||
boundedRows.off('add', addRow);
|
||||
})
|
||||
},
|
||||
recalculateTraceIndices() {
|
||||
Object.keys(this.traceIndices).forEach((key, indexOfKey) => {
|
||||
this.traceIndices[key] = indexOfKey;
|
||||
});
|
||||
},
|
||||
scheduleDraw() {
|
||||
if (!this.drawing) {
|
||||
this.drawing = true;
|
||||
requestAnimationFrame(() => {
|
||||
let dataForXAxes = [];
|
||||
let dataForYAxes = [];
|
||||
let traceIndices = [];
|
||||
Object.values(this.drawBuffers).forEach((drawBuffer) => {
|
||||
dataForXAxes.push(drawBuffer.x);
|
||||
dataForYAxes.push(drawBuffer.y);
|
||||
traceIndices.push(this.traceIndices[drawBuffer.keyString]);
|
||||
drawBuffer.x = [];
|
||||
drawBuffer.y = [];
|
||||
});
|
||||
|
||||
this.purgeDataOutsideRange();
|
||||
Plotly.extendTraces(
|
||||
this.plotElement,
|
||||
{
|
||||
x: dataForXAxes,
|
||||
y: dataForYAxes
|
||||
},
|
||||
traceIndices,
|
||||
);
|
||||
|
||||
this.drawing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,3 +0,0 @@
|
||||
.plot svg {
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import PlotlyViewProvider from './PlotlyViewProvider';
|
||||
|
||||
export default function plugin() {
|
||||
return function install(openmct) {
|
||||
openmct.objectViews.addProvider(new PlotlyViewProvider(openmct));
|
||||
|
||||
openmct.types.addType('plotlyPlot', {
|
||||
name: "Plotly Plot",
|
||||
description: "Simple plot rendered by plotly.js",
|
||||
creatable: true,
|
||||
cssClass: 'icon-plot-overlay',
|
||||
initialize: function (domainObject) {
|
||||
domainObject.composition = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
@ -34,7 +34,6 @@ define([
|
||||
'./URLIndicatorPlugin/URLIndicatorPlugin',
|
||||
'./telemetryMean/plugin',
|
||||
'./plot/plugin',
|
||||
'./plotlyPlot/plugin',
|
||||
'./telemetryTable/plugin',
|
||||
'./staticRootPlugin/plugin',
|
||||
'./notebook/plugin',
|
||||
@ -70,7 +69,6 @@ define([
|
||||
URLIndicatorPlugin,
|
||||
TelemetryMean,
|
||||
PlotPlugin,
|
||||
PlotlyPlotPlugin,
|
||||
TelemetryTablePlugin,
|
||||
StaticRootPlugin,
|
||||
Notebook,
|
||||
@ -179,8 +177,8 @@ define([
|
||||
plugins.ExampleImagery = ExampleImagery;
|
||||
plugins.ImageryPlugin = ImageryPlugin;
|
||||
plugins.Plot = PlotPlugin;
|
||||
plugins.PlotlyPlot = PlotlyPlotPlugin.default;
|
||||
plugins.TelemetryTable = TelemetryTablePlugin;
|
||||
|
||||
plugins.SummaryWidget = SummaryWidget;
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
plugins.URLIndicator = URLIndicatorPlugin;
|
||||
|
@ -73,7 +73,6 @@ define(
|
||||
* @private
|
||||
*/
|
||||
addOne(row) {
|
||||
// console.log('SortedTableRowCollection addOne', row);
|
||||
if (this.sortOptions === undefined) {
|
||||
throw 'Please specify sort options';
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
@import "../plugins/folderView/components/list-item.scss";
|
||||
@import "../plugins/folderView/components/list-view.scss";
|
||||
@import "../plugins/imagery/components/imagery-view-layout.scss";
|
||||
@import "../plugins/plotlyPlot/components/plotly.scss";
|
||||
@import "../plugins/telemetryTable/components/table-row.scss";
|
||||
@import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss";
|
||||
@import "../plugins/tabs/components/tabs.scss";
|
||||
|
Reference in New Issue
Block a user