Compare commits

..

15 Commits

Author SHA1 Message Date
57abac96b2 Implement basic plot synchronization 2018-02-24 12:03:38 -08:00
204f6be9d0 [Tests] Exclude plot from coverage requirement
Per discussions about backfilling tests with conversion away from
angular, removing plot plugin from coverage requirement so as to
allow build to complete.

https://github.com/nasa/openmct/pull/1557#issuecomment-364239059
2018-02-19 17:20:35 -08:00
c988c6c43b install plot in openmct instead of mct type 2018-02-19 17:15:00 -08:00
c143475f3e Add and update copyright notices 2018-02-19 17:02:27 -08:00
b248f57daa [ExportImage] Restore 3669e776a9
Restore changes from 3669e776a9,
such that exported image color can be overridden if desired.
2018-02-19 16:46:12 -08:00
f4e8c5ecf6 [Style] make code style compliant 2018-02-19 16:38:15 -08:00
11ef7c0ccd [Frontend] Added missing flex declaration
Fixes #1557
- Fixes disappearing plot chart area when legend is hidden;
2018-02-19 16:38:15 -08:00
9c125e0454 [Frontend] Fixed indiscriminate selector in Style Guide CSS
Fixes #1557
- Was fubaring palette example in Style Guide
2018-02-19 16:38:15 -08:00
439a654f6d [Frontend] More fixes for Sum Widgets and FP
Fixes #1557
- Fixed Palettes in Summary Widgets and Fixed Position
- Renamed .l-inline-color-palette to .l-inline-palette
2018-02-19 16:38:15 -08:00
9d32a3f1c3 [Frontend] Fixed inline palette in plots series config
Fixes #1557
- Brought back missing .inline-color-palette class
- Tweaks in _palette.scss

TODO: fix palettes in Summary Widgets
2018-02-19 16:38:15 -08:00
705e25df8a correct pallette reference 2018-02-19 16:38:15 -08:00
787d6887dc Add LAD support and state type to generators 2018-02-19 16:38:15 -08:00
1d516240a8 Telemetry API Updates 2018-02-19 16:38:15 -08:00
90ee0a309f New Plot
* Remove Old Plot Code
* Remove Telemetry Panel Type
* UTCFormat.parse: passthrough numbers
* TelemetryAPI: default request arguments
* TelemetryAPI: fix enum formatting

pass value instead of trying to create datum
Set max precision to avoid errors
Install plot by default
Make sure plots are always plots
Set default yKey on series
Use new time API
Update hints
proper id formats
Only redraw on change
Set x axis on bounds change

while MCTChart can handle inserting points in the middle of the
chart series, this is not performant with a large insert volume.

So, when merging historical requests with existing data, do a batch
sort and uniq check up front and then reset the data such that MCTChart
only has to do appends.  This is significantly faster than doing
large volumes of inserts.

[CSS] Plot Styles

Moved .view-control out of tree.scss and
generalized as a control prior to implementing
it as the expand/collapse control in plot legends
for #584 and #618.

Major layout changes to enable flex approach for
legend and plot display area; table styling for
expanded legend; legend on top, bottom, right
and left support added but still a bunch to do.

Style and markup normalization for legend elements.

Significant mods to layout in Inspector while
editing, using new .compact-form class;
mod to ul.tree li to allow .compact-form layout
class within tree scope;

Significant layout cleanup, mainly for stacked plots
and expanded legend;
Config options hide/show controls fully ported from
Pete's original version;

New 12px crosshair glyph added;
'hover-value-enabled' markup and class refinements;

Generalized .t-alert-unsynced, moved to _icons.scss;
Moved .t-object-alert to proper location in plot markup;
Added new .t-stacked-plot class;

Moved location of padding for .hover-value-enabled
elements

Minor refactor to use explicit legend-collapsed and -expanded CSS
classes, replacing ng-show statements;
Removed ng-style that was setting column widths based on char
count of column name;

lobal rename of `compact-form` to `inspector-config`;
Applied same to plot-options-browse.html for tree items;

Renamed `plot-legend-on-*` selector;
Fixed layout for plot-legend-hidden, involved
returning `plot-wrapper-axis-and-display-area` to
use position: absolute;

