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:
Diffstat (limited to 'src/plugins/charts/scatter/ScatterPlotView.vue')
-rw-r--r--src/plugins/charts/scatter/ScatterPlotView.vue346
1 files changed, 346 insertions, 0 deletions
diff --git a/src/plugins/charts/scatter/ScatterPlotView.vue b/src/plugins/charts/scatter/ScatterPlotView.vue
new file mode 100644
index 000000000..f6a69228e
--- /dev/null
+++ b/src/plugins/charts/scatter/ScatterPlotView.vue
@@ -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.
+-->
+
+<template>
+<ScatterPlotWithUnderlay
+ class="c-plot c-scatter-chart-view"
+ :data="trace"
+ :plot-axis-title="plotAxisTitle"
+ @subscribe="subscribeToAll"
+ @unsubscribe="removeAllSubscriptions"
+/>
+</template>
+
+<script>
+import ScatterPlotWithUnderlay from './ScatterPlotWithUnderlay.vue';
+import _ from 'lodash';
+
+export default {
+ components: {
+ ScatterPlotWithUnderlay
+ },
+ inject: ['openmct', 'domainObject', 'path'],
+ data() {
+ this.telemetryObjects = {};
+ this.telemetryObjectFormats = {};
+ this.valuesByTimestamp = {};
+ this.subscriptions = [];
+
+ return {
+ trace: []
+ };
+ },
+ computed: {
+ 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.setTimeContext();
+ this.loadComposition();
+ this.reloadTelemetry = this.reloadTelemetry.bind(this);
+ this.reloadTelemetry = _.debounce(this.reloadTelemetry, 500);
+ this.unobserve = this.openmct.objects.observe(this.domainObject, 'configuration.axes', this.reloadTelemetry);
+ this.unobserveUnderlayRanges = this.openmct.objects.observe(this.domainObject, 'configuration.ranges', this.reloadTelemetry);
+ },
+ beforeDestroy() {
+ this.stopFollowingTimeContext();
+
+ if (!this.composition) {
+ return;
+ }
+
+ this.removeAllSubscriptions();
+
+ this.composition.off('add', this.addToComposition);
+ this.composition.off('remove', this.removeTelemetryObject);
+ if (this.unobserve) {
+ this.unobserve();
+ }
+
+ if (this.unobserveUnderlayRanges) {
+ this.unobserveUnderlayRanges();
+ }
+ },
+ methods: {
+ setTimeContext() {
+ this.stopFollowingTimeContext();
+
+ this.timeContext = this.openmct.time.getContextForView(this.path);
+ this.followTimeContext();
+
+ },
+ followTimeContext() {
+ this.timeContext.on('bounds', this.reloadTelemetry);
+ },
+ stopFollowingTimeContext() {
+ if (this.timeContext) {
+ this.timeContext.off('bounds', this.reloadTelemetry);
+ }
+ },
+ addToComposition(telemetryObject) {
+ if (Object.values(this.telemetryObjects).length > 0) {
+ this.confirmRemoval(telemetryObject);
+ } else {
+ this.addTelemetryObject(telemetryObject);
+ }
+ },
+ removeFromComposition(telemetryObject) {
+ let composition = this.domainObject.composition.filter(id =>
+ !this.openmct.objects.areIdsEqual(id, telemetryObject.identifier)
+ );
+
+ this.openmct.objects.mutate(this.domainObject, 'composition', composition);
+ },
+ addTelemetryObject(telemetryObject) {
+ // grab information we need from the added telmetry object
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+ this.telemetryObjects[key] = telemetryObject;
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ this.telemetryObjectFormats[key] = this.openmct.telemetry.getFormatMap(metadata);
+ this.getDataForTelemetry(key);
+ },
+ confirmRemoval(telemetryObject) {
+ const dialog = this.openmct.overlays.dialog({
+ iconClass: 'alert',
+ message: 'This action will replace the current telemetry source. Do you want to continue?',
+ buttons: [
+ {
+ label: 'Ok',
+ emphasis: true,
+ callback: () => {
+ const oldTelemetryObject = Object.values(this.telemetryObjects)[0];
+ this.removeFromComposition(oldTelemetryObject);
+ this.removeTelemetryObject(oldTelemetryObject.identifier);
+ this.valuesByTimestamp = {};
+ this.addTelemetryObject(telemetryObject);
+ dialog.dismiss();
+ }
+ },
+ {
+ label: 'Cancel',
+ callback: () => {
+ this.removeFromComposition(telemetryObject);
+ dialog.dismiss();
+ }
+ }
+ ]
+ });
+ },
+ getTelemetryProcessor(keyString) {
+ return (telemetry) => {
+ //Check that telemetry object has not been removed since telemetry was requested.
+ const telemetryObject = this.telemetryObjects[keyString];
+ if (!telemetryObject) {
+ return;
+ }
+
+ telemetry.forEach(datum => {
+ this.addDataToGraph(telemetryObject, datum);
+ });
+ this.updateTrace(telemetryObject);
+ };
+ },
+ getAxisMetadata(telemetryObject) {
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ if (!metadata) {
+ return {};
+ }
+
+ return metadata.valuesForHints(['range']);
+ },
+ loadComposition() {
+ this.composition = this.openmct.composition.get(this.domainObject);
+ this.composition.on('add', this.addToComposition);
+ this.composition.on('remove', this.removeTelemetryObject);
+ this.composition.load();
+ },
+ reloadTelemetry() {
+ this.valuesByTimestamp = {};
+
+ Object.keys(this.telemetryObjects).forEach(key => {
+ this.getDataForTelemetry(key);
+ });
+ },
+ getDataForTelemetry(key) {
+ const telemetryObject = this.telemetryObjects[key];
+ if (!telemetryObject) {
+ return;
+ }
+
+ const telemetryProcessor = this.getTelemetryProcessor(key);
+ const options = this.getOptions();
+ this.openmct.telemetry.request(telemetryObject, options).then(telemetryProcessor);
+ this.subscribeToObject(telemetryObject);
+ },
+ removeTelemetryObject(identifier) {
+ const key = this.openmct.objects.makeKeyString(identifier);
+ if (this.telemetryObjects[key]) {
+ delete this.telemetryObjects[key];
+ }
+
+ if (this.telemetryObjectFormats && this.telemetryObjectFormats[key]) {
+ delete this.telemetryObjectFormats[key];
+ }
+
+ this.removeSubscription(key);
+ },
+ addDataToGraph(telemetryObject, data) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ if (data.message) {
+ this.openmct.notifications.alert(data.message);
+ }
+
+ if (!this.domainObject.configuration.axes.xKey || !this.domainObject.configuration.axes.yKey) {
+ return;
+ }
+
+ const timestamp = this.getTimestampForDatum(data, key, telemetryObject);
+ let valueForTimestamp = this.valuesByTimestamp[timestamp] || {};
+
+ //populate x values
+ let metadataKey = this.domainObject.configuration.axes.xKey;
+ if (data[metadataKey] !== undefined) {
+ valueForTimestamp.x = this.format(key, metadataKey, data);
+ }
+
+ metadataKey = this.domainObject.configuration.axes.yKey;
+ if (data[metadataKey] !== undefined) {
+ valueForTimestamp.y = this.format(key, metadataKey, data);
+ }
+
+ this.valuesByTimestamp[timestamp] = valueForTimestamp;
+ },
+ updateTrace(telemetryObject) {
+ const xAndyValues = Object.values(this.valuesByTimestamp);
+ const xValues = xAndyValues.map(value => value.x);
+ const yValues = xAndyValues.map(value => value.y);
+ const axisMetadata = this.getAxisMetadata(telemetryObject);
+ const xAxisMetadata = axisMetadata.find(metadata => metadata.source === this.domainObject.configuration.axes.xKey);
+ let yAxisMetadata = {};
+ if (this.domainObject.configuration.axes.yKey) {
+ yAxisMetadata = axisMetadata.find(metadata => metadata.source === this.domainObject.configuration.axes.yKey);
+ }
+
+ let trace = {
+ key: this.openmct.objects.makeKeyString(this.domainObject.identifier),
+ name: this.domainObject.name,
+ x: xValues,
+ y: yValues,
+ text: yValues.map(String),
+ xAxisMetadata: xAxisMetadata,
+ yAxisMetadata: yAxisMetadata,
+ type: 'scatter',
+ mode: 'markers',
+ marker: {
+ color: this.domainObject.configuration.styles.color
+ },
+ hoverinfo: 'x+y'
+ };
+
+ if (this.domainObject.configuration.ranges !== undefined && this.domainObject.configuration.ranges.domainMin !== undefined && this.domainObject.configuration.ranges.domainMax !== undefined) {
+ trace.xaxis = {
+ min: this.domainObject.configuration.ranges.domainMin,
+ max: this.domainObject.configuration.ranges.domainMax
+ };
+ }
+
+ if (this.domainObject.configuration.ranges !== undefined && this.domainObject.configuration.ranges.rangeMin !== undefined && this.domainObject.configuration.ranges.rangeMax !== undefined) {
+ trace.yaxis = {
+ min: this.domainObject.configuration.ranges.rangeMin,
+ max: this.domainObject.configuration.ranges.rangeMax
+ };
+ }
+
+ this.trace = [trace];
+ },
+ getTimestampForDatum(datum, key, telemetryObject) {
+ const timeSystemKey = this.timeContext.timeSystem().key;
+ const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
+ let metadataValue = metadata.value(timeSystemKey) || { format: timeSystemKey };
+
+ return this.parse(key, metadataValue.source, datum);
+ },
+ format(telemetryObjectKey, metadataKey, data) {
+ const formats = this.telemetryObjectFormats[telemetryObjectKey];
+
+ return formats[metadataKey].format(data);
+ },
+ parse(telemetryObjectKey, metadataKey, datum) {
+ if (!datum) {
+ return;
+ }
+
+ const formats = this.telemetryObjectFormats[telemetryObjectKey];
+
+ return formats[metadataKey].parse(datum);
+ },
+ getOptions() {
+ const { start, end } = this.timeContext.bounds();
+
+ return {
+ end,
+ start
+ };
+ },
+ subscribeToObject(telemetryObject) {
+ const key = this.openmct.objects.makeKeyString(telemetryObject.identifier);
+
+ this.removeSubscription(key);
+
+ const options = this.getOptions();
+ const unsubscribe = this.openmct.telemetry.subscribe(telemetryObject,
+ data => this.addDataToGraph(telemetryObject, data)
+ , options);
+
+ this.subscriptions.push({
+ key,
+ unsubscribe
+ });
+ },
+ subscribeToAll() {
+ const telemetryObjects = Object.values(this.telemetryObjects);
+ telemetryObjects.forEach(this.subscribeToObject);
+ },
+ 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);
+ }
+ }
+ }
+};
+
+</script>