Compare commits

..

57 Commits

Author SHA1 Message Date
Deep Tailor
043d6aa9c3 extend traces on subscribe 2020-05-07 12:23:32 -07:00
Deep Tailor
ecfab8f7f3 add subscribe 2020-05-07 12:00:39 -07:00
Deep Tailor
05c38c37aa working historical 2020-05-07 11:37:01 -07:00
Joel McKinnon
ce78925119 wip: added telemetry provider 2020-05-07 10:45:15 -07:00
Joel McKinnon
26aca0f433 working on viewProvider 2020-05-06 11:31:17 -07:00
Joel McKinnon
41259bbd40 added hardcoded test plot 2020-05-05 16:00:48 -07:00
Joel McKinnon
580640ff47 basic plotly plugin structure 2020-05-05 12:50:55 -07:00
Joel McKinnon
a4aec5d492 Merge branch 'master' of https://github.com/nasa/openmct 2020-05-01 10:46:57 -07:00
Nikhil
23303c910e Don't allow recursive Preview actions #2775 (#2869)
* Don't allow recursive Preview actions #2775

* actionsToBeIncluded and actionsToBeSkipped passed in as options object.

* Revert "actionsToBeIncluded and actionsToBeSkipped passed in as options object."

This reverts commit f501d0b4ba.

* Revert "Don't allow recursive Preview actions #2775"

This reverts commit 5563cbea3a.