Fixes #584
Fixes #618
Fixes #1590

Fix for renamed view-control classes

Fixes #1795
Also removed .has-children from plot-options html files;

Don't try and recreate datums

Clear highlights when leaving plot

Simplify Plot Series Interface

Update Plot Options to match new Inspectors

Use edit context to support subobject editing
2018-02-19 16:38:15 -08:00
72d05283da Remove Old Plot 2018-02-19 16:37:29 -08:00
26 changed files with 205 additions and 658 deletions

View File

@ -0,0 +1,146 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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.
*****************************************************************************/
/*global define*/
define([
'legacyRegistry',
'../../platform/commonUI/browse/src/InspectorRegion',
'../../platform/commonUI/regions/src/Region'
], function (
legacyRegistry,
InspectorRegion,
Region
) {
"use strict";
/**
* Add a 'plot options' region part to the inspector region for the
* Telemetry Plot type only. {@link InspectorRegion} is a default region
* implementation that is added automatically to all types. In order to
* customize what appears in the inspector region, you can start from a
* blank slate by using Region, or customize the default inspector
* region by using {@link InspectorRegion}.
*/
var plotInspector = new InspectorRegion(),
/**
* Two region parts are defined here. One that appears only in browse
* mode, and one that appears only in edit mode. For not they both point
* to the same representation, but a different key could be used here to
* include a customized representation for edit mode.
*/
plotOptionsBrowseRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
}),
plotOptionsEditRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-browse"
}
});
/**
* Both parts are added, and policies of type 'region' will determine
* which is shown based on domain object state. A default policy is
* provided which will check the 'modes' attribute of the region part
* definition.
*/
plotInspector.addRegion(plotOptionsBrowseRegion);
plotInspector.addRegion(plotOptionsEditRegion);
legacyRegistry.register("example/plotType", {
"name": "Plot Type",
"description": "Example illustrating registration of a new object type",
"extensions": {
"types": [
{
"key": "plot",
"name": "Example Telemetry Plot",
"cssClass": "icon-telemetry-panel",
"description": "For development use. A plot for displaying telemetry.",
"priority": 10,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"inspector": plotInspector,
"telemetry": {
"source": "generator",
"domains": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@ -37,13 +37,14 @@ module.exports = function(config) {
{pattern: 'bower_components/**/*.js', included: false},
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'src/**/*', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},
{pattern: 'example/**/*.json', included: false},
{pattern: 'platform/**/*.js', included: false},
{pattern: 'warp/**/*.js', included: false},
{pattern: 'platform/**/*.html', included: false},
{pattern: 'src/**/*.html', included: false},
'test-main.js'
],

View File

@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "0.13.2",
"version": "0.12.1-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"d3-array": "^1.0.2",
@ -28,7 +28,7 @@
"gulp-jshint-html-reporter": "^0.1.3",
"gulp-rename": "^1.2.2",
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^3.1.0",
"gulp-sass": "^2.2.0",
"gulp-sourcemaps": "^1.6.0",
"jasmine-core": "^2.3.0",
"jscs-html-reporter": "^0.1.0",

View File

@ -38,7 +38,6 @@
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>
@ -50,7 +49,6 @@
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>

View File

@ -88,15 +88,11 @@ define(
* @private
*/
ElementsController.prototype.refreshComposition = function (domainObject) {
var refreshTracker = {};
this.currentRefresh = refreshTracker;
var selectedObjectComposition = domainObject && domainObject.useCapability('composition');
if (selectedObjectComposition) {
selectedObjectComposition.then(function (composition) {
if (this.currentRefresh === refreshTracker) {
this.scope.composition = composition;
}
}.bind(this));
} else {
this.scope.composition = [];

View File

@ -31,34 +31,11 @@ define(
mockSelection,
mockDomainObject,
mockMutationCapability,
mockCompositionCapability,
mockCompositionObjects,
mockComposition,
mockUnlisten,
selectable = [],
controller;
function mockPromise(value) {
return {
then: function (thenFunc) {
return mockPromise(thenFunc(value));
}
};
}
function createDomainObject() {
return {
useCapability: function () {
return mockCompositionCapability;
}
};
}
beforeEach(function () {
mockComposition = ["a", "b"];
mockCompositionObjects = mockComposition.map(createDomainObject);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockUnlisten = jasmine.createSpy('unlisten');
mockMutationCapability = jasmine.createSpyObj("mutationCapability", [
"listen"
@ -68,7 +45,7 @@ define(
"getCapability",
"useCapability"
]);
mockDomainObject.useCapability.andReturn(mockCompositionCapability);
mockDomainObject.useCapability.andCallThrough();
mockDomainObject.getCapability.andReturn(mockMutationCapability);
mockScope = jasmine.createSpyObj("$scope", ['$on']);
@ -88,7 +65,7 @@ define(
}
};
spyOn(ElementsController.prototype, 'refreshComposition').andCallThrough();
spyOn(ElementsController.prototype, 'refreshComposition');
controller = new ElementsController(mockScope, mockOpenMCT);
});
@ -160,25 +137,6 @@ define(
expect(mockDomainObject.getCapability).not.toHaveBeenCalledWith('mutation');
});
it("checks concurrent changes to composition", function () {
var secondMockComposition = ["a", "b", "c"],
secondMockCompositionObjects = secondMockComposition.map(createDomainObject),
firstCompositionCallback,
secondCompositionCallback;
spyOn(mockCompositionCapability, "then").andCallThrough();
controller.refreshComposition(mockDomainObject);
controller.refreshComposition(mockDomainObject);
firstCompositionCallback = mockCompositionCapability.then.calls[0].args[0];
secondCompositionCallback = mockCompositionCapability.then.calls[1].args[0];
secondCompositionCallback(secondMockCompositionObjects);
firstCompositionCallback(mockCompositionObjects);
expect(mockScope.composition).toBe(secondMockCompositionObjects);
});
});
}
);

View File

@ -77,14 +77,6 @@
position: relative;
}
.s-menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);
@include boxShdw($shdwMenu);
@include txtShdw($shdwMenuText);
padding: $interiorMarginSm 0;
}
.menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);

