Compare commits
	
		
			18 Commits
		
	
	
		
			refactor-c
			...
			mct4102_v2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6ac9eebab3 | ||
| 
						 | 
					04ece72679 | ||
| 
						 | 
					80b4ccd562 | ||
| 
						 | 
					880c3a8850 | ||
| 
						 | 
					72b609f527 | ||
| 
						 | 
					7c883aea96 | ||
| 
						 | 
					a7ed38b7ee | ||
| 
						 | 
					1049ac38ff | ||
| 
						 | 
					2f435facd4 | ||
| 
						 | 
					121eeac27c | ||
| 
						 | 
					b67b13912e | ||
| 
						 | 
					0cb884563a | ||
| 
						 | 
					4942e27a89 | ||
| 
						 | 
					e84a3bfe97 | ||
| 
						 | 
					fdc1499339 | ||
| 
						 | 
					ae224ae567 | ||
| 
						 | 
					48322d46fd | ||
| 
						 | 
					7616610f2c | 
@@ -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
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								example/generator/SpectralAggregateGeneratorProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								example/generator/SpectralAggregateGeneratorProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										104
									
								
								example/generator/SpectralGeneratorProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								example/generator/SpectralGeneratorProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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'
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        request = request || {};
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
});
 | 
			
		||||
@@ -54,23 +54,39 @@
 | 
			
		||||
        var start = Date.now();
 | 
			
		||||
        var step = 1000 / data.dataRateInHz;
 | 
			
		||||
        var nextStep = start - (start % step) + step;
 | 
			
		||||
        let work;
 | 
			
		||||
        console.log('dataRate', data);
 | 
			
		||||
        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;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
            };
 | 
			
		||||
        } 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;
 | 
			
		||||
                return nextStep;
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions[message.id] = work;
 | 
			
		||||
@@ -111,13 +127,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 +155,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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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.",
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
@@ -41,13 +40,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 +61,7 @@
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.14.1",
 | 
			
		||||
    "painterro": "^1.2.56",
 | 
			
		||||
    "plotly.js": "^2.5.0",
 | 
			
		||||
    "printj": "^1.2.1",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "request": "^2.69.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,17 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import { SPECTRAL_AGGREGATE_KEY } from './spectralAggregatePlot/SpectralAggregateConstants';
 | 
			
		||||
import PlotViewProvider from './PlotViewProvider';
 | 
			
		||||
import SpectralPlotViewProvider from './spectralPlot/SpectralPlotViewProvider';
 | 
			
		||||
import SpectralAggregatePlotViewProvider from './spectralAggregatePlot/SpectralAggregatePlotViewProvider';
 | 
			
		||||
import OverlayPlotViewProvider from './overlayPlot/OverlayPlotViewProvider';
 | 
			
		||||
import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
 | 
			
		||||
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
 | 
			
		||||
import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy';
 | 
			
		||||
import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy';
 | 
			
		||||
import SpectralPlotCompositionPolicy from './spectralPlot/SpectralPlotCompositionPolicy';
 | 
			
		||||
