Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nasa/openmct.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShefali Joshi <simplyrender@gmail.com>2021-09-18 23:00:16 +0300
committerGitHub <noreply@github.com>2021-09-18 23:00:16 +0300
commitc4b9be18f100681fe9efbc29d9d6a938c4b7a76b (patch)
tree276eab37b03cb210de10f62fed42132fbe028eb4
parenteabdf6cd04403d1376f354ccc5e36323f31041dd (diff)
Support for Bar Graphs (#4221)1.7.8-rc1
* Adds new types for Bar Graphs (#4168) * Adds new spectral test data Co-authored-by: Scott Bell <scott@traclabs.com> Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com> Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com>
-rw-r--r--example/generator/GeneratorMetadataProvider.js103
-rw-r--r--example/generator/SpectralAggregateGeneratorProvider.js86
-rw-r--r--example/generator/SpectralGeneratorProvider.js102
-rw-r--r--example/generator/generatorWorker.js63
-rw-r--r--example/generator/plugin.js35
-rw-r--r--package.json14
-rw-r--r--src/plugins/plot/ColorSwatch.vue170
-rw-r--r--src/plugins/plot/MctPlot.vue2
-rw-r--r--src/plugins/plot/MctTicks.vue2
-rw-r--r--src/plugins/plot/axis/XAxis.vue2
-rw-r--r--src/plugins/plot/axis/YAxis.vue2
-rw-r--r--src/plugins/plot/barGraph/BarGraphCompositionPolicy.js57
-rw-r--r--src/plugins/plot/barGraph/BarGraphCompositionPolicySpec.js346
-rw-r--r--src/plugins/plot/barGraph/BarGraphConstants.js5
-rw-r--r--src/plugins/plot/barGraph/BarGraphPlot.vue293
-rw-r--r--src/plugins/plot/barGraph/BarGraphView.vue286
-rw-r--r--src/plugins/plot/barGraph/BarGraphViewProvider.js76
-rw-r--r--src/plugins/plot/barGraph/inspector/BarGraphInspectorViewProvider.js48
-rw-r--r--src/plugins/plot/barGraph/inspector/BarGraphOptions.vue107
-rw-r--r--src/plugins/plot/barGraph/inspector/Options.vue63
-rw-r--r--src/plugins/plot/chart/MctChart.vue2
-rw-r--r--src/plugins/plot/configuration/ConfigStore.js (renamed from src/plugins/plot/configuration/configStore.js)0
-rw-r--r--src/plugins/plot/configuration/PlotSeries.js2
-rw-r--r--src/plugins/plot/inspector/PlotOptionsBrowse.vue2
-rw-r--r--src/plugins/plot/inspector/PlotOptionsEdit.vue2
-rw-r--r--src/plugins/plot/inspector/PlotOptionsItem.vue23
-rw-r--r--src/plugins/plot/inspector/forms/SeriesForm.vue44
-rw-r--r--src/plugins/plot/lib/ColorPalette.js9
-rw-r--r--src/plugins/plot/plugin.js42
-rw-r--r--src/plugins/plot/pluginSpec.js213
-rw-r--r--src/plugins/plot/spectralPlot/SpectralPlotCompositionPolicy.js36
-rw-r--r--src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js75
-rw-r--r--src/plugins/plot/spectralPlot/SpectralView.vue13
-rw-r--r--src/styles/_legacy-plots.scss6
-rw-r--r--src/styles/plotly.scss5
35 files changed, 2242 insertions, 94 deletions
diff --git a/example/generator/GeneratorMetadataProvider.js b/example/generator/GeneratorMetadataProvider.js
index 179019b0d..112166117 100644
--- a/example/generator/GeneratorMetadataProvider.js
+++ b/example/generator/GeneratorMetadataProvider.js
@@ -28,6 +28,15 @@ define([
domain: 2
}
},
+ {
+ key: "cos",
+ name: "Cosine",
+ unit: "deg",
+ formatString: '%0.2f',
+ hints: {
+ domain: 3
+ }
+ },
// Need to enable "LocalTimeSystem" plugin to make use of this
// {
// key: "local",
@@ -109,6 +118,100 @@ define([
}
}
]
+ },
+ 'example.spectral-generator': {
+ values: [
+ {
+ key: "name",
+ name: "Name",
+ format: "string"
+ },
+ {
+ key: "utc",
+ name: "Time",
+ format: "utc",
+ hints: {
+ domain: 1
+ }
+ },
+ {
+ key: "wavelength",
+ name: "Wavelength",
+ unit: "Hz",
+ formatString: '%0.2f',
+ hints: {
+ domain: 2,
+ spectralAttribute: true
+ }
+ },
+ {
+ key: "cos",
+ name: "Cosine",
+ unit: "deg",
+ formatString: '%0.2f',
+ hints: {
+ range: 2,
+ spectralAttribute: true
+ }
+ }
+ ]
+ },
+ 'example.spectral-aggregate-generator': {
+ values: [
+ {
+ key: "name",
+ name: "Name",
+ format: "string"
+ },
+ {
+ key: "utc",
+ name: "Time",
+ format: "utc",
+ hints: {
+ domain: 1
+ }
+ },
+ {
+ key: "ch1",
+ name: "Channel 1",
+ format: "string",
+ hints: {
+ range: 1
+ }
+ },
+ {
+ key: "ch2",
+ name: "Channel 2",
+ format: "string",
+ hints: {
+ range: 2
+ }
+ },
+ {
+ key: "ch3",
+ name: "Channel 3",
+ format: "string",
+ hints: {
+ range: 3
+ }
+ },
+ {
+ key: "ch4",
+ name: "Channel 4",
+ format: "string",
+ hints: {
+ range: 4
+ }
+ },
+ {
+ key: "ch5",
+ name: "Channel 5",
+ format: "string",
+ hints: {
+ range: 5
+ }
+ }
+ ]
}
};
diff --git a/example/generator/SpectralAggregateGeneratorProvider.js b/example/generator/SpectralAggregateGeneratorProvider.js
new file mode 100644
index 000000000..dcd75c89e
--- /dev/null
+++ b/example/generator/SpectralAggregateGeneratorProvider.js
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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.
+ *****************************************************************************/
+
+define([
+
+], function (
+
+) {
+
+ function SpectralAggregateGeneratorProvider() {
+
+ }
+
+ function pointForTimestamp(timestamp, count, name) {
+ return {
+ name: name,
+ utc: String(Math.floor(timestamp / count) * count),
+ ch1: String(Math.floor(timestamp / count) % 1),
+ ch2: String(Math.floor(timestamp / count) % 2),
+ ch3: String(Math.floor(timestamp / count) % 3),
+ ch4: String(Math.floor(timestamp / count) % 4),
+ ch5: String(Math.floor(timestamp / count) % 5)
+ };
+ }
+
+ SpectralAggregateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
+ return domainObject.type === 'example.spectral-aggregate-generator';
+ };
+
+ SpectralAggregateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
+ var count = 5000;
+
+ var interval = setInterval(function () {
+ var now = Date.now();
+ var datum = pointForTimestamp(now, count, domainObject.name);
+ callback(datum);
+ }, count);
+
+ return function () {
+ clearInterval(interval);
+ };
+ };
+
+ SpectralAggregateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
+ return domainObject.type === 'example.spectral-aggregate-generator';
+ };
+
+ SpectralAggregateGeneratorProvider.prototype.request = function (domainObject, options) {
+ var start = options.start;
+ var end = Math.min(Date.now(), options.end); // no future values
+ var count = 5000;
+ if (options.strategy === 'latest' || options.size === 1) {
+ start = end;
+ }
+
+ var data = [];
+ while (start <= end && data.length < 5000) {
+ data.push(pointForTimestamp(start, count, domainObject.name));
+ start += count;
+ }
+
+ return Promise.resolve(data);
+ };
+
+ return SpectralAggregateGeneratorProvider;
+
+});
diff --git a/example/generator/SpectralGeneratorProvider.js b/example/generator/SpectralGeneratorProvider.js
new file mode 100644
index 000000000..4011bf90d
--- /dev/null
+++ b/example/generator/SpectralGeneratorProvider.js
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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.
+ *****************************************************************************/
+
+define([
+ './WorkerInterface'
+], function (
+ WorkerInterface
+) {
+
+ var REQUEST_DEFAULTS = {
+ amplitude: 1,
+ wavelength: 1,
+ period: 10,
+ offset: 0,
+ dataRateInHz: 1,
+ randomness: 0,
+ phase: 0
+ };
+
+ function SpectralGeneratorProvider() {
+ this.workerInterface = new WorkerInterface();
+ }
+
+ SpectralGeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
+ return domainObject.type === 'example.spectral-generator';
+ };
+
+ SpectralGeneratorProvider.prototype.supportsRequest =
+ SpectralGeneratorProvider.prototype.supportsSubscribe =
+ SpectralGeneratorProvider.prototype.canProvideTelemetry;
+
+ SpectralGeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request = {}) {
+ var props = [
+ 'amplitude',
+ 'wavelength',
+ 'period',
+ 'offset',
+ 'dataRateInHz',
+ 'phase',
+ 'randomness'
+ ];
+
+ var workerRequest = {};
+
+ props.forEach(function (prop) {
+ if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
+ workerRequest[prop] = domainObject.telemetry[prop];
+ }
+
+ if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
+ workerRequest[prop] = request[prop];
+ }
+
+ if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
+ workerRequest[prop] = REQUEST_DEFAULTS[prop];
+ }
+
+ workerRequest[prop] = Number(workerRequest[prop]);
+ });
+
+ workerRequest.name = domainObject.name;
+
+ return workerRequest;
+ };
+
+ SpectralGeneratorProvider.prototype.request = function (domainObject, request) {
+ var workerRequest = this.makeWorkerRequest(domainObject, request);
+ workerRequest.start = request.start;
+ workerRequest.end = request.end;
+ workerRequest.spectra = true;
+
+ return this.workerInterface.request(workerRequest);
+ };
+
+ SpectralGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
+ var workerRequest = this.makeWorkerRequest(domainObject, {});
+ workerRequest.spectra = true;
+
+ return this.workerInterface.subscribe(workerRequest, callback);
+ };
+
+ return SpectralGeneratorProvider;
+});
diff --git a/example/generator/generatorWorker.js b/example/generator/generatorWorker.js
index d0200650c..563dbda69 100644
--- a/example/generator/generatorWorker.js
+++ b/example/generator/generatorWorker.js
@@ -54,23 +54,38 @@
var start = Date.now();
var step = 1000 / data.dataRateInHz;
var nextStep = start - (start % step) + step;
-
- function work(now) {
- while (nextStep < now) {
- self.postMessage({
- id: message.id,
- data: {
- name: data.name,
- utc: nextStep,
- yesterday: nextStep - 60 * 60 * 24 * 1000,
- sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
- cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
- }
- });
- nextStep += step;
- }
-
- return nextStep;
+ let work;
+ if (data.spectra) {
+ work = function (now) {
+ while (nextStep < now) {
+ const messageCopy = Object.create(message);
+ message.data.start = nextStep - (60 * 1000);
+ message.data.end = nextStep;
+ onRequest(messageCopy);
+ nextStep += step;
+ }
+
+ return nextStep;
+ };
+ } else {
+ work = function (now) {
+ while (nextStep < now) {
+ self.postMessage({
+ id: message.id,
+ data: {
+ name: data.name,
+ utc: nextStep,
+ yesterday: nextStep - 60 * 60 * 24 * 1000,
+ sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
+ wavelength: wavelength(start, nextStep),
+ cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
+ }
+ });
+ nextStep += step;
+ }
+
+ return nextStep;
+ };
}
subscriptions[message.id] = work;
@@ -111,13 +126,21 @@
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
+ wavelength: wavelength(start, nextStep),
cos: cos(nextStep, period, amplitude, offset, phase, randomness)
});
}
self.postMessage({
id: message.id,
- data: data
+ data: request.spectra ? {
+ wavelength: data.map((item) => {
+ return item.wavelength;
+ }),
+ cos: data.map((item) => {
+ return item.cos;
+ })
+ } : data
});
}
@@ -131,6 +154,10 @@
* Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
+ function wavelength(start, nextStep) {
+ return (nextStep - start) / 10;
+ }
+
function sendError(error, message) {
self.postMessage({
error: error.name + ': ' + error.message,
diff --git a/example/generator/plugin.js b/example/generator/plugin.js
index a7a027e32..e98572200 100644
--- a/example/generator/plugin.js
+++ b/example/generator/plugin.js
@@ -24,11 +24,15 @@ define([
"./GeneratorProvider",
"./SinewaveLimitProvider",
"./StateGeneratorProvider",
+ "./SpectralGeneratorProvider",
+ "./SpectralAggregateGeneratorProvider",
"./GeneratorMetadataProvider"
], function (
GeneratorProvider,
SinewaveLimitProvider,
StateGeneratorProvider,
+ SpectralGeneratorProvider,
+ SpectralAggregateGeneratorProvider,
GeneratorMetadataProvider
) {
@@ -61,6 +65,37 @@ define([
openmct.telemetry.addProvider(new StateGeneratorProvider());
+ openmct.types.addType("example.spectral-generator", {
+ name: "Spectral Generator",
+ description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
+ cssClass: "icon-generator-telemetry",
+ creatable: true,
+ initialize: function (object) {
+ object.telemetry = {
+ period: 10,
+ amplitude: 1,
+ wavelength: 1,
+ frequency: 1,
+ offset: 0,
+ dataRateInHz: 1,
+ phase: 0,
+ randomness: 0
+ };
+ }
+ });
+ openmct.telemetry.addProvider(new SpectralGeneratorProvider());
+
+ openmct.types.addType("example.spectral-aggregate-generator", {
+ name: "Spectral Aggregate Generator",
+ description: "For development use. Generates example streaming telemetry data using a simple state algorithm.",
+ cssClass: "icon-generator-telemetry",
+ creatable: true,
+ initialize: function (object) {
+ object.telemetry = {};
+ }
+ });
+ openmct.telemetry.addProvider(new SpectralAggregateGeneratorProvider());
+
openmct.types.addType("generator", {
name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
diff --git a/package.json b/package.json
index 35c6b4a14..c8c96e1aa 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,6 @@
"name": "openmct",
"version": "1.7.8-SNAPSHOT",
"description": "The Open MCT core platform",
- "dependencies": {},
"devDependencies": {
"angular": ">=1.8.0",
"angular-route": "1.4.14",
@@ -12,16 +11,9 @@
"copy-webpack-plugin": "^4.5.2",
"cross-env": "^6.0.3",
"css-loader": "^1.0.0",
- "d3-array": "1.2.x",
"d3-axis": "1.0.x",
- "d3-collection": "1.0.x",
- "d3-color": "1.0.x",
- "d3-format": "1.2.x",
- "d3-interpolate": "1.1.x",
"d3-scale": "1.0.x",
"d3-selection": "1.3.x",
- "d3-time": "1.0.x",
- "d3-time-format": "2.1.x",
"eslint": "7.0.0",
"eslint-plugin-vue": "^7.5.0",
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
@@ -41,13 +33,13 @@
"jsdoc": "^3.3.2",
"karma": "6.3.4",
"karma-chrome-launcher": "3.1.0",
- "karma-firefox-launcher": "2.1.1",
"karma-cli": "2.0.0",
"karma-coverage": "2.0.3",
"karma-coverage-istanbul-reporter": "3.0.3",
- "karma-junit-reporter": "2.0.1",
+ "karma-firefox-launcher": "2.1.1",
"karma-html-reporter": "0.2.7",
"karma-jasmine": "4.0.1",
+ "karma-junit-reporter": "2.0.1",
"karma-sourcemap-loader": "0.3.8",
"karma-webpack": "4.0.2",
"location-bar": "^3.0.1",
@@ -62,6 +54,8 @@
"node-bourbon": "^4.2.3",
"node-sass": "^4.14.1",
"painterro": "^1.2.56",
+ "plotly.js-basic-dist": "^2.5.0",
+ "plotly.js-gl2d-dist": "^2.5.0",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
diff --git a/src/plugins/plot/ColorSwatch.vue b/src/plugins/plot/ColorSwatch.vue
new file mode 100644
index 000000000..77b305e92
--- /dev/null
+++ b/src/plugins/plot/ColorSwatch.vue
@@ -0,0 +1,170 @@
+<!--
+ Open MCT, Copyright (c) 2014-2020, 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="u-contents">
+ <ul v-if="canEdit"
+ class="l-inspector-part"
+ >
+ <h2 v-if="heading"
+ :title="heading"
+ >{{ heading }}</h2>
+ <li class="grid-row">
+ <div class="grid-cell label"
+ :title="editTitle"
+ >{{ shortLabel }}</div>
+ <div class="grid-cell value">
+ <div class="c-click-swatch c-click-swatch--menu"
+ @click="toggleSwatch()"
+ >
+ <span class="c-color-swatch"
+ :style="{ background: currentColor }"
+ >
+ </span>
+ </div>
+ <div class="c-palette c-palette--color">
+ <div v-show="swatchActive"
+ class="c-palette__items"
+ >
+ <div v-for="group in colorPaletteGroups"
+ :key="group.id"
+ class="u-contents"
+ >
+ <div v-for="color in group"
+ :key="color.id"
+ class="c-palette__item"
+ :class="{ 'selected': currentColor === color.hexString }"
+ :style="{ background: color.hexString }"
+ @click="setColor(color)"
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ <ul v-else
+ class="l-inspector-part"
+ >
+ <h2 v-if="heading"
+ :title="heading"
+ >{{ heading }}</h2>
+ <li class="grid-row">
+ <div class="grid-cell label"
+ :title="viewTitle"
+ >{{ shortLabel }}</div>
+ <div class="grid-cell value">
+ <span class="c-color-swatch"
+ :style="{
+ 'background': currentColor
+ }"
+ >
+ </span>
+ </div>
+ </li>
+ </ul>
+</div>
+</template>
+
+<script>
+import ColorPalette from './lib/ColorPalette';
+
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ currentColor: {
+ type: String,
+ default() {
+ return '';
+ }
+ },
+ editTitle: {
+ type: String,
+ default() {
+ return 'Set the color.';
+ }
+ },
+ viewTitle: {
+ type: String,
+ default() {
+ return 'The current color.';
+ }
+ },
+ shortLabel: {
+ type: String,
+ default() {
+ return 'Color';
+ }
+ },
+ heading: {
+ type: String,
+ default() {
+ return '';
+ }
+ }
+ },
+ data() {
+ return {
+ swatchActive: false,
+ colorPaletteGroups: [],
+ isEditing: this.openmct.editor.isEditing()
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ mounted() {
+ this.colorPalette = new ColorPalette();
+ this.openmct.editor.on('isEditing', this.setEditState);
+ this.initialize();
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ },
+ methods: {
+ initialize() {
+ const colorPaletteGroups = this.colorPalette.groups();
+ colorPaletteGroups.forEach((group, index) => {
+ let groupId = [];
+ group.forEach(color => {
+ color.hexString = color.asHexString();
+ color.id = `${color.hexString}-${index}`;
+ groupId.push(color.id);
+ });
+ group.id = groupId.join('-');
+ });
+ this.colorPaletteGroups = colorPaletteGroups;
+ },
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ },
+ setColor(chosenColor) {
+ this.$emit('colorSet', chosenColor);
+ },
+ toggleSwatch() {
+ this.swatchActive = !this.swatchActive;
+ }
+ }
+};
+</script>
diff --git a/src/plugins/plot/MctPlot.vue b/src/plugins/plot/MctPlot.vue
index 16917ad22..d801479be 100644
--- a/src/plugins/plot/MctPlot.vue
+++ b/src/plugins/plot/MctPlot.vue
@@ -156,7 +156,7 @@
import eventHelpers from './lib/eventHelpers';
import LinearScale from "./LinearScale";
import PlotConfigurationModel from './configuration/PlotConfigurationModel';
-import configStore from './configuration/configStore';
+import configStore from './configuration/ConfigStore';
import PlotLegend from "./legend/PlotLegend.vue";
import MctTicks from "./MctTicks.vue";
diff --git a/src/plugins/plot/MctTicks.vue b/src/plugins/plot/MctTicks.vue
index ef7388f6e..fa1a0f398 100644
--- a/src/plugins/plot/MctTicks.vue
+++ b/src/plugins/plot/MctTicks.vue
@@ -77,7 +77,7 @@
<script>
import eventHelpers from "./lib/eventHelpers";
import { ticks, getFormattedTicks } from "./tickUtils";
-import configStore from "./configuration/configStore";
+import configStore from "./configuration/ConfigStore";
export default {
inject: ['openmct', 'domainObject'],
diff --git a/src/plugins/plot/axis/XAxis.vue b/src/plugins/plot/axis/XAxis.vue
index 8e414ca8c..93bcdddcc 100644
--- a/src/plugins/plot/axis/XAxis.vue
+++ b/src/plugins/plot/axis/XAxis.vue
@@ -54,7 +54,7 @@
<script>
import MctTicks from "../MctTicks.vue";
import eventHelpers from '../lib/eventHelpers';
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
export default {
components: {
diff --git a/src/plugins/plot/axis/YAxis.vue b/src/plugins/plot/axis/YAxis.vue
index 1a2b1fd97..fec2e3a5f 100644
--- a/src/plugins/plot/axis/YAxis.vue
+++ b/src/plugins/plot/axis/YAxis.vue
@@ -57,7 +57,7 @@
<script>
import MctTicks from "../MctTicks.vue";
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
export default {
components: {
diff --git a/src/plugins/plot/barGraph/BarGraphCompositionPolicy.js b/src/plugins/plot/barGraph/BarGraphCompositionPolicy.js
new file mode 100644
index 000000000..9380ef809
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphCompositionPolicy.js
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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 { BAR_GRAPH_KEY } from './BarGraphConstants';
+
+export default function BarGraphCompositionPolicy(openmct) {
+ function hasAggregateDomainAndRange(metadata) {
+ const rangeValues = metadata.valuesForHints(['range']);
+
+ return rangeValues.length > 0;
+ }
+
+ function hasBarGraphTelemetry(domainObject) {
+ if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) {
+ return false;
+ }
+
+ let metadata = openmct.telemetry.getMetadata(domainObject);
+
+ return metadata.values().length > 0 && hasAggregateDomainAndRange(metadata);
+ }
+
+ function hasNoChildren(parentObject) {
+ return parentObject.composition && parentObject.composition.length < 1;
+ }
+
+ return {
+ allow: function (parent, child) {
+ if ((parent.type === BAR_GRAPH_KEY)
+ && ((child.type !== 'telemetry.plot.overlay') && (hasBarGraphTelemetry(child) === false))
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+ };
+}
diff --git a/src/plugins/plot/barGraph/BarGraphCompositionPolicySpec.js b/src/plugins/plot/barGraph/BarGraphCompositionPolicySpec.js
new file mode 100644
index 000000000..eb6baa509
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphCompositionPolicySpec.js
@@ -0,0 +1,346 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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 BarGraphCompositionPolicy from "./BarGraphCompositionPolicy";
+import { createOpenMct } from "utils/testing";
+
+describe("The bar graph composition policy", () => {
+ let openmct;
+ const mockMetaDataWithNoRangeHints = {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0,
+ valuesForHints: () => {
+ return [];
+ },
+ values: [
+ {
+ "key": "name",
+ "name": "Name",
+ "format": "string"
+ },
+ {
+ "key": "utc",
+ "name": "Time",
+ "format": "utc",
+ "hints": {
+ "domain": 1,
+ "priority": 1
+ },
+ "source": "utc"
+ }
+ ]
+ };
+ const mockMetaDataWithRangeHints = {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0,
+ "wavelength": 0,
+ valuesForHints: () => {
+ return [
+ {
+ "key": "sin",
+ "name": "Sine",
+ "unit": "Hz",
+ "formatString": "%0.2f",
+ "hints": {
+ "range": 1,
+ "priority": 4
+ },
+ "source": "sin"
+ },
+ {
+ "key": "cos",
+ "name": "Cosine",
+ "unit": "deg",
+ "formatString": "%0.2f",
+ "hints": {
+ "range": 2,
+ "priority": 5
+ },
+ "source": "cos"
+ }
+ ];
+ },
+ values: [
+ {
+ "key": "name",
+ "name": "Name",
+ "format": "string",
+ "source": "name",
+ "hints": {
+ "priority": 0
+ }
+ },
+ {
+ "key": "utc",
+ "name": "Time",
+ "format": "utc",
+ "hints": {
+ "domain": 1,
+ "priority": 1
+ },
+ "source": "utc"
+ },
+ {
+ "key": "yesterday",
+ "name": "Yesterday",
+ "format": "utc",
+ "hints": {
+ "domain": 2,
+ "priority": 2
+ },
+ "source": "yesterday"
+ },
+ {
+ "key": "sin",
+ "name": "Sine",
+ "unit": "Hz",
+ "formatString": "%0.2f",
+ "hints": {
+ "range": 1,
+ "spectralAttribute": true
+ },
+ "source": "sin"
+ },
+ {
+ "key": "cos",
+ "name": "Cosine",
+ "unit": "deg",
+ "formatString": "%0.2f",
+ "hints": {
+ "range": 2,
+ "priority": 5
+ },
+ "source": "cos"
+ }
+ ]
+ };
+
+ beforeEach(() => {
+ openmct = createOpenMct();
+ const mockTypeDef = {
+ telemetry: mockMetaDataWithRangeHints
+ };
+ const mockTypeService = {
+ getType: () => {
+ return {
+ typeDef: mockTypeDef
+ };
+ }
+ };
+ openmct.$injector = {
+ get: () => {
+ return mockTypeService;
+ }
+ };
+
+ openmct.telemetry.isTelemetryObject = function (domainObject) {
+ return true;
+ };
+ });
+
+ it("exists", () => {
+ expect(BarGraphCompositionPolicy(openmct).allow).toBeDefined();
+ });
+
+ xit("allow composition for telemetry that provides/supports bar graph meta data", () => {
+ const parent = {
+ "composition": [],
+ "configuration": {},
+ "name": "Some Bar Graph",
+ "type": "telemetry.plot.bar-graph",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const child = {
+ "telemetry": {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0
+ },
+ "name": "Unnamed Sine Wave Generator",
+ "type": "generator",
+ "location": "mine",
+ "modified": 1630399715531,
+ "persisted": 1630399715531,
+ "identifier": {
+ "namespace": "",
+ "key": "21d61f2d-6d2d-4bea-8b0a-7f59fd504c6c"
+ }
+ };
+ expect(BarGraphCompositionPolicy(openmct).allow(parent, child)).toEqual(true);
+ });
+
+ it("allows composition for telemetry that contain at least one range", () => {
+ const mockTypeDef = {
+ telemetry: mockMetaDataWithRangeHints
+ };
+ const mockTypeService = {
+ getType: () => {
+ return {
+ typeDef: mockTypeDef
+ };
+ }
+ };
+ openmct.$injector = {
+ get: () => {
+ return mockTypeService;
+ }
+ };
+ const parent = {
+ "composition": [],
+ "configuration": {},
+ "name": "Some Bar Graph",
+ "type": "telemetry.plot.bar-graph",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const child = {
+ "telemetry": {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0
+ },
+ "name": "Unnamed Sine Wave Generator",
+ "type": "generator",
+ "location": "mine",
+ "modified": 1630399715531,
+ "persisted": 1630399715531,
+ "identifier": {
+ "namespace": "",
+ "key": "21d61f2d-6d2d-4bea-8b0a-7f59fd504c6c"
+ }
+ };
+ expect(BarGraphCompositionPolicy(openmct).allow(parent, child)).toEqual(true);
+ });
+
+ it("disallows composition for telemetry that don't contain any range hints", () => {
+ const mockTypeDef = {
+ telemetry: mockMetaDataWithNoRangeHints
+ };
+ const mockTypeService = {
+ getType: () => {
+ return {
+ typeDef: mockTypeDef
+ };
+ }
+ };
+ openmct.$injector = {
+ get: () => {
+ return mockTypeService;
+ }
+ };
+ const parent = {
+ "composition": [],
+ "configuration": {},
+ "name": "Some Bar Graph",
+ "type": "telemetry.plot.bar-graph",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const child = {
+ "telemetry": {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0
+ },
+ "name": "Unnamed Sine Wave Generator",
+ "type": "generator",
+ "location": "mine",
+ "modified": 1630399715531,
+ "persisted": 1630399715531,
+ "identifier": {
+ "namespace": "",
+ "key": "21d61f2d-6d2d-4bea-8b0a-7f59fd504c6c"
+ }
+ };
+ expect(BarGraphCompositionPolicy(openmct).allow(parent, child)).toEqual(false);
+ });
+
+ it("passthrough for composition for non bar graph plots", () => {
+ const parent = {
+ "composition": [],
+ "configuration": {},
+ "name": "Some Stacked Plot",
+ "type": "telemetry.plot.stacked",
+ "location": "mine",
+ "modified": 1631005183584,
+ "persisted": 1631005183502,
+ "identifier": {
+ "namespace": "",
+ "key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
+ }
+ };
+ const child = {
+ "telemetry": {
+ "period": 10,
+ "amplitude": 1,
+ "offset": 0,
+ "dataRateInHz": 1,
+ "phase": 0,
+ "randomness": 0
+ },
+ "name": "Unnamed Sine Wave Generator",
+ "type": "generator",
+ "location": "mine",
+ "modified": 1630399715531,
+ "persisted": 1630399715531,
+ "identifier": {
+ "namespace": "",
+ "key": "21d61f2d-6d2d-4bea-8b0a-7f59fd504c6c"
+ }
+ };
+ expect(BarGraphCompositionPolicy(openmct).allow(parent, child)).toEqual(true);
+ });
+});
+
diff --git a/src/plugins/plot/barGraph/BarGraphConstants.js b/src/plugins/plot/barGraph/BarGraphConstants.js
new file mode 100644
index 000000000..ba3f78e73
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphConstants.js
@@ -0,0 +1,5 @@
+export const BAR_GRAPH_VIEW = 'bar-graph.view';
+export const BAR_GRAPH_KEY = 'telemetry.plot.bar-graph';
+export const BAR_GRAPH_INSPECTOR_KEY = 'telemetry.plot.bar-graph.inspector';
+export const SUBSCRIBE = 'subscribe';
+export const UNSUBSCRIBE = 'unsubscribe';
diff --git a/src/plugins/plot/barGraph/BarGraphPlot.vue b/src/plugins/plot/barGraph/BarGraphPlot.vue
new file mode 100644
index 000000000..97cfdf9ee
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphPlot.vue
@@ -0,0 +1,293 @@
+<template>
+<div ref="plotWrapper"
+ class="has-local-controls"
+ :class="{ 's-unsynced' : isZoomed }"
+>
+ <div v-if="isZoomed"
+ class="l-state-indicators"
+ >
+ <span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
+ title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
+ ></span>
+ </div>
+ <div ref="plot"
+ class="c-bar-chart"
+ ></div>
+ <div v-if="false"
+ ref="localControl"
+ class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover"
+ >
+ <button v-if="data.length"
+ class="c-button icon-reset"
+ :disabled="!isZoomed"
+ title="Reset pan/zoom"
+ @click="reset()"
+ >
+ </button>
+ </div>
+</div>
+</template>
+<script>
+import Plotly from 'plotly.js-basic-dist';
+import { SUBSCRIBE, UNSUBSCRIBE } from './BarGraphConstants';
+
+const MULTI_AXES_X_PADDING_PERCENT = {
+ LEFT: 8,
+ RIGHT: 94
+};
+
+export default {
+ inject: ['openmct', 'domainObject'],
+ props: {
+ data: {
+ type: Array,
+ default() {
+ return [];
+ }
+ },
+ plotAxisTitle: {
+ type: Object,
+ default() {
+ return {};
+ }
+ }
+ },
+ data() {
+ return {
+ isZoomed: false,
+ primaryYAxisRange: {
+ min: '',
+ max: ''
+ },
+ xAxisRange: {
+ min: '',
+ max: ''
+ }
+ };
+ },
+ watch: {
+ data: {
+ immediate: false,
+ handler: 'updateData'
+ }
+ },
+ mounted() {
+ Plotly.newPlot(this.$refs.plot, Array.from(this.data), this.getLayout(), {
+ responsive: true,
+ displayModeBar: false
+ });
+ this.registerListeners();
+ },
+ beforeDestroy() {
+ this.$refs.plot.removeAllListeners();
+
+ if (this.plotResizeObserver) {
+ this.plotResizeObserver.unobserve(this.$refs.plotWrapper);
+ clearTimeout(this.resizeTimer);
+ }
+
+ if (this.removeBarColorListener) {
+ this.removeBarColorListener();
+ }
+ },
+ methods: {
+ getAxisMinMax(axis) {
+ const min = axis.autoSize
+ ? ''
+ : axis.min;
+ const max = axis.autoSize
+ ? ''
+ : axis.max;
+
+ return {
+ min,
+ max
+ };
+ },
+ getLayout() {
+ const yAxesMeta = this.getYAxisMeta();
+ const primaryYaxis = this.getYaxisLayout(yAxesMeta['1']);
+ const xAxisDomain = this.getXAxisDomain(yAxesMeta);
+
+ return {
+ autosize: true,
+ showlegend: false,
+ textposition: 'auto',
+ font: {
+ family: 'Helvetica Neue, Helvetica, Arial, sans-serif',
+ size: '12px',
+ color: '#666'
+ },
+ xaxis: {
+ domain: xAxisDomain,
+ range: [this.xAxisRange.min, this.xAxisRange.max],
+ title: this.plotAxisTitle.xAxisTitle,
+ automargin: true,
+ fixedrange: true
+ },
+ yaxis: primaryYaxis,
+ margin: {
+ l: 5,
+ r: 5,
+ t: 5,
+ b: 0
+ },
+ paper_bgcolor: 'transparent',
+ plot_bgcolor: 'transparent'
+ };
+ },
+ getYAxisMeta() {
+ const yAxisMeta = {};
+
+ this.data.forEach(d => {
+ const yAxisMetadata = d.yAxisMetadata;
+ const range = '1';
+ const side = 'left';
+ const name = '';
+ const unit = yAxisMetadata.units;
+
+ yAxisMeta[range] = {
+ range,
+ side,
+ name,
+ unit
+ };
+ });
+
+ return yAxisMeta;
+ },
+ getXAxisDomain(yAxisMeta) {
+ let leftPaddingPerc = 0;
+ let rightPaddingPerc = 100;
+ let rightSide = yAxisMeta && Object.values(yAxisMeta).filter((axisMeta => axisMeta.side === 'right'));
+ let leftSide = yAxisMeta && Object.values(yAxisMeta).filter((axisMeta => axisMeta.side === 'left'));
+ if (yAxisMeta && rightSide.length > 1) {
+ rightPaddingPerc = MULTI_AXES_X_PADDING_PERCENT.RIGHT;
+ }
+
+ if (yAxisMeta && leftSide.length > 1) {
+ leftPaddingPerc = MULTI_AXES_X_PADDING_PERCENT.LEFT;
+ }
+
+ return [leftPaddingPerc / 100, rightPaddingPerc / 100];
+ },
+ getYaxisLayout(yAxisMeta) {
+ if (!yAxisMeta) {
+ return {};
+ }
+
+ const { name, range, side = 'left', unit } = yAxisMeta;
+ const title = `${name} ${unit ? '(' + unit + ')' : ''}`;
+ const yaxis = {
+ automargin: true,
+ fixedrange: true,
+ title
+ };
+
+ let yAxistype = this.primaryYAxisRange;
+ if (range === '1') {
+ yaxis.range = [yAxistype.min, yAxistype.max];
+
+ return yaxis;
+ }
+
+ yaxis.range = [yAxistype.min, yAxistype.max];
+ yaxis.anchor = side.toLowerCase() === 'left'
+ ? 'free'
+ : 'x';
+ yaxis.showline = side.toLowerCase() === 'left';
+ yaxis.side = side.toLowerCase();
+ yaxis.overlaying = 'y';
+ yaxis.position = 0.01;
+
+ return yaxis;
+ },
+ registerListeners() {
+ this.$refs.plot.on('plotly_relayout', this.zoom);
+
+ this.removeBarColorListener = this.openmct.objects.observe(
+ this.domainObject,
+ 'configuration.barStyles',
+ this.barColorChanged
+ );
+ this.resizeTimer = false;
+ if (window.ResizeObserver) {
+ this.plotResizeObserver = new ResizeObserver(() => {
+ // debounce and trigger window resize so that plotly can resize the plot
+ clearTimeout(this.resizeTimer);
+ this.resizeTimer = setTimeout(() => {
+ window.dispatchEvent(new Event('resize'));
+ }, 250);
+ });
+ this.plotResizeObserver.observe(this.$refs.plotWrapper);
+ }
+ },
+ reset() {
+ this.updatePlot();
+
+ this.isZoomed = false;
+ this.$emit(SUBSCRIBE);
+ },
+ barColorChanged() {
+ const colors = [];
+ const indices = [];
+ this.data.forEach((item, index) => {
+ const key = item.key;
+ const color = this.domainObject.configuration.barStyles[key] && this.domainObject.configuration.barStyles[key].color;
+ indices.push(index);
+ if (color) {
+ colors.push();
+ } else {
+ colors.push(item.marker.color);
+ }
+ });
+ const plotUpdate = {
+ 'marker.color': colors
+ };
+ Plotly.restyle(this.$refs.plot, plotUpdate, indices);
+ },
+ updateData() {
+ this.updatePlot();
+ },
+ updateLocalControlPosition() {
+ const localControl = this.$refs.localControl;
+ localControl.style.display = 'none';
+
+ const plot = this.$refs.plot;
+ const bgLayer = this.$el.querySelector('.bglayer');
+
+ const plotBoundingRect = plot.getBoundingClientRect();
+ const bgLayerBoundingRect = bgLayer.getBoundingClientRect();
+
+ const top = bgLayerBoundingRect.top - plotBoundingRect.top + 5;
+ const left = bgLayerBoundingRect.left - plotBoundingRect.left + 5;
+
+ localControl.style.top = `${top}px`;
+ localControl.style.left = `${left}px`;
+ localControl.style.display = 'block';
+ },
+ updatePlot() {
+ if (!this.$refs || !this.$refs.plot) {
+ return;
+ }
+
+ Plotly.react(this.$refs.plot, Array.from(this.data), this.getLayout());
+ },
+ zoom(eventData) {
+ const autorange = eventData['xaxis.autorange'];
+ const { autosize } = eventData;
+
+ if (autosize || autorange) {
+ this.isZoomed = false;
+ this.reset();
+
+ return;
+ }
+
+ this.isZoomed = true;
+ this.$emit(UNSUBSCRIBE);
+ }
+ }
+};
+</script>
+
diff --git a/src/plugins/plot/barGraph/BarGraphView.vue b/src/plugins/plot/barGraph/BarGraphView.vue
new file mode 100644
index 000000000..a7fce7675
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphView.vue
@@ -0,0 +1,286 @@
+<!--
+ Open MCT, Copyright (c) 2014-2021, 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>
+<BarGraph ref="barGraph"
+ class="c-plot c-bar-chart-view"
+ :data="trace"
+ :plot-axis-title="plotAxisTitle"
+/>
+</template>
+
+<script>
+import * as SPECTRAL_AGGREGATE from './BarGraphConstants';
+import ColorPalette from '../lib/ColorPalette';
+import BarGraph from './BarGraphPlot.vue';
+import Color from "@/plugins/plot/lib/Color";
+
+export default {
+ components: {
+ BarGraph
+ },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ composition: {},
+ currentDomainObject: this.domainObject,
+ subscriptions: [],
+ telemetryObjects: {},
+ trace: []
+ };
+ },
+ computed: {
+ activeClock() {
+ return this.openmct.time.activeClock;
+ },
+ plotAxisTitle() {
+ const { xAxisMetadata = {}, yAxisMetadata = {} } = this.trace[0] || {};
+ const xAxisUnit = xAxisMetadata.units ? `(${xAxisMetadata.units})` : '';
+ const yAxisUnit = yAxisMetadata.units ? `(${yAxisMetadata.units})` : '';
+
+ return {
+ xAxisTitle: `${xAxisMetadata.name || ''} ${xAxisUnit}`,
+ yAxisTitle: `${yAxisMetadata.name || ''} ${yAxisUnit}`
+ };
+ }
+ },
+ mounted() {
+ this.colorPalette = new ColorPalette();
+ this.loadComposition();
+
+ this.openmct.time.on('bounds', this.refreshData);
+ this.openmct.time.on('clock', this.clockChanged);
+
+ this.$refs.barGraph.$on(SPECTRAL_AGGREGATE.SUBSCRIBE, this.subscribeToAll);
+ this.$refs.barGraph.$on(SPECTRAL_AGGREGATE.UNSUBSCRIBE, this.removeAllSubscriptions);
+
+ this.unobserve = this.openmct.objects.observe(this.currentDomainObject, '*', this.updateDomainObject);
+ },
+ beforeDestroy() {
+ this.$refs.barGraph.$off();
+ this.openmct.time.off('bounds', this.refreshData);
+ this.openmct.time.off('clock', this.clockChanged);
+
+ this.removeAllSubscriptions();
+ this.unobserve();
+
+ if (!this.composition) {
+ return;
+ }
+
+ this.composition.off('add', this.addTelemetryObject);
+ this.composition.off('remove', this.removeTelemetryObject);
+ },
+ methods: {
+ addTelemetryObject(telemetryObject) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ if (!this.domainObject.configuration.barStyles) {
+ this.domainObject.configuration.barStyles = {};
+ }
+
+ // check to see if we've set a bar color
+ if (!this.domainObject.configuration.barStyles[key] || !this.domainObject.configuration.barStyles[key].color) {
+ const color = this.colorPalette.getNextColor().asHexString();
+ this.domainObject.configuration.barStyles[key] = {
+ name: telemetryObject.name,
+ color
+ };
+ this.openmct.objects.mutate(
+ this.domainObject,
+ `configuration.barStyles[${this.key}]`,
+ this.domainObject.configuration.barStyles[key]
+ );
+ } else {
+ let color = this.domainObject.configuration.barStyles[key].color;
+ if (!(color instanceof Color)) {
+ color = Color.fromHexString(color);
+ }
+
+ this.colorPalette.remove(color);
+ }
+
+ this.telemetryObjects[key] = telemetryObject;
+
+ this.requestDataFor(telemetryObject);
+ this.subscribeToObject(telemetryObject);
+ },
+ addTrace(trace, key) {
+ if (!this.trace.length) {
+ this.trace = this.trace.concat([trace]);
+
+ return;
+ }
+
+ let isInTrace = false;
+ const newTrace = this.trace.map((currentTrace, index) => {
+ if (currentTrace.key !== key) {
+ return currentTrace;
+ }
+
+ isInTrace = true;
+
+ return trace;
+ });
+
+ this.trace = isInTrace ? newTrace : newTrace.concat([trace]);
+ },
+ clockChanged() {
+ this.removeAllSubscriptions();
+ this.subscribeToAll();
+ },
+ getAxisMetadata(telemetryObject) {
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ const yAxisMetadata = metadata.valuesForHints(['range'])[0];
+ //Exclude 'name' and 'time' based metadata specifically, from the x-Axis values by using range hints only
+ const xAxisMetadata = metadata.valuesForHints(['range']);
+
+ return {
+ xAxisMetadata,
+ yAxisMetadata
+ };
+ },
+ getOptions(telemetryObject) {
+ const { start, end } = this.openmct.time.bounds();
+
+ return {
+ end,
+ start,
+ startTime: null,
+ spectra: true
+ };
+ },
+ loadComposition() {
+ this.composition = this.openmct.composition.get(this.currentDomainObject);
+
+ if (!this.composition) {
+ this.addTelemetryObject(this.currentDomainObject);
+
+ return;
+ }
+
+ this.composition.on('add', this.addTelemetryObject);
+ this.composition.on('remove', this.removeTelemetryObject);
+ this.composition.load();
+ },
+ refreshData(bounds, isTick) {
+ if (!isTick) {
+ const telemetryObjects = Object.values(this.telemetryObjects);
+ telemetryObjects.forEach(this.requestDataFor);
+ }
+ },
+ removeAllSubscriptions() {
+ this.subscriptions.forEach(subscription => subscription.unsubscribe());
+ this.subscriptions = [];
+ },
+ removeSubscription(key) {
+ const found = this.subscriptions.findIndex(subscription => subscription.key === key);
+ if (found > -1) {
+ this.subscriptions[found].unsubscribe();
+ this.subscriptions.splice(found, 1);
+ }
+ },
+ removeTelemetryObject(identifier) {
+ const key = this.openmct.objects.makeKeyString(identifier);
+ delete this.telemetryObjects[key];
+ if (this.domainObject.configuration.barStyles[key]) {
+ delete this.domainObject.configuration.barStyles[key];
+ }
+
+ this.removeSubscription(key);
+
+ this.trace = this.trace.filter(t => t.key !== key);
+ },
+ processData(telemetryObject, data, axisMetadata) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ if (data.message) {
+ this.openmct.notifications.alert(data.message);
+ }
+
+ let xValues = [];
+ let yValues = [];
+
+ //populate X and Y values for plotly
+ axisMetadata.xAxisMetadata.forEach((metadata) => {
+ xValues.push(metadata.name);
+ if (data[metadata.key]) {
+ //TODO: Format the data?
+ yValues.push(data[metadata.key]);
+ } else {
+ yValues.push('');
+ }
+ });
+
+ const trace = {
+ key,
+ name: telemetryObject.name,
+ x: xValues,
+ y: yValues,
+ text: yValues.map(String),
+ xAxisMetadata: axisMetadata.xAxisMetadata,
+ yAxisMetadata: axisMetadata.yAxisMetadata,
+ type: 'bar',
+ marker: {
+ color: this.domainObject.configuration.barStyles[key].color
+ },
+ hoverinfo: 'skip'
+ };
+
+ this.addTrace(trace, key);
+ },
+ requestDataFor(telemetryObject) {
+ const axisMetadata = this.getAxisMetadata(telemetryObject);
+ this.openmct.telemetry.request(telemetryObject, this.getOptions(telemetryObject))
+ .then(data => {
+ data.forEach((datum) => {
+ this.processData(telemetryObject, datum, axisMetadata);
+ });
+ });
+ },
+ subscribeToObject(telemetryObject) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ this.removeSubscription(key);
+
+ const options = this.getOptions(telemetryObject);
+ const axisMetadata = this.getAxisMetadata(telemetryObject);
+ const unsubscribe = this.openmct.telemetry.subscribe(telemetryObject,
+ data => this.processData(telemetryObject, data, axisMetadata)
+ , options);
+
+ this.subscriptions.push({
+ key,
+ unsubscribe
+ });
+ },
+ subscribeToAll() {
+ const telemetryObjects = Object.values(this.telemetryObjects);
+ telemetryObjects.forEach(this.subscribeToObject);
+ },
+ updateDomainObject(newDomainObject) {
+ this.currentDomainObject = newDomainObject;
+ }
+ }
+};
+
+</script>
diff --git a/src/plugins/plot/barGraph/BarGraphViewProvider.js b/src/plugins/plot/barGraph/BarGraphViewProvider.js
new file mode 100644
index 000000000..9ddbb8c9f
--- /dev/null
+++ b/src/plugins/plot/barGraph/BarGraphViewProvider.js
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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 BarGraphView from './BarGraphView.vue';
+import { BAR_GRAPH_KEY, BAR_GRAPH_VIEW } from './BarGraphConstants';
+import Vue from 'vue';
+
+export default function BarGraphViewProvider(openmct) {
+ function isCompactView(objectPath) {
+ return objectPath.find(object => object.type === 'time-strip');
+ }
+
+ return {
+ key: BAR_GRAPH_VIEW,
+ name: 'Spectral Aggregate Plot',
+ cssClass: 'icon-telemetry',
+ canView(domainObject, objectPath) {
+ return domainObject && domainObject.type === BAR_GRAPH_KEY;
+ },
+
+ canEdit(domainObject, objectPath) {
+ return domainObject && domainObject.type === BAR_GRAPH_KEY;
+ },
+
+ view: function (domainObject, objectPath) {
+ let component;
+
+ return {
+ show: function (element) {
+ let isCompact = isCompactView(objectPath);
+ component = new Vue({
+ el: element,
+ components: {
+ BarGraphView
+ },
+ provide: {
+ openmct,
+ domainObject
+ },
+ data() {
+ return {
+ options: {
+ compact: isCompact
+ }
+ };
+ },
+ template: '<bar-graph-view :options="options"></bar-graph-view>'
+ });
+ },
+ destroy: function () {
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ }
+ };
+}
diff --git a/src/plugins/plot/barGraph/inspector/BarGraphInspectorViewProvider.js b/src/plugins/plot/barGraph/inspector/BarGraphInspectorViewProvider.js
new file mode 100644
index 000000000..6888dc1cd
--- /dev/null
+++ b/src/plugins/plot/barGraph/inspector/BarGraphInspectorViewProvider.js
@@ -0,0 +1,48 @@
+import { BAR_GRAPH_INSPECTOR_KEY, BAR_GRAPH_KEY } from '../BarGraphConstants';
+import Vue from 'vue';
+import Options from "./Options.vue";
+
+export default function BarGraphInspectorViewProvider(openmct) {
+ return {
+ key: BAR_GRAPH_INSPECTOR_KEY,
+ name: 'Bar Graph Inspector View',
+ canView: function (selection) {
+ if (selection.length === 0 || selection[0].length === 0) {
+ return false;
+ }
+
+ let object = selection[0][0].context.item;
+
+ return object
+ && object.type === BAR_GRAPH_KEY;
+ },
+ view: function (selection) {
+ let component;
+
+ return {
+ show: function (element) {
+ component = new Vue({
+ el: element,
+ components: {
+ Options
+ },
+ provide: {
+ openmct,
+ domainObject: selection[0][0].context.item
+ },
+ template: '<options></options>'
+ });
+ },
+ destroy: function () {
+ if (component) {
+ component.$destroy();
+ component = undefined;
+ }
+ }
+ };
+ },
+ priority: function () {
+ return 1;
+ }
+ };
+}
diff --git a/src/plugins/plot/barGraph/inspector/BarGraphOptions.vue b/src/plugins/plot/barGraph/inspector/BarGraphOptions.vue
new file mode 100644
index 000000000..04bd6801d
--- /dev/null
+++ b/src/plugins/plot/barGraph/inspector/BarGraphOptions.vue
@@ -0,0 +1,107 @@
+<!--
+ Open MCT, Copyright (c) 2014-2020, 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>
+<ul>
+ <li class="c-tree__item menus-to-left">
+ <span class="c-disclosure-triangle is-enabled flex-elem"
+ :class="expandedCssClass"
+ @click="expanded = !expanded"
+ >
+ </span>
+ <div>
+ <div class="c-object-label__name">{{ name }}</div>
+ </div>
+ </li>
+ <ColorSwatch v-if="expanded"
+ :current-color="currentColor"
+ title="Manually set the color for this bar graph."
+ edit-title="Manually set the color for this bar graph"
+ view-title="The color for this bar graph."
+ short-label="Color"
+ class="grid-properties"
+ @colorSet="setColor"
+ />
+</ul>
+</template>
+
+<script>
+import ColorSwatch from '../../ColorSwatch.vue';
+
+export default {
+ components: {
+ ColorSwatch
+ },
+ inject: ['openmct', 'domainObject'],
+ props: {
+ item: {
+ type: Object,
+ required: true
+ }
+ },
+ data() {
+ return {
+ currentColor: undefined,
+ name: '',
+ expanded: false
+ };
+ },
+ computed: {
+ expandedCssClass() {
+ return this.expanded ? 'c-disclosure-triangle--expanded' : '';
+ }
+ },
+ watch: {
+ item: {
+ handler() {
+ this.initColor();
+ },
+ deep: true
+ }
+ },
+ mounted() {
+ this.key = this.openmct.objects.makeKeyString(this.item);
+ this.initColor();
+ this.unObserve = this.openmct.objects.observe(this.domainObject, `this.domainObject.configuration.barStyles[${this.key}]`, this.initColor);
+ },
+ beforeDestroy() {
+ if (this.unObserve) {
+ this.unObserve();
+ }
+ },
+ methods: {
+ initColor() {
+ if (this.domainObject.configuration.barStyles && this.domainObject.configuration.barStyles[this.key]) {
+ this.currentColor = this.domainObject.configuration.barStyles[this.key].color;
+ this.name = this.domainObject.configuration.barStyles[this.key].name;
+ }
+ },
+ setColor(chosenColor) {
+ this.currentColor = chosenColor.asHexString();
+ this.openmct.objects.mutate(
+ this.domainObject,
+ `configuration.barStyles[${this.key}].color`,
+ this.currentColor
+ );
+ }
+ }
+};
+</script>
diff --git a/src/plugins/plot/barGraph/inspector/Options.vue b/src/plugins/plot/barGraph/inspector/Options.vue
new file mode 100644
index 000000000..72528fd39
--- /dev/null
+++ b/src/plugins/plot/barGraph/inspector/Options.vue
@@ -0,0 +1,63 @@
+<!--
+ Open MCT, Copyright (c) 2014-2020, 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>
+ <ul class="c-tree">
+ <li v-for="series in domainObject.composition"
+ :key="series.key"
+ >
+ <bar-graph-options :item="series" />
+ </li>
+ </ul>
+</div>
+</template>
+
+<script>
+import BarGraphOptions from "./BarGraphOptions.vue";
+export default {
+ components: {
+ BarGraphOptions
+ },
+ inject: ['openmct', 'domainObject'],
+ data() {
+ return {
+ isEditing: this.openmct.editor.isEditing()
+ };
+ },
+ computed: {
+ canEdit() {
+ return this.isEditing && !this.domainObject.locked;
+ }
+ },
+ mounted() {
+ this.openmct.editor.on('isEditing', this.setEditState);
+ },
+ beforeDestroy() {
+ this.openmct.editor.off('isEditing', this.setEditState);
+ },
+ methods: {
+ setEditState(isEditing) {
+ this.isEditing = isEditing;
+ }
+ }
+};
+</script>
diff --git a/src/plugins/plot/chart/MctChart.vue b/src/plugins/plot/chart/MctChart.vue
index e6db5cd43..a480331f2 100644
--- a/src/plugins/plot/chart/MctChart.vue
+++ b/src/plugins/plot/chart/MctChart.vue
@@ -38,7 +38,7 @@ import MCTChartLineStepAfter from './MCTChartLineStepAfter';
import MCTChartPointSet from './MCTChartPointSet';
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
import LimitLine from "./LimitLine.vue";
import LimitLabel from "./LimitLabel.vue";
diff --git a/src/plugins/plot/configuration/configStore.js b/src/plugins/plot/configuration/ConfigStore.js
index c7e07a82e..c7e07a82e 100644
--- a/src/plugins/plot/configuration/configStore.js
+++ b/src/plugins/plot/configuration/ConfigStore.js
diff --git a/src/plugins/plot/configuration/PlotSeries.js b/src/plugins/plot/configuration/PlotSeries.js
index 7863dcdeb..2d33eeaed 100644
--- a/src/plugins/plot/configuration/PlotSeries.js
+++ b/src/plugins/plot/configuration/PlotSeries.js
@@ -22,7 +22,7 @@
import _ from 'lodash';
import Model from "./Model";
import { MARKER_SHAPES } from '../draw/MarkerShapes';
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
/**
* Plot series handle interpreting telemetry metadata for a single telemetry
diff --git a/src/plugins/plot/inspector/PlotOptionsBrowse.vue b/src/plugins/plot/inspector/PlotOptionsBrowse.vue
index acb3bbbe2..2ab893ff6 100644
--- a/src/plugins/plot/inspector/PlotOptionsBrowse.vue
+++ b/src/plugins/plot/inspector/PlotOptionsBrowse.vue
@@ -115,7 +115,7 @@
<script>
import PlotOptionsItem from "./PlotOptionsItem.vue";
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
import eventHelpers from "../lib/eventHelpers";
export default {
diff --git a/src/plugins/plot/inspector/PlotOptionsEdit.vue b/src/plugins/plot/inspector/PlotOptionsEdit.vue
index 5c251c246..3adcc0fb0 100644
--- a/src/plugins/plot/inspector/PlotOptionsEdit.vue
+++ b/src/plugins/plot/inspector/PlotOptionsEdit.vue
@@ -49,7 +49,7 @@ import SeriesForm from "./forms/SeriesForm.vue";
import YAxisForm from "./forms/YAxisForm.vue";
import LegendForm from "./forms/LegendForm.vue";
import eventHelpers from "../lib/eventHelpers";
-import configStore from "../configuration/configStore";
+import configStore from "../configuration/ConfigStore";
export default {
components: {
diff --git a/src/plugins/plot/inspector/PlotOptionsItem.vue b/src/plugins/plot/inspector/PlotOptionsItem.vue
index 42684ae9d..668900263 100644
--- a/src/plugins/plot/inspector/PlotOptionsItem.vue
+++ b/src/plugins/plot/inspector/PlotOptionsItem.vue
@@ -68,26 +68,23 @@
{{ limitLines ? "Enabled" : "Disabled" }}
</div>
</li>
- <li class="grid-row">
- <div class="grid-cell label"
- title="The plot line and marker color for this series."
- >Color</div>
- <div class="grid-cell value">
- <span class="c-color-swatch"
- :style="{
- 'background': seriesHexColor
- }"
- >
- </span>
- </div>
- </li>
+ <ColorSwatch :current-color="seriesHexColor"
+ edit-title="Manually set the plot line and marker color for this series."
+ view-title="The plot line and marker color for this series."
+ short-label="Color"
+ />
</ul>
</li>
</ul>
</template>
<script>
+import ColorSwatch from "@/plugins/plot/ColorSwatch.vue";
+
export default {
+ components: {
+ ColorSwatch
+ },
inject: ['openmct', 'domainObject', 'path'],
props: {
series: {
diff --git a/src/plugins/plot/inspector/forms/SeriesForm.vue b/src/plugins/plot/inspector/forms/SeriesForm.vue
index 5c36ae20a..6a73bfddd 100644
--- a/src/plugins/plot/inspector/forms/SeriesForm.vue
+++ b/src/plugins/plot/inspector/forms/SeriesForm.vue
@@ -117,49 +117,27 @@
<li v-show="interpolate !== 'none' || markers"
class="grid-row"
>
- <div class="grid-cell label"
- title="Manually set the plot line and marker color for this series."
- >Color</div>
- <div class="grid-cell value">
- <div class="c-click-swatch c-click-swatch--menu"
- @click="toggleSwatch()"
- >
- <span class="c-color-swatch"
- :style="{ background: seriesColorAsHex }"
- >
- </span>
- </div>
- <div class="c-palette c-palette--color">
- <div v-show="swatchActive"
- class="c-palette__items"
- >
- <div v-for="(group, index) in colorPalette"
- :key="index"
- class="u-contents"
- >
- <div v-for="(color, colorIndex) in group"
- :key="colorIndex"
- class="c-palette__item"
- :class="{ 'selected': series.get('color').equalTo(color) }"
- :style="{ background: color.asHexString() }"
- @click="setColor(color)"
- >
- </div>
- </div>
- </div>
- </div>
- </div>
+ <ColorSwatch :current-color="currentColor"
+ edit-title="Manually set the plot line and marker color for this series."
+ view-title="The plot line and marker color for this series."
+ short-label="Color"
+ @colorSet="setColor"
+ />
</li>
</ul>
</ul>
</template>
<script>
+import ColorSwatch from '../../ColorSwatch.vue';
import { MARKER_SHAPES } from "../../draw/MarkerShapes";
import { objectPath, validate, coerce } from "./formUtil";
import _ from 'lodash';
export default {
+ components: {
+ ColorSwatch
+ },
inject: ['openmct', 'domainObject', 'path'],
props: {
series: {
@@ -209,7 +187,7 @@ export default {
expandedCssClass() {
return this.expanded ? 'c-disclosure-triangle--expanded' : '';
},
- seriesColorAsHex() {
+ currentColor() {
return this.series.get('color').asHexString();
}
},
diff --git a/src/plugins/plot/lib/ColorPalette.js b/src/plugins/plot/lib/ColorPalette.js
index 099ecd8ae..7463a4678 100644
--- a/src/plugins/plot/lib/ColorPalette.js
+++ b/src/plugins/plot/lib/ColorPalette.js
@@ -89,13 +89,4 @@ ColorPalette.prototype.getNextColor = function () {
return this.availableColors.shift();
};
-/**
- * @param {number} index the index of the color to return. An index
- * value larger than the size of the index will wrap around.
- * @returns {Color}
- */
-ColorPalette.prototype.getColor = function (index) {
- return this.colors[index % this.colors.length];
-};
-
export default ColorPalette;
diff --git a/src/plugins/plot/plugin.js b/src/plugins/plot/plugin.js
index 091977d17..53908b06c 100644
--- a/src/plugins/plot/plugin.js
+++ b/src/plugins/plot/plugin.js
@@ -19,13 +19,18 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
-
+import { BAR_GRAPH_KEY } from './barGraph/BarGraphConstants';
import PlotViewProvider from './PlotViewProvider';
+import SpectralPlotViewProvider from './spectralPlot/SpectralPlotViewProvider';
+import BarGraphViewProvider from './barGraph/BarGraphViewProvider';
import OverlayPlotViewProvider from './overlayPlot/OverlayPlotViewProvider';
import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
+import BarGraphInspectorViewProvider from './barGraph/inspector/BarGraphInspectorViewProvider';
import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy';
import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy';
+import SpectralPlotCompositionPolicy from './spectralPlot/SpectralPlotCompositionPolicy';
+import BarGraphCompositionPolicy from './barGraph/BarGraphCompositionPolicy';
export default function () {
return function install(openmct) {
@@ -59,13 +64,48 @@ export default function () {
},
priority: 890
});
+ openmct.types.addType('telemetry.plot.spectral', {
+ key: "telemetry.plot.spectral",
+ name: "Spectral Plot",
+ cssClass: "icon-plot-stacked",
+ description: "View Spectra on Y Axes with non-time domain on the X axis. Can be added to Display Layouts.",
+ //Temporarily disabling spectral plots
+ creatable: false,
+ initialize: function (domainObject) {
+ domainObject.composition = [];
+ domainObject.configuration = {};
+ },
+ priority: 890
+ });
+
+ openmct.types.addType(BAR_GRAPH_KEY, {
+ key: BAR_GRAPH_KEY,
+ name: "Bar Graph",
+ cssClass: "icon-bar-chart",
+ description: "View data as a bar graph. Can be added to Display Layouts.",
+ creatable: true,
+ initialize: function (domainObject) {
+ domainObject.composition = [];
+ domainObject.configuration = {
+ plotType: 'bar'
+ };
+ },
+ priority: 891
+ });
openmct.objectViews.addProvider(new StackedPlotViewProvider(openmct));
openmct.objectViews.addProvider(new OverlayPlotViewProvider(openmct));
openmct.objectViews.addProvider(new PlotViewProvider(openmct));
+ openmct.objectViews.addProvider(new SpectralPlotViewProvider(openmct));
+ openmct.objectViews.addProvider(new BarGraphViewProvider(openmct));
+
openmct.inspectorViews.addProvider(new PlotsInspectorViewProvider(openmct));
+ openmct.inspectorViews.addProvider(new BarGraphInspectorViewProvider(openmct));
+
openmct.composition.addPolicy(new OverlayPlotCompositionPolicy(openmct).allow);
openmct.composition.addPolicy(new StackedPlotCompositionPolicy(openmct).allow);
+ openmct.composition.addPolicy(new SpectralPlotCompositionPolicy(openmct).allow);
+ openmct.composition.addPolicy(new BarGraphCompositionPolicy(openmct).allow);
};
}
diff --git a/src/plugins/plot/pluginSpec.js b/src/plugins/plot/pluginSpec.js
index 96dc95b68..15342bcbe 100644
--- a/src/plugins/plot/pluginSpec.js
+++ b/src/plugins/plot/pluginSpec.js
@@ -24,10 +24,12 @@ import {createMouseEvent, createOpenMct, resetApplicationState, spyOnBuiltins} f
import PlotVuePlugin from "./plugin";
import Vue from "vue";
import StackedPlot from "./stackedPlot/StackedPlot.vue";
-import configStore from "./configuration/configStore";
+// import SpectralPlot from "./spectralPlot/SpectralPlot.vue";
+import configStore from "./configuration/ConfigStore";
import EventEmitter from "EventEmitter";
import PlotOptions from "./inspector/PlotOptions.vue";
import PlotConfigurationModel from "./configuration/PlotConfigurationModel";
+import { BAR_GRAPH_VIEW, BAR_GRAPH_KEY } from './barGraph/BarGraphConstants';
describe("the plugin", function () {
let element;
@@ -312,6 +314,38 @@ describe("the plugin", function () {
let plotView = applicableViews.find((viewProvider) => viewProvider.key === "plot-stacked");
expect(plotView).toBeDefined();
});
+
+ it("provides a spectral plot view for objects with telemetry", () => {
+ const testTelemetryObject = {
+ id: "test-object",
+ type: "telemetry.plot.spectral",
+ telemetry: {
+ values: [{
+ key: "a-very-fine-key"
+ }]
+ }
+ };
+
+ const applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
+ let plotView = applicableViews.find((viewProvider) => viewProvider.key === "plot-spectral");
+ expect(plotView).toBeDefined();
+ });
+
+ it("provides a spectral aggregate plot view for objects with telemetry", () => {
+ const testTelemetryObject = {
+ id: "test-object",
+ type: BAR_GRAPH_KEY,
+ telemetry: {
+ values: [{
+ key: "lots-of-aggregate-telemetry"
+ }]
+ }
+ };
+
+ const applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
+ let plotView = applicableViews.find((viewProvider) => viewProvider.key === BAR_GRAPH_VIEW);
+ expect(plotView).toBeDefined();
+ });
});
describe("The single plot view", () => {
@@ -462,6 +496,146 @@ describe("the plugin", function () {
});
});
+ /*
+ * disabling this until we develop the plot view
+ describe("The spectral plot view", () => {
+ let testTelemetryObject;
+ // eslint-disable-next-line no-unused-vars
+ let testTelemetryObject2;
+ // eslint-disable-next-line no-unused-vars
+ let config;
+ let spectralPlotObject;
+ let component;
+ let mockComposition;
+ // eslint-disable-next-line no-unused-vars
+ let plotViewComponentObject;
+
+ beforeEach(() => {
+ const getFunc = openmct.$injector.get;
+ spyOn(openmct.$injector, "get")
+ .withArgs("exportImageService").and.returnValue({
+ exportPNG: () => {},
+ exportJPG: () => {}
+ })
+ .and.callFake(getFunc);
+
+ spectralPlotObject = {
+ identifier: {
+ namespace: "",
+ key: "test-spectral-plot"
+ },
+ type: "telemetry.plot.spectral",
+ name: "Test Spectral Plot"
+ };
+
+ testTelemetryObject = {
+ identifier: {
+ namespace: "",
+ key: "test-object"
+ },
+ type: "test-object",
+ name: "Test Object",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "some-key",
+ name: "Some attribute",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key",
+ name: "Another attribute",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ testTelemetryObject2 = {
+ identifier: {
+ namespace: "",
+ key: "test-object2"
+ },
+ type: "test-object",
+ name: "Test Object2",
+ telemetry: {
+ values: [{
+ key: "utc",
+ format: "utc",
+ name: "Time",
+ hints: {
+ domain: 1
+ }
+ }, {
+ key: "wavelength",
+ name: "Wavelength",
+ hints: {
+ range: 1
+ }
+ }, {
+ key: "some-other-key2",
+ name: "Another attribute2",
+ hints: {
+ range: 2
+ }
+ }]
+ }
+ };
+
+ mockComposition = new EventEmitter();
+ mockComposition.load = () => {
+ mockComposition.emit('add', testTelemetryObject);
+
+ return [testTelemetryObject];
+ };
+
+ spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
+
+ let viewContainer = document.createElement("div");
+ child.append(viewContainer);
+ component = new Vue({
+ el: viewContainer,
+ components: {
+ SpectralPlot
+ },
+ provide: {
+ openmct: openmct,
+ domainObject: spectralPlotObject,
+ composition: openmct.composition.get(spectralPlotObject)
+ },
+ template: "<spectral-plot></spectral-plot>"
+ });
+
+ cleanupFirst.push(() => {
+ component.$destroy();
+ component = undefined;
+ });
+
+ return telemetryPromise
+ .then(Vue.nextTick())
+ .then(() => {
+ plotViewComponentObject = component.$root.$children[0];
+ const configId = openmct.objects.makeKeyString(testTelemetryObject.identifier);
+ config = configStore.get(configId);
+ });
+ });
+
+ it("Renders a collapsed legend for every telemetry", () => {
+ let legend = element.querySelectorAll(".plot-wrapper-collapsed-legend .plot-series-name");
+ expect(legend.length).toBe(1);
+ expect(legend[0].innerHTML).toEqual("Test Object");
+ });
+
+ }); */
+
describe("The stacked plot view", () => {
let testTelemetryObject;
let testTelemetryObject2;
@@ -963,7 +1137,7 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const plotOptionsProperties = editOptionsEl.querySelectorAll(".js-plot-options-edit-properties .grid-row");
- expect(plotOptionsProperties.length).toEqual(7);
+ expect(plotOptionsProperties.length).toEqual(8);
});
it('shows yKeyOptions', () => {
@@ -991,4 +1165,39 @@ describe("the plugin", function () {
});
});
+ describe("the spectral plot", () => {
+ const mockObject = {
+ name: 'A Very Nice Spectral Plot',
+ key: 'telemetry.plot.spectral',
+ creatable: true
+ };
+
+ it('defines a spectral plot object type with the correct key', () => {
+ const objectDef = openmct.types.get('telemetry.plot.spectral').definition;
+ expect(objectDef.key).toEqual(mockObject.key);
+ });
+
+ xit('is creatable', () => {
+ const objectDef = openmct.types.get('telemetry.plot.spectral').definition;
+ expect(objectDef.creatable).toEqual(mockObject.creatable);
+ });
+ });
+
+ describe("the aggregate spectral plot", () => {
+ const mockObject = {
+ name: 'An Even Nicer Aggregate Spectral Plot',
+ key: BAR_GRAPH_KEY,
+ creatable: true
+ };
+
+ it('defines a spectral plot object type with the correct key', () => {
+ const objectDef = openmct.types.get(BAR_GRAPH_KEY).definition;
+ expect(objectDef.key).toEqual(mockObject.key);
+ });
+
+ it('is creatable', () => {
+ const objectDef = openmct.types.get(BAR_GRAPH_KEY).definition;
+ expect(objectDef.creatable).toEqual(mockObject.creatable);
+ });
+ });
});
diff --git a/src/plugins/plot/spectralPlot/SpectralPlotCompositionPolicy.js b/src/plugins/plot/spectralPlot/SpectralPlotCompositionPolicy.js
new file mode 100644
index 000000000..681e0c140
--- /dev/null
+++ b/src/plugins/plot/spectralPlot/SpectralPlotCompositionPolicy.js
@@ -0,0 +1,36 @@
+export default function SpectralPlotCompositionPolicy(openmct) {
+ function hasSpectralDomainAndRange(metadata) {
+ const rangeValues = metadata.valuesForHints(['range']);
+ const domainValues = metadata.valuesForHints(['domain']);
+ const containsSomeSpectralData = domainValues.some(value => {
+ return ((value.key === 'wavelength') || (value.key === 'frequency'));
+ });
+
+ return rangeValues.length > 0
+ && domainValues.length > 0
+ && containsSomeSpectralData;
+ }
+
+ function hasSpectralTelemetry(domainObject) {
+ if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) {
+ return false;
+ }
+
+ let metadata = openmct.telemetry.getMetadata(domainObject);
+
+ return metadata.values().length > 0 && hasSpectralDomainAndRange(metadata);
+ }
+
+ return {
+ allow: function (parent, child) {
+
+ if ((parent.type === 'telemetry.plot.spectral')
+ && ((child.type !== 'telemetry.plot.overlay') && (hasSpectralTelemetry(child) === false))
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+ };
+}
diff --git a/src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js b/src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js
new file mode 100644
index 000000000..0634ddc7c
--- /dev/null
+++ b/src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2021, 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 SpectralView from './SpectralView.vue';
+import Vue from 'vue';
+
+export default function SpectralPlotViewProvider(openmct) {
+ function isCompactView(objectPath) {
+ return objectPath.find(object => object.type === 'time-strip');
+ }
+
+ return {
+ key: 'plot-spectral',
+ name: 'Spectral Plot',
+ cssClass: 'icon-telemetry',
+ canView(domainObject, objectPath) {
+ return domainObject && domainObject.type === 'telemetry.plot.spectral';
+ },
+
+ canEdit(domainObject, objectPath) {
+ return domainObject && domainObject.type === 'telemetry.plot.spectral';
+ },
+
+ view: function (domainObject, objectPath) {
+ let component;
+
+ return {
+ show: function (element) {
+ let isCompact = isCompactView(objectPath);
+ component = new Vue({
+ el: element,
+ components: {
+ SpectralView
+ },
+ provide: {
+ openmct,
+ domainObject
+ },
+ data() {
+ return {
+ options: {
+ compact: isCompact
+ }
+ };
+ },
+ template: '<spectral-view :options="options"></spectral-view>'
+ });
+ },
+ destroy: function () {
+ component.$destroy();
+ component = undefined;
+ }
+ };
+ }
+ };
+}
diff --git a/src/plugins/plot/spectralPlot/SpectralView.vue b/src/plugins/plot/spectralPlot/SpectralView.vue
new file mode 100644
index 000000000..d3345fdda
--- /dev/null
+++ b/src/plugins/plot/spectralPlot/SpectralView.vue
@@ -0,0 +1,13 @@
+<template>
+<div>
+
+</div>
+</template>
+
+<script>
+
+export default {
+ inject: ['openmct', 'domainObject']
+};
+
+</script>
diff --git a/src/styles/_legacy-plots.scss b/src/styles/_legacy-plots.scss
index 7bf59300a..0a95af0bb 100644
--- a/src/styles/_legacy-plots.scss
+++ b/src/styles/_legacy-plots.scss
@@ -729,6 +729,12 @@ mct-plot {
}
+/********************************************************************* BAR CHARTS */
+.c-bar-chart {
+ flex: 1 1 auto;
+ overflow: hidden;
+}
+
/***************** CURSOR GUIDES */
[class*='c-cursor-guide'] {
box-shadow: $shdwCursorGuide;
diff --git a/src/styles/plotly.scss b/src/styles/plotly.scss
index 073bb2d40..dbd13f4fb 100644
--- a/src/styles/plotly.scss
+++ b/src/styles/plotly.scss
@@ -40,6 +40,11 @@
}
}
+ .zerolinelayer {
+ // Hide unneeded plotly-styled horizontal line
+ display: none;
+ }
+
path.xy2-y {
stroke: $colorPlotHash !important; // Using this instead of $colorPlotAreaBorder because that is an rgba
opacity: $opacityPlotHash !important;