* Don't allow recursive Preview actions

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-01 09:33:10 -07:00
Joel McKinnon
00b3f3ac0b Merge branch 'master' of https://github.com/nasa/openmct 2020-05-01 09:25:44 -07:00
Nikhil
3282934cf6 fix test coverage. (#2951)
* fix test coverage.

* changes per comments + added test-coverage script to increase max-old-space-size of V8
ref: https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_mbytes

* renamed test:coverage and test:debug.

* circle-ci to use test:coverage.

* reduced test coverage thresholds.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:57:53 -07:00
Nikhil
c157fab081 [Notebook] Save snapshot dropdown should be available from "view large" overlay #2922 (#2926)
* [Notebook] Save snapshot dropdown should be available from "view large" overlay #2922\
* Significant improvements to Snapshot styling
* [Notebook] Embed links aren't navigating #2979

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:39:20 -07:00
Nikhil
7c07b66cc9 [Notebook] : Error in event handler for "updateSection": "TypeError: Cannot read property 'id' of undefined" #2921 (#2924)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 16:30:09 -07:00
Shefali Joshi
7a906ccf5c Preview condition styles on selecting that condition or one of it's styles (#2925)
* Preview condition styles on selecting that condition or one of it's styles
* Do not evaluate conditional styles in edit mode

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 13:00:43 -07:00
Nikhil
ff7debfb81 [Notebook] Entries and Embeds need to use the same timesystem #2920 (#2923)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:56:08 -07:00
Joel McKinnon
92ba103f45 modified eslint script and fixed errors found (#2905)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 11:53:07 -07:00
Nikhil
2c2d8d6b56 [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843 (#2866)
* [Notebook]: Unnecessary notification "Time bounds changed to fixed timespan mode" #2843

* removed stopclock call.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-30 10:47:49 -07:00
Charles Hacskaylo
cfadb9f4fd Fixes #2901 (#2902)
- Mod PreviewAction to prevent folders from being previewed;

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-30 09:48:16 -07:00
Joel McKinnon
c185f77a15 Merge branch 'master' of https://github.com/nasa/openmct 2020-04-29 16:52:05 -07:00
David Tsay
396817b2d1 handle non-valid requests (#2984)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 15:24:12 -07:00
David Tsay
96eb6d6b74 [Conditionals] evaluation fixes (#2981)
* change single output to state and value

* do not send telemetryObjects to telemetry api request cal

* normalize data on requests

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 14:56:07 -07:00
Shefali Joshi
cb5d47f66f Use only the values required for description (#2919)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-28 16:58:58 -07:00
Shefali Joshi
ea90d02d66 Show the Styles tab for non creatable layout objects including condition sets (#2975) 2020-04-28 13:10:29 -07:00
David Tsay
95f73d8eb8 [Conditionals] fix #2961 in master (#2969)
* use correct id for telemetry requests

* request and subscription data cache should be mutually exclusive

use latest timestamp for any/all requests

* do not add prop to datum

remove unnecessary if check

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-28 13:02:23 -07:00
Joel McKinnon
0dff431f4a Merge branch 'master' of https://github.com/nasa/openmct 2020-04-27 12:02:12 -07:00
Joel McKinnon
a37c686993 Merge pull request #2968 from nasa/revert-2885-lodash-upgrade-test
Revert "Lodash upgrade"
2020-04-24 11:55:44 -07:00
Deep Tailor
f12166097c Revert "Lodash upgrade (#2885)"
This reverts commit d103a22fa0.
2020-04-24 11:53:31 -07:00
Joel McKinnon
61d238a097 Merge branch 'master' of https://github.com/nasa/openmct 2020-04-23 16:30:01 -07:00
Joel McKinnon
d103a22fa0 Lodash upgrade (#2885)
* upgraded lodash, changed method names
* native implementations as requested
2020-04-23 10:38:44 -07:00
Deep Tailor
04a60cfcbb fixes #2713 (#2928)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-22 15:22:11 -07:00
Joel McKinnon
8d723960f4 removed acorn from package.json (#2906)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:50:45 -07:00
Joel McKinnon
6d3cd2c699 Upgrade angular from 1.4.14 to 1.7.9 (#2955)
* successfully upgraded to v1.6 with $compileProvider.preAssignBindingsEnabled(true)
* removed $compileProvider.preAssignBindingsEnabled(true), wrapped constructors for plot and chart inside onInit function
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:34:12 -07:00
Andrew Henry
87bf94fe0a Updated year in index.html (#2930) 2020-04-21 15:28:10 -07:00
Joel McKinnon
af93823b6f Elasticsearch support for change to typeless API (#2941)
* added elasticsearch to bundlemap
2020-04-21 15:23:43 -07:00
Joel McKinnon
f9deb80350 Merge branch 'master' of https://github.com/nasa/openmct 2020-04-16 15:01:42 -07:00
Joel McKinnon
021d730814 resolve merge conflicts 2020-04-13 09:05:43 -07:00
David Tsay
ae62b15abf Merge branch 'fix-default-output' of github.com:nasa/openmct into fix-default-output 2020-04-10 15:44:42 -07:00
David Tsay
ba41c1a30e fix unit tests 2020-04-10 15:43:48 -07:00
Andrew Henry
b9a85d9c4d Merge branch 'master' into fix-default-output 2020-04-10 15:34:10 -07:00
David Tsay
80eab8bad1 fix telemetrycriterion and unit testing 2020-04-10 15:26:18 -07:00
Andrew Henry
b2d8d640ae Merge branch 'master' into fix-default-output 2020-04-10 15:23:10 -07:00
Andrew Henry
56e6fa66c2 Merge branch 'master' into fix-default-output 2020-04-10 15:13:36 -07:00
David Tsay
9fa4707c82 Merge branch 'master' into fix-default-output 2020-04-10 13:04:14 -07:00
David Tsay
7e2cfa36de AllTelemetryCriterion extends TelemetryCriterion 2020-04-10 12:17:55 -07:00
David Tsay
aaa60a1545 scope function names 2020-04-10 11:28:13 -07:00
David Tsay
717231fed2 use current timesystem to compare latest 2020-04-10 11:20:51 -07:00
David Tsay
7fb2bc9729 tie in requests and eliminate unused code 2020-04-10 10:42:31 -07:00
David Tsay
addeb635e9 refactor all/any telemetry criterion to use new evaluator 2020-04-10 09:48:31 -07:00
David Tsay
608d63a7b0 telemetry criterion stores its own result 2020-04-10 09:00:39 -07:00
David Tsay
10679e5f4f remove commented code 2020-04-10 00:12:55 -07:00
David Tsay
38b8f03b1a linting 2020-04-09 21:03:56 -07:00
David Tsay
779a42c28c remove unused listeners, events, and helpers 2020-04-09 18:14:19 -07:00
David Tsay
80c2504768 get results directly instead of using events 2020-04-09 17:54:47 -07:00
David Tsay
80359e3f16 remove generating timestamp for telemetry data 2020-04-09 16:20:16 -07:00
Joshi
66aa4f099f Remove unused code 2020-04-09 15:22:35 -07:00
Joshi
aa6c6cb88b Removes timestamp based evalutation from conditionManager 2020-04-09 15:21:25 -07:00
Joshi
4e5cc840d7 Ensures that results for a specific datapoint are evaluated atomically. 2020-04-09 12:10:29 -07:00
67 changed files with 992 additions and 351 deletions

View File

@@ -20,8 +20,8 @@ jobs:
paths: paths:
- node_modules - node_modules
- run: - run:
name: npm run test name: npm run test:coverage
command: npm run test command: npm run test:coverage
- run: - run:
name: npm run lint name: npm run lint
command: npm run lint command: npm run lint

View File

@@ -8,11 +8,9 @@ module.exports = {
"globals": { "globals": {
"_": "readonly" "_": "readonly"
}, },
"plugins": ["lodash"],
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:vue/recommended", "plugin:vue/recommended"
"plugin:lodash/recommended"
], ],
"parser": "vue-eslint-parser", "parser": "vue-eslint-parser",
"parserOptions": { "parserOptions": {
@@ -24,14 +22,6 @@ module.exports = {
} }
}, },
"rules": { "rules": {
"lodash/prefer-lodash-method": "off",
"lodash/prefer-lodash-typecheck": "off",
"lodash/prefer-constant": "off",
"lodash/prefer-noop": "off",
"lodash/prefer-matches": "off",
"lodash/prefer-includes": "off",
"lodash/prefer-startswith": "off",
"lodash/prefer-get": "off",
"no-bitwise": "error", "no-bitwise": "error",
"curly": "error", "curly": "error",
"eqeqeq": "error", "eqeqeq": "error",

View File

@@ -1,5 +1,5 @@
<!-- <!--
Open MCT, Copyright (c) 2014-2017, United States Government Open MCT, Copyright (c) 2014-2020, United States Government
as represented by the Administrator of the National Aeronautics and Space as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved. Administration. All rights reserved.
@@ -43,9 +43,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
); );
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso()); openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems()); openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator()); openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.UTCTimeSystem());

View File

@@ -24,16 +24,27 @@
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless']; const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
const coverageEnabled = process.env.COVERAGE === 'true';
const reporters = ['progress', 'html'];
if (coverageEnabled) {
reporters.push('coverage-istanbul');
}
module.exports = (config) => { module.exports = (config) => {
const webpackConfig = require('./webpack.config.js'); const webpackConfig = require('./webpack.config.js');
delete webpackConfig.output; delete webpackConfig.output;
if (!devMode) { if (!devMode || coverageEnabled) {
webpackConfig.module.rules.push({ webpackConfig.module.rules.push({
test: /\.js$/, test: /\.js$/,
exclude: /node_modules|example/, exclude: /node_modules|example|lib|dist/,
use: 'istanbul-instrumenter-loader' use: {
loader: 'istanbul-instrumenter-loader',
options: {
esModules: true
}
}
}); });
} }
@@ -45,11 +56,7 @@ module.exports = (config) => {
'src/**/*Spec.js' 'src/**/*Spec.js'
], ],
port: 9876, port: 9876,
reporters: [ reporters: reporters,
'progress',
'coverage',
'html'
],
browsers: browsers, browsers: browsers,
customLaunchers: { customLaunchers: {
ChromeDebugging: { ChromeDebugging: {
@@ -61,27 +68,27 @@ module.exports = (config) => {
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
coverageReporter: {
dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' :
"dist/reports/coverage",
check: {
global: {
lines: 80,
excludes: ['src/plugins/plot/**/*.js']
}
}
},
// HTML test reporting. // HTML test reporting.
htmlReporter: { htmlReporter: {
outputDir: "dist/reports/tests", outputDir: "dist/reports/tests",
preserveDescribeNesting: true, preserveDescribeNesting: true,
foldAll: false foldAll: false
}, },
coverageIstanbulReporter: {
fixWebpackSourcePaths: true,
dir: process.env.CIRCLE_ARTIFACTS ?
process.env.CIRCLE_ARTIFACTS + '/coverage' :
"dist/reports/coverage",
reports: ['html', 'lcovonly', 'text-summary'],
thresholds: {
global: {
lines: 62
}
}
},
preprocessors: { preprocessors: {
// add webpack as preprocessor 'platform/**/*Spec.js': ['webpack', 'sourcemap'],
'platform/**/*Spec.js': [ 'webpack', 'sourcemap' ], 'src/**/*Spec.js': ['webpack', 'sourcemap']
'src/**/*Spec.js': [ 'webpack', 'sourcemap' ]
}, },
webpack: webpackConfig, webpack: webpackConfig,
webpackMiddleware: { webpackMiddleware: {

View File

@@ -2,10 +2,11 @@
"name": "openmct", "name": "openmct",
"version": "1.0.0-snapshot", "version": "1.0.0-snapshot",
"description": "The Open MCT core platform", "description": "The Open MCT core platform",
"dependencies": {}, "dependencies": {
"plotly.js-dist": "^1.54.1"
},
"devDependencies": { "devDependencies": {
"acorn": "6.2.0", "angular": "1.7.9",
"angular": "1.4.14",
"angular-route": "1.4.14", "angular-route": "1.4.14",
"babel-eslint": "8.2.6", "babel-eslint": "8.2.6",
"comma-separated-values": "^3.6.4", "comma-separated-values": "^3.6.4",
@@ -24,7 +25,6 @@
"d3-time": "1.0.x", "d3-time": "1.0.x",
"d3-time-format": "2.1.x", "d3-time-format": "2.1.x",
"eslint": "5.2.0", "eslint": "5.2.0",
"eslint-plugin-lodash": "^6.0.0",
"eslint-plugin-vue": "^6.0.0", "eslint-plugin-vue": "^6.0.0",
"eventemitter3": "^1.2.0", "eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0", "exports-loader": "^0.7.0",
@@ -44,17 +44,18 @@
"karma-chrome-launcher": "^2.2.0", "karma-chrome-launcher": "^2.2.0",
"karma-cli": "^1.0.1", "karma-cli": "^1.0.1",
"karma-coverage": "^1.1.2", "karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-html-reporter": "^0.2.7", "karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2", "karma-jasmine": "^1.1.2",
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.0", "karma-webpack": "^3.0.0",
"location-bar": "^3.0.1", "location-bar": "^3.0.1",
"lodash": "^4.17.12", "lodash": "^3.10.1",
"markdown-toc": "^0.11.7", "markdown-toc": "^0.11.7",
"marked": "^0.3.5", "marked": "^0.3.5",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"minimist": "^1.1.1", "minimist": "^1.1.1",
"moment": "^2.11.1", "moment": "^2.25.3",
"moment-duration-format": "^2.2.2", "moment-duration-format": "^2.2.2",
"moment-timezone": "^0.5.21", "moment-timezone": "^0.5.21",
"node-bourbon": "^4.2.3", "node-bourbon": "^4.2.3",
@@ -77,14 +78,16 @@
"zepto": "^1.2.0" "zepto": "^1.2.0"
}, },
"scripts": { "scripts": {
"clean": "rm -rf ./dist",
"start": "node app.js", "start": "node app.js",
"lint": "eslint platform example src/**/*.{js,vue} openmct.js", "lint": "eslint platform example src --ext .js,.vue openmct.js",
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix", "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
"build:prod": "cross-env NODE_ENV=production webpack", "build:prod": "cross-env NODE_ENV=production webpack",
"build:dev": "webpack", "build:dev": "webpack",
"build:watch": "webpack --watch", "build:watch": "webpack --watch",
"test": "karma start --single-run", "test": "karma start --single-run",
"test-debug": "cross-env NODE_ENV=debug karma start --no-single-run", "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
"test:coverage": "./scripts/test-coverage.sh",
"test:watch": "karma start --no-single-run", "test:watch": "karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'", "verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api", "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",

View File

@@ -87,6 +87,11 @@ define([
bootstrapper bootstrapper
); );
// Override of angular1.6 ! hashPrefix
app.config(['$locationProvider', function ($locationProvider) {
$locationProvider.hashPrefix('');
}]);
// Apply logging levels; this must be done now, before the // Apply logging levels; this must be done now, before the
// first log statement. // first log statement.
new LogLevel(logLevel).configure(app, $log); new LogLevel(logLevel).configure(app, $log);

View File

@@ -71,7 +71,7 @@ define([
}, },
{ {
"key": "ELASTIC_PATH", "key": "ELASTIC_PATH",
"value": "mct/domain_object", "value": "mct/_doc",
"priority": "fallback" "priority": "fallback"
}, },
{ {

View File

@@ -32,9 +32,9 @@ define(
// JSLint doesn't like underscore-prefixed properties, // JSLint doesn't like underscore-prefixed properties,
// so hide them here. // so hide them here.
var SRC = "_source", var SRC = "_source",
REV = "_version", CONFLICT = 409,
ID = "_id", SEQ_NO = "_seq_no",
CONFLICT = 409; PRIMARY_TERM = "_primary_term";
/** /**
* The ElasticPersistenceProvider reads and writes JSON documents * The ElasticPersistenceProvider reads and writes JSON documents
@@ -104,7 +104,8 @@ define(
// Get a domain object model out of ElasticSearch's response // Get a domain object model out of ElasticSearch's response
ElasticPersistenceProvider.prototype.getModel = function (response) { ElasticPersistenceProvider.prototype.getModel = function (response) {
if (response && response[SRC]) { if (response && response[SRC]) {
this.revs[response[ID]] = response[REV]; this.revs[response[SEQ_NO]] = response[SEQ_NO];
this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
return response[SRC]; return response[SRC];
} else { } else {
return undefined; return undefined;
@@ -116,7 +117,8 @@ define(
// indicate that the request failed. // indicate that the request failed.
ElasticPersistenceProvider.prototype.checkResponse = function (response, key) { ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
if (response && !response.error) { if (response && !response.error) {
this.revs[key] = response[REV]; this.revs[SEQ_NO] = response[SEQ_NO];
this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
return response; return response;
} else { } else {
return this.handleError(response, key); return this.handleError(response, key);
@@ -147,7 +149,7 @@ define(
function checkUpdate(response) { function checkUpdate(response) {
return self.checkResponse(response, key); return self.checkResponse(response, key);
} }
return this.put(key, value, { version: this.revs[key] }) return this.put(key, value)
.then(checkUpdate); .then(checkUpdate);
}; };

View File

@@ -85,7 +85,7 @@ define(
it("allows object creation", function () { it("allows object creation", function () {
var model = { someKey: "some value" }; var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1 } data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
})); }));
provider.createObject("testSpace", "abc", model).then(capture); provider.createObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -100,7 +100,7 @@ define(
it("allows object models to be read back", function () { it("allows object models to be read back", function () {
var model = { someKey: "some value" }; var model = { someKey: "some value" };
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1, "_source": model } data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
})); }));
provider.readObject("testSpace", "abc").then(capture); provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -117,19 +117,19 @@ define(
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
// Now perform an update // Now perform an update
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 43, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.updateObject("testSpace", "abc", model).then(capture); provider.updateObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc", url: "/test/db/abc",
method: "PUT", method: "PUT",
params: { version: 42 }, params: undefined,
data: model data: model
}); });
expect(capture.calls.mostRecent().args[0]).toBeTruthy(); expect(capture.calls.mostRecent().args[0]).toBeTruthy();
@@ -138,13 +138,13 @@ define(
it("allows object deletion", function () { it("allows object deletion", function () {
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
// Now perform an update // Now perform an update
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_source": {} }
})); }));
provider.deleteObject("testSpace", "abc", {}).then(capture); provider.deleteObject("testSpace", "abc", {}).then(capture);
expect(mockHttp).toHaveBeenCalledWith({ expect(mockHttp).toHaveBeenCalledWith({
@@ -167,13 +167,13 @@ define(
expect(capture).toHaveBeenCalledWith(undefined); expect(capture).toHaveBeenCalledWith(undefined);
}); });
it("handles rejection due to version", function () { it("handles rejection due to _seq_no", function () {
var model = { someKey: "some value" }, var model = { someKey: "some value" },
mockErrorCallback = jasmine.createSpy('error'); mockErrorCallback = jasmine.createSpy('error');
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");
@@ -196,7 +196,7 @@ define(
// First do a read to populate rev tags... // First do a read to populate rev tags...
mockHttp.and.returnValue(mockPromise({ mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} } data: { "_id": "abc", "_seq_no": 1, "_source": {} }
})); }));
provider.readObject("testSpace", "abc"); provider.readObject("testSpace", "abc");

View File

@@ -235,7 +235,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0]; var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined; defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.keyBy(metadata.values(), 'key'); var sourceMap = _.indexBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) === var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
telemetryAPI.legacyProvider; telemetryAPI.legacyProvider;
@@ -300,7 +300,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0]; var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined; defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.keyBy(metadata.values(), 'key'); var sourceMap = _.indexBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) === var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
telemetryAPI.legacyProvider; telemetryAPI.legacyProvider;

2
scripts/test-coverage.sh Executable file
View File

@@ -0,0 +1,2 @@
export NODE_OPTIONS=--max_old_space_size=4096
cross-env COVERAGE=true karma start --single-run

View File

@@ -252,6 +252,7 @@ define([
// Plugin's that are installed by default // Plugin's that are installed by default
this.install(this.plugins.Plot()); this.install(this.plugins.Plot());
this.install(this.plugins.PlotlyPlot());
this.install(this.plugins.TelemetryTable()); this.install(this.plugins.TelemetryTable());
this.install(PreviewPlugin.default()); this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin()); this.install(LegacyIndicatorsPlugin());

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import eq from 'lodash/eq'; import _ from 'lodash';
const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"]; const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "link", "remove", "locate"]; const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
@@ -74,7 +74,7 @@ export default class LegacyContextMenuAction {
* Is the object being edited, or a child of the object being edited? * Is the object being edited, or a child of the object being edited?
*/ */
function isInsideEditPath() { function isInsideEditPath() {
return objectPath.some((object) => eq(object.identifier, navigatedObject.identifier)); return objectPath.some((object) => _.eq(object.identifier, navigatedObject.identifier));
} }
if (isEditing) { if (isEditing) {

View File

@@ -196,8 +196,8 @@ define([
* @private * @private
*/ */
DefaultCompositionProvider.prototype.includes = function (parent, childId) { DefaultCompositionProvider.prototype.includes = function (parent, childId) {
return parent.composition.some(composee => return parent.composition.findIndex(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId)); this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
}; };
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) { DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {

View File

@@ -85,7 +85,7 @@ define([
value: +e.value value: +e.value
}; };
}), 'e.value'); }), 'e.value');
valueMetadata.values = _.map(valueMetadata.enumerations, 'value'); valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values); valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values); valueMetadata.min = _.min(valueMetadata.values);
} }

View File

@@ -370,7 +370,7 @@ define([
TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) { TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
var options = metadatas.map(function (metadata) { var options = metadatas.map(function (metadata) {
var values = metadata.valuesForHints(hints); var values = metadata.valuesForHints(hints);
return _.keyBy(values, 'key'); return _.indexBy(values, 'key');
}).reduce(function (a, b) { }).reduce(function (a, b) {
var results = {}; var results = {};
Object.keys(a).forEach(function (key) { Object.keys(a).forEach(function (key) {
@@ -383,7 +383,7 @@ define([
var sortKeys = hints.map(function (h) { var sortKeys = hints.map(function (h) {
return 'hints.' + h; return 'hints.' + h;
}); });
return _.sortBy(options, sortKeys); return _.sortByAll(options, sortKeys);
}; };
/** /**

View File

@@ -57,7 +57,7 @@ define([
if (valueMetadata.format === 'enum') { if (valueMetadata.format === 'enum') {
if (!valueMetadata.values) { if (!valueMetadata.values) {
valueMetadata.values = _.map(valueMetadata.enumerations, 'value'); valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
} }
if (!valueMetadata.hasOwnProperty('max')) { if (!valueMetadata.hasOwnProperty('max')) {
valueMetadata.max = _.max(valueMetadata.values) + 1; valueMetadata.max = _.max(valueMetadata.values) + 1;
@@ -121,7 +121,7 @@ define([
return metadata.hints[hint]; return metadata.hints[hint];
} }
}); });
return _.sortBy(matchingMetadata, ...iteratees); return _.sortByAll(matchingMetadata, ...iteratees);
}; };
TelemetryMetadataManager.prototype.getFilterableValues = function () { TelemetryMetadataManager.prototype.getFilterableValues = function () {

View File

@@ -69,7 +69,6 @@ export default class ConditionClass extends EventEmitter {
console.log('no data received'); console.log('no data received');
return; return;
} }
this.criteria.forEach(criterion => { this.criteria.forEach(criterion => {
if (this.isAnyOrAllTelemetry(criterion)) { if (this.isAnyOrAllTelemetry(criterion)) {
criterion.getResult(datum, this.conditionManager.telemetryObjects); criterion.getResult(datum, this.conditionManager.telemetryObjects);
@@ -204,7 +203,7 @@ export default class ConditionClass extends EventEmitter {
let latestTimestamp; let latestTimestamp;
let criteriaResults = {}; let criteriaResults = {};
const criteriaRequests = this.criteria const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects})); .map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests) return Promise.all(criteriaRequests)
.then(results => { .then(results => {

View File

@@ -55,8 +55,9 @@ export default class ConditionManager extends EventEmitter {
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas}); this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
this.subscriptions[id] = this.openmct.telemetry.subscribe( this.subscriptions[id] = this.openmct.telemetry.subscribe(
endpoint, endpoint,
this.telemetryReceived.bind(this, id) this.telemetryReceived.bind(this, endpoint)
); );
// TODO check if this is needed
this.updateConditionTelemetry(); this.updateConditionTelemetry();
} }
@@ -258,9 +259,13 @@ export default class ConditionManager extends EventEmitter {
this.openmct.time.timeSystem() this.openmct.time.timeSystem()
); );
}); });
const currentCondition = this.getCurrentConditionLAD(conditionResults);
return Object.assign( if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
return [];
}
const currentCondition = this.getCurrentConditionLAD(conditionResults);
const currentOutput = Object.assign(
{ {
output: currentCondition.configuration.output, output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier, id: this.conditionSetDomainObject.identifier,
@@ -268,11 +273,15 @@ export default class ConditionManager extends EventEmitter {
}, },
latestTimestamp latestTimestamp
); );
return [currentOutput];
}); });
}); });
} }
isTelemetryUsed(id) { isTelemetryUsed(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditionClassCollection) { for(const condition of this.conditionClassCollection) {
if (condition.isTelemetryUsed(id)) { if (condition.isTelemetryUsed(id)) {
return true; return true;
@@ -282,12 +291,12 @@ export default class ConditionManager extends EventEmitter {
return false; return false;
} }
telemetryReceived(id, datum) { telemetryReceived(endpoint, datum) {
if (!this.isTelemetryUsed(id)) { if (!this.isTelemetryUsed(endpoint)) {
return; return;
} }
const normalizedDatum = this.createNormalizedDatum(datum, id); const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
const timeSystemKey = this.openmct.time.timeSystem().key; const timeSystemKey = this.openmct.time.timeSystem().key;
let timestamp = {}; let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey]; timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
@@ -321,8 +330,11 @@ export default class ConditionManager extends EventEmitter {
return data; return data;
} }
createNormalizedDatum(telemetryDatum, id) { createNormalizedDatum(telemetryDatum, endpoint) {
const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => { const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const testValue = this.getTestData(metadatum); const testValue = this.getTestData(metadatum);
const formatter = this.openmct.telemetry.getValueFormatter(metadatum); const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]); datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);

View File

@@ -54,13 +54,22 @@ export default class ConditionSetMetadataProvider {
return { return {
values: this.getDomains().concat([ values: this.getDomains().concat([
{ {
name: 'Output', key: "state",
key: 'output', source: "output",
format: 'enum', name: "State",
format: "enum",
enumerations: enumerations, enumerations: enumerations,
hints: { hints: {
range: 1 range: 1
} }
},
{
key: "output",
name: "Value",
format: "string",
hints: {
range: 2
}
} }
]) ])
}; };

View File

@@ -45,7 +45,7 @@ export default class ConditionSetTelemetryProvider {
return conditionManager.requestLADConditionSetOutput() return conditionManager.requestLADConditionSetOutput()
.then(latestOutput => { .then(latestOutput => {
return latestOutput ? [latestOutput] : []; return latestOutput;
}); });
} }

View File

@@ -23,10 +23,13 @@
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
export default class StyleRuleManager extends EventEmitter { export default class StyleRuleManager extends EventEmitter {
constructor(styleConfiguration, openmct, callback) { constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
super(); super();
this.openmct = openmct; this.openmct = openmct;
this.callback = callback; this.callback = callback;
if (suppressSubscriptionOnEdit) {
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
}
if (styleConfiguration) { if (styleConfiguration) {
this.initialize(styleConfiguration); this.initialize(styleConfiguration);
if (styleConfiguration.conditionSetIdentifier) { if (styleConfiguration.conditionSetIdentifier) {
@@ -37,9 +40,25 @@ export default class StyleRuleManager extends EventEmitter {
} }
} }
toggleSubscription(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetIdentifier) {
this.applySelectedConditionStyle();
}
} else if (this.conditionSetIdentifier) {
this.subscribeToConditionSet();
}
}
initialize(styleConfiguration) { initialize(styleConfiguration) {
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier; this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
this.staticStyle = styleConfiguration.staticStyle; this.staticStyle = styleConfiguration.staticStyle;
this.selectedConditionId = styleConfiguration.selectedConditionId;
this.defaultConditionId = styleConfiguration.defaultConditionId;
this.updateConditionStylesMap(styleConfiguration.styles || []); this.updateConditionStylesMap(styleConfiguration.styles || []);
} }
@@ -54,7 +73,7 @@ export default class StyleRuleManager extends EventEmitter {
this.handleConditionSetResultUpdated(output[0]); this.handleConditionSetResultUpdated(output[0]);
} }
}); });
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output)); this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}); });
} }
@@ -66,9 +85,13 @@ export default class StyleRuleManager extends EventEmitter {
let isNewConditionSet = !this.conditionSetIdentifier || let isNewConditionSet = !this.conditionSetIdentifier ||
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier); !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
this.initialize(styleConfiguration); this.initialize(styleConfiguration);
//Only resubscribe if the conditionSet has changed. if (this.isEditing) {
if (isNewConditionSet) { this.applySelectedConditionStyle();
this.subscribeToConditionSet(); } else {
//Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) {
this.subscribeToConditionSet();
}
} }
} }
} }
@@ -103,6 +126,16 @@ export default class StyleRuleManager extends EventEmitter {
} }
} }
applySelectedConditionStyle() {
const conditionId = this.selectedConditionId || this.defaultConditionId;
if (!conditionId) {
this.applyStaticStyle();
} else if (this.conditionalStyleMap[conditionId]) {
this.currentStyle = this.conditionalStyleMap[conditionId];
this.updateDomainObjectStyle();
}
}
applyStaticStyle() { applyStaticStyle() {
if (this.staticStyle) { if (this.staticStyle) {
this.currentStyle = this.staticStyle.style; this.currentStyle = this.staticStyle.style;
@@ -123,6 +156,8 @@ export default class StyleRuleManager extends EventEmitter {
} }
delete this.stopProvidingTelemetry; delete this.stopProvidingTelemetry;
this.conditionSetIdentifier = undefined; this.conditionSetIdentifier = undefined;
this.isEditing = undefined;
this.callback = undefined;
} }
} }

View File

@@ -79,6 +79,8 @@
<div v-for="(conditionStyle, index) in conditionalStyles" <div v-for="(conditionStyle, index) in conditionalStyles"
:key="index" :key="index"
class="c-inspect-styles__condition" class="c-inspect-styles__condition"
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
> >
<condition-error :show-label="true" <condition-error :show-label="true"
:condition="getCondition(conditionStyle.conditionId)" :condition="getCondition(conditionStyle.conditionId)"
@@ -126,7 +128,8 @@ export default {
isEditing: this.openmct.editor.isEditing(), isEditing: this.openmct.editor.isEditing(),
conditions: undefined, conditions: undefined,
conditionsLoaded: false, conditionsLoaded: false,
navigateToPath: '' navigateToPath: '',
selectedConditionId: ''
} }
}, },
destroyed() { destroyed() {
@@ -191,6 +194,9 @@ export default {
if (this.stopObservingItems) { if (this.stopObservingItems) {
this.stopObservingItems(); this.stopObservingItems();
} }
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
}, },
initialize(conditionSetDomainObject) { initialize(conditionSetDomainObject) {
//If there are new conditions in the conditionSet we need to set those styles to default //If there are new conditions in the conditionSet we need to set those styles to default
@@ -200,6 +206,13 @@ export default {
}, },
setEditState(isEditing) { setEditState(isEditing) {
this.isEditing = isEditing; this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
} else {
this.subscribeToConditionSet();
}
}, },
addConditionSet() { addConditionSet() {
let conditionSetDomainObject; let conditionSetDomainObject;
@@ -270,6 +283,8 @@ export default {
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {}; let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) { if (this.itemId) {
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined; domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
domainObjectStyles[this.itemId].selectedConditionId = undefined;
domainObjectStyles[this.itemId].defaultConditionId = undefined;
delete domainObjectStyles[this.itemId].conditionSetIdentifier; delete domainObjectStyles[this.itemId].conditionSetIdentifier;
domainObjectStyles[this.itemId].styles = undefined; domainObjectStyles[this.itemId].styles = undefined;
delete domainObjectStyles[this.itemId].styles; delete domainObjectStyles[this.itemId].styles;
@@ -278,6 +293,8 @@ export default {
} }
} else { } else {
domainObjectStyles.conditionSetIdentifier = undefined; domainObjectStyles.conditionSetIdentifier = undefined;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
delete domainObjectStyles.conditionSetIdentifier; delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined; domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles; delete domainObjectStyles.styles;
@@ -287,16 +304,23 @@ export default {
} }
this.persist(domainObjectStyles); this.persist(domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
}, },
updateDomainObjectItemStyles(newItems) { updateDomainObjectItemStyles(newItems) {
//check that all items that have been styles still exist. Otherwise delete those styles //check that all items that have been styles still exist. Otherwise delete those styles
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {}; let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
let itemsToRemove = []; let itemsToRemove = [];
let keys = Object.keys(domainObjectStyles); let keys = Object.keys(domainObjectStyles);
//TODO: Need an easier way to find which properties are itemIds
keys.forEach((key) => { keys.forEach((key) => {
if ((key !== 'styles') && const keyIsItemId = (key !== 'styles') &&
(key !== 'staticStyle') && (key !== 'staticStyle') &&
(key !== 'conditionSetIdentifier')) { (key !== 'defaultConditionId') &&
(key !== 'selectedConditionId') &&
(key !== 'conditionSetIdentifier');
if (keyIsItemId) {
if (!(newItems.find(item => item.id === key))) { if (!(newItems.find(item => item.id === key))) {
itemsToRemove.push(key); itemsToRemove.push(key);
} }
@@ -324,6 +348,9 @@ export default {
} }
let conditionalStyles = []; let conditionalStyles = [];
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => { this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
if (conditionConfiguration.isDefault) {
this.selectedConditionId = conditionConfiguration.id;
}
this.conditions[conditionConfiguration.id] = conditionConfiguration; this.conditions[conditionConfiguration.id] = conditionConfiguration;
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id); let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
if (foundStyle) { if (foundStyle) {
@@ -339,7 +366,27 @@ export default {
//we're doing this so that we remove styles for any conditions that have been removed from the condition set //we're doing this so that we remove styles for any conditions that have been removed from the condition set
this.conditionalStyles = conditionalStyles; this.conditionalStyles = conditionalStyles;
this.conditionsLoaded = true; this.conditionsLoaded = true;
this.persist(this.getDomainObjectConditionalStyle()); this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
if (!this.isEditing) {
this.subscribeToConditionSet();
}
},
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetDomainObject) {
this.openmct.telemetry.request(this.conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}
},
handleConditionSetResultUpdated(resultData) {
this.selectedConditionId = resultData ? resultData.conditionId : '';
}, },
initializeStaticStyle(objectStyles) { initializeStaticStyle(objectStyles) {
let staticStyle = objectStyles && objectStyles.staticStyle; let staticStyle = objectStyles && objectStyles.staticStyle;
@@ -364,14 +411,19 @@ export default {
let found = this.findStyleByConditionId(conditionStyle.conditionId); let found = this.findStyleByConditionId(conditionStyle.conditionId);
if (found) { if (found) {
found.style = conditionStyle.style; found.style = conditionStyle.style;
this.selectedConditionId = found.conditionId;
this.persist(this.getDomainObjectConditionalStyle()); this.persist(this.getDomainObjectConditionalStyle());
} }
}, },
getDomainObjectConditionalStyle() { getDomainObjectConditionalStyle(defaultConditionId) {
let objectStyle = { let objectStyle = {
styles: this.conditionalStyles, styles: this.conditionalStyles,
staticStyle: this.staticStyle staticStyle: this.staticStyle,
selectedConditionId: this.selectedConditionId
}; };
if (defaultConditionId) {
objectStyle.defaultConditionId = defaultConditionId;
}
if (this.conditionSetDomainObject) { if (this.conditionSetDomainObject) {
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier; objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
} }
@@ -393,6 +445,10 @@ export default {
getCondition(id) { getCondition(id) {
return this.conditions ? this.conditions[id] : {}; return this.conditions ? this.conditions[id] : {};
}, },
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.persist(this.getDomainObjectConditionalStyle());
},
persist(style) { persist(style) {
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style); this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
} }

View File

@@ -60,6 +60,31 @@
&__condition { &__condition {
@include discreteItem(); @include discreteItem();
border: 1px solid transparent;
pointer-events: none; // Prevent selecting when the object isn't being edited
&.is-current {
$c: $colorBodyFg;
border-color: rgba($c, 0.5);
background: rgba($c, 0.2);
}
.is-editing & {
cursor: pointer;
pointer-events: initial;
transition: $transOut;
&:hover {
background: rgba($colorBodyFg, 0.1);
transition: $transIn;
}
&.is-current {
$c: $editUIColorBg;
border-color: $c;
background: rgba($c, 0.1);
}
}
} }
.c-style { .c-style {

View File

@@ -22,6 +22,7 @@
import TelemetryCriterion from './TelemetryCriterion'; import TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator"; import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time';
export default class AllTelemetryCriterion extends TelemetryCriterion { export default class AllTelemetryCriterion extends TelemetryCriterion {
@@ -107,40 +108,53 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry); this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
} }
requestLAD(options) { requestLAD(telemetryObjects) {
options = Object.assign({}, const options = {
options, strategy: 'latest',
{ size: 1
strategy: 'latest', };
size: 1
}
);
if (!this.isValid()) { if (!this.isValid()) {
return this.formatData({}, options.telemetryObjects); return this.formatData({}, telemetryObjects);
} }
let keys = Object.keys(Object.assign({}, options.telemetryObjects)); let keys = Object.keys(Object.assign({}, telemetryObjects));
const telemetryRequests = keys const telemetryRequests = keys
.map(key => this.openmct.telemetry.request( .map(key => this.openmct.telemetry.request(
options.telemetryObjects[key], telemetryObjects[key],
options options
)); ));
let telemetryDataCache = {};
return Promise.all(telemetryRequests) return Promise.all(telemetryRequests)
.then(telemetryRequestsResults => { .then(telemetryRequestsResults => {
let latestDatum; let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => { telemetryRequestsResults.forEach((results, index) => {
latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
if (index < telemetryRequestsResults.length-1) { const datumId = keys[index];
if (latestDatum) { const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
} telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
}
latestTimestamp = getLatestTimestamp(
latestTimestamp,
normalizedDatum,
timeSystems,
timeSystem
);
}); });
const datum = {
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
...latestTimestamp
};
return { return {
id: this.id, id: this.id,
data: this.formatData(latestDatum, options.telemetryObjects) data: datum
}; };
}); });
} }

View File

@@ -61,6 +61,21 @@ export default class TelemetryCriterion extends EventEmitter {
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString]; this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
} }
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
return datum;
}, {});
normalizedDatum.id = id;
return normalizedDatum;
}
formatData(data) { formatData(data) {
const datum = { const datum = {
result: this.computeResult(data) result: this.computeResult(data)
@@ -79,14 +94,11 @@ export default class TelemetryCriterion extends EventEmitter {
this.result = this.computeResult(validatedData); this.result = this.computeResult(validatedData);
} }
requestLAD(options) { requestLAD() {
options = Object.assign({}, const options = {
options, strategy: 'latest',
{ size: 1
strategy: 'latest', };
size: 1
}
);
if (!this.isValid()) { if (!this.isValid()) {
return { return {
@@ -100,9 +112,11 @@ export default class TelemetryCriterion extends EventEmitter {
options options
).then(results => { ).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
return { return {
id: this.id, id: this.id,
data: this.formatData(latestDatum) data: this.formatData(normalizedDatum)
}; };
}); });
} }

View File

@@ -20,6 +20,8 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import _ from 'lodash';
const convertToNumbers = (input) => { const convertToNumbers = (input) => {
let numberInputs = []; let numberInputs = [];
input.forEach(inputValue => numberInputs.push(Number(inputValue))); input.forEach(inputValue => numberInputs.push(Number(inputValue)));
@@ -32,6 +34,10 @@ const convertToStrings = (input) => {
return stringInputs; return stringInputs;
}; };
const joinValues = (values, length) => {
return values.slice(0, length).join(', ');
};
export const OPERATIONS = [ export const OPERATIONS = [
{ {
name: 'equalTo', name: 'equalTo',
@@ -42,7 +48,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is ' + values.join(', '); return ' is ' + joinValues(values, 1);
} }
}, },
{ {
@@ -54,7 +60,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is not ' + values.join(', '); return ' is not ' + joinValues(values, 1);
} }
}, },
{ {
@@ -66,7 +72,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' > ' + values.join(', '); return ' > ' + joinValues(values, 1);
} }
}, },
{ {
@@ -78,7 +84,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' < ' + values.join(', '); return ' < ' + joinValues(values, 1);
} }
}, },
{ {
@@ -90,7 +96,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' >= ' + values.join(', '); return ' >= ' + joinValues(values, 1);
} }
}, },
{ {
@@ -102,7 +108,7 @@ export const OPERATIONS = [
appliesTo: ['number'], appliesTo: ['number'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' <= ' + values.join(', '); return ' <= ' + joinValues(values, 1);
} }
}, },
{ {
@@ -144,7 +150,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' contains ' + values.join(', '); return ' contains ' + joinValues(values, 1);
} }
}, },
{ {
@@ -156,7 +162,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' does not contain ' + values.join(', '); return ' does not contain ' + joinValues(values, 1);
} }
}, },
{ {
@@ -168,7 +174,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' starts with ' + values.join(', '); return ' starts with ' + joinValues(values, 1);
} }
}, },
{ {
@@ -180,7 +186,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' ends with ' + values.join(', '); return ' ends with ' + joinValues(values, 1);
} }
}, },
{ {
@@ -192,7 +198,7 @@ export const OPERATIONS = [
appliesTo: ['string'], appliesTo: ['string'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is exactly ' + values.join(', '); return ' is exactly ' + joinValues(values, 1);
} }
}, },
{ {
@@ -229,7 +235,7 @@ export const OPERATIONS = [
appliesTo: ['enum'], appliesTo: ['enum'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is ' + values.join(', '); return ' is ' + joinValues(values, 1);
} }
}, },
{ {
@@ -242,7 +248,7 @@ export const OPERATIONS = [
appliesTo: ['enum'], appliesTo: ['enum'],
inputCount: 1, inputCount: 1,
getDescription: function (values) { getDescription: function (values) {
return ' is not ' + values.join(', '); return ' is not ' + joinValues(values, 1);
} }
}, },
{ {
@@ -251,7 +257,7 @@ export const OPERATIONS = [
const lhsValue = input[0] !== undefined ? input[0].toString() : ''; const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) { if (input[1]) {
const values = input[1].split(','); const values = input[1].split(',');
return values.find((value) => lhsValue === value.toString().trim()); return values.find((value) => lhsValue === _.trim(value.toString()));
} }
return false; return false;
}, },
@@ -268,7 +274,7 @@ export const OPERATIONS = [
const lhsValue = input[0] !== undefined ? input[0].toString() : ''; const lhsValue = input[0] !== undefined ? input[0].toString() : '';
if (input[1]) { if (input[1]) {
const values = input[1].split(','); const values = input[1].split(',');
const found = values.find((value) => lhsValue === value.toString().trim()); const found = values.find((value) => lhsValue === _.trim(value.toString()));
return !found; return !found;
} }
return false; return false;

View File

@@ -52,7 +52,7 @@ export default {
}, },
initObjectStyles() { initObjectStyles() {
if (!this.styleRuleManager) { if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this)); this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this), true);
} else { } else {
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle); this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
} }

View File

@@ -59,7 +59,7 @@
</template> </template>
<script> <script>
import sortedIndexBy from 'lodash/sortedIndexBy'; import _ from 'lodash';
export default { export default {
inject: ['openmct', 'domainObject'], inject: ['openmct', 'domainObject'],
@@ -219,7 +219,8 @@ export default {
if (this.datumMatchesMostRecent(datum)) { if (this.datumMatchesMostRecent(datum)) {
return; return;
} }
const index = sortedIndexBy(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
const index = _.sortedIndex(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
this.imageHistory.splice(index, 0, datum); this.imageHistory.splice(index, 0, datum);
}, },
updateValues(datum) { updateValues(datum) {

View File

@@ -162,19 +162,31 @@ export default {
}).show(this.embed.snapshot.src); }).show(this.embed.snapshot.src);
}, },
changeLocation() { changeLocation() {
this.openmct.time.stopClock();
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
const link = this.embed.historicLink; const link = this.embed.historicLink;
if (!link) { if (!link) {
return; return;
} }
const bounds = this.openmct.time.bounds();
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
&& this.embed.bounds.end !== bounds.end;
const isFixedTimespanMode = !this.openmct.time.clock();
window.location.href = link; window.location.href = link;
const message = 'Time bounds changed to fixed timespan mode';
let message = '';
if (isTimeBoundChanged) {
this.openmct.time.bounds({
start: this.embed.bounds.start,
end: this.embed.bounds.end
});
message = 'Time bound values changed';
}
if (!isFixedTimespanMode) {
message = 'Time bound values changed to fixed timespan mode';
}
this.openmct.notifications.alert(message); this.openmct.notifications.alert(message);
}, },
formatTime(unixTime, timeFormat) { formatTime(unixTime, timeFormat) {

View File

@@ -221,7 +221,7 @@ export default {
return position; return position;
}, },
formatTime(unixTime, timeFormat) { formatTime(unixTime, timeFormat) {
return Moment(unixTime).format(timeFormat); return Moment.utc(unixTime).format(timeFormat);
}, },
moveSnapshot(snapshotId) { moveSnapshot(snapshotId) {
const snapshot = this.snapshotContainer.getSnapshot(snapshotId); const snapshot = this.snapshotContainer.getSnapshot(snapshotId);

View File

@@ -40,6 +40,18 @@ export default {
default() { default() {
return {}; return {};
} }
},
ignoreLink: {
type: Boolean,
default() {
return false;
}
},
objectPath: {
type: Array,
default() {
return null;
}
} }
}, },
data() { data() {
@@ -97,17 +109,27 @@ export default {
this.showMenu = false; this.showMenu = false;
}, },
snapshot(notebook) { snapshot(notebook) {
let element = document.getElementsByClassName("l-shell__main-container")[0]; this.hideMenu();
const bounds = this.openmct.time.bounds();
const objectPath = this.openmct.router.path;
const snapshotMeta = {
bounds,
link: window.location.href,
objectPath,
openmct: this.openmct
};
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element); this.$nextTick(() => {
const element = document.querySelector('.c-overlay__contents')
|| document.getElementsByClassName('l-shell__main-container')[0];
const bounds = this.openmct.time.bounds();
const link = !this.ignoreLink
? window.location.href
: null;
const objectPath = this.objectPath || this.openmct.router.path;
const snapshotMeta = {
bounds,
link,
objectPath,
openmct: this.openmct
};
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
});
} }
} }
} }

View File

@@ -111,7 +111,7 @@ import SearchResults from './search-results.vue';
import Sidebar from './sidebar.vue'; import Sidebar from './sidebar.vue';
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage'; import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
import { addNotebookEntry, createNewEmbed, getNotebookEntries } from '../utils/notebook-entries'; import { addNotebookEntry, createNewEmbed, getNotebookEntries } from '../utils/notebook-entries';
import throttle from 'lodash/throttle'; import { throttle } from 'lodash';
const DEFAULT_CLASS = 'is-notebook-default'; const DEFAULT_CLASS = 'is-notebook-default';
@@ -495,7 +495,7 @@ export default {
return; return;
} }
if (section.id !== defaultNotebookSection.id) { if (id !== defaultNotebookSection.id) {
return; return;
} }

View File

@@ -21,7 +21,7 @@
--> -->
<div ng-controller="StackedPlotController as stackedPlot" <div ng-controller="StackedPlotController as stackedPlot"
class="c-plot c-plot--stacked holder holder-plot has-control-bar"> class="c-plot c-plot--stacked holder holder-plot has-control-bar">
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons"> <div class="c-control-bar" ng-show="!stackedPlot.hideExportButtons">
<span class="c-button-set c-button-set--strip-h"> <span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download" <button class="c-button icon-download"
ng-click="stackedPlot.exportPNG()" ng-click="stackedPlot.exportPNG()"

View File

@@ -54,27 +54,29 @@ function (
* @constructor * @constructor
*/ */
function MCTChartController($scope) { function MCTChartController($scope) {
this.$scope = $scope; this.$onInit = () => {
this.isDestroyed = false; this.$scope = $scope;
this.lines = []; this.isDestroyed = false;
this.pointSets = []; this.lines = [];
this.alarmSets = []; this.pointSets = [];
this.offset = {}; this.alarmSets = [];
this.config = $scope.config; this.offset = {};
this.listenTo(this.$scope, '$destroy', this.destroy, this); this.config = $scope.config;
this.draw = this.draw.bind(this); this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.scheduleDraw = this.scheduleDraw.bind(this); this.draw = this.draw.bind(this);
this.seriesElements = new WeakMap(); this.scheduleDraw = this.scheduleDraw.bind(this);
this.seriesElements = new WeakMap();
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this); this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this); this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this); this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this); this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw); this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw); this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.$scope.$watch('highlights', this.scheduleDraw); this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw); this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this); this.config.series.forEach(this.onSeriesAdd, this);
}
} }
eventHelpers.extend(MCTChartController.prototype); eventHelpers.extend(MCTChartController.prototype);
@@ -150,7 +152,7 @@ function (
MCTChartController.prototype.destroy = function () { MCTChartController.prototype.destroy = function () {
this.isDestroyed = true; this.isDestroyed = true;
this.stopListening(); this.stopListening();
_.invokeMap(this.lines, 'destroy'); _.invoke(this.lines, 'destroy');
DrawLoader.releaseDrawAPI(this.drawAPI); DrawLoader.releaseDrawAPI(this.drawAPI);
}; };

View File

@@ -55,10 +55,10 @@ define([
}; };
} }
var newRange = {}; var newRange = {};
if (!_.isNil(range.min)) { if (typeof range.min !== 'undefined' && range.min !== null) {
newRange.min = Number(range.min); newRange.min = Number(range.min);
} }
if (!_.isNil(range.max)) { if (typeof range.max !== 'undefined' && range.max !== null) {
newRange.max = Number(range.max); newRange.max = Number(range.max);
} }
return newRange; return newRange;

View File

@@ -34,25 +34,27 @@ define([
* values near the cursor. * values near the cursor.
*/ */
function MCTPlotController($scope, $element, $window) { function MCTPlotController($scope, $element, $window) {
this.$scope = $scope; this.$onInit = () => {
this.$scope.config = this.config; this.$scope = $scope;
this.$scope.plot = this; this.$scope.config = this.config;
this.$element = $element; this.$scope.plot = this;
this.$window = $window; this.$element = $element;
this.$window = $window;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange')); this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange')); this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
this.pan = undefined; this.pan = undefined;
this.marquee = undefined; this.marquee = undefined;
this.chartElementBounds = undefined; this.chartElementBounds = undefined;
this.tickUpdate = false; this.tickUpdate = false;
this.$scope.plotHistory = this.plotHistory = []; this.$scope.plotHistory = this.plotHistory = [];
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this); this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize(); this.initialize();
}
} }
MCTPlotController.$inject = ['$scope', '$element', '$window']; MCTPlotController.$inject = ['$scope', '$element', '$window'];

View File

@@ -114,15 +114,17 @@ define([
} }
function MCTTicksController($scope, $element) { function MCTTicksController($scope, $element) {
this.$scope = $scope; this.$onInit = () => {
this.$element = $element; this.$scope = $scope;
this.$element = $element;
this.tickCount = 4; this.tickCount = 4;
this.tickUpdate = false; this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this); this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
this.listenTo(this.axis, 'change:format', this.updateTicks, this); this.listenTo(this.axis, 'change:format', this.updateTicks, this);
this.listenTo(this.$scope, '$destroy', this.stopListening, this); this.listenTo(this.$scope, '$destroy', this.stopListening, this);
this.updateTicks(); this.updateTicks();
}
} }
MCTTicksController.$inject = ['$scope', '$element']; MCTTicksController.$inject = ['$scope', '$element'];

View File

@@ -81,7 +81,8 @@ define(
clonedElement.classList.add(className); clonedElement.classList.add(className);
} }
element.id = oldId; element.id = oldId;
} },
removeContainer: true // Set to false to debug what html2canvas renders
}).then(function (canvas) { }).then(function (canvas) {
dialog.dismiss(); dialog.dismiss();
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {

View File

@@ -76,7 +76,7 @@ define([
if (childObj) { if (childObj) {
var index = telemetryObjects.indexOf(childObj); var index = telemetryObjects.indexOf(childObj);
telemetryObjects.splice(index, 1); telemetryObjects.splice(index, 1);
$scope.$broadcast('plot:tickWidth', _.max(Object.values(tickWidthMap))); $scope.$broadcast('plot:tickWidth', _.max(tickWidthMap));
} }
} }

View File

@@ -0,0 +1,35 @@
export default class PlotlyTelemetryProvider {
constructor(openmct) {
this.openmct = openmct;
}
isTelemetryObject(domainObject) {
return domainObject.type === 'plotlyPlot';
}
supportsRequest(domainObject) {
return domainObject.type === 'plotlyPlot';
}
supportsSubscribe(domainObject) {
return domainObject.type === 'plotlyPlot';
}
request(domainObject) {
// let conditionManager = this.getConditionManager(domainObject);
// return conditionManager.requestLADConditionSetOutput()
// .then(latestOutput => {
// return latestOutput;
// });
}
subscribe(domainObject, callback) {
// let conditionManager = this.getConditionManager(domainObject);
// conditionManager.on('conditionSetResultUpdated', callback);
// return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
}
}

View File

@@ -0,0 +1,73 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import PlotlyViewLayout from './components/PlotlyViewLayout.vue';
import Vue from 'vue';
export default function PlotlyViewProvider(openmct) {
return {
key: 'plotlyPlot',
name: 'Plotly Plot',
cssClass: 'icon-plot-overlay',
canView: function (domainObject) {
return domainObject.type === 'plotlyPlot';
},
canEdit: function (domainObject) {
return domainObject.type === 'plotlyPlot';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element, isEditing) {
component = new Vue({
provide: {
openmct,
domainObject,
objectPath
},
el: element,
components: {
PlotlyViewLayout
},
data() {
return {
isEditing
}
},
template: '<plotly-view-layout :isEditing="isEditing"></plotly-view-layout>'
});
},
onEditModeChange: function (isEditing) {
component.isEditing = isEditing;
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}

View File

@@ -0,0 +1,138 @@
<template>
<div class="l-view-section"></div>
</template>
<script>
import Plotly from 'plotly.js-dist';
import moment from 'moment'
export default {
inject: ['openmct', 'domainObject', 'objectPath'],
data: function () {
return {
telemetryObjects: []
// currentDomainObject: this.domainObject
}
},
mounted() {
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addTelemetry);
this.composition.load();
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
console.log('this.metadata', this.metadata);
// this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
// this.subscribe(this.domainObject);
this.plotElement = document.querySelector('.l-view-section');
// Plotly.newPlot(this.plotElement, [{
// x: [1, 2, 3, 4, 5],
// y: [1, 2, 4, 8, 16]
// }], this.getLayout(), {displayModeBar: false});
},
methods: {
getLayout() {
return {
hovermode: 'compare',
hoverdistance: -1,
autosize: "true",
showlegend: false,
font: {
family: "'Helvetica Neue', Helvetica, Arial, sans-serif",
size: "12px",
color: "#666"
},
xaxis: {
// title: this.plotAxisTitle.xAxisTitle,
zeroline: false
},
yaxis: {
// title: this.plotAxisTitle.yAxisTitle,
zeroline: false
},
margin: {
l: 20,
r: 10,
b: 20,
t: 10
},
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent'
}
},
addTelemetry(telemetryObject) {
return this.openmct.telemetry.request(telemetryObject)
.then(telemetryData => {
this.createPlot(telemetryData, telemetryObject);
}, () => {console.log(error)});
},
formatDatumX(datum) {
let timestamp = moment.utc(datum.utc).format('YYYY-MM-DD hh:mm:ss.ms');
return timestamp;
},
formatDatumY(datum) {
return datum.sin;
},
createPlot(telemetryData, telemetryObject) {
let x = [],
y = [];
telemetryData.forEach((datum, index) => {
x.push(this.formatDatumX(datum));
y.push(this.formatDatumY(datum));
})
let data = [{
x,
y,
mode: 'line'
}];
var layout = {
title:'Line and Scatter Plot'
};
Plotly.newPlot(
this.plotElement,
data,
this.getLayout()
)
this.subscribe(telemetryObject);
},
subscribe(domainObject) {
// this.date = ''
// this.openmct.objects.get(this.keystring)
// .then((object) => {
// const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
// console.log('metadata', metadata);
// // this.timeKey = this.openmct.time.timeSystem().key;
// // this.timeFormat = this.openmct.telemetry.getValueFormatter(metadata.value(this.timeKey));
// // // this.imageFormat = this.openmct.telemetry.getValueFormatter(metadata.valuesForHints(['image'])[0]);
// // this.unsubscribe = this.openmct.telemetry
// // .subscribe(this.domainObject, (datum) => {
// // this.updateHistory(datum);
// // this.updateValues(datum);
// // });
// // this.requestHistory(this.openmct.time.bounds());
// });
this.openmct.telemetry.subscribe(domainObject, (datum) => {
this.updateData(datum)
})
},
updateData(datum) {
Plotly.extendTraces(
this.plotElement,
{
x: [[this.formatDatumX(datum)]],
y: [[this.formatDatumY(datum)]]
},
[0]
);
}
}
}
</script>

View File

@@ -0,0 +1,2 @@
.plot svg {
}

View File

@@ -0,0 +1,20 @@
import PlotlyViewProvider from './PlotlyViewProvider.js';
import PlotlyTelemetryProvider from './PlotlyTelemetryProvider.js';
export default function () {
return function install(openmct) {
openmct.objectViews.addProvider(new PlotlyViewProvider(openmct));
openmct.telemetry.addProvider(new PlotlyTelemetryProvider(openmct));
openmct.types.addType('plotlyPlot', {
name: "Plotly Plot",
description: "Simple plot rendered by plotly.js",
creatable: true,
cssClass: 'icon-plot-overlay',
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.telemetry = {};
}
});
};
}

View File

@@ -34,6 +34,7 @@ define([
'./URLIndicatorPlugin/URLIndicatorPlugin', './URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin', './telemetryMean/plugin',
'./plot/plugin', './plot/plugin',
'./plotlyPlot/plugin',
'./telemetryTable/plugin', './telemetryTable/plugin',
'./staticRootPlugin/plugin', './staticRootPlugin/plugin',
'./notebook/plugin', './notebook/plugin',
@@ -66,6 +67,7 @@ define([
URLIndicatorPlugin, URLIndicatorPlugin,
TelemetryMean, TelemetryMean,
PlotPlugin, PlotPlugin,
PlotlyPlotPlugin,
TelemetryTablePlugin, TelemetryTablePlugin,
StaticRootPlugin, StaticRootPlugin,
Notebook, Notebook,
@@ -88,7 +90,8 @@ define([
var bundleMap = { var bundleMap = {
LocalStorage: 'platform/persistence/local', LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items', MyItems: 'platform/features/my-items',
CouchDB: 'platform/persistence/couch' CouchDB: 'platform/persistence/couch',
Elasticsearch: 'platform/persistence/elastic'
}; };
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) { var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
@@ -170,8 +173,8 @@ define([
plugins.ExampleImagery = ExampleImagery; plugins.ExampleImagery = ExampleImagery;
plugins.ImageryPlugin = ImageryPlugin; plugins.ImageryPlugin = ImageryPlugin;
plugins.Plot = PlotPlugin; plugins.Plot = PlotPlugin;
plugins.PlotlyPlot = PlotlyPlotPlugin.default;
plugins.TelemetryTable = TelemetryTablePlugin; plugins.TelemetryTable = TelemetryTablePlugin;
plugins.SummaryWidget = SummaryWidget; plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean; plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin; plugins.URLIndicator = URLIndicatorPlugin;

View File

@@ -154,7 +154,7 @@ define([
return _(this.baseState) return _(this.baseState)
.values() .values()
.map(_.clone) .map(_.clone)
.keyBy('id') .indexBy('id')
.value(); .value();
}.bind(this)); }.bind(this));
}; };

View File

@@ -57,7 +57,7 @@
<script> <script>
import ObjectView from '../../../ui/components/ObjectView.vue'; import ObjectView from '../../../ui/components/ObjectView.vue';
import isEqual from 'lodash/isEqual'; import _ from 'lodash';
var unknownObjectType = { var unknownObjectType = {
definition: { definition: {
@@ -155,7 +155,7 @@ export default {
this.allowDrop = false; this.allowDrop = false;
}, },
isCurrent(tab) { isCurrent(tab) {
return isEqual(this.currentTab, tab) return _.isEqual(this.currentTab, tab)
} }
} }
} }

View File

@@ -100,7 +100,7 @@ define([
hasColumnWithKey(columnKey) { hasColumnWithKey(columnKey) {
return _.flatten(Object.values(this.columns)) return _.flatten(Object.values(this.columns))
.some(column => column.getKey() === columnKey); .findIndex(column => column.getKey() === columnKey) !== -1;
} }
getColumns() { getColumns() {

View File

@@ -93,7 +93,7 @@ define(
// same time stamp // same time stamp
let potentialDupes = this.rows.slice(startIx, endIx + 1); let potentialDupes = this.rows.slice(startIx, endIx + 1);
// Search potential dupes for exact dupe // Search potential dupes for exact dupe
isDuplicate = potentialDupes.some(_.isEqual.bind(undefined, row)); isDuplicate = _.findIndex(potentialDupes, _.isEqual.bind(undefined, row)) > -1;
} }
if (!isDuplicate) { if (!isDuplicate) {
@@ -201,7 +201,7 @@ define(
sortBy(sortOptions) { sortBy(sortOptions) {
if (arguments.length > 0) { if (arguments.length > 0) {
this.sortOptions = sortOptions; this.sortOptions = sortOptions;
this.rows = _.orderBy(this.rows, (row) => row.getParsedValue(sortOptions.key) , sortOptions.direction); this.rows = _.sortByOrder(this.rows, (row) => row.getParsedValue(sortOptions.key) , sortOptions.direction);
this.emit('sort'); this.emit('sort');
} }
// Return duplicate to avoid direct modification of underlying object // Return duplicate to avoid direct modification of underlying object

View File

@@ -165,7 +165,16 @@
/******************************* LEGACY */ /******************************* LEGACY */
.s-status-taking-snapshot, .s-status-taking-snapshot,
.overlay.snapshot { .overlay.snapshot {
// Handle overflow-y issues with tables and html2canvas .c-table {
// Replaces .l-sticky-headers .l-tabular-body { overflow: auto; } &__body-w {
.c-table__body-w { overflow: auto; } overflow: auto; // Handle overflow-y issues with tables and html2canvas
}
&-control-bar {
display: none;
+ * {
margin-top: 0 !important;
}
}
}
} }

View File

@@ -260,8 +260,7 @@ import search from '../../../ui/components/search.vue';
import TableColumnHeader from './table-column-header.vue'; import TableColumnHeader from './table-column-header.vue';
import TelemetryFilterIndicator from './TelemetryFilterIndicator.vue'; import TelemetryFilterIndicator from './TelemetryFilterIndicator.vue';
import CSVExporter from '../../../exporters/CSVExporter.js'; import CSVExporter from '../../../exporters/CSVExporter.js';
import debounce from 'lodash/debounce'; import _ from 'lodash';
import throttle from 'lodash/throttle';
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue'; import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
const VISIBLE_ROW_COUNT = 100; const VISIBLE_ROW_COUNT = 100;
@@ -379,13 +378,13 @@ export default {
} }
}, },
created() { created() {
this.filterChanged = debounce(this.filterChanged, 500); this.filterChanged = _.debounce(this.filterChanged, 500);
}, },
mounted() { mounted() {
this.csvExporter = new CSVExporter(); this.csvExporter = new CSVExporter();
this.rowsAdded = throttle(this.rowsAdded, 200); this.rowsAdded = _.throttle(this.rowsAdded, 200);
this.rowsRemoved = throttle(this.rowsRemoved, 200); this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
this.scroll = throttle(this.scroll, 100); this.scroll = _.throttle(this.scroll, 100);
this.table.on('object-added', this.addObject); this.table.on('object-added', this.addObject);
this.table.on('object-removed', this.removeObject); this.table.on('object-removed', this.removeObject);

View File

@@ -565,6 +565,20 @@ select {
} }
} }
} }
/******************************************************** CONTROL BARS */
.c-control-bar {
display: flex;
align-items: center;
> * + * {
margin-left: $interiorMarginSm;
}
&__label {
display: inline-block;
white-space: nowrap;
}
}
/******************************************************** PALETTES */ /******************************************************** PALETTES */
.c-palette { .c-palette {

View File

@@ -40,28 +40,58 @@ mct-plot {
} }
} }
} }
.c-plot,
.gl-plot {
.s-status-taking-snapshot & {
.c-control-bar {
display: none;
}
.gl-plot-y-label__select {
display: none;
}
}
}
.c-plot { .c-plot {
$p: $mainViewPad; //$p: $mainViewPad;
position: absolute; @include abs($mainViewPad);
top: $p; right: $p; bottom: $p; left: $p; //position: absolute;
//top: $p; right: $p; bottom: $p; left: $p;
display: flex;
flex-direction: column;
> * + * {
margin-top: $interiorMargin;
}
.l-control-bar {
flex: 0 0 auto;
}
.l-view-section {
display: flex;
flex: 1 1 auto;
flex-direction: column;
}
&--stacked { &--stacked {
.l-view-section { .child-frame {
// Make this a flex container .has-control-bar {
display: flex; .c-control-bar {
flex-flow: column nowrap; // Hides buttons per plot element in a stacked plot
.gl-plot.child-frame { display: none;
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
&:not(:first-child) {
margin-top: $interiorMargin;
} }
} }
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
} }
.s-status-timeconductor-unsynced .holder-plot { .s-status-timeconductor-unsynced .holder-plot {
@@ -70,7 +100,6 @@ mct-plot {
} }
} }
} }
} }
@@ -186,7 +215,7 @@ mct-plot {
left: 0; top: 0; right: auto; bottom: 0; left: 0; top: 0; right: auto; bottom: 0;
padding-left: 5px; padding-left: 5px;
text-orientation: mixed; text-orientation: mixed;
overflow: hidden; //overflow: hidden;
writing-mode: vertical-lr; writing-mode: vertical-lr;
&:before { &:before {
// Icon denoting configurability // Icon denoting configurability
@@ -339,11 +368,11 @@ mct-plot {
z-index: -10; z-index: -10;
.l-view-section { .l-view-section {
$m: $interiorMargin; //$m: $interiorMargin;
top: $m !important; //top: $m !important;
right: $m; //right: $m;
bottom: $m; //bottom: $m;
left: $m; //left: $m;
.s-status-timeconductor-unsynced .holder-plot { .s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced { .t-object-alert.t-alert-unsynced {

View File

@@ -19,53 +19,6 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/******************************************************************* VIEWS */
// From _views.scss
// Legacy overlay and stacked plots depend on this for now
// Styles for sub-dividing views generically
.l-control-bar {
// Element that can be placed above l-view-section, holds controls, buttons, etc.
height: $controlBarH;
}
.c-control-bar {
display: flex;
align-items: center;
> * + * {
margin-left: $interiorMarginSm;
}
&__label {
display: inline-block;
white-space: nowrap;
}
}
.l-view-section {
@include abs();
overflow: auto;
}
.has-control-bar {
.l-view-section {
top: $controlBarH + $interiorMargin;
}
}
.child-frame {
.has-control-bar {
.l-control-bar,
.c-control-bar {
// Hides buttons per plot element in a stacked plot
display: none;
}
.l-view-section {
top: 0;
}
}
}
/*********************************************************************** CLOCKS AND TIMERS */ /*********************************************************************** CLOCKS AND TIMERS */
.c-clock, .c-clock,
.c-timer { .c-timer {

View File

@@ -388,7 +388,21 @@
.s-status-taking-snapshot, .s-status-taking-snapshot,
.overlay.snapshot { .overlay.snapshot {
// Handle overflow-y issues with tables and html2canvas // Handle overflow-y issues with tables and html2canvas
background: $colorBodyBg; // Prevent html2canvas from using white background
color: $colorBodyFg;
padding: $interiorMarginSm !important; // Prevents items from going right to the edge of the image
.l-sticky-headers .l-tabular-body { overflow: auto; } .l-sticky-headers .l-tabular-body { overflow: auto; }
.l-browse-bar {
display: none; // Suppress browse-bar when snapshotting from view-large overlay
+ * {
margin-top: 0 !important; // Remove margin from any following elements
}
}
* {
box-shadow: none !important; // Prevent html2canvas problems with box-shadow
}
} }
.c-notebook-snapshot { .c-notebook-snapshot {

View File

@@ -18,6 +18,7 @@
@import "../plugins/folderView/components/list-item.scss"; @import "../plugins/folderView/components/list-item.scss";
@import "../plugins/folderView/components/list-view.scss"; @import "../plugins/folderView/components/list-view.scss";
@import "../plugins/imagery/components/imagery-view-layout.scss"; @import "../plugins/imagery/components/imagery-view-layout.scss";
@import "../plugins/plotlyPlot/components/plotly.scss";
@import "../plugins/telemetryTable/components/table-row.scss"; @import "../plugins/telemetryTable/components/table-row.scss";
@import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss"; @import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss";
@import "../plugins/tabs/components/tabs.scss"; @import "../plugins/tabs/components/tabs.scss";

View File

@@ -59,6 +59,8 @@
<script> <script>
import ObjectView from './ObjectView.vue' import ObjectView from './ObjectView.vue'
import ContextMenuDropDown from './contextMenuDropDown.vue'; import ContextMenuDropDown from './contextMenuDropDown.vue';
import PreviewHeader from '@/ui/preview/preview-header.vue';
import Vue from 'vue';
const SIMPLE_CONTENT_TYPES = [ const SIMPLE_CONTENT_TYPES = [
'clock', 'clock',
@@ -116,13 +118,41 @@ export default {
childElement = parentElement.children[0]; childElement = parentElement.children[0];
this.openmct.overlays.overlay({ this.openmct.overlays.overlay({
element: childElement, element: this.getOverlayElement(childElement),
size: 'large', size: 'large',
onDestroy() { onDestroy() {
parentElement.append(childElement); parentElement.append(childElement);
} }
}); });
}, },
getOverlayElement(childElement) {
const fragment = new DocumentFragment();
const header = this.getPreviewHeader();
fragment.append(header);
fragment.append(childElement);
return fragment;
},
getPreviewHeader() {
const domainObject = this.objectPath[0];
const preview = new Vue({
components: {
PreviewHeader
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath
},
data() {
return {
domainObject
}
},
template: '<PreviewHeader :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
});
return preview.$mount().$el;
},
getSelectionContext() { getSelectionContext() {
return this.$refs.objectView.getSelectionContext(); return this.$refs.objectView.getSelectionContext();
} }

View File

@@ -3,9 +3,9 @@
</template> </template>
<script> <script>
import debounce from "lodash/debounce" import _ from "lodash"
import StyleRuleManager from "@/plugins/condition/StyleRuleManager"; import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
import { STYLE_CONSTANTS } from "@/plugins/condition/utils/constants"; import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
export default { export default {
inject: ["openmct"], inject: ["openmct"],
@@ -48,7 +48,7 @@ export default {
} }
}, },
created() { created() {
this.debounceUpdateView = debounce(this.updateView, 10); this.debounceUpdateView = _.debounce(this.updateView, 10);
}, },
mounted() { mounted() {
this.currentObject = this.object; this.currentObject = this.object;
@@ -201,7 +201,7 @@ export default {
}, },
initObjectStyles() { initObjectStyles() {
if (!this.styleRuleManager) { if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this)); this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this), true);
} else { } else {
this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles); this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles);
} }

View File

@@ -23,9 +23,12 @@
} }
&:not(.c-so-view--no-frame) { &:not(.c-so-view--no-frame) {
background: $colorBodyBg;
border: $browseFrameBorder; border: $browseFrameBorder;
padding: $interiorMargin; padding: $interiorMargin;
.is-editing & {
background: rgba($colorBodyBg, 0.8);
}
} }
&--no-frame { &--no-frame {

View File

@@ -49,9 +49,9 @@
</template> </template>
<script> <script>
import _ from 'lodash';
import Search from '../components/search.vue'; import Search from '../components/search.vue';
import ObjectLabel from '../components/ObjectLabel.vue'; import ObjectLabel from '../components/ObjectLabel.vue';
import isEqual from 'lodash/isEqual';
export default { export default {
inject: ['openmct'], inject: ['openmct'],
@@ -94,7 +94,7 @@ export default {
this.showSelection(this.openmct.selection.get()); this.showSelection(this.openmct.selection.get());
}, },
showSelection(selection) { showSelection(selection) {
if (isEqual(this.selection, selection)) { if (_.isEqual(this.selection, selection)) {
return; return;
} }
this.selection = selection; this.selection = selection;

View File

@@ -47,13 +47,14 @@ import Location from './Location.vue';
import Properties from './Properties.vue'; import Properties from './Properties.vue';
import ObjectName from './ObjectName.vue'; import ObjectName from './ObjectName.vue';
import InspectorViews from './InspectorViews.vue'; import InspectorViews from './InspectorViews.vue';
import StylesInspectorView from './StylesInspectorView.vue'; import _ from "lodash";
import isEqual from 'lodash/isEqual'; import StylesInspectorView from "./StylesInspectorView.vue";
export default { export default {
inject: ['openmct'], inject: ['openmct'],
components: { components: {
StylesInspectorView, StylesInspectorView,
// StylesInspectorView,
multipane, multipane,
pane, pane,
Elements, Elements,
@@ -107,7 +108,7 @@ export default {
let object = selection[0][0].context.item; let object = selection[0][0].context.item;
if (object) { if (object) {
let type = this.openmct.types.get(object.type); let type = this.openmct.types.get(object.type);
this.showStyles = (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable; this.showStyles = this.isLayoutObject(selection[0], object.type) || this.isCreatableObject(object, type);
} }
if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key)) if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
{ {
@@ -115,11 +116,19 @@ export default {
} }
} }
}, },
isLayoutObject(selection, objectType) {
//we allow conditionSets to be styled if they're part of a layout
return selection.length > 1 &&
((objectType === 'conditionSet') || (this.excludeObjectTypes.indexOf(objectType) < 0));
},
isCreatableObject(object, type) {
return (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
},
updateCurrentTab(view) { updateCurrentTab(view) {
this.currentTabbedView = view; this.currentTabbedView = view;
}, },
isCurrent(view) { isCurrent(view) {
return isEqual(this.currentTabbedView, view) return _.isEqual(this.currentTabbedView, view)
} }
} }
} }

View File

@@ -36,6 +36,7 @@
<!-- Action buttons --> <!-- Action buttons -->
<NotebookMenuSwitcher v-if="notebookEnabled" <NotebookMenuSwitcher v-if="notebookEnabled"
:domain-object="domainObject" :domain-object="domainObject"
:object-path="openmct.router.path"
class="c-notebook-snapshot-menubutton" class="c-notebook-snapshot-menubutton"
/> />
<div class="l-browse-bar__actions"> <div class="l-browse-bar__actions">
@@ -198,8 +199,6 @@ export default {
updateName(event) { updateName(event) {
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) { if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText); this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
} else {
event.target.innerText = this.domainObject.name;
} }
}, },
updateNameOnEnterKeyPress(event) { updateNameOnEnterKeyPress(event) {

View File

@@ -21,28 +21,12 @@
*****************************************************************************/ *****************************************************************************/
<template> <template>
<div class="l-preview-window"> <div class="l-preview-window">
<div class="l-browse-bar"> <PreviewHeader
<div class="l-browse-bar__start"> :current-view="currentView"
<div :domain-object="domainObject"
class="l-browse-bar__object-name--w" :views="views"
:class="type.cssClass" @setView="setView"
> />
<span class="l-browse-bar__object-name">
{{ domainObject.name }}
</span>
<context-menu-drop-down :object-path="objectPath" />
</div>
</div>
<div class="l-browse-bar__end">
<div class="l-browse-bar__actions">
<view-switcher
:views="views"
:current-view="currentView"
@setView="setView"
/>
</div>
</div>
</div>
<div class="l-preview-window__object-view"> <div class="l-preview-window__object-view">
<div ref="objectView"></div> <div ref="objectView"></div>
</div> </div>
@@ -50,13 +34,11 @@
</template> </template>
<script> <script>
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue'; import PreviewHeader from './preview-header.vue';
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
export default { export default {
components: { components: {
ContextMenuDropDown, PreviewHeader
ViewSwitcher
}, },
inject: [ inject: [
'openmct', 'openmct',
@@ -64,12 +46,9 @@ export default {
], ],
data() { data() {
let domainObject = this.objectPath[0]; let domainObject = this.objectPath[0];
let type = this.openmct.types.get(domainObject.type);
return { return {
domainObject: domainObject, domainObject: domainObject,
type: type,
notebookEnabled: false,
viewKey: undefined viewKey: undefined
}; };
}, },
@@ -97,6 +76,7 @@ export default {
this.view.destroy(); this.view.destroy();
this.$refs.objectView.innerHTML = ''; this.$refs.objectView.innerHTML = '';
} }
delete this.view; delete this.view;
delete this.viewContainer; delete this.viewContainer;
}, },

View File

@@ -36,7 +36,12 @@ export default class PreviewAction {
* Dependencies * Dependencies
*/ */
this._openmct = openmct; this._openmct = openmct;
if (PreviewAction.isVisible === undefined) {
PreviewAction.isVisible = false;
}
} }
invoke(objectPath) { invoke(objectPath) {
let preview = new Vue({ let preview = new Vue({
components: { components: {
@@ -59,16 +64,27 @@ export default class PreviewAction {
callback: () => overlay.dismiss() callback: () => overlay.dismiss()
} }
], ],
onDestroy: () => preview.$destroy() onDestroy: () => {
PreviewAction.isVisible = false;
preview.$destroy()
}
}); });
PreviewAction.isVisible = true;
} }
appliesTo(objectPath) { appliesTo(objectPath) {
return !this._isNavigatedObject(objectPath) return !PreviewAction.isVisible && !this._isNavigatedObject(objectPath);
} }
_isNavigatedObject(objectPath) { _isNavigatedObject(objectPath) {
let targetObject = objectPath[0]; let targetObject = objectPath[0];
let navigatedObject = this._openmct.router.path[0]; let navigatedObject = this._openmct.router.path[0];
return targetObject.identifier.namespace === navigatedObject.identifier.namespace && return targetObject.identifier.namespace === navigatedObject.identifier.namespace &&
targetObject.identifier.key === navigatedObject.identifier.key; targetObject.identifier.key === navigatedObject.identifier.key;
} }
_preventPreview(objectPath) {
const noPreviewTypes = ['folder'];
return noPreviewTypes.includes(objectPath[0].type);
}
} }

View File

@@ -0,0 +1,92 @@
<template>
<div class="l-browse-bar">
<div class="l-browse-bar__start">
<div
class="l-browse-bar__object-name--w"
:class="type.cssClass"
>
<span class="l-browse-bar__object-name">
{{ domainObject.name }}
</span>
<context-menu-drop-down :object-path="objectPath" />
</div>
</div>
<div class="l-browse-bar__end">
<div class="l-browse-bar__actions">
<view-switcher
:v-if="!hideViewSwitcher"
:views="views"
:current-view="currentView"
@setView="setView"
/>
<NotebookMenuSwitcher v-if="showNotebookMenuSwitcher"
:domain-object="domainObject"
:ignore-link="true"
:object-path="objectPath"
class="c-notebook-snapshot-menubutton"
/>
</div>
</div>
</div>
</template>
<script>
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
import NotebookMenuSwitcher from '@/plugins/notebook/components/notebook-menu-switcher.vue';
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
export default {
inject: [
'openmct',
'objectPath'
],
components: {
ContextMenuDropDown,
NotebookMenuSwitcher,
ViewSwitcher
},
props: {
currentView: {
type: Object,
default: () => {
return {};
}
},
domainObject: {
type: Object,
default: () => {
return {};
}
},
hideViewSwitcher: {
type: Boolean,
default: () => {
return false;
}
},
showNotebookMenuSwitcher: {
type: Boolean,
default: () => {
return false;
}
},
views: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
type: this.openmct.types.get(this.domainObject.type)
};
},
methods: {
setView(view) {
this.$emit('setView', view);
}
}
}
</script>

View File

@@ -20,8 +20,8 @@ import toolbarMenu from './components/toolbar-menu.vue';
import toolbarSelectMenu from './components/toolbar-select-menu.vue'; import toolbarSelectMenu from './components/toolbar-select-menu.vue';
import toolbarSeparator from './components/toolbar-separator.vue'; import toolbarSeparator from './components/toolbar-separator.vue';
import toolbarToggleButton from './components/toolbar-toggle-button.vue'; import toolbarToggleButton from './components/toolbar-toggle-button.vue';
import isEqual from 'lodash/isEqual';
import get from 'lodash/get'; import _ from 'lodash';
export default { export default {
inject: ['openmct'], inject: ['openmct'],
@@ -161,7 +161,7 @@ export default {
property = property + "." + formKey; property = property + "." + formKey;
} }
return get(domainObject, property); return _.get(domainObject, property);
}, },
getFormValue(domainObject, toolbarItem) { getFormValue(domainObject, toolbarItem) {
let value = {}; let value = {};
@@ -209,7 +209,7 @@ export default {
if (toolbarItem.domainObject) { if (toolbarItem.domainObject) {
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier); let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
if (changedItemId === id && isEqual(toolbarItem, item)) { if (changedItemId === id && _.isEqual(toolbarItem, item)) {
toolbarItem.value = value; toolbarItem.value = value;
} }
} }