View File

@ -373,10 +373,10 @@ define(
*/
FixedController.prototype.updateView = function (telemetryObject, datum) {
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
var valueMetadata = this.chooseValueMetadataToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(valueMetadata, datum);
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, valueMetadata);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
this.setDisplayedValue(
telemetryObject,
@ -389,28 +389,29 @@ define(
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (valueMetadata, datum) {
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
var valueMetadata = metadata.value(telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum);
return formatter.format(datum[valueMetadata.key]);
};
/**
* @private
*/
FixedController.prototype.chooseValueMetadataToDisplay = function (metadata) {
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var valueMetadata = metadata.valuesForHints(['range'])[0];
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (valueMetadata === undefined) {
if (telemetryKeyToDisplay === undefined) {
var valuesOrderedByPriority = metadata.values();
valueMetadata = valuesOrderedByPriority.filter(function (values) {
return !(values.hints.domain);
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
return !(valueMetadata.hints.domain);
})[0];
}
return valueMetadata;
return telemetryKeyToDisplay.source;
};
/**

View File

@ -106,8 +106,8 @@ define(
'telemetryFormatter',
['format']
);
mockFormatter.format.andCallFake(function (valueMetadata) {
return "Formatted " + valueMetadata.value;
mockFormatter.format.andCallFake(function (value) {
return "Formatted " + value;
});
mockDomainObject = jasmine.createSpyObj(
@ -697,7 +697,7 @@ define(
source: 'range'
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('range');
});
@ -719,7 +719,7 @@ define(
}
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('image');
});

View File

@ -1,269 +0,0 @@
define([
'./CompositionAPI',
'./CompositionCollection'
], function (
CompositionAPI,
CompositionCollection
) {
describe('The Composition API', function () {
var publicAPI;
var compositionAPI;
var topicService;
var mutationTopic;
beforeEach(function () {
mutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
topicService = jasmine.createSpy('topicService');
topicService.andReturn(mutationTopic);
publicAPI = {};
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
'get'
]);
publicAPI.objects.get.andCallFake(function (identifier) {
return Promise.resolve({identifier: identifier});
});
publicAPI.$injector = jasmine.createSpyObj('$injector', [
'get'
]);
publicAPI.$injector.get.andReturn(topicService);
compositionAPI = new CompositionAPI(publicAPI);
});
it('returns falsy if an object does not support composition', function () {
expect(compositionAPI.get({})).toBeFalsy();
});
describe('default composition', function () {
var domainObject;
var composition;
beforeEach(function () {
domainObject = {
name: 'test folder',
identifier: {
namespace: 'test',
key: '1'
},
composition: [
{
namespace: 'test',
key: 'a'
}
]
};
composition = compositionAPI.get(domainObject);
});
it('returns composition collection', function () {
expect(composition).toBeDefined();
expect(composition).toEqual(jasmine.any(CompositionCollection));
});
it('loads composition from domain object', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'test', key: 'a'}
});
});
});
// TODO: Implement add/removal in new default provider.
xit('synchronizes changes between instances', function () {
var otherComposition = compositionAPI.get(domainObject);
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var otherAddListener = jasmine.createSpy('otherAddListener');
var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
composition.on('add', addListener);
composition.on('remove', removeListener);
otherComposition.on('add', otherAddListener);
otherComposition.on('remove', otherRemoveListener);
var loaded = false;
Promise.all([composition.load(), otherComposition.load()])
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
expect(otherRemoveListener).not.toHaveBeenCalled();
var object = addListener.mostRecentCall.args[0];
composition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
composition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
removeListener.reset();
otherRemoveListener.reset();
otherComposition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
otherComposition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
});
});
});
describe('static custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A simple custom provider, returns the same composition for
// all objects of a given type.
customProvider = {
appliesTo: function (object) {
return object.type === 'custom-object-type';
},
load: function (object) {
return Promise.resolve([
{
namespace: 'custom',
key: 'thing'
}
]);
}
};
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
describe('dynamic custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A dynamic provider, loads an empty composition and exposes
// listener functions.
customProvider = jasmine.createSpyObj('dynamicProvider', [
'appliesTo',
'load',
'on',
'off'
]);
customProvider.appliesTo.andReturn('true');
customProvider.load.andReturn(Promise.resolve([]));
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var loaded = false;
composition.on('add', addListener);
composition.on('remove', removeListener);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'add',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'remove',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
var add = customProvider.on.calls[0].args[2];
var remove = customProvider.on.calls[1].args[2];
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).not.toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
add({namespace: 'custom', key: 'thing'});
});
waitsFor(function () {
return addListener.calls.length > 0;
});
runs(function () {
expect(addListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
remove(addListener.mostRecentCall.args[0]);
});
waitsFor(function () {
return removeListener.calls.length > 0;
});
runs(function () {
expect(removeListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
});
});

View File

@ -38,6 +38,7 @@ define([
'../example/msl/bundle',
'../example/notifications/bundle',
'../example/persistence/bundle',
'../example/plotOptions/bundle',
'../example/policy/bundle',
'../example/profiling/bundle',
'../example/scratchpad/bundle',

View File

@ -166,6 +166,11 @@
ng-show="plotHistory.length"
style="position: absolute; top: 8px; right: 8px;">
<a class="s-button icon-brackets"
ng-click="plot.syncConductor()"
title="Synchronize Time Conductor to plot bounds">
</a>
<a class="s-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">

View File

@ -75,6 +75,7 @@ function (
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
window.chart = this;
}
eventHelpers.extend(MCTChartController.prototype);

View File

@ -85,19 +85,6 @@ define([
this.listenTo(this, 'destroy', this.onDestroy, this);
},
/**
* Retrieve the persisted series config for a given identifier.
*/
getPersistedSeriesConfig: function (identifier) {
var domainObject = this.get('domainObject');
if (!domainObject.configuration || !domainObject.configuration.series) {
return;
}
return domainObject.configuration.series.filter(function (seriesConfig) {
return seriesConfig.identifier.key === identifier.key &&
seriesConfig.identifier.namespace === identifier.namespace;
})[0];
},
/**
* Update the domain object with the given value.
*/

View File

@ -165,14 +165,11 @@ define([
return;
}
var valueMetadata = this.metadata.value(newKey);
var persistedConfig = this.get('persistedConfiguration');
if (!persistedConfig || !persistedConfig.interpolate) {
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
}
}
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
}.bind(this);
@ -236,6 +233,8 @@ define([
* @returns {Promise}
*/
load: function (options) {
this.resetOnAppend = true;
return this.fetch(options)
.then(function (res) {
this.emit('load');

View File

@ -41,7 +41,6 @@ define([
this.palette = new color.ColorPalette();
this.listenTo(this, 'add', this.onSeriesAdd, this);
this.listenTo(this, 'remove', this.onSeriesRemove, this);
this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this);
var domainObject = this.plot.get('domainObject');
if (domainObject.telemetry) {
@ -50,14 +49,6 @@ define([
this.watchTelemetryContainer(domainObject);
}
},
trackPersistedConfig: function (domainObject) {
domainObject.configuration.series.forEach(function (seriesConfig) {
var series = this.byIdentifier(seriesConfig.identifier);
if (series) {
series.set('persistedConfiguration', seriesConfig);
}
}, this);
},
watchTelemetryContainer: function (domainObject) {
var composition = this.openmct.composition.get(domainObject);
this.listenTo(composition, 'add', this.addTelemetryObject, this);
@ -84,9 +75,6 @@ define([
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
}
seriesConfig.persistedConfiguration =
this.plot.getPersistedSeriesConfig(domainObject.identifier);
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
@ -137,19 +125,12 @@ define([
},
updateColorPalette: function (newColor, oldColor) {
this.palette.remove(newColor);
var seriesWithColor = this.filter(function (series) {
var seriesWithColor = this.series.filter(function (series) {
return series.get('color') === newColor;
})[0];
if (!seriesWithColor) {
this.palette.return(oldColor);
}
},
byIdentifier: function (identifier) {
return this.filter(function (series) {
var seriesIdentifier = series.get('identifier');
return seriesIdentifier.namespace === identifier.namespace &&
seriesIdentifier.key === identifier.key;
})[0];
}
});

View File

@ -48,6 +48,7 @@ define([
this.configId = $scope.domainObject.getId();
this.setUpScope();
window.config = this;
}
eventHelpers.extend(PlotOptionsController.prototype);

View File

@ -33,12 +33,13 @@ define([
* It supports pan and zoom, implements zoom history, and supports locating
* values near the cursor.
*/
function MCTPlotController($scope, $element, $window) {
function MCTPlotController($scope, $element, $window, openmct) {
this.$scope = $scope;
this.$scope.config = this.config;
this.$scope.plot = this;
this.$element = $element;
this.$window = $window;
this.openmct = openmct;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
@ -53,6 +54,7 @@ define([
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize();
window.control = this;
}
MCTPlotController.$inject = ['$scope', '$element', '$window'];
@ -331,6 +333,13 @@ define([
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.syncConductor = function () {
var xDisplayRange = this.config.xAxis.get('displayRange');
this.openmct.time.stopClock();
this.openmct.time.bounds({start: xDisplayRange.min, end: xDisplayRange.max});
};
MCTPlotController.prototype.destroy = function () {
this.stopListening();
};

View File

@ -33,7 +33,7 @@ define([
return {
restrict: "E",
template: PlotTemplate,
controller: MCTPlotController,
controller: ['$scope', '$element', '$window', 'openmct', MCTPlotController],
controllerAs: 'mctPlotController',
bindToController: {
config: "="

View File

@ -117,6 +117,12 @@ define([
this.$scope = $scope;
this.$element = $element;
if (!window.ticks) {
window.ticks = [];
}
window.ticks.push(this);
this.tickCount = 4;
this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);

View File

@ -71,6 +71,7 @@ define([
this.config.series.forEach(this.addSeries, this);
this.followTimeConductor();
window.plot = this;
}
eventHelpers.extend(PlotController.prototype);

View File

@ -31,8 +31,7 @@ define([
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin',
'./staticRootPlugin/plugin'
'./plot/plugin'
], function (
_,
UTCTimeSystem,
@ -44,8 +43,7 @@ define([
SummaryWidget,
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin,
StaticRootPlugin
PlotPlugin
) {
var bundleMap = {
CouchDB: 'platform/persistence/couch',
@ -68,8 +66,6 @@ define([
plugins.ImportExport = ImportExport;
plugins.StaticRootPlugin = StaticRootPlugin;
/**
* A tabular view showing the latest values of multiple telemetry points at
* once. Formatted so that labels and values are aligned.

View File

@ -1,78 +0,0 @@
define([
'../../api/objects/object-utils'
], function (
objectUtils
) {
/**
* Transforms an import json blob into a object map that can be used to
* provide objects. Rewrites root identifier in import data with provided
* rootIdentifier, and rewrites all child object identifiers so that they
* exist in the same namespace as the rootIdentifier.
*/
function rewriteObjectIdentifiers(importData, rootIdentifier) {
var rootId = importData.rootId;
var objectString = JSON.stringify(importData.openmct);
Object.keys(importData.openmct).forEach(function (originalId, i) {
var newId;
if (originalId === rootId) {
newId = objectUtils.makeKeyString(rootIdentifier);
} else {
newId = objectUtils.makeKeyString({
namespace: rootIdentifier.namespace,
key: i
});
}
while (objectString.indexOf(originalId) !== -1) {
objectString = objectString.replace(
'"' + originalId + '"',
'"' + newId + '"'
);
}
});
return JSON.parse(objectString);
}
/**
* Convets all objects in an object make from old format objects to new
* format objects.
*/
function convertToNewObjects(oldObjectMap) {
return Object.keys(oldObjectMap)
.reduce(function (newObjectMap, key) {
newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key);
return newObjectMap;
}, {});
}
/* Set the root location correctly for a top-level object */
function setRootLocation(objectMap, rootIdentifier) {
objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT';
return objectMap;
}
/**
* Takes importData (as provided by the ImportExport plugin) and exposes
* an object provider to fetch those objects.
*/
function StaticModelProvider(importData, rootIdentifier) {
var oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier);
var newFormatObjectMap = convertToNewObjects(oldFormatObjectMap);
this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier);
}
/**
* Standard "Get".
*/
StaticModelProvider.prototype.get = function (identifier) {
var keyString = objectUtils.makeKeyString(identifier);
if (this.objectMap[keyString]) {
return this.objectMap[keyString];
}
throw new Error(keyString + ' not found in import models.');
};
return StaticModelProvider;
});

View File

@ -1,133 +0,0 @@
define([
'./StaticModelProvider',
'text!./static-provider-test.json'
], function (
StaticModelProvider,
testStaticDataText
) {
describe('StaticModelProvider', function () {
var staticProvider;
beforeEach(function () {
var staticData = JSON.parse(testStaticDataText);
staticProvider = new StaticModelProvider(staticData, {
namespace: 'my-import',
key: 'root'
});
});
describe('rootObject', function () {
var rootModel;
beforeEach(function () {
rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
it('is located at top level', function () {
expect(rootModel.location).toBe('ROOT');
});
it('has new-format identifier', function () {
expect(rootModel.identifier).toEqual({
namespace: 'my-import',
key: 'root'
});
});
it('has new-format composition', function () {
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '2'
});
});
});
describe('childObjects', function () {
var swg;
var layout;
var fixed;
beforeEach(function () {
swg = staticProvider.get({
namespace: 'my-import',
key: '1'
});
layout = staticProvider.get({
namespace: 'my-import',
key: '2'
});
fixed = staticProvider.get({
namespace: 'my-import',
key: '3'
});
});
it('match expected ordering', function () {
// this is a sanity check to make sure the identifiers map in
// the correct order.
expect(swg.type).toBe('generator');
expect(layout.type).toBe('layout');
expect(fixed.type).toBe('telemetry.fixed');
});
it('have new-style identifiers', function () {
expect(swg.identifier).toEqual({
namespace: 'my-import',
key: '1'
});
expect(layout.identifier).toEqual({
namespace: 'my-import',
key: '2'
});
expect(fixed.identifier).toEqual({
namespace: 'my-import',
key: '3'
});
});
it('have new-style composition', function () {
expect(layout.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(layout.composition).toContain({
namespace: 'my-import',
key: '3'
});
expect(fixed.composition).toContain({
namespace: 'my-import',
key: '1'
});
});
it('rewrites locations', function () {
expect(swg.location).toBe('my-import:root');
expect(layout.location).toBe('my-import:root');
expect(fixed.location).toBe('my-import:2');
});
it('rewrites matched identifiers in objects', function () {
expect(layout.configuration.layout.panels['my-import:1'])
.toBeDefined();
expect(layout.configuration.layout.panels['my-import:3'])
.toBeDefined();
expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
.not.toBeDefined();
expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
.not.toBeDefined();
expect(fixed.configuration['fixed-display'].elements[0].id)
.toBe('my-import:1');
});
});
});
});

View File

@ -1,51 +0,0 @@
define([
'./StaticModelProvider'
], function (
StaticModelProvider
) {
/**
* Static Root Plugin: takes an export file and exposes it as a new root
* object.
*/
function StaticRootPlugin(namespace, exportUrl) {
var rootIdentifier = {
namespace: namespace,
key: 'root'
};
var cachedProvider;
var loadProvider = function () {
return fetch(exportUrl)
.then(function (response) {
return response.json();
})
.then(function (importData) {
cachedProvider = new StaticModelProvider(importData, rootIdentifier);
return cachedProvider;
});
};
var getProvider = function () {
if (!cachedProvider) {
cachedProvider = loadProvider();
}
return Promise.resolve(cachedProvider);
};
return function install(openmct) {
openmct.objects.addRoot(rootIdentifier);
openmct.objects.addProvider(namespace, {
get: function (identifier) {
return getProvider().then(function (provider) {
return provider.get(identifier);
});
}
});
};
}
return StaticRootPlugin;
});

View File

@ -1 +0,0 @@
{"openmct":{"a9122832-4b6e-43ea-8219-5359c14c5de8":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","d2ac3ae4-0af2-49fe-81af-adac09936215"],"name":"import-provider-test","type":"folder","notes":"test data for import provider.","modified":1508522673278,"location":"mine","persisted":1508522673278},"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"telemetry":{"period":10,"amplitude":1,"offset":0,"dataRateInHz":1,"values":[{"key":"utc","name":"Time","format":"utc","hints":{"domain":1,"priority":0},"source":"utc"},{"key":"yesterday","name":"Yesterday","format":"utc","hints":{"domain":2,"priority":1},"source":"yesterday"},{"key":"sin","name":"Sine","hints":{"range":1,"priority":2},"source":"sin"},{"key":"cos","name":"Cosine","hints":{"range":2,"priority":3},"source":"cos"}]},"name":"SWG-10","type":"generator","modified":1508522652874,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522652874},"d2ac3ae4-0af2-49fe-81af-adac09936215":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","20273193-f069-49e9-b4f7-b97a87ed755d"],"name":"Layout","type":"layout","configuration":{"layout":{"panels":{"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"position":[0,0],"dimensions":[17,8]},"20273193-f069-49e9-b4f7-b97a87ed755d":{"position":[0,8],"dimensions":[17,1],"hasFrame":false}}}},"modified":1508522745580,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522745580},"20273193-f069-49e9-b4f7-b97a87ed755d":{"layoutGrid":[64,16],"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0"],"name":"FP Test","type":"telemetry.fixed","configuration":{"fixed-display":{"elements":[{"type":"fixed.telemetry","x":0,"y":0,"id":"483c00d4-bb1d-4b42-b29a-c58e06b322a0","stroke":"transparent","color":"","titled":true,"width":8,"height":2,"useGrid":true,"size":"24px"}]}},"modified":1508522717619,"location":"d2ac3ae4-0af2-49fe-81af-adac09936215","persisted":1508522717619}},"rootId":"a9122832-4b6e-43ea-8219-5359c14c5de8"}