import SpectralAggregatePlotCompositionPolicy from './spectralAggregatePlot/SpectralAggregatePlotCompositionPolicy';
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    return function install(openmct) {
 | 
			
		||||
@@ -59,13 +63,46 @@ 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.",
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            initialize: function (domainObject) {
 | 
			
		||||
                domainObject.composition = [];
 | 
			
		||||
                domainObject.configuration = {};
 | 
			
		||||
            },
 | 
			
		||||
            priority: 890
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        openmct.types.addType(SPECTRAL_AGGREGATE_KEY, {
 | 
			
		||||
            key: SPECTRAL_AGGREGATE_KEY,
 | 
			
		||||
            name: "Spectral Aggregate 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.",
 | 
			
		||||
            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 SpectralAggregatePlotViewProvider(openmct));
 | 
			
		||||
 | 
			
		||||
        openmct.inspectorViews.addProvider(new PlotsInspectorViewProvider(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 SpectralAggregatePlotCompositionPolicy(openmct).allow);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 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 { SPECTRAL_AGGREGATE_VIEW, SPECTRAL_AGGREGATE_KEY } from './spectralAggregatePlot/SpectralAggregateConstants';
 | 
			
		||||
 | 
			
		||||
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: SPECTRAL_AGGREGATE_KEY,
 | 
			
		||||
                telemetry: {
 | 
			
		||||
                    values: [{
 | 
			
		||||
                        key: "lots-of-aggregate-telemetry"
 | 
			
		||||
                    }]
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const applicableViews = openmct.objectViews.get(testTelemetryObject, mockObjectPath);
 | 
			
		||||
            let plotView = applicableViews.find((viewProvider) => viewProvider.key === SPECTRAL_AGGREGATE_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;
 | 
			
		||||
@@ -990,4 +1164,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);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('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: SPECTRAL_AGGREGATE_KEY,
 | 
			
		||||
            creatable: true
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        it('defines a spectral plot object type with the correct key', () => {
 | 
			
		||||
            const objectDef = openmct.types.get(SPECTRAL_AGGREGATE_KEY).definition;
 | 
			
		||||
            expect(objectDef.key).toEqual(mockObject.key);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('is creatable', () => {
 | 
			
		||||
            const objectDef = openmct.types.get(SPECTRAL_AGGREGATE_KEY).definition;
 | 
			
		||||
            expect(objectDef.creatable).toEqual(mockObject.creatable);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
export const CONFIGURATION_CHANGED = 'configurationChanged';
 | 
			
		||||
export const HOVER_VALUES_CHANGED = 'hoverValuesChanged';
 | 
			
		||||
export const HOVER_VALUES_CLEARED = 'hoverValuesCleared';
 | 
			
		||||
export const LEGEND_EXPANDED = 'plot-legends-expanded';
 | 
			
		||||
export const REALTIME_POLL_INTERVAL_IN_MS = 5000;
 | 
			
		||||
 | 
			
		||||
export const SPECTRAL_AGGREGATE_VIEW = 'plot.spectral.aggregate.view';
 | 
			
		||||
export const SPECTRAL_AGGREGATE_KEY = 'telemetry.plot.spectral.aggregate';
 | 
			
		||||
export const SUBSCRIBE = 'subscribe';
 | 
			
		||||
export const UNSUBSCRIBE = 'unsubscribe';
 | 
			
		||||
							
								
								
									
										289
									
								
								src/plugins/plot/spectralAggregatePlot/SpectralAggregatePlot.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/plugins/plot/spectralAggregatePlot/SpectralAggregatePlot.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
<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-spectral-aggregate-plot__plot"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div 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/dist/plotly';
 | 
			
		||||
import { HOVER_VALUES_CLEARED, HOVER_VALUES_CHANGED, SUBSCRIBE, UNSUBSCRIBE } from './SpectralAggregateConstants';
 | 
			
		||||
 | 
			
		||||
const PLOT_PADDING_IN_PERCENT = 1;
 | 
			
		||||
const MULTI_AXES_X_PADDING_PERCENT = {
 | 
			
		||||
    LEFT: 8,
 | 
			
		||||
    RIGHT: 94
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
    props: [
 | 
			
		||||
        'data',
 | 
			
		||||
        'legendExpanded',
 | 
			
		||||
        'plotAxisTitle'
 | 
			
		||||
    ],
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            isZoomed: false,
 | 
			
		||||
            primaryYAxisRange: {
 | 
			
		||||
                min: '',
 | 
			
		||||
                max: ''
 | 
			
		||||
            },
 | 
			
		||||
            xAxisRange: {
 | 
			
		||||
                min: '',
 | 
			
		||||
                max: ''
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        data: {
 | 
			
		||||
            immediate: false,
 | 
			
		||||
            handler: 'updateData'
 | 
			
		||||
        },
 | 
			
		||||
        legendExpanded: {
 | 
			
		||||
            immediate: false,
 | 
			
		||||
            handler: 'updatePlot'
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getAxisMinMax(axis) {
 | 
			
		||||
            const min = axis.autoSize
 | 
			
		||||
                ? ''
 | 
			
		||||
                : axis.min;
 | 
			
		||||
            const max = axis.autoSize
 | 
			
		||||
                ? ''
 | 
			
		||||
                : axis.max;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                min,
 | 
			
		||||
                max
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        getAxisPadding(min, max) {
 | 
			
		||||
            return (Math.abs(max - min) * PLOT_PADDING_IN_PERCENT / 100) || 0;
 | 
			
		||||
        },
 | 
			
		||||
        getLayout() {
 | 
			
		||||
            const yAxesMeta = this.getYAxisMeta();
 | 
			
		||||
            const primaryYaxis = this.getYaxisLayout(yAxesMeta['1']);
 | 
			
		||||
            const xAxisDomain = this.getXAxisDomain(yAxesMeta);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                // hovermode: 'closest',
 | 
			
		||||
                // hoverdistance: -1,
 | 
			
		||||
                autosize: true,
 | 
			
		||||
                showlegend: false,
 | 
			
		||||
                font: {
 | 
			
		||||
                    family: 'Helvetica Neue, Helvetica, Arial, sans-serif',
 | 
			
		||||
                    size: '12px',
 | 
			
		||||
                    color: '#666'
 | 
			
		||||
                },
 | 
			
		||||
                xaxis: {
 | 
			
		||||
                    domain: xAxisDomain,
 | 
			
		||||
                    // hoverformat: '.2r',
 | 
			
		||||
                    range: [this.xAxisRange.min, this.xAxisRange.max],
 | 
			
		||||
                    title: this.plotAxisTitle.xAxisTitle
 | 
			
		||||
                    // zeroline: false
 | 
			
		||||
                },
 | 
			
		||||
                yaxis: primaryYaxis,
 | 
			
		||||
                margin: {
 | 
			
		||||
                    l: 40,
 | 
			
		||||
                    r: 10,
 | 
			
		||||
                    b: 40,
 | 
			
		||||
                    t: 20
 | 
			
		||||
                },
 | 
			
		||||
                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 = {
 | 
			
		||||
                // hoverformat: '.2r',
 | 
			
		||||
                // showgrid: true,
 | 
			
		||||
                title
 | 
			
		||||
                // zeroline: false
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
        },
 | 
			
		||||
        handleHover(isHovered, itemValues) {
 | 
			
		||||
            return () => {
 | 
			
		||||
                this.updateLocalControlPosition(isHovered);
 | 
			
		||||
 | 
			
		||||
                const eventName = isHovered ? HOVER_VALUES_CHANGED : HOVER_VALUES_CLEARED;
 | 
			
		||||
                this.$emit(eventName, { itemValues });
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        registerListeners() {
 | 
			
		||||
            this.$refs.plot.on('plotly_hover', this.handleHover.bind(this, true));
 | 
			
		||||
            this.$refs.plot.on('plotly_unhover', this.handleHover.bind(this, false));
 | 
			
		||||
            this.$refs.plot.on('plotly_relayout', this.zoom);
 | 
			
		||||
            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);
 | 
			
		||||
        },
 | 
			
		||||
        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>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
    .c-spectral-aggregate-plot__plot {
 | 
			
		||||
        height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-spectral-aggregate-plot-view .gl-plot__local-controls {
 | 
			
		||||
        top: 25px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .has-local-controls {
 | 
			
		||||
        border: 1px solid transparent;
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 { SPECTRAL_AGGREGATE_KEY } from './SpectralAggregateConstants';
 | 
			
		||||
export default function SpectralAggregatePlotCompositionPolicy(openmct) {
 | 
			
		||||
    function hasAggregateDomainAndRange(metadata) {
 | 
			
		||||
        const rangeValues = metadata.valuesForHints(['domain']);
 | 
			
		||||
        const domainValues = metadata.valuesForHints(['range']);
 | 
			
		||||
 | 
			
		||||
        return rangeValues.length > 0
 | 
			
		||||
        || domainValues.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function hasSpectralAggregateTelemetry(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 === SPECTRAL_AGGREGATE_KEY)
 | 
			
		||||
                && ((child.type !== 'telemetry.plot.overlay') && (hasSpectralAggregateTelemetry(child) === false) || (hasNoChildren(parent) === false))
 | 
			
		||||
            ) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,376 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 SpectralAggregatePlotCompositionPolicy from "./SpectralAggregatePlotCompositionPolicy";
 | 
			
		||||
import { createOpenMct } from "utils/testing";
 | 
			
		||||
 | 
			
		||||
describe("The spectral aggregation plot composition policy", () => {
 | 
			
		||||
    let openmct;
 | 
			
		||||
    const mockNonSpectralMetaData = {
 | 
			
		||||
        "period": 10,
 | 
			
		||||
        "amplitude": 1,
 | 
			
		||||
        "offset": 0,
 | 
			
		||||
        "dataRateInHz": 1,
 | 
			
		||||
        "phase": 0,
 | 
			
		||||
        "randomness": 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": "cos",
 | 
			
		||||
                "name": "Cosine",
 | 
			
		||||
                "unit": "deg",
 | 
			
		||||
                "formatString": "%0.2f",
 | 
			
		||||
                "hints": {
 | 
			
		||||
                    "domain": 3,
 | 
			
		||||
                    "priority": 3
 | 
			
		||||
                },
 | 
			
		||||
                "source": "cos"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "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"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
    const mockGoodSpectralMetaData = {
 | 
			
		||||
        "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": "cos",
 | 
			
		||||
                "name": "Cosine",
 | 
			
		||||
                "unit": "deg",
 | 
			
		||||
                "formatString": "%0.2f",
 | 
			
		||||
                "hints": {
 | 
			
		||||
                    "domain": 3,
 | 
			
		||||
                    "priority": 3
 | 
			
		||||
                },
 | 
			
		||||
                "source": "cos"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "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: mockGoodSpectralMetaData
 | 
			
		||||
        };
 | 
			
		||||
        const mockTypeService = {
 | 
			
		||||
            getType: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    typeDef: mockTypeDef
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        openmct.$injector = {
 | 
			
		||||
            get: () => {
 | 
			
		||||
                return mockTypeService;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        openmct.telemetry.isTelemetryObject = function (domainObject) {
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("exists", () => {
 | 
			
		||||
        expect(SpectralAggregatePlotCompositionPolicy(openmct).allow).toBeDefined();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("allow composition only for telemetry that provides/supports spectral data", () => {
 | 
			
		||||
        const parent = {
 | 
			
		||||
            "composition": [],
 | 
			
		||||
            "configuration": {},
 | 
			
		||||
            "name": "Some Spectral Aggregate Plot",
 | 
			
		||||
            "type": "telemetry.plot.spectral.aggregate",
 | 
			
		||||
            "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(SpectralAggregatePlotCompositionPolicy(openmct).allow(parent, child)).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("disallows composition for telemetry that contain anything else", () => {
 | 
			
		||||
        const mockTypeDef = {
 | 
			
		||||
            telemetry: mockNonSpectralMetaData
 | 
			
		||||
        };
 | 
			
		||||
        const mockTypeService = {
 | 
			
		||||
            getType: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    typeDef: mockTypeDef
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        openmct.$injector = {
 | 
			
		||||
            get: () => {
 | 
			
		||||
                return mockTypeService;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        const parent = {
 | 
			
		||||
            "composition": [],
 | 
			
		||||
            "configuration": {},
 | 
			
		||||
            "name": "Some Spectral Aggregate Plot",
 | 
			
		||||
            "type": "telemetry.plot.spectral.aggregate",
 | 
			
		||||
            "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(SpectralAggregatePlotCompositionPolicy(openmct).allow(parent, child)).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("passthrough for composition for non spectral aggregate 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(SpectralAggregatePlotCompositionPolicy(openmct).allow(parent, child)).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -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 SpectralAggregateView from './SpectralAggregateView.vue';
 | 
			
		||||
import { SPECTRAL_AGGREGATE_KEY, SPECTRAL_AGGREGATE_VIEW } from './SpectralAggregateConstants';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default function SpectralAggregatePlotViewProvider(openmct) {
 | 
			
		||||
    function isCompactView(objectPath) {
 | 
			
		||||
        return objectPath.find(object => object.type === 'time-strip');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        key: SPECTRAL_AGGREGATE_VIEW,
 | 
			
		||||
        name: 'Spectral Aggregate Plot',
 | 
			
		||||
        cssClass: 'icon-telemetry',
 | 
			
		||||
        canView(domainObject, objectPath) {
 | 
			
		||||
            return domainObject && domainObject.type === SPECTRAL_AGGREGATE_KEY;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        canEdit(domainObject, objectPath) {
 | 
			
		||||
            return domainObject && domainObject.type === SPECTRAL_AGGREGATE_KEY;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        view: function (domainObject, objectPath) {
 | 
			
		||||
            let component;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    let isCompact = isCompactView(objectPath);
 | 
			
		||||
                    component = new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            SpectralAggregateView
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct,
 | 
			
		||||
                            domainObject
 | 
			
		||||
                        },
 | 
			
		||||
                        data() {
 | 
			
		||||
                            return {
 | 
			
		||||
                                options: {
 | 
			
		||||
                                    compact: isCompact
 | 
			
		||||
                                }
 | 
			
		||||
                            };
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<spectral-aggregate-view :options="options"></spectral-aggregate-view>'
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                destroy: function () {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										332
									
								
								src/plugins/plot/spectralAggregatePlot/SpectralAggregateView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								src/plugins/plot/spectralAggregatePlot/SpectralAggregateView.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,332 @@
 | 
			
		||||
<!--
 | 
			
		||||
 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>
 | 
			
		||||
<div class="c-spectral-aggregate-plot-view gl-plot plot-legend-bottom"
 | 
			
		||||
     :class="{'plot-legend-expanded': legendExpanded, 'plot-legend-collapsed': !legendExpanded }"
 | 
			
		||||
>
 | 
			
		||||
    <SpectralAggregatePlot ref="spectralAggregatePlot"
 | 
			
		||||
                           class="c-spectral-aggregate-plot__plot-wrapper"
 | 
			
		||||
                           :data="visibleData"
 | 
			
		||||
                           :plot-axis-title="plotAxisTitle"
 | 
			
		||||
                           :legend-expanded="legendExpanded"
 | 
			
		||||
    />
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import * as SPECTRAL_AGGREGATE from './SpectralAggregateConstants';
 | 
			
		||||
import ColorPalette from '../lib/ColorPalette';
 | 
			
		||||
import objectUtils from 'objectUtils';
 | 
			
		||||
import SpectralAggregatePlot from './SpectralAggregatePlot.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        SpectralAggregatePlot
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            colorMapping: {},
 | 
			
		||||
            composition: {},
 | 
			
		||||
            currentDomainObject: this.domainObject,
 | 
			
		||||
            isRealTime: (this.openmct.time.clock() !== undefined),
 | 
			
		||||
            spectralAggregateTypes: {},
 | 
			
		||||
            subscriptions: [],
 | 
			
		||||
            telemetryObjects: {},
 | 
			
		||||
            trace: [],
 | 
			
		||||
            legendExpanded: false
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    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}`
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        visibleData() {
 | 
			
		||||
            return this.trace.filter((trace) => trace.hidden !== true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.colorPalette = new ColorPalette();
 | 
			
		||||
        this.loadComposition();
 | 
			
		||||
 | 
			
		||||
        this.openmct.time.on('bounds', this.refreshData);
 | 
			
		||||
        this.openmct.time.on('clock', this.clockChanged);
 | 
			
		||||
 | 
			
		||||
        this.$refs.spectralAggregatePlot.$on(SPECTRAL_AGGREGATE.SUBSCRIBE, this.subscribeToAll);
 | 
			
		||||
        this.$refs.spectralAggregatePlot.$on(SPECTRAL_AGGREGATE.UNSUBSCRIBE, this.removeAllSubscriptions);
 | 
			
		||||
 | 
			
		||||
        this.unobserve = this.openmct.objects.observe(this.currentDomainObject, '*', this.updateDomainObject);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.$refs.spectralAggregatePlot.$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: {
 | 
			
		||||
        addColorForTelemetry(key) {
 | 
			
		||||
            const color = this.colorPalette.getNextColor().asHexString();
 | 
			
		||||
            this.colorMapping[key] = color;
 | 
			
		||||
 | 
			
		||||
            return color;
 | 
			
		||||
        },
 | 
			
		||||
        addSpectralAggregateType(key, name, timestamp, color) {
 | 
			
		||||
            this.$set(this.spectralAggregateTypes, key, {
 | 
			
		||||
                name,
 | 
			
		||||
                timestamp,
 | 
			
		||||
                color
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        addTelemetryObject(telemetryObject) {
 | 
			
		||||
            const key = objectUtils.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!this.colorMapping[key]) {
 | 
			
		||||
                this.addColorForTelemetry(key);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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]);
 | 
			
		||||
            this.updateTraceVisibility();
 | 
			
		||||
        },
 | 
			
		||||
        clockChanged() {
 | 
			
		||||
            this.isRealTime = this.openmct.time.clock() !== undefined;
 | 
			
		||||
 | 
			
		||||
            this.removeAllSubscriptions();
 | 
			
		||||
            this.subscribeToAll();
 | 
			
		||||
        },
 | 
			
		||||
        getAxisMetadata(telemetryObject) {
 | 
			
		||||
            const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
 | 
			
		||||
            const yAxisMetadata = metadata.valuesForHints(['range'])[0];
 | 
			
		||||
            const xAxisMetadata = metadata.valueMetadatas.filter((valueMetadata => {
 | 
			
		||||
                return valueMetadata.key !== 'name' && !this.openmct.time.getAllTimeSystems().find((timeSystem) => timeSystem.key === valueMetadata.key);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                xAxisMetadata,
 | 
			
		||||
                yAxisMetadata
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        getOptions(telemetryObject) {
 | 
			
		||||
            const { start, end } = this.openmct.time.bounds();
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                averages: 0,
 | 
			
		||||
                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) {
 | 
			
		||||
                this.colorPalette.reset();
 | 
			
		||||
                const telemetryObjects = Object.values(this.telemetryObjects);
 | 
			
		||||
                telemetryObjects.forEach(this.requestDataFor);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        removeAllSubscriptions() {
 | 
			
		||||
            this.subscriptions.forEach(subscription => subscription.unsubscribe());
 | 
			
		||||
            this.subscriptions = [];
 | 
			
		||||
        },
 | 
			
		||||
        removeTelemetryObject(identifier) {
 | 
			
		||||
            const key = objectUtils.makeKeyString(identifier);
 | 
			
		||||
            delete this.telemetryObjects[key];
 | 
			
		||||
            this.$delete(this.spectralAggregateTypes, key);
 | 
			
		||||
 | 
			
		||||
            this.subscriptions.forEach(subscription => {
 | 
			
		||||
                if (subscription.key !== key) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                subscription.unsubscribe();
 | 
			
		||||
                delete this.subscriptions[key];
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this.trace = this.trace.filter(t => t.key !== key);
 | 
			
		||||
        },
 | 
			
		||||
        processData(telemetryObject, data, axisMetadata) {
 | 
			
		||||
            const key = objectUtils.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
 | 
			
		||||
            const formattedTimestamp = 'N/A';
 | 
			
		||||
 | 
			
		||||
            const color = this.colorMapping[key];
 | 
			
		||||
            const spectralAggregateValue = this.spectralAggregateTypes[key];
 | 
			
		||||
            if (!spectralAggregateValue) {
 | 
			
		||||
                this.addSpectralAggregateType(key, telemetryObject.name, formattedTimestamp, color);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (data.message) {
 | 
			
		||||
                this.openmct.notifications.alert(data.message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let xValues = [];
 | 
			
		||||
            let yValues = [];
 | 
			
		||||
            axisMetadata.xAxisMetadata.forEach((metadata) => {
 | 
			
		||||
                xValues.push(metadata.name);
 | 
			
		||||
                if (data[metadata.key]) {
 | 
			
		||||
                    yValues.push(data[metadata.key]);
 | 
			
		||||
                } else {
 | 
			
		||||
                    yValues.push('');
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const trace = {
 | 
			
		||||
                key,
 | 
			
		||||
                name: telemetryObject.name,
 | 
			
		||||
                x: xValues,
 | 
			
		||||
                y: yValues,
 | 
			
		||||
                xAxisMetadata: axisMetadata.xAxisMetadata,
 | 
			
		||||
                yAxisMetadata: axisMetadata.yAxisMetadata,
 | 
			
		||||
                type: 'bar'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            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 = objectUtils.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
            const found = Object.values(this.subscriptions).findIndex(objectKey => objectKey === key);
 | 
			
		||||
            if (found > -1) {
 | 
			
		||||
                this.subscriptions[found].unsubscribe();
 | 
			
		||||
                delete this.subscriptions[found];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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() {
 | 
			
		||||
            this.colorPalette.reset();
 | 
			
		||||
            const telemetryObjects = Object.values(this.telemetryObjects);
 | 
			
		||||
            telemetryObjects.forEach(this.subscribeToObject);
 | 
			
		||||
        },
 | 
			
		||||
        updateDomainObject(newDomainObject) {
 | 
			
		||||
            this.currentDomainObject = newDomainObject;
 | 
			
		||||
        },
 | 
			
		||||
        updateTraceVisibility() {
 | 
			
		||||
            // We create a copy here to improve performance since visibleData - a computed property - is dependent on this.trace.
 | 
			
		||||
            this.trace = this.trace.map((currentTrace, index) => {
 | 
			
		||||
                currentTrace.hidden = this.spectralAggregateTypes[currentTrace.key].hidden === true;
 | 
			
		||||
 | 
			
		||||
                return currentTrace;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
    .c-spectral-aggregate-plot {
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: 5px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &-view {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            flex-direction: column;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__plot-wrapper {
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
            min-height: 300px;
 | 
			
		||||
            min-width: 300px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__legend-wrapper {
 | 
			
		||||
            flex: 0 1 auto;
 | 
			
		||||
            overflow: auto;
 | 
			
		||||
            padding-right: 5px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/plugins/plot/spectralPlot/SpectralPlotViewProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/plugins/plot/spectralPlot/SpectralView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/plugins/plot/spectralPlot/SpectralView.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject']
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
		Reference in New Issue
	
	Block a user