mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 10:44:21 +00:00
Compare commits
48 Commits
windowLayo
...
angular-up
Author | SHA1 | Date | |
---|---|---|---|
5f0096ba16 | |||
771fb9c044 | |||
b4e6893627 | |||
055cf2b118 | |||
67ebcf4749 | |||
38dbf2ccab | |||
e9968e3649 | |||
d9fafd2956 | |||
b5aba7ce8f | |||
0db5648e10 | |||
83325da738 | |||
4d1b2f3456 | |||
6137700c82 | |||
91a1b3f31d | |||
357b25a76b | |||
bab53ad9bd | |||
d0d4579f13 | |||
02b537580c | |||
7073b0717f | |||
d6bb1b2a12 | |||
c256696790 | |||
d5480e7524 | |||
ab463e93fe | |||
8363c65312 | |||
04598b6cf1 | |||
43628ad9d6 | |||
67bea86bc8 | |||
4eb4cbfffc | |||
eda01abcbc | |||
2fa29124bf | |||
694b8f4666 | |||
33faeafa98 | |||
a40ff07353 | |||
41dc9c794d | |||
f1faf3965d | |||
da2ecbbcad | |||
32c892fe98 | |||
bbb271a678 | |||
fec1438806 | |||
28f19ec310 | |||
f934454c25 | |||
eb49ffae02 | |||
5751012872 | |||
aa041e04cf | |||
24e7ea143a | |||
79d5d9c4d0 | |||
b5bfdc4418 | |||
59730c60ec |
@ -2,7 +2,7 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:13-browsers
|
||||||
environment:
|
environment:
|
||||||
CHROME_BIN: "/usr/bin/google-chrome"
|
CHROME_BIN: "/usr/bin/google-chrome"
|
||||||
steps:
|
steps:
|
||||||
@ -11,12 +11,12 @@ jobs:
|
|||||||
name: Update npm
|
name: Update npm
|
||||||
command: 'sudo npm install -g npm@latest'
|
command: 'sudo npm install -g npm@latest'
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-{{ checksum "package.json" }}
|
key: dependency-cache-13-{{ checksum "package.json" }}
|
||||||
- run:
|
- run:
|
||||||
name: Installing dependencies (npm install)
|
name: Installing dependencies (npm install)
|
||||||
command: npm install
|
command: npm install
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: dependency-cache-{{ checksum "package.json" }}
|
key: dependency-cache-13-{{ checksum "package.json" }}
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
- run:
|
- run:
|
||||||
|
64
.eslintrc.js
64
.eslintrc.js
@ -1,3 +1,4 @@
|
|||||||
|
const LEGACY_FILES = ["platform/**", "example/**"];
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
@ -10,7 +11,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:vue/recommended"
|
"plugin:vue/recommended",
|
||||||
|
"plugin:you-dont-need-lodash-underscore/compatible"
|
||||||
],
|
],
|
||||||
"parser": "vue-eslint-parser",
|
"parser": "vue-eslint-parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
@ -22,6 +24,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"you-dont-need-lodash-underscore/omit": "off",
|
||||||
|
"you-dont-need-lodash-underscore/throttle": "off",
|
||||||
|
"you-dont-need-lodash-underscore/flatten": "off",
|
||||||
"no-bitwise": "error",
|
"no-bitwise": "error",
|
||||||
"curly": "error",
|
"curly": "error",
|
||||||
"eqeqeq": "error",
|
"eqeqeq": "error",
|
||||||
@ -66,6 +71,56 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
"dot-notation": "error",
|
"dot-notation": "error",
|
||||||
"indent": ["error", 4],
|
"indent": ["error", 4],
|
||||||
|
|
||||||
|
// https://eslint.org/docs/rules/no-case-declarations
|
||||||
|
"no-case-declarations": "error",
|
||||||
|
// https://eslint.org/docs/rules/max-classes-per-file
|
||||||
|
"max-classes-per-file": ["error", 1],
|
||||||
|
// https://eslint.org/docs/rules/no-eq-null
|
||||||
|
"no-eq-null": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-eval
|
||||||
|
"no-eval": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-floating-decimal
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-implicit-globals
|
||||||
|
"no-implicit-globals": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-implied-eval
|
||||||
|
"no-implied-eval": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-lone-blocks
|
||||||
|
"no-lone-blocks": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-loop-func
|
||||||
|
"no-loop-func": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-new-func
|
||||||
|
"no-new-func": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-new-wrappers
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-octal-escape
|
||||||
|
"no-octal-escape": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-proto
|
||||||
|
"no-proto": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-return-await
|
||||||
|
"no-return-await": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-script-url
|
||||||
|
"no-script-url": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-self-compare
|
||||||
|
"no-self-compare": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-sequences
|
||||||
|
"no-sequences": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-unmodified-loop-condition
|
||||||
|
"no-unmodified-loop-condition": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-useless-call
|
||||||
|
"no-useless-call": "error",
|
||||||
|
// https://eslint.org/docs/rules/wrap-iife
|
||||||
|
"wrap-iife": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-nested-ternary
|
||||||
|
"no-nested-ternary": "error",
|
||||||
|
// https://eslint.org/docs/rules/switch-colon-spacing
|
||||||
|
"switch-colon-spacing": "error",
|
||||||
|
// https://eslint.org/docs/rules/no-useless-computed-key
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
// https://eslint.org/docs/rules/rest-spread-spacing
|
||||||
|
"rest-spread-spacing": ["error"],
|
||||||
|
|
||||||
"vue/html-indent": [
|
"vue/html-indent": [
|
||||||
"error",
|
"error",
|
||||||
4,
|
4,
|
||||||
@ -112,6 +167,13 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
"files": LEGACY_FILES,
|
||||||
|
"rules": {
|
||||||
|
// https://eslint.org/docs/rules/no-nested-ternary
|
||||||
|
"no-nested-ternary": "off",
|
||||||
|
"no-var": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
4
API.md
4
API.md
@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints. Keys in this thi
|
|||||||
|
|
||||||
Known hints:
|
Known hints:
|
||||||
|
|
||||||
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
||||||
* `range`: Indicates that the value is the "output" of a datum. Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
||||||
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
|
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
|
||||||
|
|
||||||
##### The Time Conductor and Telemetry
|
##### The Time Conductor and Telemetry
|
||||||
|
172
CONTRIBUTING.md
172
CONTRIBUTING.md
@ -103,7 +103,7 @@ the name chosen could not be mistaken for a topic or master branch.
|
|||||||
### Merging
|
### Merging
|
||||||
|
|
||||||
When development is complete on an issue, the first step toward merging it
|
When development is complete on an issue, the first step toward merging it
|
||||||
back into the master branch is to file a Pull Request. The contributions
|
back into the master branch is to file a Pull Request (PR). The contributions
|
||||||
should meet code, test, and commit message standards as described below,
|
should meet code, test, and commit message standards as described below,
|
||||||
and the pull request should include a completed author checklist, also
|
and the pull request should include a completed author checklist, also
|
||||||
as described below. Pull requests may be assigned to specific team
|
as described below. Pull requests may be assigned to specific team
|
||||||
@ -114,6 +114,15 @@ request. When the reviewer is satisfied, they should add a comment to
|
|||||||
the pull request containing the reviewer checklist (from below) and complete
|
the pull request containing the reviewer checklist (from below) and complete
|
||||||
the merge back to the master branch.
|
the merge back to the master branch.
|
||||||
|
|
||||||
|
Additionally:
|
||||||
|
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, create one.
|
||||||
|
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
|
||||||
|
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull request’s __author__.
|
||||||
|
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
|
||||||
|
* Every PR must have two reviewers assigned, though only one approval is necessary for merge.
|
||||||
|
* Changes to API require approval by a senior developer.
|
||||||
|
* When creating a PR, it is the author's responsibility to apply any priority label from the issue to the PR as well. This helps with prioritization.
|
||||||
|
|
||||||
## Standards
|
## Standards
|
||||||
|
|
||||||
Contributions to Open MCT are expected to meet the following standards.
|
Contributions to Open MCT are expected to meet the following standards.
|
||||||
@ -122,89 +131,96 @@ changes.
|
|||||||
|
|
||||||
### Code Standards
|
### Code Standards
|
||||||
|
|
||||||
JavaScript sources in Open MCT must satisfy JSLint under its default
|
JavaScript sources in Open MCT must satisfy the ESLint rules defined in
|
||||||
settings. This is verified by the command line build.
|
this repository. This is verified by the command line build.
|
||||||
|
|
||||||
#### Code Guidelines
|
#### Code Guidelines
|
||||||
|
|
||||||
JavaScript sources in Open MCT should:
|
The following guidelines are provided for anyone contributing source code to the Open MCT project:
|
||||||
|
|
||||||
* Use four spaces for indentation. Tabs should not be used.
|
|
||||||
* Include JSDoc for any exposed API (e.g. public methods, constructors).
|
|
||||||
* Include non-JSDoc comments as-needed for explaining private variables,
|
|
||||||
methods, or algorithms when they are non-obvious.
|
|
||||||
* Define one public class per script, expressed as a constructor function
|
|
||||||
returned from an AMD-style module.
|
|
||||||
* Follow “Java-like” naming conventions. These includes:
|
|
||||||
* Classes should use camel case, first letter capitalized
|
|
||||||
(e.g. SomeClassName).
|
|
||||||
* Methods, variables, fields, and function names should use camel case,
|
|
||||||
first letter lower-case (e.g. someVariableName).
|
|
||||||
* Constants (variables or fields which are meant to be declared and
|
|
||||||
initialized statically, and never changed) should use only capital
|
|
||||||
letters, with underscores between words (e.g. SOME_CONSTANT).
|
|
||||||
* File names should be the name of the exported class, plus a .js extension
|
|
||||||
(e.g. SomeClassName.js).
|
|
||||||
* Avoid anonymous functions, except when functions are short (a few lines)
|
|
||||||
and/or their inclusion makes sense within the flow of the code
|
|
||||||
(e.g. as arguments to a forEach call).
|
|
||||||
* Avoid deep nesting (especially of functions), except where necessary
|
|
||||||
(e.g. due to closure scope).
|
|
||||||
* End with a single new-line character.
|
|
||||||
* Expose public methods by declaring them on the class's prototype.
|
|
||||||
* Within a given function's scope, do not mix declarations and imperative
|
|
||||||
code, and present these in the following order:
|
|
||||||
* First, variable declarations and initialization.
|
|
||||||
* Second, function declarations.
|
|
||||||
* Third, imperative statements.
|
|
||||||
* Finally, the returned value.
|
|
||||||
|
|
||||||
|
1. Write clean code. Here’s a good summary - https://github.com/ryanmcdermott/clean-code-javascript.
|
||||||
|
1. Include JSDoc for any exposed API (e.g. public methods, classes).
|
||||||
|
1. Include non-JSDoc comments as-needed for explaining private variables,
|
||||||
|
methods, or algorithms when they are non-obvious. Otherwise code
|
||||||
|
should be self-documenting.
|
||||||
|
1. Classes and Vue components should use camel case, first letter capitalized
|
||||||
|
(e.g. SomeClassName).
|
||||||
|
1. Methods, variables, fields, events, and function names should use camelCase,
|
||||||
|
first letter lower-case (e.g. someVariableName).
|
||||||
|
1. Source files that export functions should use camelCase, first letter lower-case (eg. testTools.js)
|
||||||
|
1. Constants (variables or fields which are meant to be declared and
|
||||||
|
initialized statically, and never changed) should use only capital
|
||||||
|
letters, with underscores between words (e.g. SOME_CONSTANT). They should always be declared as `const`s
|
||||||
|
1. File names should be the name of the exported class, plus a .js extension
|
||||||
|
(e.g. SomeClassName.js).
|
||||||
|
1. Avoid anonymous functions, except when functions are short (one or two lines)
|
||||||
|
and their inclusion makes sense within the flow of the code
|
||||||
|
(e.g. as arguments to a forEach call). Anonymous functions should always be arrow functions.
|
||||||
|
1. Named functions are preferred over functions assigned to variables.
|
||||||
|
eg.
|
||||||
|
```JavaScript
|
||||||
|
function renameObject(object, newName) {
|
||||||
|
Object.name = newName;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
is preferable to
|
||||||
|
```JavaScript
|
||||||
|
const rename = (object, newName) => {
|
||||||
|
Object.name = newName;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
1. Avoid deep nesting (especially of functions), except where necessary
|
||||||
|
(e.g. due to closure scope).
|
||||||
|
1. End with a single new-line character.
|
||||||
|
1. Always use ES6 `Class`es and inheritence rather than the pre-ES6 prototypal
|
||||||
|
pattern.
|
||||||
|
1. Within a given function's scope, do not mix declarations and imperative
|
||||||
|
code, and present these in the following order:
|
||||||
|
* First, variable declarations and initialization.
|
||||||
|
* Secondly, imperative statements.
|
||||||
|
* Finally, the returned value. A single return statement at the end of the function should be used, except where an early return would improve code clarity.
|
||||||
|
1. Avoid the use of "magic" values.
|
||||||
|
eg.
|
||||||
|
```JavaScript
|
||||||
|
Const UNAUTHORIZED = 401
|
||||||
|
if (responseCode === UNAUTHORIZED)
|
||||||
|
```
|
||||||
|
is preferable to
|
||||||
|
```JavaScript
|
||||||
|
if (responseCode === 401)
|
||||||
|
```
|
||||||
|
1. Use the ternary operator only for simple cases such as variable assignment. Nested ternaries should be avoided in all cases.
|
||||||
|
1. Test specs should reside alongside the source code they test, not in a separate directory.
|
||||||
|
1. Organize code by feature, not by type.
|
||||||
|
eg.
|
||||||
|
```
|
||||||
|
- telemetryTable
|
||||||
|
- row
|
||||||
|
TableRow.js
|
||||||
|
TableRowCollection.js
|
||||||
|
TableRow.vue
|
||||||
|
- column
|
||||||
|
TableColumn.js
|
||||||
|
TableColumn.vue
|
||||||
|
plugin.js
|
||||||
|
pluginSpec.js
|
||||||
|
```
|
||||||
|
is preferable to
|
||||||
|
```
|
||||||
|
- telemetryTable
|
||||||
|
- components
|
||||||
|
TableRow.vue
|
||||||
|
TableColumn.vue
|
||||||
|
- collections
|
||||||
|
TableRowCollection.js
|
||||||
|
TableColumn.js
|
||||||
|
TableRow.js
|
||||||
|
plugin.js
|
||||||
|
pluginSpec.js
|
||||||
|
```
|
||||||
Deviations from Open MCT code style guidelines require two-party agreement,
|
Deviations from Open MCT code style guidelines require two-party agreement,
|
||||||
typically from the author of the change and its reviewer.
|
typically from the author of the change and its reviewer.
|
||||||
|
|
||||||
#### Code Example
|
|
||||||
|
|
||||||
```js
|
|
||||||
/*global define*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bundles should declare themselves as namespaces in whichever source
|
|
||||||
* file is most like the "main point of entry" to the bundle.
|
|
||||||
* @namespace some/bundle
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
['./OtherClass'],
|
|
||||||
function (OtherClass) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A summary of how to use this class goes here.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @memberof some/bundle
|
|
||||||
*/
|
|
||||||
function ExampleClass() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods which are not intended for external use should
|
|
||||||
// not have JSDoc (or should be marked @private)
|
|
||||||
ExampleClass.prototype.privateMethod = function () {
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A summary of this method goes here.
|
|
||||||
* @param {number} n a parameter
|
|
||||||
* @returns {number} a return value
|
|
||||||
*/
|
|
||||||
ExampleClass.prototype.publicMethod = function (n) {
|
|
||||||
return n * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExampleClass;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Standards
|
### Test Standards
|
||||||
|
|
||||||
Automated testing shall occur whenever changes are merged into the main
|
Automated testing shall occur whenever changes are merged into the main
|
||||||
@ -292,6 +308,7 @@ checklist).
|
|||||||
2. Unit tests included and/or updated with changes?
|
2. Unit tests included and/or updated with changes?
|
||||||
3. Command line build passes?
|
3. Command line build passes?
|
||||||
4. Changes have been smoke-tested?
|
4. Changes have been smoke-tested?
|
||||||
|
5. Testing instructions included?
|
||||||
|
|
||||||
### Reviewer Checklist
|
### Reviewer Checklist
|
||||||
|
|
||||||
@ -299,3 +316,4 @@ checklist).
|
|||||||
2. Appropriate unit tests included?
|
2. Appropriate unit tests included?
|
||||||
3. Code style and in-line documentation are appropriate?
|
3. Code style and in-line documentation are appropriate?
|
||||||
4. Commit messages meet standards?
|
4. Commit messages meet standards?
|
||||||
|
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||||
|
@ -125,3 +125,22 @@ A release is not closed until both categories have been performed on
|
|||||||
the latest snapshot of the software, _and_ no issues labelled as
|
the latest snapshot of the software, _and_ no issues labelled as
|
||||||
["blocker" or "critical"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
["blocker" or "critical"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||||
remain open.
|
remain open.
|
||||||
|
|
||||||
|
### Testathons
|
||||||
|
Testathons can be used as a means of performing per-sprint and per-release testing.
|
||||||
|
|
||||||
|
#### Timing
|
||||||
|
For per-sprint testing, a testathon is typically performed at the beginning of the third week of a sprint, and again later that week to verify any fixes. For per-release testing, a testathon is typically performed prior to any formal testing processes that are applicable to that release.
|
||||||
|
|
||||||
|
#### Process
|
||||||
|
|
||||||
|
1. Prior to the scheduled testathon, a list will be compiled of all issues that are closed and unverified.
|
||||||
|
2. For each issue, testers should review the associated PR for testing instructions. See the contributing guide for instructions on [pull requests](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md#merging).
|
||||||
|
3. As each issue is verified via testing, any team members testing it should leave a comment on that issue indicating that it has been verified fixed.
|
||||||
|
4. If a bug is found that relates to an issue being tested, notes should be included on the associated issue, and the issue should be reopened. Bug notes should include reproduction steps.
|
||||||
|
5. For any bugs that are not obviously related to any of the issues under test, a new issue should be created with details about the bug, including reproduction steps. If unsure about whether a bug relates to an issue being tested, just create a new issue.
|
||||||
|
6. At the end of the testathon, triage will take place, where all tested issues will be reviewed.
|
||||||
|
7. If verified fixed, an issue will remain closed, and will have the “unverified” label removed.
|
||||||
|
8. For any bugs found, a severity will be assigned.
|
||||||
|
9. A second testathon will be scheduled for later in the week that will aim to address all issues identified as blockers, as well as any other issues scoped by the team during triage.
|
||||||
|
10. Any issues that were not tested will remain "unverified" and will be picked up in the next testathon.
|
||||||
|
@ -50,7 +50,8 @@ define([
|
|||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
name: "Name"
|
name: "Name",
|
||||||
|
format: "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "utc",
|
key: "utc",
|
||||||
@ -99,7 +100,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
|
||||||
return _.extend(
|
return Object.assign(
|
||||||
{},
|
{},
|
||||||
domainObject.telemetry,
|
domainObject.telemetry,
|
||||||
METADATA_BY_TYPE[domainObject.type]
|
METADATA_BY_TYPE[domainObject.type]
|
||||||
|
@ -52,6 +52,7 @@ define([
|
|||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
utc: Math.floor(timestamp / 5000) * 5000,
|
utc: Math.floor(timestamp / 5000) * 5000,
|
||||||
|
local: Math.floor(timestamp / 5000) * 5000,
|
||||||
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
|
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ define([
|
|||||||
},
|
},
|
||||||
request: function (domainObject, options) {
|
request: function (domainObject, options) {
|
||||||
var start = options.start;
|
var start = options.start;
|
||||||
var end = options.end;
|
var end = Math.min(options.end, Date.now());
|
||||||
var data = [];
|
var data = [];
|
||||||
while (start <= end && data.length < 5000) {
|
while (start <= end && data.length < 5000) {
|
||||||
data.push(pointForTimestamp(start, domainObject.name));
|
data.push(pointForTimestamp(start, domainObject.name));
|
||||||
@ -118,6 +119,14 @@ define([
|
|||||||
name: 'Time',
|
name: 'Time',
|
||||||
key: 'utc',
|
key: 'utc',
|
||||||
format: 'utc',
|
format: 'utc',
|
||||||
|
hints: {
|
||||||
|
domain: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Local Time',
|
||||||
|
key: 'local',
|
||||||
|
format: 'local-format',
|
||||||
hints: {
|
hints: {
|
||||||
domain: 1
|
domain: 1
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,6 @@
|
|||||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||||
openmct.install(openmct.plugins.ObjectMigration());
|
openmct.install(openmct.plugins.ObjectMigration());
|
||||||
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
||||||
openmct.install(openmct.plugins.WindowLayout());
|
|
||||||
openmct.start();
|
openmct.start();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
11
package.json
11
package.json
@ -4,7 +4,7 @@
|
|||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"angular": "1.7.9",
|
"angular": ">=1.8.0",
|
||||||
"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,6 +24,7 @@
|
|||||||
"d3-time-format": "2.1.x",
|
"d3-time-format": "2.1.x",
|
||||||
"eslint": "5.2.0",
|
"eslint": "5.2.0",
|
||||||
"eslint-plugin-vue": "^6.0.0",
|
"eslint-plugin-vue": "^6.0.0",
|
||||||
|
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
|
||||||
"eventemitter3": "^1.2.0",
|
"eventemitter3": "^1.2.0",
|
||||||
"exports-loader": "^0.7.0",
|
"exports-loader": "^0.7.0",
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
@ -48,7 +49,7 @@
|
|||||||
"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": "^3.10.1",
|
"lodash": "^4.17.12",
|
||||||
"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",
|
||||||
@ -83,10 +84,10 @@
|
|||||||
"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": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" 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:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
|
||||||
"test:watch": "karma start --no-single-run",
|
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" 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",
|
||||||
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
|
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
ng-show="ngModel.dialog.messages.length > 1 ||
|
ng-show="ngModel.dialog.messages.length > 1 ||
|
||||||
ngModel.dialog.messages.length == 0">s</span>
|
ngModel.dialog.messages.length == 0">s</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
ng-if="ngModel.dialog.topBarButton"
|
||||||
|
class="c-button c-button--major"
|
||||||
|
ng-click="ngModel.topBarButton.onClick">
|
||||||
|
{{ ngModel.topBarButton.label }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-messages c-overlay__messages">
|
<div class="w-messages c-overlay__messages">
|
||||||
<mct-include
|
<mct-include
|
||||||
@ -16,7 +22,7 @@
|
|||||||
<button ng-repeat="dialogAction in ngModel.dialog.actions"
|
<button ng-repeat="dialogAction in ngModel.dialog.actions"
|
||||||
class="c-button c-button--major"
|
class="c-button c-button--major"
|
||||||
ng-click="dialogAction.action()">
|
ng-click="dialogAction.action()">
|
||||||
{{dialogAction.label}}
|
{{ dialogAction.label }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['../../../../../src/api/objects/object-utils'],
|
['objectUtils'],
|
||||||
function (objectUtils) {
|
function (objectUtils) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,44 +21,15 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
"./src/NotificationIndicatorController",
|
"./src/NotificationService"
|
||||||
"./src/NotificationIndicator",
|
|
||||||
"./src/NotificationService",
|
|
||||||
"./res/notification-indicator.html"
|
|
||||||
], function (
|
], function (
|
||||||
NotificationIndicatorController,
|
NotificationService
|
||||||
NotificationIndicator,
|
|
||||||
NotificationService,
|
|
||||||
notificationIndicatorTemplate
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name:"platform/commonUI/notification",
|
name:"platform/commonUI/notification",
|
||||||
definition: {
|
definition: {
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"templates": [
|
|
||||||
{
|
|
||||||
"key": "notificationIndicatorTemplate",
|
|
||||||
"template": notificationIndicatorTemplate
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"controllers": [
|
|
||||||
{
|
|
||||||
"key": "NotificationIndicatorController",
|
|
||||||
"implementation": NotificationIndicatorController,
|
|
||||||
"depends": [
|
|
||||||
"$scope",
|
|
||||||
"openmct",
|
|
||||||
"dialogService"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indicators": [
|
|
||||||
{
|
|
||||||
"implementation": NotificationIndicator,
|
|
||||||
"priority": "fallback"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": [
|
"services": [
|
||||||
{
|
{
|
||||||
"key": "notificationService",
|
"key": "notificationService",
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
|
||||||
<div ng-show="notifications.length > 0" class="c-indicator c-indicator--clickable s-status-{{highest.severity}} icon-bell"
|
|
||||||
ng-controller="NotificationIndicatorController">
|
|
||||||
<span class="label c-indicator__label">
|
|
||||||
<button ng-click="showNotificationsList()">
|
|
||||||
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span></button>
|
|
||||||
</span><span class="c-indicator__count">{{notifications.length}}</span>
|
|
||||||
</div>
|
|
@ -1,73 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides an indicator that is visible when there are
|
|
||||||
* banner notifications that have been minimized. Will also indicate
|
|
||||||
* the number of notifications. Notifications can be viewed by
|
|
||||||
* clicking on the indicator to launch a dialog showing a list of
|
|
||||||
* notifications.
|
|
||||||
* @param $scope
|
|
||||||
* @param notificationService
|
|
||||||
* @param dialogService
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function NotificationIndicatorController($scope, openmct, dialogService) {
|
|
||||||
$scope.notifications = openmct.notifications.notifications;
|
|
||||||
$scope.highest = openmct.notifications.highest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch a dialog showing a list of current notifications.
|
|
||||||
*/
|
|
||||||
$scope.showNotificationsList = function () {
|
|
||||||
let notificationsList = openmct.notifications.notifications.map(notification => {
|
|
||||||
if (notification.model.severity === 'alert' || notification.model.severity === 'info') {
|
|
||||||
notification.model.primaryOption = {
|
|
||||||
label: 'Dismiss',
|
|
||||||
callback: () => {
|
|
||||||
let currentIndex = notificationsList.indexOf(notification);
|
|
||||||
notification.dismiss();
|
|
||||||
notificationsList.splice(currentIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return notification;
|
|
||||||
})
|
|
||||||
dialogService.getDialogResponse('overlay-message-list', {
|
|
||||||
dialog: {
|
|
||||||
title: "Messages",
|
|
||||||
//Launch the message list dialog with the models
|
|
||||||
// from the notifications
|
|
||||||
messages: notificationsList
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return NotificationIndicatorController;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
['../src/NotificationIndicatorController'],
|
|
||||||
function (NotificationIndicatorController) {
|
|
||||||
|
|
||||||
xdescribe("The notification indicator controller ", function () {
|
|
||||||
var mockNotificationService,
|
|
||||||
mockScope,
|
|
||||||
mockDialogService,
|
|
||||||
controller;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockNotificationService = jasmine.createSpy("notificationService");
|
|
||||||
mockScope = jasmine.createSpy("$scope");
|
|
||||||
mockDialogService = jasmine.createSpyObj(
|
|
||||||
"dialogService",
|
|
||||||
["getDialogResponse","dismiss"]
|
|
||||||
);
|
|
||||||
mockNotificationService.highest = {
|
|
||||||
severity: "error"
|
|
||||||
};
|
|
||||||
controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("exposes the highest notification severity to the template", function () {
|
|
||||||
expect(mockScope.highest).toBeTruthy();
|
|
||||||
expect(mockScope.highest.severity).toBe("error");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("invokes the dialog service to show list of messages", function () {
|
|
||||||
expect(mockScope.showNotificationsList).toBeDefined();
|
|
||||||
mockScope.showNotificationsList();
|
|
||||||
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
|
|
||||||
expect(mockDialogService.getDialogResponse.calls.mostRecent().args[0]).toBe('overlay-message-list');
|
|
||||||
expect(mockDialogService.getDialogResponse.calls.mostRecent().args[1].dialog).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -26,7 +26,7 @@
|
|||||||
* @namespace platform/containment
|
* @namespace platform/containment
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
['../../../src/api/objects/object-utils'],
|
['objectUtils'],
|
||||||
function (objectUtils) {
|
function (objectUtils) {
|
||||||
|
|
||||||
function PersistableCompositionPolicy(openmct) {
|
function PersistableCompositionPolicy(openmct) {
|
||||||
|
@ -81,7 +81,7 @@ define(
|
|||||||
baseContext = context || {};
|
baseContext = context || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionContext = _.extend({}, baseContext);
|
var actionContext = Object.assign({}, baseContext);
|
||||||
actionContext.domainObject = this.domainObject;
|
actionContext.domainObject = this.domainObject;
|
||||||
|
|
||||||
return this.actionService.getActions(actionContext);
|
return this.actionService.getActions(actionContext);
|
||||||
|
@ -121,7 +121,7 @@ define(['lodash'], function (_) {
|
|||||||
*/
|
*/
|
||||||
ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
|
ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
|
||||||
this.externalIdentifiers.push(this.getId(child));
|
this.externalIdentifiers.push(this.getId(child));
|
||||||
var index = _.findIndex(parent.composition, function (id) {
|
var index = parent.composition.findIndex(id => {
|
||||||
return _.isEqual(child.identifier, id);
|
return _.isEqual(child.identifier, id);
|
||||||
});
|
});
|
||||||
var copyOfChild = this.copyObject(child);
|
var copyOfChild = this.copyObject(child);
|
||||||
|
@ -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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, objectUtils) {
|
define(['zepto', 'objectUtils'], function ($, objectUtils) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ImportAsJSONAction is available from context menus and allows a user
|
* The ImportAsJSONAction is available from context menus and allows a user
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
|
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'../../../../src/api/objects/object-utils',
|
'objectUtils',
|
||||||
'lodash'
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
objectUtils,
|
objectUtils,
|
||||||
@ -191,7 +191,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
var domainObject = objectUtils.toNewFormat(model, id);
|
var domainObject = objectUtils.toNewFormat(model, id);
|
||||||
var composition = _.find(this.openmct.composition.registry, function (p) {
|
var composition = this.openmct.composition.registry.find(p => {
|
||||||
return p.appliesTo(domainObject);
|
return p.appliesTo(domainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
[
|
[
|
||||||
'../../../src/api/objects/object-utils',
|
'objectUtils',
|
||||||
'lodash'
|
'lodash'
|
||||||
],
|
],
|
||||||
function (
|
function (
|
||||||
@ -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 = _.indexBy(metadata.values(), 'key');
|
var sourceMap = _.keyBy(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 = _.indexBy(metadata.values(), 'key');
|
var sourceMap = _.keyBy(metadata.values(), 'key');
|
||||||
|
|
||||||
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
|
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
|
||||||
telemetryAPI.legacyProvider;
|
telemetryAPI.legacyProvider;
|
||||||
|
621
report.20200527.134750.93992.001.json
Normal file
621
report.20200527.134750.93992.001.json
Normal file
@ -0,0 +1,621 @@
|
|||||||
|
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"event": "Allocation failed - JavaScript heap out of memory",
|
||||||
|
"location": "OnFatalError",
|
||||||
|
"filename": "report.20200527.134750.93992.001.json",
|
||||||
|
"dumpEventTime": "2020-05-27T13:47:50Z",
|
||||||
|
"dumpEventTimeStamp": "1590612470877",
|
||||||
|
"processId": 93992,
|
||||||
|
"commandLine": [
|
||||||
|
"node",
|
||||||
|
"/Users/dtailor/Desktop/openmct/node_modules/.bin/karma",
|
||||||
|
"start",
|
||||||
|
"--single-run"
|
||||||
|
],
|
||||||
|
"nodejsVersion": "v11.9.0",
|
||||||
|
"wordSize": 64,
|
||||||
|
"componentVersions": {
|
||||||
|
"node": "11.9.0",
|
||||||
|
"v8": "7.0.276.38-node.16",
|
||||||
|
"uv": "1.25.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"brotli": "1.0.7",
|
||||||
|
"ares": "1.15.0",
|
||||||
|
"modules": "67",
|
||||||
|
"nghttp2": "1.34.0",
|
||||||
|
"napi": "4",
|
||||||
|
"llhttp": "1.0.1",
|
||||||
|
"http_parser": "2.8.0",
|
||||||
|
"openssl": "1.1.1a",
|
||||||
|
"cldr": "34.0",
|
||||||
|
"icu": "63.1",
|
||||||
|
"tz": "2018e",
|
||||||
|
"unicode": "11.0",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"release": "node"
|
||||||
|
},
|
||||||
|
"osVersion": "Darwin 18.7.0 Darwin Kernel Version 18.7.0: Thu Jan 23 06:52:12 PST 2020; root:xnu-4903.278.25~1/RELEASE_X86_64",
|
||||||
|
"machine": "Darwin 18.7.0 Darwin Kernel Version 18.7.0: Thu Jan 23 06:52:12 PST 2020; root:xnu-4903.278.25~1/RELEASE_X86_64tailor x86_64"
|
||||||
|
},
|
||||||
|
"javascriptStack": {
|
||||||
|
"message": "No stack.",
|
||||||
|
"stack": [
|
||||||
|
"Unavailable."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nativeStack": [
|
||||||
|
" [pc=0x10013090e] report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, v8::Local<v8::String>) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x100063744] node::OnFatalError(char const*, char const*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1001a8c47] v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1001a8be4] v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005add42] v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005b0273] v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005ac7a8] v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005aa965] v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005b720c] v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1005b728f] v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x100586484] v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x1008389a4] v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object**, v8::internal::Isolate*) [/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node]",
|
||||||
|
" [pc=0x14acfddcfc7d] "
|
||||||
|
],
|
||||||
|
"javascriptHeap": {
|
||||||
|
"totalMemory": 1479229440,
|
||||||
|
"totalCommittedMemory": 1477309024,
|
||||||
|
"usedMemory": 1445511032,
|
||||||
|
"availableMemory": 50296592,
|
||||||
|
"memoryLimit": 1526909922,
|
||||||
|
"heapSpaces": {
|
||||||
|
"read_only_space": {
|
||||||
|
"memorySize": 524288,
|
||||||
|
"committedMemory": 42224,
|
||||||
|
"capacity": 515584,
|
||||||
|
"used": 33520,
|
||||||
|
"available": 482064
|
||||||
|
},
|
||||||
|
"new_space": {
|
||||||
|
"memorySize": 4194304,
|
||||||
|
"committedMemory": 4194288,
|
||||||
|
"capacity": 2062336,
|
||||||
|
"used": 59016,
|
||||||
|
"available": 2003320
|
||||||
|
},
|
||||||
|
"old_space": {
|
||||||
|
"memorySize": 305860608,
|
||||||
|
"committedMemory": 305138544,
|
||||||
|
"capacity": 283264904,
|
||||||
|
"used": 282942208,
|
||||||
|
"available": 322696
|
||||||
|
},
|
||||||
|
"code_space": {
|
||||||
|
"memorySize": 6291456,
|
||||||
|
"committedMemory": 5687328,
|
||||||
|
"capacity": 5237152,
|
||||||
|
"used": 5237152,
|
||||||
|
"available": 0
|
||||||
|
},
|
||||||
|
"map_space": {
|
||||||
|
"memorySize": 5255168,
|
||||||
|
"committedMemory": 5143024,
|
||||||
|
"capacity": 2523280,
|
||||||
|
"used": 2523280,
|
||||||
|
"available": 0
|
||||||
|
},
|
||||||
|
"large_object_space": {
|
||||||
|
"memorySize": 1157103616,
|
||||||
|
"committedMemory": 1157103616,
|
||||||
|
"capacity": 1202204368,
|
||||||
|
"used": 1154715856,
|
||||||
|
"available": 47488512
|
||||||
|
},
|
||||||
|
"new_large_object_space": {
|
||||||
|
"memorySize": 0,
|
||||||
|
"committedMemory": 0,
|
||||||
|
"capacity": 0,
|
||||||
|
"used": 0,
|
||||||
|
"available": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resourceUsage": {
|
||||||
|
"userCpuSeconds": 43.1616,
|
||||||
|
"kernelCpuSeconds": 43.1616,
|
||||||
|
"cpuConsumptionPercent": 5.42705e-06,
|
||||||
|
"maxRss": 1966080000000,
|
||||||
|
"pageFaults": {
|
||||||
|
"IORequired": 245,
|
||||||
|
"IONotRequired": 832598
|
||||||
|
},
|
||||||
|
"fsActivity": {
|
||||||
|
"reads": 0,
|
||||||
|
"writes": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libuv": [
|
||||||
|
],
|
||||||
|
"environmentVariables": {
|
||||||
|
"npm_config_save_dev": "",
|
||||||
|
"npm_config_legacy_bundling": "",
|
||||||
|
"npm_config_dry_run": "",
|
||||||
|
"npm_package_devDependencies_markdown_toc": "^0.11.7",
|
||||||
|
"npm_config_only": "",
|
||||||
|
"npm_config_browser": "",
|
||||||
|
"npm_config_viewer": "man",
|
||||||
|
"npm_config_commit_hooks": "true",
|
||||||
|
"npm_package_gitHead": "7126abe7ec1d66d3252f3598fbd6bd27217018bc",
|
||||||
|
"npm_config_also": "",
|
||||||
|
"npm_package_scripts_otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
|
||||||
|
"npm_package_devDependencies_minimist": "^1.1.1",
|
||||||
|
"npm_config_sign_git_commit": "",
|
||||||
|
"npm_config_rollback": "true",
|
||||||
|
"npm_package_devDependencies_fast_sass_loader": "1.4.6",
|
||||||
|
"TERM_PROGRAM": "Apple_Terminal",
|
||||||
|
"npm_config_usage": "",
|
||||||
|
"npm_config_audit": "true",
|
||||||
|
"npm_package_devDependencies_git_rev_sync": "^1.4.0",
|
||||||
|
"npm_package_devDependencies_file_loader": "^1.1.11",
|
||||||
|
"npm_package_devDependencies_d3_selection": "1.3.x",
|
||||||
|
"NODE": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
|
||||||
|
"npm_package_homepage": "https://github.com/nasa/openmct#readme",
|
||||||
|
"INIT_CWD": "/Users/dtailor/Desktop/openmct",
|
||||||
|
"NVM_CD_FLAGS": "",
|
||||||
|
"npm_config_globalignorefile": "/Users/dtailor/.nvm/versions/node/v11.9.0/etc/npmignore",
|
||||||
|
"npm_package_devDependencies_comma_separated_values": "^3.6.4",
|
||||||
|
"SHELL": "/bin/bash",
|
||||||
|
"TERM": "xterm-256color",
|
||||||
|
"npm_config_init_author_url": "",
|
||||||
|
"npm_config_shell": "/bin/bash",
|
||||||
|
"npm_config_maxsockets": "50",
|
||||||
|
"npm_package_devDependencies_vue_template_compiler": "2.5.6",
|
||||||
|
"npm_package_devDependencies_style_loader": "^1.0.1",
|
||||||
|
"npm_package_devDependencies_moment_duration_format": "^2.2.2",
|
||||||
|
"npm_config_parseable": "",
|
||||||
|
"npm_config_shrinkwrap": "true",
|
||||||
|
"npm_config_metrics_registry": "https://registry.npmjs.org/",
|
||||||
|
"TMPDIR": "/var/folders/ks/ytghmh9x4lj3cchr5km5lhkcb7v9y2/T/",
|
||||||
|
"npm_config_timing": "",
|
||||||
|
"npm_config_init_license": "ISC",
|
||||||
|
"npm_package_scripts_lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||||
|
"npm_package_devDependencies_d3_array": "1.2.x",
|
||||||
|
"Apple_PubSub_Socket_Render": "/private/tmp/com.apple.launchd.PsV6Dfq4Tm/Render",
|
||||||
|
"npm_config_if_present": "",
|
||||||
|
"npm_package_devDependencies_concurrently": "^3.6.1",
|
||||||
|
"TERM_PROGRAM_VERSION": "421.2",
|
||||||
|
"npm_package_scripts_jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
|
||||||
|
"npm_config_sign_git_tag": "",
|
||||||
|
"npm_config_init_author_email": "",
|
||||||
|
"npm_config_cache_max": "Infinity",
|
||||||
|
"npm_config_preid": "",
|
||||||
|
"npm_config_long": "",
|
||||||
|
"npm_config_local_address": "",
|
||||||
|
"npm_config_cert": "",
|
||||||
|
"npm_config_git_tag_version": "true",
|
||||||
|
"npm_package_devDependencies_exports_loader": "^0.7.0",
|
||||||
|
"TERM_SESSION_ID": "0630D2FA-BAC2-48D3-A21D-9AB58A79FB14",
|
||||||
|
"npm_config_noproxy": "",
|
||||||
|
"npm_config_registry": "https://registry.npmjs.org/",
|
||||||
|
"npm_config_fetch_retries": "2",
|
||||||
|
"npm_package_private": "true",
|
||||||
|
"npm_package_devDependencies_karma_jasmine": "^1.1.2",
|
||||||
|
"npm_package_repository_url": "git+https://github.com/nasa/openmct.git",
|
||||||
|
"npm_config_versions": "",
|
||||||
|
"npm_config_key": "",
|
||||||
|
"npm_config_message": "%s",
|
||||||
|
"npm_package_readmeFilename": "README.md",
|
||||||
|
"npm_package_devDependencies_painterro": "^0.2.65",
|
||||||
|
"npm_package_scripts_verify": "concurrently 'npm:test' 'npm:lint'",
|
||||||
|
"npm_package_devDependencies_webpack": "^4.16.2",
|
||||||
|
"npm_package_devDependencies_eventemitter3": "^1.2.0",
|
||||||
|
"npm_package_description": "The Open MCT core platform",
|
||||||
|
"USER": "dtailor",
|
||||||
|
"NVM_DIR": "/Users/dtailor/.nvm",
|
||||||
|
"npm_package_license": "Apache-2.0",
|
||||||
|
"npm_package_scripts_build_dev": "webpack",
|
||||||
|
"npm_package_devDependencies_webpack_cli": "^3.1.0",
|
||||||
|
"npm_package_devDependencies_location_bar": "^3.0.1",
|
||||||
|
"npm_package_devDependencies_jasmine_core": "^3.1.0",
|
||||||
|
"npm_config_globalconfig": "/Users/dtailor/.nvm/versions/node/v11.9.0/etc/npmrc",
|
||||||
|
"npm_package_devDependencies_karma": "^2.0.3",
|
||||||
|
"npm_config_prefer_online": "",
|
||||||
|
"npm_config_always_auth": "",
|
||||||
|
"npm_config_logs_max": "10",
|
||||||
|
"npm_package_devDependencies_angular": "1.7.9",
|
||||||
|
"SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.JH8E4KgH06/Listeners",
|
||||||
|
"npm_package_devDependencies_request": "^2.69.0",
|
||||||
|
"npm_package_devDependencies_eslint": "5.2.0",
|
||||||
|
"__CF_USER_TEXT_ENCODING": "0x167DA7C2:0x0:0x0",
|
||||||
|
"npm_execpath": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/bin/npm-cli.js",
|
||||||
|
"npm_config_global_style": "",
|
||||||
|
"npm_config_cache_lock_retries": "10",
|
||||||
|
"npm_config_cafile": "",
|
||||||
|
"npm_config_update_notifier": "true",
|
||||||
|
"npm_package_scripts_test_debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||||
|
"npm_package_devDependencies_glob": ">= 3.0.0",
|
||||||
|
"npm_config_heading": "npm",
|
||||||
|
"npm_config_audit_level": "low",
|
||||||
|
"npm_package_devDependencies_mini_css_extract_plugin": "^0.4.1",
|
||||||
|
"npm_package_devDependencies_copy_webpack_plugin": "^4.5.2",
|
||||||
|
"npm_config_read_only": "",
|
||||||
|
"npm_config_offline": "",
|
||||||
|
"npm_config_searchlimit": "20",
|
||||||
|
"npm_config_fetch_retry_mintimeout": "10000",
|
||||||
|
"npm_package_devDependencies_webpack_dev_middleware": "^3.1.3",
|
||||||
|
"npm_config_json": "",
|
||||||
|
"npm_config_access": "",
|
||||||
|
"npm_config_argv": "{\"remain\":[],\"cooked\":[\"run\",\"test\"],\"original\":[\"run\",\"test\"]}",
|
||||||
|
"npm_package_scripts_lint_fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||||
|
"npm_package_devDependencies_uuid": "^3.3.3",
|
||||||
|
"npm_package_devDependencies_karma_coverage": "^1.1.2",
|
||||||
|
"PATH": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/dtailor/Desktop/openmct/node_modules/.bin:/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/dtailor/Desktop/openmct/node_modules/.bin:/Users/dtailor/.nvm/versions/node/v11.9.0/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/local/bin:/Users/dtailor/.homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/dtailor/Applications/Visual Studio Code.app/Contents/Resources/app/bin:/opt/local/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/.homebrew/bin:/Users/dtailor/local/bin:/Users/dtailor/.homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin",
|
||||||
|
"npm_config_allow_same_version": "",
|
||||||
|
"npm_config_https_proxy": "",
|
||||||
|
"npm_config_engine_strict": "",
|
||||||
|
"npm_config_description": "true",
|
||||||
|
"npm_package_devDependencies_html2canvas": "^1.0.0-alpha.12",
|
||||||
|
"_": "/Users/dtailor/Desktop/openmct/node_modules/.bin/karma",
|
||||||
|
"npm_config_userconfig": "/Users/dtailor/.npmrc",
|
||||||
|
"npm_config_init_module": "/Users/dtailor/.npm-init.js",
|
||||||
|
"npm_package_author": "",
|
||||||
|
"npm_package_devDependencies_karma_chrome_launcher": "^2.2.0",
|
||||||
|
"npm_package_devDependencies_d3_scale": "1.0.x",
|
||||||
|
"npm_config_cidr": "",
|
||||||
|
"npm_package_devDependencies_printj": "^1.2.1",
|
||||||
|
"PWD": "/Users/dtailor/Desktop/openmct",
|
||||||
|
"npm_config_user": "377333698",
|
||||||
|
"npm_config_node_version": "11.9.0",
|
||||||
|
"npm_package_bugs_url": "https://github.com/nasa/openmct/issues",
|
||||||
|
"npm_package_scripts_test_watch": "karma start --no-single-run",
|
||||||
|
"npm_lifecycle_event": "test",
|
||||||
|
"npm_package_devDependencies_v8_compile_cache": "^1.1.0",
|
||||||
|
"npm_config_ignore_prepublish": "",
|
||||||
|
"npm_config_save": "true",
|
||||||
|
"npm_config_editor": "vi",
|
||||||
|
"npm_config_auth_type": "legacy",
|
||||||
|
"npm_package_repository_type": "git",
|
||||||
|
"npm_package_devDependencies_vue": "2.5.6",
|
||||||
|
"npm_package_devDependencies_marked": "^0.3.5",
|
||||||
|
"npm_package_devDependencies_angular_route": "1.4.14",
|
||||||
|
"npm_package_name": "openmct",
|
||||||
|
"LANG": "en_US.UTF-8",
|
||||||
|
"npm_config_script_shell": "",
|
||||||
|
"npm_config_tag": "latest",
|
||||||
|
"npm_config_global": "",
|
||||||
|
"npm_config_progress": "true",
|
||||||
|
"npm_package_scripts_start": "node app.js",
|
||||||
|
"npm_package_devDependencies_karma_coverage_istanbul_reporter": "^2.1.1",
|
||||||
|
"npm_config_ham_it_up": "",
|
||||||
|
"npm_config_searchstaleness": "900",
|
||||||
|
"npm_config_optional": "true",
|
||||||
|
"npm_package_scripts_docs": "npm run jsdoc ; npm run otherdoc",
|
||||||
|
"npm_package_devDependencies_istanbul_instrumenter_loader": "^3.0.1",
|
||||||
|
"XPC_FLAGS": "0x0",
|
||||||
|
"npm_config_save_prod": "",
|
||||||
|
"npm_config_force": "",
|
||||||
|
"npm_config_bin_links": "true",
|
||||||
|
"npm_package_devDependencies_moment": "2.25.3",
|
||||||
|
"npm_package_devDependencies_karma_webpack": "^3.0.0",
|
||||||
|
"npm_package_devDependencies_express": "^4.13.1",
|
||||||
|
"npm_config_searchopts": "",
|
||||||
|
"npm_package_devDependencies_d3_time": "1.0.x",
|
||||||
|
"FORCE_COLOR": "2",
|
||||||
|
"npm_config_node_gyp": "/Users/dtailor/.nvm/versions/node/v11.9.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",
|
||||||
|
"npm_config_depth": "Infinity",
|
||||||
|
"npm_package_scripts_build_prod": "cross-env NODE_ENV=production webpack",
|
||||||
|
"npm_config_sso_poll_frequency": "500",
|
||||||
|
"npm_config_rebuild_bundle": "true",
|
||||||
|
"npm_package_version": "1.0.0-snapshot",
|
||||||
|
"XPC_SERVICE_NAME": "0",
|
||||||
|
"npm_config_unicode": "true",
|
||||||
|
"npm_package_devDependencies_jsdoc": "^3.3.2",
|
||||||
|
"SHLVL": "4",
|
||||||
|
"HOME": "/Users/dtailor",
|
||||||
|
"npm_config_fetch_retry_maxtimeout": "60000",
|
||||||
|
"npm_package_scripts_test": "karma start --single-run",
|
||||||
|
"npm_package_devDependencies_zepto": "^1.2.0",
|
||||||
|
"npm_package_devDependencies_eslint_plugin_vue": "^6.0.0",
|
||||||
|
"npm_config_ca": "",
|
||||||
|
"npm_config_tag_version_prefix": "v",
|
||||||
|
"npm_config_strict_ssl": "true",
|
||||||
|
"npm_config_sso_type": "oauth",
|
||||||
|
"npm_config_scripts_prepend_node_path": "warn-only",
|
||||||
|
"npm_config_save_prefix": "^",
|
||||||
|
"npm_config_loglevel": "notice",
|
||||||
|
"npm_package_devDependencies_lodash": "^3.10.1",
|
||||||
|
"npm_package_devDependencies_karma_cli": "^1.0.1",
|
||||||
|
"npm_package_devDependencies_d3_color": "1.0.x",
|
||||||
|
"npm_config_save_exact": "",
|
||||||
|
"npm_config_dev": "",
|
||||||
|
"npm_config_group": "1286109195",
|
||||||
|
"npm_config_fetch_retry_factor": "10",
|
||||||
|
"npm_package_devDependencies_webpack_hot_middleware": "^2.22.3",
|
||||||
|
"npm_package_devDependencies_cross_env": "^6.0.3",
|
||||||
|
"npm_package_devDependencies_babel_eslint": "8.2.6",
|
||||||
|
"HOMEBREW_PREFIX": "/Users/dtailor/.homebrew",
|
||||||
|
"npm_config_version": "",
|
||||||
|
"npm_config_prefer_offline": "",
|
||||||
|
"npm_config_cache_lock_stale": "60000",
|
||||||
|
"npm_config_otp": "",
|
||||||
|
"npm_config_cache_min": "10",
|
||||||
|
"npm_package_devDependencies_vue_loader": "^15.2.6",
|
||||||
|
"npm_config_searchexclude": "",
|
||||||
|
"npm_config_cache": "/Users/dtailor/.npm",
|
||||||
|
"npm_package_scripts_test_coverage": "./scripts/test-coverage.sh",
|
||||||
|
"npm_package_devDependencies_d3_interpolate": "1.1.x",
|
||||||
|
"npm_package_devDependencies_d3_format": "1.2.x",
|
||||||
|
"LOGNAME": "dtailor",
|
||||||
|
"npm_lifecycle_script": "karma start --single-run",
|
||||||
|
"npm_config_color": "true",
|
||||||
|
"npm_package_devDependencies_node_bourbon": "^4.2.3",
|
||||||
|
"npm_package_devDependencies_karma_sourcemap_loader": "^0.3.7",
|
||||||
|
"npm_package_devDependencies_karma_html_reporter": "^0.2.7",
|
||||||
|
"npm_config_proxy": "",
|
||||||
|
"npm_config_package_lock": "true",
|
||||||
|
"npm_package_devDependencies_d3_time_format": "2.1.x",
|
||||||
|
"npm_package_devDependencies_d3_axis": "1.0.x",
|
||||||
|
"npm_config_package_lock_only": "",
|
||||||
|
"npm_package_devDependencies_moment_timezone": "0.5.28",
|
||||||
|
"npm_config_save_optional": "",
|
||||||
|
"NVM_BIN": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin",
|
||||||
|
"npm_config_ignore_scripts": "",
|
||||||
|
"npm_config_user_agent": "npm/6.5.0 node/v11.9.0 darwin x64",
|
||||||
|
"npm_package_devDependencies_imports_loader": "^0.8.0",
|
||||||
|
"npm_package_devDependencies_file_saver": "^1.3.8",
|
||||||
|
"npm_config_cache_lock_wait": "10000",
|
||||||
|
"npm_config_production": "",
|
||||||
|
"npm_package_scripts_build_watch": "webpack --watch",
|
||||||
|
"DISPLAY": "/private/tmp/com.apple.launchd.E3N8oC6RMf/org.macosforge.xquartz:0",
|
||||||
|
"npm_config_send_metrics": "",
|
||||||
|
"npm_config_save_bundle": "",
|
||||||
|
"npm_package_scripts_prepare": "npm run build:prod",
|
||||||
|
"npm_config_node_options": "",
|
||||||
|
"npm_config_umask": "0022",
|
||||||
|
"npm_config_init_version": "1.0.0",
|
||||||
|
"npm_package_devDependencies_split": "^1.0.0",
|
||||||
|
"npm_package_devDependencies_raw_loader": "^0.5.1",
|
||||||
|
"npm_config_init_author_name": "",
|
||||||
|
"npm_config_git": "git",
|
||||||
|
"npm_config_scope": "",
|
||||||
|
"npm_package_scripts_clean": "rm -rf ./dist",
|
||||||
|
"npm_package_devDependencies_node_sass": "^4.9.2",
|
||||||
|
"npm_package_devDependencies_css_loader": "^1.0.0",
|
||||||
|
"DISABLE_UPDATE_CHECK": "1",
|
||||||
|
"npm_config_onload_script": "",
|
||||||
|
"npm_config_unsafe_perm": "true",
|
||||||
|
"npm_config_tmp": "/var/folders/ks/ytghmh9x4lj3cchr5km5lhkcb7v9y2/T",
|
||||||
|
"npm_package_devDependencies_d3_collection": "1.0.x",
|
||||||
|
"npm_node_execpath": "/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
|
||||||
|
"npm_config_link": "",
|
||||||
|
"npm_config_prefix": "/Users/dtailor/.nvm/versions/node/v11.9.0",
|
||||||
|
"npm_package_devDependencies_html_loader": "^0.5.5"
|
||||||
|
},
|
||||||
|
"userLimits": {
|
||||||
|
"core_file_size_blocks": {
|
||||||
|
"soft": 0,
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"data_seg_size_kbytes": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"file_size_blocks": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"max_locked_memory_bytes": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"max_memory_size_kbytes": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"open_files": {
|
||||||
|
"soft": 24576,
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"stack_size_bytes": {
|
||||||
|
"soft": 8388608,
|
||||||
|
"hard": 67104768
|
||||||
|
},
|
||||||
|
"cpu_time_seconds": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
},
|
||||||
|
"max_user_processes": {
|
||||||
|
"soft": 1418,
|
||||||
|
"hard": 2128
|
||||||
|
},
|
||||||
|
"virtual_memory_kbytes": {
|
||||||
|
"soft": "unlimited",
|
||||||
|
"hard": "unlimited"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sharedObjects": [
|
||||||
|
"/Users/dtailor/.nvm/versions/node/v11.9.0/bin/node",
|
||||||
|
"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
|
||||||
|
"/usr/lib/libSystem.B.dylib",
|
||||||
|
"/usr/lib/libc++.1.dylib",
|
||||||
|
"/usr/lib/libobjc.A.dylib",
|
||||||
|
"/usr/lib/libDiagnosticMessagesClient.dylib",
|
||||||
|
"/usr/lib/libicucore.A.dylib",
|
||||||
|
"/usr/lib/libz.1.dylib",
|
||||||
|
"/usr/lib/libc++abi.dylib",
|
||||||
|
"/usr/lib/system/libcache.dylib",
|
||||||
|
"/usr/lib/system/libcommonCrypto.dylib",
|
||||||
|
"/usr/lib/system/libcompiler_rt.dylib",
|
||||||
|
"/usr/lib/system/libcopyfile.dylib",
|
||||||
|
"/usr/lib/system/libcorecrypto.dylib",
|
||||||
|
"/usr/lib/system/libdispatch.dylib",
|
||||||
|
"/usr/lib/system/libdyld.dylib",
|
||||||
|
"/usr/lib/system/libkeymgr.dylib",
|
||||||
|
"/usr/lib/system/liblaunch.dylib",
|
||||||
|
"/usr/lib/system/libmacho.dylib",
|
||||||
|
"/usr/lib/system/libquarantine.dylib",
|
||||||
|
"/usr/lib/system/libremovefile.dylib",
|
||||||
|
"/usr/lib/system/libsystem_asl.dylib",
|
||||||
|
"/usr/lib/system/libsystem_blocks.dylib",
|
||||||
|
"/usr/lib/system/libsystem_c.dylib",
|
||||||
|
"/usr/lib/system/libsystem_configuration.dylib",
|
||||||
|
"/usr/lib/system/libsystem_coreservices.dylib",
|
||||||
|
"/usr/lib/system/libsystem_darwin.dylib",
|
||||||
|
"/usr/lib/system/libsystem_dnssd.dylib",
|
||||||
|
"/usr/lib/system/libsystem_info.dylib",
|
||||||
|
"/usr/lib/system/libsystem_m.dylib",
|
||||||
|
"/usr/lib/system/libsystem_malloc.dylib",
|
||||||
|
"/usr/lib/system/libsystem_networkextension.dylib",
|
||||||
|
"/usr/lib/system/libsystem_notify.dylib",
|
||||||
|
"/usr/lib/system/libsystem_sandbox.dylib",
|
||||||
|
"/usr/lib/system/libsystem_secinit.dylib",
|
||||||
|
"/usr/lib/system/libsystem_kernel.dylib",
|
||||||
|
"/usr/lib/system/libsystem_platform.dylib",
|
||||||
|
"/usr/lib/system/libsystem_pthread.dylib",
|
||||||
|
"/usr/lib/system/libsystem_symptoms.dylib",
|
||||||
|
"/usr/lib/system/libsystem_trace.dylib",
|
||||||
|
"/usr/lib/system/libunwind.dylib",
|
||||||
|
"/usr/lib/system/libxpc.dylib",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
|
||||||
|
"/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics",
|
||||||
|
"/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO",
|
||||||
|
"/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LangAnalysis.framework/Versions/A/LangAnalysis",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis",
|
||||||
|
"/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight",
|
||||||
|
"/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface",
|
||||||
|
"/usr/lib/libxml2.2.dylib",
|
||||||
|
"/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate",
|
||||||
|
"/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation",
|
||||||
|
"/usr/lib/libcompression.dylib",
|
||||||
|
"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
|
||||||
|
"/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay",
|
||||||
|
"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit",
|
||||||
|
"/System/Library/Frameworks/Metal.framework/Versions/A/Metal",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders",
|
||||||
|
"/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport",
|
||||||
|
"/System/Library/Frameworks/Security.framework/Versions/A/Security",
|
||||||
|
"/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
|
||||||
|
"/usr/lib/libbsm.0.dylib",
|
||||||
|
"/usr/lib/liblzma.5.dylib",
|
||||||
|
"/usr/lib/libauto.dylib",
|
||||||
|
"/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration",
|
||||||
|
"/usr/lib/libarchive.2.dylib",
|
||||||
|
"/usr/lib/liblangid.dylib",
|
||||||
|
"/usr/lib/libCRFSuite.dylib",
|
||||||
|
"/usr/lib/libenergytrace.dylib",
|
||||||
|
"/usr/lib/system/libkxld.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression",
|
||||||
|
"/usr/lib/libOpenScriptingUtil.dylib",
|
||||||
|
"/usr/lib/libcoretls.dylib",
|
||||||
|
"/usr/lib/libcoretls_cfhelpers.dylib",
|
||||||
|
"/usr/lib/libpam.2.dylib",
|
||||||
|
"/usr/lib/libsqlite3.dylib",
|
||||||
|
"/usr/lib/libxar.1.dylib",
|
||||||
|
"/usr/lib/libbz2.1.0.dylib",
|
||||||
|
"/usr/lib/libnetwork.dylib",
|
||||||
|
"/usr/lib/libapple_nghttp2.dylib",
|
||||||
|
"/usr/lib/libpcap.A.dylib",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices",
|
||||||
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList",
|
||||||
|
"/System/Library/Frameworks/NetFS.framework/Versions/A/NetFS",
|
||||||
|
"/System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth",
|
||||||
|
"/System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport",
|
||||||
|
"/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC",
|
||||||
|
"/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP",
|
||||||
|
"/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities",
|
||||||
|
"/usr/lib/libmecabra.dylib",
|
||||||
|
"/usr/lib/libmecab.1.0.0.dylib",
|
||||||
|
"/usr/lib/libgermantok.dylib",
|
||||||
|
"/usr/lib/libThaiTokenizer.dylib",
|
||||||
|
"/usr/lib/libChineseTokenizer.dylib",
|
||||||
|
"/usr/lib/libiconv.2.dylib",
|
||||||
|
"/usr/lib/libcharset.1.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling",
|
||||||
|
"/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji",
|
||||||
|
"/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon",
|
||||||
|
"/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData",
|
||||||
|
"/usr/lib/libcmph.dylib",
|
||||||
|
"/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData",
|
||||||
|
"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory",
|
||||||
|
"/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS",
|
||||||
|
"/usr/lib/libutil.dylib",
|
||||||
|
"/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement",
|
||||||
|
"/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement",
|
||||||
|
"/usr/lib/libxslt.1.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib",
|
||||||
|
"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler",
|
||||||
|
"/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator",
|
||||||
|
"/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment",
|
||||||
|
"/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSCore.framework/Versions/A/MPSCore",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSImage.framework/Versions/A/MPSImage",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix",
|
||||||
|
"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector",
|
||||||
|
"/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools",
|
||||||
|
"/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary",
|
||||||
|
"/usr/lib/libMobileGestalt.dylib",
|
||||||
|
"/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage",
|
||||||
|
"/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL",
|
||||||
|
"/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer",
|
||||||
|
"/System/Library/PrivateFrameworks/FaceCore.framework/Versions/A/FaceCore",
|
||||||
|
"/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL",
|
||||||
|
"/usr/lib/libFosl_dynamic.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontParser.dylib",
|
||||||
|
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib",
|
||||||
|
"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib",
|
||||||
|
"/usr/lib/libcups.2.dylib",
|
||||||
|
"/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos",
|
||||||
|
"/System/Library/Frameworks/GSS.framework/Versions/A/GSS",
|
||||||
|
"/usr/lib/libresolv.9.dylib",
|
||||||
|
"/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal",
|
||||||
|
"/usr/lib/libheimdal-asn1.dylib",
|
||||||
|
"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory",
|
||||||
|
"/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth",
|
||||||
|
"/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation",
|
||||||
|
"/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio",
|
||||||
|
"/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox",
|
||||||
|
"/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce",
|
||||||
|
"/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices",
|
||||||
|
"/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard"
|
||||||
|
]
|
||||||
|
}
|
@ -1,2 +0,0 @@
|
|||||||
export NODE_OPTIONS=--max_old_space_size=4096
|
|
||||||
cross-env COVERAGE=true karma start --single-run
|
|
47
src/MCT.js
47
src/MCT.js
@ -28,7 +28,7 @@ define([
|
|||||||
'./api/api',
|
'./api/api',
|
||||||
'./api/overlays/OverlayAPI',
|
'./api/overlays/OverlayAPI',
|
||||||
'./selection/Selection',
|
'./selection/Selection',
|
||||||
'./api/objects/object-utils',
|
'objectUtils',
|
||||||
'./plugins/plugins',
|
'./plugins/plugins',
|
||||||
'./adapter/indicators/legacy-indicators-plugin',
|
'./adapter/indicators/legacy-indicators-plugin',
|
||||||
'./plugins/buildInfo/plugin',
|
'./plugins/buildInfo/plugin',
|
||||||
@ -249,7 +249,7 @@ define([
|
|||||||
this.legacyRegistry = new BundleRegistry();
|
this.legacyRegistry = new BundleRegistry();
|
||||||
installDefaultBundles(this.legacyRegistry);
|
installDefaultBundles(this.legacyRegistry);
|
||||||
|
|
||||||
// Plugin's that are installed by default
|
// Plugins that are installed by default
|
||||||
|
|
||||||
this.install(this.plugins.Plot());
|
this.install(this.plugins.Plot());
|
||||||
this.install(this.plugins.TelemetryTable());
|
this.install(this.plugins.TelemetryTable());
|
||||||
@ -266,6 +266,8 @@ define([
|
|||||||
this.install(this.plugins.WebPage());
|
this.install(this.plugins.WebPage());
|
||||||
this.install(this.plugins.Condition());
|
this.install(this.plugins.Condition());
|
||||||
this.install(this.plugins.ConditionWidget());
|
this.install(this.plugins.ConditionWidget());
|
||||||
|
this.install(this.plugins.URLTimeSettingsSynchronizer());
|
||||||
|
this.install(this.plugins.NotificationIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||||
@ -350,17 +352,13 @@ define([
|
|||||||
* @param {HTMLElement} [domElement] the DOM element in which to run
|
* @param {HTMLElement} [domElement] the DOM element in which to run
|
||||||
* MCT; if undefined, MCT will be run in the body of the document
|
* MCT; if undefined, MCT will be run in the body of the document
|
||||||
*/
|
*/
|
||||||
MCT.prototype.start = function (domElement) {
|
MCT.prototype.start = function (domElement = document.body, isHeadlessMode = false) {
|
||||||
if (!this.plugins.DisplayLayout._installed) {
|
if (!this.plugins.DisplayLayout._installed) {
|
||||||
this.install(this.plugins.DisplayLayout({
|
this.install(this.plugins.DisplayLayout({
|
||||||
showAsView: ['summary-widget']
|
showAsView: ['summary-widget']
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!domElement) {
|
|
||||||
domElement = document.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element = domElement;
|
this.element = domElement;
|
||||||
|
|
||||||
this.legacyExtension('runs', {
|
this.legacyExtension('runs', {
|
||||||
@ -400,24 +398,31 @@ define([
|
|||||||
// something has depended upon objectService. Cool, right?
|
// something has depended upon objectService. Cool, right?
|
||||||
this.$injector.get('objectService');
|
this.$injector.get('objectService');
|
||||||
|
|
||||||
var appLayout = new Vue({
|
if (!isHeadlessMode) {
|
||||||
components: {
|
var appLayout = new Vue({
|
||||||
'Layout': Layout.default
|
components: {
|
||||||
},
|
'Layout': Layout.default
|
||||||
provide: {
|
},
|
||||||
openmct: this
|
provide: {
|
||||||
},
|
openmct: this
|
||||||
template: '<Layout ref="layout"></Layout>'
|
},
|
||||||
});
|
template: '<Layout ref="layout"></Layout>'
|
||||||
domElement.appendChild(appLayout.$mount().$el);
|
});
|
||||||
|
domElement.appendChild(appLayout.$mount().$el);
|
||||||
|
|
||||||
this.layout = appLayout.$refs.layout;
|
this.layout = appLayout.$refs.layout;
|
||||||
Browse(this);
|
Browse(this);
|
||||||
|
}
|
||||||
this.router.start();
|
this.router.start();
|
||||||
this.emit('start');
|
this.emit('start');
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MCT.prototype.startHeadless = function () {
|
||||||
|
let unreachableNode = document.createElement('div');
|
||||||
|
return this.start(unreachableNode, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a plugin in MCT.
|
* Install a plugin in MCT.
|
||||||
*
|
*
|
||||||
@ -429,6 +434,10 @@ define([
|
|||||||
plugin(this);
|
plugin(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MCT.prototype.destroy = function () {
|
||||||
|
this.emit('destroy');
|
||||||
|
};
|
||||||
|
|
||||||
MCT.prototype.plugins = plugins;
|
MCT.prototype.plugins = plugins;
|
||||||
|
|
||||||
return MCT;
|
return MCT;
|
||||||
|
@ -21,24 +21,28 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'./MCT',
|
|
||||||
'./plugins/plugins',
|
'./plugins/plugins',
|
||||||
'legacyRegistry'
|
'legacyRegistry',
|
||||||
], function (MCT, plugins, legacyRegistry) {
|
'utils/testing'
|
||||||
xdescribe("MCT", function () {
|
], function (plugins, legacyRegistry, testUtils) {
|
||||||
|
describe("MCT", function () {
|
||||||
var openmct;
|
var openmct;
|
||||||
var mockPlugin;
|
var mockPlugin;
|
||||||
var mockPlugin2;
|
var mockPlugin2;
|
||||||
var mockListener;
|
var mockListener;
|
||||||
var oldBundles;
|
var oldBundles;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
testUtils.resetApplicationState();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockPlugin = jasmine.createSpy('plugin');
|
mockPlugin = jasmine.createSpy('plugin');
|
||||||
mockPlugin2 = jasmine.createSpy('plugin2');
|
mockPlugin2 = jasmine.createSpy('plugin2');
|
||||||
mockListener = jasmine.createSpy('listener');
|
mockListener = jasmine.createSpy('listener');
|
||||||
oldBundles = legacyRegistry.list();
|
oldBundles = legacyRegistry.list();
|
||||||
|
|
||||||
openmct = new MCT();
|
openmct = testUtils.createOpenMct();
|
||||||
|
|
||||||
openmct.install(mockPlugin);
|
openmct.install(mockPlugin);
|
||||||
openmct.install(mockPlugin2);
|
openmct.install(mockPlugin2);
|
||||||
@ -52,6 +56,7 @@ define([
|
|||||||
legacyRegistry.delete(bundle);
|
legacyRegistry.delete(bundle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
testUtils.resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exposes plugins", function () {
|
it("exposes plugins", function () {
|
||||||
@ -63,8 +68,11 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("start", function () {
|
describe("start", function () {
|
||||||
beforeEach(function () {
|
let appHolder;
|
||||||
openmct.start();
|
beforeEach(function (done) {
|
||||||
|
appHolder = document.createElement("div");
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.start(appHolder);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls plugins for configuration", function () {
|
it("calls plugins for configuration", function () {
|
||||||
@ -75,25 +83,51 @@ define([
|
|||||||
it("emits a start event", function () {
|
it("emits a start event", function () {
|
||||||
expect(mockListener).toHaveBeenCalled();
|
expect(mockListener).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Renders the application into the provided container element", function () {
|
||||||
|
let openMctShellElements = appHolder.querySelectorAll('div.l-shell');
|
||||||
|
expect(openMctShellElements.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("startHeadless", function () {
|
||||||
|
beforeEach(function (done) {
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls plugins for configuration", function () {
|
||||||
|
expect(mockPlugin).toHaveBeenCalledWith(openmct);
|
||||||
|
expect(mockPlugin2).toHaveBeenCalledWith(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emits a start event", function () {
|
||||||
|
expect(mockListener).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not render Open MCT", function () {
|
||||||
|
let openMctShellElements = document.body.querySelectorAll('div.l-shell');
|
||||||
|
expect(openMctShellElements.length).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("setAssetPath", function () {
|
describe("setAssetPath", function () {
|
||||||
var testAssetPath;
|
var testAssetPath;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testAssetPath = "some/path";
|
|
||||||
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
|
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
|
||||||
openmct.setAssetPath(testAssetPath);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("internally configures the path for assets", function () {
|
it("configures the path for assets", function () {
|
||||||
expect(openmct.legacyExtension).toHaveBeenCalledWith(
|
testAssetPath = "some/path/";
|
||||||
'constants',
|
openmct.setAssetPath(testAssetPath);
|
||||||
{
|
expect(openmct.getAssetPath()).toBe(testAssetPath);
|
||||||
key: "ASSETS_PATH",
|
});
|
||||||
value: testAssetPath
|
|
||||||
}
|
it("adds a trailing /", function () {
|
||||||
);
|
testAssetPath = "some/path";
|
||||||
|
openmct.setAssetPath(testAssetPath);
|
||||||
|
expect(openmct.getAssetPath()).toBe(testAssetPath + "/");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (objectUtils) {
|
], function (objectUtils) {
|
||||||
function ActionDialogDecorator(mct, actionService) {
|
function ActionDialogDecorator(mct, actionService) {
|
||||||
this.mct = mct;
|
this.mct = mct;
|
||||||
|
@ -29,7 +29,6 @@ define([
|
|||||||
'./capabilities/APICapabilityDecorator',
|
'./capabilities/APICapabilityDecorator',
|
||||||
'./policies/AdaptedViewPolicy',
|
'./policies/AdaptedViewPolicy',
|
||||||
'./runs/AlternateCompositionInitializer',
|
'./runs/AlternateCompositionInitializer',
|
||||||
'./runs/TimeSettingsURLHandler',
|
|
||||||
'./runs/TypeDeprecationChecker',
|
'./runs/TypeDeprecationChecker',
|
||||||
'./runs/LegacyTelemetryProvider',
|
'./runs/LegacyTelemetryProvider',
|
||||||
'./runs/RegisterLegacyTypes',
|
'./runs/RegisterLegacyTypes',
|
||||||
@ -46,7 +45,6 @@ define([
|
|||||||
APICapabilityDecorator,
|
APICapabilityDecorator,
|
||||||
AdaptedViewPolicy,
|
AdaptedViewPolicy,
|
||||||
AlternateCompositionInitializer,
|
AlternateCompositionInitializer,
|
||||||
TimeSettingsURLHandler,
|
|
||||||
TypeDeprecationChecker,
|
TypeDeprecationChecker,
|
||||||
LegacyTelemetryProvider,
|
LegacyTelemetryProvider,
|
||||||
RegisterLegacyTypes,
|
RegisterLegacyTypes,
|
||||||
@ -134,16 +132,6 @@ define([
|
|||||||
implementation: AlternateCompositionInitializer,
|
implementation: AlternateCompositionInitializer,
|
||||||
depends: ["openmct"]
|
depends: ["openmct"]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
implementation: function (openmct, $location, $rootScope) {
|
|
||||||
return new TimeSettingsURLHandler(
|
|
||||||
openmct.time,
|
|
||||||
$location,
|
|
||||||
$rootScope
|
|
||||||
);
|
|
||||||
},
|
|
||||||
depends: ["openmct", "$location", "$rootScope"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
implementation: LegacyTelemetryProvider,
|
implementation: LegacyTelemetryProvider,
|
||||||
depends: [
|
depends: [
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['../../api/objects/object-utils'], function (objectUtils) {
|
define(['objectUtils'], function (objectUtils) {
|
||||||
function AdapterCapability(domainObject) {
|
function AdapterCapability(domainObject) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
* Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
|
* Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'../../api/objects/object-utils',
|
'objectUtils',
|
||||||
'../../../platform/core/src/capabilities/ContextualDomainObject'
|
'../../../platform/core/src/capabilities/ContextualDomainObject'
|
||||||
], function (objectUtils, ContextualDomainObject) {
|
], function (objectUtils, ContextualDomainObject) {
|
||||||
function AlternateCompositionCapability($injector, domainObject) {
|
function AlternateCompositionCapability($injector, domainObject) {
|
||||||
|
@ -31,6 +31,7 @@ define([
|
|||||||
var capability = viewConstructor(domainObject);
|
var capability = viewConstructor(domainObject);
|
||||||
var oldInvoke = capability.invoke.bind(capability);
|
var oldInvoke = capability.invoke.bind(capability);
|
||||||
|
|
||||||
|
/* eslint-disable you-dont-need-lodash-underscore/map */
|
||||||
capability.invoke = function () {
|
capability.invoke = function () {
|
||||||
var availableViews = oldInvoke();
|
var availableViews = oldInvoke();
|
||||||
var newDomainObject = capability
|
var newDomainObject = capability
|
||||||
@ -52,6 +53,8 @@ define([
|
|||||||
.map('view')
|
.map('view')
|
||||||
.value();
|
.value();
|
||||||
};
|
};
|
||||||
|
/* eslint-enable you-dont-need-lodash-underscore/map */
|
||||||
|
|
||||||
return capability;
|
return capability;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'../capabilities/AlternateCompositionCapability',
|
'../capabilities/AlternateCompositionCapability',
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
AlternateCompositionCapability,
|
AlternateCompositionCapability,
|
||||||
objectUtils
|
objectUtils
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
utils
|
utils
|
||||||
) {
|
) {
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define([
|
|
||||||
'lodash'
|
|
||||||
], function (
|
|
||||||
_
|
|
||||||
) {
|
|
||||||
// Parameter names in query string
|
|
||||||
var SEARCH = {
|
|
||||||
MODE: 'tc.mode',
|
|
||||||
TIME_SYSTEM: 'tc.timeSystem',
|
|
||||||
START_BOUND: 'tc.startBound',
|
|
||||||
END_BOUND: 'tc.endBound',
|
|
||||||
START_DELTA: 'tc.startDelta',
|
|
||||||
END_DELTA: 'tc.endDelta'
|
|
||||||
};
|
|
||||||
var TIME_EVENTS = ['bounds', 'timeSystem', 'clock', 'clockOffsets'];
|
|
||||||
// Used to shorthand calls to $location, which clears null parameters
|
|
||||||
var NULL_PARAMETERS = { key: null, start: null, end: null };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Communicates settings from the URL to the time API,
|
|
||||||
* and vice versa.
|
|
||||||
*/
|
|
||||||
function TimeSettingsURLHandler(time, $location, $rootScope) {
|
|
||||||
this.time = time;
|
|
||||||
this.$location = $location;
|
|
||||||
|
|
||||||
$rootScope.$on('$locationChangeSuccess', this.updateTime.bind(this));
|
|
||||||
|
|
||||||
TIME_EVENTS.forEach(function (event) {
|
|
||||||
this.time.on(event, this.updateQueryParams.bind(this));
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.updateTime(); // Initialize
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeSettingsURLHandler.prototype.updateQueryParams = function () {
|
|
||||||
var clock = this.time.clock();
|
|
||||||
var fixed = !clock;
|
|
||||||
var mode = fixed ? 'fixed' : clock.key;
|
|
||||||
var timeSystem = this.time.timeSystem() || NULL_PARAMETERS;
|
|
||||||
var bounds = fixed ? this.time.bounds() : NULL_PARAMETERS;
|
|
||||||
var deltas = fixed ? NULL_PARAMETERS : this.time.clockOffsets();
|
|
||||||
|
|
||||||
bounds = bounds || NULL_PARAMETERS;
|
|
||||||
deltas = deltas || NULL_PARAMETERS;
|
|
||||||
if (deltas.start) {
|
|
||||||
deltas = { start: -deltas.start, end: deltas.end };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$location.search(SEARCH.MODE, mode);
|
|
||||||
this.$location.search(SEARCH.TIME_SYSTEM, timeSystem.key);
|
|
||||||
this.$location.search(SEARCH.START_BOUND, bounds.start);
|
|
||||||
this.$location.search(SEARCH.END_BOUND, bounds.end);
|
|
||||||
this.$location.search(SEARCH.START_DELTA, deltas.start);
|
|
||||||
this.$location.search(SEARCH.END_DELTA, deltas.end);
|
|
||||||
};
|
|
||||||
|
|
||||||
TimeSettingsURLHandler.prototype.parseQueryParams = function () {
|
|
||||||
var searchParams = _.pick(this.$location.search(), _.values(SEARCH));
|
|
||||||
var parsedParams = {
|
|
||||||
clock: searchParams[SEARCH.MODE],
|
|
||||||
timeSystem: searchParams[SEARCH.TIME_SYSTEM]
|
|
||||||
};
|
|
||||||
if (!isNaN(parseInt(searchParams[SEARCH.START_DELTA], 0xA)) &&
|
|
||||||
!isNaN(parseInt(searchParams[SEARCH.END_DELTA], 0xA))) {
|
|
||||||
parsedParams.clockOffsets = {
|
|
||||||
start: -searchParams[SEARCH.START_DELTA],
|
|
||||||
end: +searchParams[SEARCH.END_DELTA]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!isNaN(parseInt(searchParams[SEARCH.START_BOUND], 0xA)) &&
|
|
||||||
!isNaN(parseInt(searchParams[SEARCH.END_BOUND], 0xA))) {
|
|
||||||
parsedParams.bounds = {
|
|
||||||
start: +searchParams[SEARCH.START_BOUND],
|
|
||||||
end: +searchParams[SEARCH.END_BOUND]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return parsedParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
TimeSettingsURLHandler.prototype.updateTime = function () {
|
|
||||||
var params = this.parseQueryParams();
|
|
||||||
if (_.isEqual(params, this.last)) {
|
|
||||||
return; // Do nothing;
|
|
||||||
}
|
|
||||||
this.last = params;
|
|
||||||
|
|
||||||
if (!params.timeSystem) {
|
|
||||||
this.updateQueryParams();
|
|
||||||
} else if (params.clock === 'fixed' && params.bounds) {
|
|
||||||
if (!this.time.timeSystem() ||
|
|
||||||
this.time.timeSystem().key !== params.timeSystem) {
|
|
||||||
|
|
||||||
this.time.timeSystem(
|
|
||||||
params.timeSystem,
|
|
||||||
params.bounds
|
|
||||||
);
|
|
||||||
} else if (!_.isEqual(this.time.bounds(), params.bounds)) {
|
|
||||||
this.time.bounds(params.bounds);
|
|
||||||
}
|
|
||||||
if (this.time.clock()) {
|
|
||||||
this.time.stopClock();
|
|
||||||
}
|
|
||||||
} else if (params.clockOffsets) {
|
|
||||||
if (params.clock === 'fixed') {
|
|
||||||
this.time.stopClock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.time.clock() ||
|
|
||||||
this.time.clock().key !== params.clock) {
|
|
||||||
|
|
||||||
this.time.clock(params.clock, params.clockOffsets);
|
|
||||||
} else if (!_.isEqual(this.time.clockOffsets(), params.clockOffsets)) {
|
|
||||||
this.time.clockOffsets(params.clockOffsets);
|
|
||||||
}
|
|
||||||
if (!this.time.timeSystem() ||
|
|
||||||
this.time.timeSystem().key !== params.timeSystem) {
|
|
||||||
|
|
||||||
this.time.timeSystem(params.timeSystem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Neither found, update from timeSystem.
|
|
||||||
this.updateQueryParams();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return TimeSettingsURLHandler;
|
|
||||||
});
|
|
@ -1,576 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define([
|
|
||||||
'./TimeSettingsURLHandler',
|
|
||||||
'../../api/time/TimeAPI'
|
|
||||||
], function (
|
|
||||||
TimeSettingsURLHandler,
|
|
||||||
TimeAPI
|
|
||||||
) {
|
|
||||||
describe("TimeSettingsURLHandler", function () {
|
|
||||||
var time;
|
|
||||||
var $location;
|
|
||||||
var $rootScope;
|
|
||||||
var search;
|
|
||||||
var handler; // eslint-disable-line
|
|
||||||
var clockA;
|
|
||||||
var clockB;
|
|
||||||
var timeSystemA;
|
|
||||||
var timeSystemB;
|
|
||||||
var boundsA;
|
|
||||||
var boundsB;
|
|
||||||
var offsetsA;
|
|
||||||
var offsetsB;
|
|
||||||
var initialize;
|
|
||||||
var triggerLocationChange;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
clockA = jasmine.createSpyObj('clockA', ['on', 'off']);
|
|
||||||
clockA.key = 'clockA';
|
|
||||||
clockA.currentValue = function () {
|
|
||||||
return 1000;
|
|
||||||
};
|
|
||||||
clockB = jasmine.createSpyObj('clockB', ['on', 'off']);
|
|
||||||
clockB.key = 'clockB';
|
|
||||||
clockB.currentValue = function () {
|
|
||||||
return 2000;
|
|
||||||
};
|
|
||||||
timeSystemA = {key: 'timeSystemA'};
|
|
||||||
timeSystemB = {key: 'timeSystemB'};
|
|
||||||
boundsA = {
|
|
||||||
start: 10,
|
|
||||||
end: 20
|
|
||||||
};
|
|
||||||
boundsB = {
|
|
||||||
start: 120,
|
|
||||||
end: 360
|
|
||||||
};
|
|
||||||
offsetsA = {
|
|
||||||
start: -100,
|
|
||||||
end: 0
|
|
||||||
};
|
|
||||||
offsetsB = {
|
|
||||||
start: -50,
|
|
||||||
end: 50
|
|
||||||
};
|
|
||||||
|
|
||||||
time = new TimeAPI();
|
|
||||||
|
|
||||||
[
|
|
||||||
'on',
|
|
||||||
'bounds',
|
|
||||||
'clockOffsets',
|
|
||||||
'timeSystem',
|
|
||||||
'clock',
|
|
||||||
'stopClock'
|
|
||||||
].forEach(function (method) {
|
|
||||||
spyOn(time, method).and.callThrough();
|
|
||||||
});
|
|
||||||
time.addTimeSystem(timeSystemA);
|
|
||||||
time.addTimeSystem(timeSystemB);
|
|
||||||
time.addClock(clockA);
|
|
||||||
time.addClock(clockB);
|
|
||||||
|
|
||||||
$location = jasmine.createSpyObj('$location', [
|
|
||||||
'search'
|
|
||||||
]);
|
|
||||||
$rootScope = jasmine.createSpyObj('$rootScope', [
|
|
||||||
'$on'
|
|
||||||
]);
|
|
||||||
|
|
||||||
search = {};
|
|
||||||
$location.search.and.callFake(function (key, value) {
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
return search;
|
|
||||||
}
|
|
||||||
if (value === null) {
|
|
||||||
delete search[key];
|
|
||||||
} else {
|
|
||||||
search[key] = String(value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(time.timeSystem()).toBeUndefined();
|
|
||||||
expect(time.bounds()).toEqual({});
|
|
||||||
expect(time.clockOffsets()).toBeUndefined();
|
|
||||||
expect(time.clock()).toBeUndefined();
|
|
||||||
|
|
||||||
initialize = function () {
|
|
||||||
handler = new TimeSettingsURLHandler(
|
|
||||||
time,
|
|
||||||
$location,
|
|
||||||
$rootScope
|
|
||||||
);
|
|
||||||
expect($rootScope.$on).toHaveBeenCalledWith(
|
|
||||||
'$locationChangeSuccess',
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
triggerLocationChange = $rootScope.$on.calls.mostRecent().args[1];
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initializes with missing time system", function () {
|
|
||||||
// This handles an odd transitory case where a url does not include
|
|
||||||
// a timeSystem. It's generally only experienced by those who
|
|
||||||
// based their code on the tutorial before it specified a time
|
|
||||||
// system.
|
|
||||||
search['tc.mode'] = 'clockA';
|
|
||||||
search['tc.timeSystem'] = undefined;
|
|
||||||
search['tc.startDelta'] = '123';
|
|
||||||
search['tc.endDelta'] = '456';
|
|
||||||
|
|
||||||
// We don't specify behavior right now other than "don't break."
|
|
||||||
expect(initialize).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can initalize fixed mode from location", function () {
|
|
||||||
search['tc.mode'] = 'fixed';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemA';
|
|
||||||
search['tc.startBound'] = '123';
|
|
||||||
search['tc.endBound'] = '456';
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemA',
|
|
||||||
{
|
|
||||||
start: 123,
|
|
||||||
end: 456
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can initialize clock mode from location", function () {
|
|
||||||
search['tc.mode'] = 'clockA';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemA';
|
|
||||||
search['tc.startDelta'] = '123';
|
|
||||||
search['tc.endDelta'] = '456';
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockA',
|
|
||||||
{
|
|
||||||
start: -123,
|
|
||||||
end: 456
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can initialize fixed mode from time API", function () {
|
|
||||||
time.timeSystem(timeSystemA.key, boundsA);
|
|
||||||
initialize();
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.mode', 'fixed');
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.timeSystem', 'timeSystemA');
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.startBound', 10);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.endBound', 20);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.startDelta', null);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.endDelta', null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can initialize clock mode from time API", function () {
|
|
||||||
time.clock(clockA.key, offsetsA);
|
|
||||||
time.timeSystem(timeSystemA.key);
|
|
||||||
initialize();
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.mode', 'clockA');
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.timeSystem', 'timeSystemA');
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.startBound', null);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.endBound', null);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.startDelta', 100);
|
|
||||||
expect($location.search)
|
|
||||||
.toHaveBeenCalledWith('tc.endDelta', 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('location changes in fixed mode', function () {
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
time.timeSystem(timeSystemA.key, boundsA);
|
|
||||||
initialize();
|
|
||||||
time.timeSystem.calls.reset();
|
|
||||||
time.bounds.calls.reset();
|
|
||||||
time.clock.calls.reset();
|
|
||||||
time.stopClock.calls.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not change on spurious location change", function () {
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
'timeSystemA',
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.bounds).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.stopClock).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates timeSystem changes", function () {
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemB',
|
|
||||||
{
|
|
||||||
start: 10,
|
|
||||||
end: 20
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates bounds changes", function () {
|
|
||||||
search['tc.startBound'] = '100';
|
|
||||||
search['tc.endBound'] = '200';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.anything(), jasmine.anything()
|
|
||||||
);
|
|
||||||
expect(time.bounds).toHaveBeenCalledWith({
|
|
||||||
start: 100,
|
|
||||||
end: 200
|
|
||||||
});
|
|
||||||
search['tc.endBound'] = '300';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.anything(), jasmine.anything()
|
|
||||||
);
|
|
||||||
expect(time.bounds).toHaveBeenCalledWith({
|
|
||||||
start: 100,
|
|
||||||
end: 300
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clock mode w/o timeSystem change", function () {
|
|
||||||
search['tc.mode'] = 'clockA';
|
|
||||||
search['tc.startDelta'] = '50';
|
|
||||||
search['tc.endDelta'] = '50';
|
|
||||||
delete search['tc.endBound'];
|
|
||||||
delete search['tc.startBound'];
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockA',
|
|
||||||
{
|
|
||||||
start: -50,
|
|
||||||
end: 50
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.anything(), jasmine.anything()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clock mode and timeSystem", function () {
|
|
||||||
search['tc.mode'] = 'clockA';
|
|
||||||
search['tc.startDelta'] = '50';
|
|
||||||
search['tc.endDelta'] = '50';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
delete search['tc.endBound'];
|
|
||||||
delete search['tc.startBound'];
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockA',
|
|
||||||
{
|
|
||||||
start: -50,
|
|
||||||
end: 50
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith('timeSystemB');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('location changes in clock mode', function () {
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
time.clock(clockA.key, offsetsA);
|
|
||||||
time.timeSystem(timeSystemA.key);
|
|
||||||
initialize();
|
|
||||||
time.timeSystem.calls.reset();
|
|
||||||
time.bounds.calls.reset();
|
|
||||||
time.clock.calls.reset();
|
|
||||||
time.clockOffsets.calls.reset();
|
|
||||||
time.stopClock.calls.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not change on spurious location change", function () {
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
'timeSystemA',
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.clockOffsets).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.clock).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.bounds).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes time system", function () {
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemB'
|
|
||||||
);
|
|
||||||
expect(time.clockOffsets).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.clock).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.stopClock).not.toHaveBeenCalled();
|
|
||||||
expect(time.bounds).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes offsets", function () {
|
|
||||||
search['tc.startDelta'] = '50';
|
|
||||||
search['tc.endDelta'] = '50';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
'timeSystemA',
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
expect(time.clockOffsets).toHaveBeenCalledWith(
|
|
||||||
{
|
|
||||||
start: -50,
|
|
||||||
end: 50
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.clock).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates to fixed w/o timeSystem change", function () {
|
|
||||||
search['tc.mode'] = 'fixed';
|
|
||||||
search['tc.startBound'] = '234';
|
|
||||||
search['tc.endBound'] = '567';
|
|
||||||
delete search['tc.endDelta'];
|
|
||||||
delete search['tc.startDelta'];
|
|
||||||
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.stopClock).toHaveBeenCalled();
|
|
||||||
expect(time.bounds).toHaveBeenCalledWith({
|
|
||||||
start: 234,
|
|
||||||
end: 567
|
|
||||||
});
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(
|
|
||||||
jasmine.anything(), jasmine.anything()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates fixed and timeSystem", function () {
|
|
||||||
search['tc.mode'] = 'fixed';
|
|
||||||
search['tc.startBound'] = '234';
|
|
||||||
search['tc.endBound'] = '567';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
delete search['tc.endDelta'];
|
|
||||||
delete search['tc.startDelta'];
|
|
||||||
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.stopClock).toHaveBeenCalled();
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemB',
|
|
||||||
{
|
|
||||||
start: 234,
|
|
||||||
end: 567
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clock", function () {
|
|
||||||
search['tc.mode'] = 'clockB';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockB',
|
|
||||||
{
|
|
||||||
start: -100,
|
|
||||||
end: 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).not.toHaveBeenCalledWith(jasmine.anything());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clock and timeSystem", function () {
|
|
||||||
search['tc.mode'] = 'clockB';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockB',
|
|
||||||
{
|
|
||||||
start: -100,
|
|
||||||
end: 0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemB'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clock and timeSystem and offsets", function () {
|
|
||||||
search['tc.mode'] = 'clockB';
|
|
||||||
search['tc.timeSystem'] = 'timeSystemB';
|
|
||||||
search['tc.startDelta'] = '50';
|
|
||||||
search['tc.endDelta'] = '50';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.clock).toHaveBeenCalledWith(
|
|
||||||
'clockB',
|
|
||||||
{
|
|
||||||
start: -50,
|
|
||||||
end: 50
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(time.timeSystem).toHaveBeenCalledWith(
|
|
||||||
'timeSystemB'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("stops the clock", function () {
|
|
||||||
// this is a robustness test, unsure if desired, requires
|
|
||||||
// user to be manually editing location strings.
|
|
||||||
search['tc.mode'] = 'fixed';
|
|
||||||
triggerLocationChange();
|
|
||||||
expect(time.stopClock).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe("location updates from time API in fixed", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
time.timeSystem(timeSystemA.key, boundsA);
|
|
||||||
initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates on bounds change", function () {
|
|
||||||
time.bounds(boundsB);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'fixed',
|
|
||||||
'tc.startBound': '120',
|
|
||||||
'tc.endBound': '360',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates on timeSystem change", function () {
|
|
||||||
time.timeSystem(timeSystemB, boundsA);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'fixed',
|
|
||||||
'tc.startBound': '10',
|
|
||||||
'tc.endBound': '20',
|
|
||||||
'tc.timeSystem': 'timeSystemB'
|
|
||||||
});
|
|
||||||
time.timeSystem(timeSystemA, boundsB);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'fixed',
|
|
||||||
'tc.startBound': '120',
|
|
||||||
'tc.endBound': '360',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Updates to clock", function () {
|
|
||||||
time.clock(clockA, offsetsA);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'clockA',
|
|
||||||
'tc.startDelta': '100',
|
|
||||||
'tc.endDelta': '0',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("location updates from time API in fixed", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
time.clock(clockA.key, offsetsA);
|
|
||||||
time.timeSystem(timeSystemA.key);
|
|
||||||
initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates offsets", function () {
|
|
||||||
time.clockOffsets(offsetsB);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'clockA',
|
|
||||||
'tc.startDelta': '50',
|
|
||||||
'tc.endDelta': '50',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates clocks", function () {
|
|
||||||
time.clock(clockB, offsetsA);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'clockB',
|
|
||||||
'tc.startDelta': '100',
|
|
||||||
'tc.endDelta': '0',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
time.clock(clockA, offsetsB);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'clockA',
|
|
||||||
'tc.startDelta': '50',
|
|
||||||
'tc.endDelta': '50',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates timesystems", function () {
|
|
||||||
time.timeSystem(timeSystemB);
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'clockA',
|
|
||||||
'tc.startDelta': '100',
|
|
||||||
'tc.endDelta': '0',
|
|
||||||
'tc.timeSystem': 'timeSystemB'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("stops the clock", function () {
|
|
||||||
time.stopClock();
|
|
||||||
expect(search).toEqual({
|
|
||||||
'tc.mode': 'fixed',
|
|
||||||
'tc.startBound': '900',
|
|
||||||
'tc.endBound': '1000',
|
|
||||||
'tc.timeSystem': 'timeSystemA'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
utils
|
utils
|
||||||
) {
|
) {
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
objectUtils
|
objectUtils
|
||||||
) {
|
) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
'./LegacyViewProvider',
|
'./LegacyViewProvider',
|
||||||
'./TypeInspectorViewProvider',
|
'./TypeInspectorViewProvider',
|
||||||
'../../api/objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
LegacyViewProvider,
|
LegacyViewProvider,
|
||||||
TypeInspectorViewProvider,
|
TypeInspectorViewProvider,
|
||||||
|
@ -70,7 +70,7 @@ define([
|
|||||||
* @memberof module:openmct.CompositionAPI#
|
* @memberof module:openmct.CompositionAPI#
|
||||||
*/
|
*/
|
||||||
CompositionAPI.prototype.get = function (domainObject) {
|
CompositionAPI.prototype.get = function (domainObject) {
|
||||||
var provider = _.find(this.registry, function (p) {
|
var provider = this.registry.find(p => {
|
||||||
return p.appliesTo(domainObject);
|
return p.appliesTo(domainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ define([
|
|||||||
throw new Error('Event not supported by composition: ' + event);
|
throw new Error('Event not supported by composition: ' + event);
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = _.findIndex(this.listeners[event], function (l) {
|
var index = this.listeners[event].findIndex(l => {
|
||||||
return l.callback === callback && l.context === context;
|
return l.callback === callback && l.context === context;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'lodash',
|
'lodash',
|
||||||
'../objects/object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
objectUtils
|
objectUtils
|
||||||
@ -143,7 +143,7 @@ define([
|
|||||||
var keyString = objectUtils.makeKeyString(domainObject.identifier);
|
var keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||||
var objectListeners = this.listeningTo[keyString];
|
var objectListeners = this.listeningTo[keyString];
|
||||||
|
|
||||||
var index = _.findIndex(objectListeners[event], function (l) {
|
var index = objectListeners[event].findIndex(l => {
|
||||||
return l.callback === callback && l.context === context;
|
return l.callback === callback && l.context === context;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -196,8 +196,8 @@ define([
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
|
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
|
||||||
return parent.composition.findIndex(composee =>
|
return parent.composition.some(composee =>
|
||||||
this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
|
this.publicAPI.objects.areIdsEqual(composee, childId));
|
||||||
};
|
};
|
||||||
|
|
||||||
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
||||||
|
@ -128,6 +128,11 @@ export default class NotificationAPI extends EventEmitter {
|
|||||||
return this._notify(notificationModel);
|
return this._notify(notificationModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dismissAllNotifications() {
|
||||||
|
this.notifications = [];
|
||||||
|
this.emit('dismiss-all');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimize a notification. The notification will still be available
|
* Minimize a notification. The notification will still be available
|
||||||
* from the notification list. Typically notifications with a
|
* from the notification list. Typically notifications with a
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'./object-utils.js',
|
'objectUtils',
|
||||||
'lodash'
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
utils,
|
utils,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'lodash',
|
'lodash',
|
||||||
'./object-utils',
|
'objectUtils',
|
||||||
'./MutableObject',
|
'./MutableObject',
|
||||||
'./RootRegistry',
|
'./RootRegistry',
|
||||||
'./RootObjectProvider',
|
'./RootObjectProvider',
|
||||||
|
@ -43,7 +43,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
RootRegistry.prototype.addRoot = function (key) {
|
RootRegistry.prototype.addRoot = function (key) {
|
||||||
if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) {
|
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
|
||||||
this.providers.push(function () {
|
this.providers.push(function () {
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
define([
|
define([
|
||||||
'../object-utils'
|
'objectUtils'
|
||||||
], function (
|
], function (
|
||||||
objectUtils
|
objectUtils
|
||||||
) {
|
) {
|
||||||
|
@ -85,9 +85,9 @@ define([
|
|||||||
value: +e.value
|
value: +e.value
|
||||||
};
|
};
|
||||||
}), 'e.value');
|
}), 'e.value');
|
||||||
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
|
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
|
||||||
valueMetadata.max = _.max(valueMetadata.values);
|
valueMetadata.max = Math.max(valueMetadata.values);
|
||||||
valueMetadata.min = _.min(valueMetadata.values);
|
valueMetadata.min = Math.min(valueMetadata.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
valueMetadatas.push(valueMetadata);
|
valueMetadatas.push(valueMetadata);
|
||||||
@ -103,7 +103,7 @@ define([
|
|||||||
var metadata = domainObject.telemetry || {};
|
var metadata = domainObject.telemetry || {};
|
||||||
if (this.typeHasTelemetry(domainObject)) {
|
if (this.typeHasTelemetry(domainObject)) {
|
||||||
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
|
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
|
||||||
_.extend(metadata, typeMetadata);
|
Object.assign(metadata, typeMetadata);
|
||||||
if (!metadata.values) {
|
if (!metadata.values) {
|
||||||
metadata.values = valueMetadatasFromOldFormat(metadata);
|
metadata.values = valueMetadatasFromOldFormat(metadata);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ define([
|
|||||||
'./TelemetryMetadataManager',
|
'./TelemetryMetadataManager',
|
||||||
'./TelemetryValueFormatter',
|
'./TelemetryValueFormatter',
|
||||||
'./DefaultMetadataProvider',
|
'./DefaultMetadataProvider',
|
||||||
'../objects/object-utils',
|
'objectUtils',
|
||||||
'lodash'
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
TelemetryMetadataManager,
|
TelemetryMetadataManager,
|
||||||
@ -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 _.indexBy(values, 'key');
|
return _.keyBy(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 _.sortByAll(options, sortKeys);
|
return _.sortBy(options, sortKeys);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,13 +57,13 @@ define([
|
|||||||
|
|
||||||
if (valueMetadata.format === 'enum') {
|
if (valueMetadata.format === 'enum') {
|
||||||
if (!valueMetadata.values) {
|
if (!valueMetadata.values) {
|
||||||
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
|
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
|
||||||
}
|
}
|
||||||
if (!valueMetadata.hasOwnProperty('max')) {
|
if (!valueMetadata.hasOwnProperty('max')) {
|
||||||
valueMetadata.max = _.max(valueMetadata.values) + 1;
|
valueMetadata.max = Math.max(valueMetadata.values) + 1;
|
||||||
}
|
}
|
||||||
if (!valueMetadata.hasOwnProperty('min')) {
|
if (!valueMetadata.hasOwnProperty('min')) {
|
||||||
valueMetadata.min = _.min(valueMetadata.values) - 1;
|
valueMetadata.min = Math.min(valueMetadata.values) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ define([
|
|||||||
return metadata.hints[hint];
|
return metadata.hints[hint];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return _.sortByAll(matchingMetadata, ...iteratees);
|
return _.sortBy(matchingMetadata, ...iteratees);
|
||||||
};
|
};
|
||||||
|
|
||||||
TelemetryMetadataManager.prototype.getFilterableValues = function () {
|
TelemetryMetadataManager.prototype.getFilterableValues = function () {
|
||||||
|
39
src/plugins/LADTable/LADTableCompositionPolicy.js
Normal file
39
src/plugins/LADTable/LADTableCompositionPolicy.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export default class LADTableCompositionPolicy {
|
||||||
|
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
return this.allow.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
allow(parent, child) {
|
||||||
|
if(parent.type === 'LadTable') {
|
||||||
|
return this.openmct.telemetry.isTelemetryObject(child);
|
||||||
|
} else if(parent.type === 'LadTableSet') {
|
||||||
|
return child.type === 'LadTable';
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -19,53 +19,46 @@
|
|||||||
* 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 LadTableSet from './components/LadTableSet.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
define([
|
export default function LADTableSetViewProvider(openmct) {
|
||||||
'./components/LadTableSet.vue',
|
return {
|
||||||
'vue'
|
key: 'LadTableSet',
|
||||||
], function (
|
name: 'LAD Table Set',
|
||||||
LadTableSet,
|
cssClass: 'icon-tabular-lad-set',
|
||||||
Vue
|
canView: function (domainObject) {
|
||||||
) {
|
return domainObject.type === 'LadTableSet';
|
||||||
function LADTableSetViewProvider(openmct) {
|
},
|
||||||
return {
|
canEdit: function (domainObject) {
|
||||||
key: 'LadTableSet',
|
return domainObject.type === 'LadTableSet';
|
||||||
name: 'LAD Table Set',
|
},
|
||||||
cssClass: 'icon-tabular-lad-set',
|
view: function (domainObject, objectPath) {
|
||||||
canView: function (domainObject) {
|
let component;
|
||||||
return domainObject.type === 'LadTableSet';
|
|
||||||
},
|
|
||||||
canEdit: function (domainObject) {
|
|
||||||
return domainObject.type === 'LadTableSet';
|
|
||||||
},
|
|
||||||
view: function (domainObject, objectPath) {
|
|
||||||
let component;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
show: function (element) {
|
show: function (element) {
|
||||||
component = new Vue({
|
component = new Vue({
|
||||||
el: element,
|
el: element,
|
||||||
components: {
|
components: {
|
||||||
LadTableSet: LadTableSet.default
|
LadTableSet: LadTableSet
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject,
|
domainObject,
|
||||||
objectPath
|
objectPath
|
||||||
},
|
},
|
||||||
template: '<lad-table-set></lad-table-set>'
|
template: '<lad-table-set></lad-table-set>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroy: function (element) {
|
destroy: function (element) {
|
||||||
component.$destroy();
|
component.$destroy();
|
||||||
component = undefined;
|
component = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
priority: function () {
|
priority: function () {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return LADTableSetViewProvider;
|
|
||||||
});
|
|
||||||
|
@ -19,53 +19,46 @@
|
|||||||
* 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 LadTable from './components/LADTable.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
define([
|
export default function LADTableViewProvider(openmct) {
|
||||||
'./components/LADTable.vue',
|
return {
|
||||||
'vue'
|
key: 'LadTable',
|
||||||
], function (
|
name: 'LAD Table',
|
||||||
LadTableComponent,
|
cssClass: 'icon-tabular-lad',
|
||||||
Vue
|
canView: function (domainObject) {
|
||||||
) {
|
return domainObject.type === 'LadTable';
|
||||||
function LADTableViewProvider(openmct) {
|
},
|
||||||
return {
|
canEdit: function (domainObject) {
|
||||||
key: 'LadTable',
|
return domainObject.type === 'LadTable';
|
||||||
name: 'LAD Table',
|
},
|
||||||
cssClass: 'icon-tabular-lad',
|
view: function (domainObject, objectPath) {
|
||||||
canView: function (domainObject) {
|
let component;
|
||||||
return domainObject.type === 'LadTable';
|
|
||||||
},
|
|
||||||
canEdit: function (domainObject) {
|
|
||||||
return domainObject.type === 'LadTable';
|
|
||||||
},
|
|
||||||
view: function (domainObject, objectPath) {
|
|
||||||
let component;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
show: function (element) {
|
show: function (element) {
|
||||||
component = new Vue({
|
component = new Vue({
|
||||||
el: element,
|
el: element,
|
||||||
components: {
|
components: {
|
||||||
LadTableComponent: LadTableComponent.default
|
LadTableComponent: LadTable
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject,
|
domainObject,
|
||||||
objectPath
|
objectPath
|
||||||
},
|
},
|
||||||
template: '<lad-table-component></lad-table-component>'
|
template: '<lad-table-component></lad-table-component>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroy: function (element) {
|
destroy: function (element) {
|
||||||
component.$destroy();
|
component.$destroy();
|
||||||
component = undefined;
|
component = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
priority: function () {
|
priority: function () {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return LADTableViewProvider;
|
|
||||||
});
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, 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.
|
||||||
*
|
*
|
||||||
@ -22,12 +22,16 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<tr @contextmenu.prevent="showContextMenu">
|
<tr
|
||||||
<td>{{ name }}</td>
|
class="js-lad-table__body__row"
|
||||||
<td>{{ timestamp }}</td>
|
@contextmenu.prevent="showContextMenu"
|
||||||
<td :class="valueClass">
|
>
|
||||||
{{ value }}
|
<td class="js-first-data">{{ name }}</td>
|
||||||
</td>
|
<td class="js-second-data">{{ formattedTimestamp }}</td>
|
||||||
|
<td
|
||||||
|
class="js-third-data"
|
||||||
|
:class="valueClass"
|
||||||
|
>{{ value }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -52,16 +56,22 @@ export default {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: this.domainObject.name,
|
name: this.domainObject.name,
|
||||||
timestamp: '---',
|
timestamp: undefined,
|
||||||
value: '---',
|
value: '---',
|
||||||
valueClass: '',
|
valueClass: '',
|
||||||
currentObjectPath
|
currentObjectPath
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
formattedTimestamp() {
|
||||||
|
return this.timestamp !== undefined ? this.formats[this.timestampKey].format(this.timestamp) : '---';
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
this.bounds = this.openmct.time.bounds();
|
||||||
|
|
||||||
this.limitEvaluator = this.openmct
|
this.limitEvaluator = this.openmct
|
||||||
.telemetry
|
.telemetry
|
||||||
@ -76,6 +86,7 @@ export default {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.openmct.time.on('timeSystem', this.updateTimeSystem);
|
this.openmct.time.on('timeSystem', this.updateTimeSystem);
|
||||||
|
this.openmct.time.on('bounds', this.updateBounds);
|
||||||
|
|
||||||
this.timestampKey = this.openmct.time.timeSystem().key;
|
this.timestampKey = this.openmct.time.timeSystem().key;
|
||||||
|
|
||||||
@ -89,43 +100,63 @@ export default {
|
|||||||
.telemetry
|
.telemetry
|
||||||
.subscribe(this.domainObject, this.updateValues);
|
.subscribe(this.domainObject, this.updateValues);
|
||||||
|
|
||||||
this.openmct
|
this.requestHistory();
|
||||||
.telemetry
|
|
||||||
.request(this.domainObject, {strategy: 'latest'})
|
|
||||||
.then((array) => this.updateValues(array[array.length - 1]));
|
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.stopWatchingMutation();
|
this.stopWatchingMutation();
|
||||||
this.unsubscribe();
|
this.unsubscribe();
|
||||||
this.openmct.off('timeSystem', this.updateTimeSystem);
|
this.openmct.time.off('timeSystem', this.updateTimeSystem);
|
||||||
|
this.openmct.time.off('bounds', this.updateBounds);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateValues(datum) {
|
updateValues(datum) {
|
||||||
this.timestamp = this.formats[this.timestampKey].format(datum);
|
let newTimestamp = this.formats[this.timestampKey].parse(datum),
|
||||||
this.value = this.formats[this.valueKey].format(datum);
|
limit;
|
||||||
|
|
||||||
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
if(this.shouldUpdate(newTimestamp)) {
|
||||||
|
this.timestamp = this.formats[this.timestampKey].parse(datum);
|
||||||
if (limit) {
|
this.value = this.formats[this.valueKey].format(datum);
|
||||||
this.valueClass = limit.cssClass;
|
limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||||
} else {
|
if (limit) {
|
||||||
this.valueClass = '';
|
this.valueClass = limit.cssClass;
|
||||||
|
} else {
|
||||||
|
this.valueClass = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
shouldUpdate(newTimestamp) {
|
||||||
|
return (this.timestamp === undefined) ||
|
||||||
|
(this.inBounds(newTimestamp) &&
|
||||||
|
newTimestamp > this.timestamp);
|
||||||
|
},
|
||||||
|
requestHistory() {
|
||||||
|
this.openmct
|
||||||
|
.telemetry
|
||||||
|
.request(this.domainObject, {
|
||||||
|
start: this.bounds.start,
|
||||||
|
end: this.bounds.end,
|
||||||
|
size: 1,
|
||||||
|
strategy: 'latest'
|
||||||
|
})
|
||||||
|
.then((array) => this.updateValues(array[array.length - 1]));
|
||||||
|
},
|
||||||
updateName(name) {
|
updateName(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
},
|
},
|
||||||
|
updateBounds(bounds, isTick) {
|
||||||
|
this.bounds = bounds;
|
||||||
|
if(!isTick) {
|
||||||
|
this.requestHistory();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inBounds(timestamp) {
|
||||||
|
return timestamp >= this.bounds.start && timestamp <= this.bounds.end;
|
||||||
|
},
|
||||||
updateTimeSystem(timeSystem) {
|
updateTimeSystem(timeSystem) {
|
||||||
this.value = '---';
|
this.value = '---';
|
||||||
this.timestamp = '---';
|
this.timestamp = '---';
|
||||||
this.valueClass = '';
|
this.valueClass = '';
|
||||||
this.timestampKey = timeSystem.key;
|
this.timestampKey = timeSystem.key;
|
||||||
|
|
||||||
this.openmct
|
|
||||||
.telemetry
|
|
||||||
.request(this.domainObject, {strategy: 'latest'})
|
|
||||||
.then((array) => this.updateValues(array[array.length - 1]));
|
|
||||||
|
|
||||||
},
|
},
|
||||||
showContextMenu(event) {
|
showContextMenu(event) {
|
||||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||||
|
@ -75,7 +75,7 @@ export default {
|
|||||||
this.items.push(item);
|
this.items.push(item);
|
||||||
},
|
},
|
||||||
removeItem(identifier) {
|
removeItem(identifier) {
|
||||||
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
|
let index = this.items.findIndex(item => this.openmct.objects.makeKeyString(identifier) === item.key);
|
||||||
|
|
||||||
this.items.splice(index, 1);
|
this.items.splice(index, 1);
|
||||||
},
|
},
|
||||||
@ -88,4 +88,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
>
|
>
|
||||||
<tr
|
<tr
|
||||||
:key="primary.key"
|
:key="primary.key"
|
||||||
class="c-table__group-header"
|
class="c-table__group-header js-lad-table-set__table-headers"
|
||||||
>
|
>
|
||||||
<td colspan="10">
|
<td colspan="10">
|
||||||
{{ primary.domainObject.name }}
|
{{ primary.domainObject.name }}
|
||||||
@ -102,7 +102,7 @@ export default {
|
|||||||
this.compositions.push({composition, addCallback, removeCallback});
|
this.compositions.push({composition, addCallback, removeCallback});
|
||||||
},
|
},
|
||||||
removePrimary(identifier) {
|
removePrimary(identifier) {
|
||||||
let index = _.findIndex(this.primaryTelemetryObjects, (primary) => this.openmct.objects.makeKeyString(identifier) === primary.key),
|
let index = this.primaryTelemetryObjects.findIndex(primary => this.openmct.objects.makeKeyString(identifier) === primary.key),
|
||||||
primary = this.primaryTelemetryObjects[index];
|
primary = this.primaryTelemetryObjects[index];
|
||||||
|
|
||||||
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
|
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
|
||||||
@ -130,7 +130,7 @@ export default {
|
|||||||
removeSecondary(primary) {
|
removeSecondary(primary) {
|
||||||
return (identifier) => {
|
return (identifier) => {
|
||||||
let array = this.secondaryTelemetryObjects[primary.key],
|
let array = this.secondaryTelemetryObjects[primary.key],
|
||||||
index = _.findIndex(array, (secondary) => this.openmct.objects.makeKeyString(identifier) === secondary.key);
|
index = array.findIndex(secondary => this.openmct.objects.makeKeyString(identifier) === secondary.key);
|
||||||
|
|
||||||
array.splice(index, 1);
|
array.splice(index, 1);
|
||||||
|
|
||||||
|
@ -19,38 +19,36 @@
|
|||||||
* 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 LADTableViewProvider from './LADTableViewProvider';
|
||||||
|
import LADTableSetViewProvider from './LADTableSetViewProvider';
|
||||||
|
import LADTableCompositionPolicy from './LADTableCompositionPolicy';
|
||||||
|
|
||||||
define([
|
export default function plugin() {
|
||||||
'./LADTableViewProvider',
|
return function install(openmct) {
|
||||||
'./LADTableSetViewProvider'
|
|
||||||
], function (
|
|
||||||
LADTableViewProvider,
|
|
||||||
LADTableSetViewProvider
|
|
||||||
) {
|
|
||||||
return function plugin() {
|
|
||||||
return function install(openmct) {
|
|
||||||
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
|
|
||||||
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
|
|
||||||
|
|
||||||
openmct.types.addType('LadTable', {
|
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
|
||||||
name: "LAD Table",
|
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
|
||||||
creatable: true,
|
|
||||||
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
|
||||||
cssClass: 'icon-tabular-lad',
|
|
||||||
initialize(domainObject) {
|
|
||||||
domainObject.composition = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
openmct.types.addType('LadTableSet', {
|
openmct.types.addType('LadTable', {
|
||||||
name: "LAD Table Set",
|
name: "LAD Table",
|
||||||
creatable: true,
|
creatable: true,
|
||||||
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
||||||
cssClass: 'icon-tabular-lad-set',
|
cssClass: 'icon-tabular-lad',
|
||||||
initialize(domainObject) {
|
initialize(domainObject) {
|
||||||
domainObject.composition = [];
|
domainObject.composition = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
openmct.types.addType('LadTableSet', {
|
||||||
|
name: "LAD Table Set",
|
||||||
|
creatable: true,
|
||||||
|
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
||||||
|
cssClass: 'icon-tabular-lad-set',
|
||||||
|
initialize(domainObject) {
|
||||||
|
domainObject.composition = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.composition.addPolicy(new LADTableCompositionPolicy(openmct));
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
365
src/plugins/LADTable/pluginSpec.js
Normal file
365
src/plugins/LADTable/pluginSpec.js
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2018, 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 LadPlugin from './plugin.js';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import {
|
||||||
|
createOpenMct,
|
||||||
|
getMockObjects,
|
||||||
|
getMockTelemetry,
|
||||||
|
getLatestTelemetry,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
const TABLE_BODY_ROWS = '.js-lad-table__body__row';
|
||||||
|
const TABLE_BODY_FIRST_ROW = TABLE_BODY_ROWS + ':first-child';
|
||||||
|
const TABLE_BODY_FIRST_ROW_FIRST_DATA = TABLE_BODY_FIRST_ROW + ' .js-first-data';
|
||||||
|
const TABLE_BODY_FIRST_ROW_SECOND_DATA = TABLE_BODY_FIRST_ROW + ' .js-second-data';
|
||||||
|
const TABLE_BODY_FIRST_ROW_THIRD_DATA = TABLE_BODY_FIRST_ROW + ' .js-third-data';
|
||||||
|
const LAD_SET_TABLE_HEADERS = '.js-lad-table-set__table-headers';
|
||||||
|
|
||||||
|
function utcTimeFormat(value) {
|
||||||
|
return new Date(value).toISOString().replace('T', ' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("The LAD Table", () => {
|
||||||
|
const ladTableKey = 'LadTable';
|
||||||
|
|
||||||
|
let openmct,
|
||||||
|
ladPlugin,
|
||||||
|
parent,
|
||||||
|
child,
|
||||||
|
telemetryCount = 3,
|
||||||
|
timeFormat = 'utc',
|
||||||
|
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
|
||||||
|
mockObj = getMockObjects({
|
||||||
|
objectKeyStrings: ['ladTable', 'telemetry'],
|
||||||
|
format: timeFormat
|
||||||
|
}),
|
||||||
|
bounds = {
|
||||||
|
start: 0,
|
||||||
|
end: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
// add telemetry object as composition in lad table
|
||||||
|
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
|
||||||
|
|
||||||
|
// this setups up the app
|
||||||
|
beforeEach((done) => {
|
||||||
|
const appHolder = document.createElement('div');
|
||||||
|
appHolder.style.width = '640px';
|
||||||
|
appHolder.style.height = '480px';
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
parent = document.createElement('div');
|
||||||
|
child = document.createElement('div');
|
||||||
|
parent.appendChild(child);
|
||||||
|
|
||||||
|
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
||||||
|
|
||||||
|
ladPlugin = new LadPlugin();
|
||||||
|
openmct.install(ladPlugin);
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
||||||
|
|
||||||
|
openmct.time.bounds({ start: bounds.start, end: bounds.end });
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless(appHolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should provide a table view only for lad table objects", () => {
|
||||||
|
let applicableViews = openmct.objectViews.get(mockObj.ladTable),
|
||||||
|
ladTableView = applicableViews.find(
|
||||||
|
(viewProvider) => viewProvider.key === ladTableKey
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(applicableViews.length).toEqual(1);
|
||||||
|
expect(ladTableView).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('composition', () => {
|
||||||
|
let ladTableCompositionCollection;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ladTableCompositionCollection = openmct.composition.get(mockObj.ladTable);
|
||||||
|
ladTableCompositionCollection.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should accept telemetry producing objects", () => {
|
||||||
|
expect(() => {
|
||||||
|
ladTableCompositionCollection.add(mockObj.telemetry);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reject non-telemtry producing objects", () => {
|
||||||
|
expect(()=> {
|
||||||
|
ladTableCompositionCollection.add(mockObj.ladTable);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("table view", () => {
|
||||||
|
let applicableViews,
|
||||||
|
ladTableViewProvider,
|
||||||
|
ladTableView,
|
||||||
|
anotherTelemetryObj = getMockObjects({
|
||||||
|
objectKeyStrings: ['telemetry'],
|
||||||
|
overwrite: {
|
||||||
|
telemetry: {
|
||||||
|
name: "New Telemetry Object",
|
||||||
|
identifier: { namespace: "", key: "another-telemetry-object" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).telemetry;
|
||||||
|
|
||||||
|
// add another telemetry object as composition in lad table to test multi rows
|
||||||
|
mockObj.ladTable.composition.push(anotherTelemetryObj.identifier);
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
let telemetryRequestResolve,
|
||||||
|
telemetryObjectResolve,
|
||||||
|
anotherTelemetryObjectResolve;
|
||||||
|
let telemetryRequestPromise = new Promise((resolve) => {
|
||||||
|
telemetryRequestResolve = resolve;
|
||||||
|
}),
|
||||||
|
telemetryObjectPromise = new Promise((resolve) => {
|
||||||
|
telemetryObjectResolve = resolve;
|
||||||
|
}),
|
||||||
|
anotherTelemetryObjectPromise = new Promise((resolve) => {
|
||||||
|
anotherTelemetryObjectResolve = resolve;
|
||||||
|
})
|
||||||
|
openmct.telemetry.request.and.callFake(() => {
|
||||||
|
telemetryRequestResolve(mockTelemetry);
|
||||||
|
return telemetryRequestPromise;
|
||||||
|
});
|
||||||
|
openmct.objects.get.and.callFake((obj) => {
|
||||||
|
if(obj.key === 'telemetry-object') {
|
||||||
|
telemetryObjectResolve(mockObj.telemetry);
|
||||||
|
return telemetryObjectPromise;
|
||||||
|
} else {
|
||||||
|
anotherTelemetryObjectResolve(anotherTelemetryObj);
|
||||||
|
return anotherTelemetryObjectPromise;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.time.bounds({ start: bounds.start, end: bounds.end });
|
||||||
|
|
||||||
|
applicableViews = openmct.objectViews.get(mockObj.ladTable);
|
||||||
|
ladTableViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableKey);
|
||||||
|
ladTableView = ladTableViewProvider.view(mockObj.ladTable, [mockObj.ladTable]);
|
||||||
|
ladTableView.show(child, true);
|
||||||
|
|
||||||
|
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise]);
|
||||||
|
await Vue.nextTick();
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show one row per object in the composition", () => {
|
||||||
|
const rowCount = parent.querySelectorAll(TABLE_BODY_ROWS).length;
|
||||||
|
expect(rowCount).toBe(mockObj.ladTable.composition.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show the most recent datum from the telemetry producing object", async () => {
|
||||||
|
const latestDatum = getLatestTelemetry(mockTelemetry, { timeFormat });
|
||||||
|
const expectedDate = utcTimeFormat(latestDatum[timeFormat]);
|
||||||
|
await Vue.nextTick();
|
||||||
|
const latestDate = parent.querySelector(TABLE_BODY_FIRST_ROW_SECOND_DATA).innerText;
|
||||||
|
expect(latestDate).toBe(expectedDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show the name provided for the the telemetry producing object", () => {
|
||||||
|
const rowName = parent.querySelector(TABLE_BODY_FIRST_ROW_FIRST_DATA).innerText,
|
||||||
|
expectedName = mockObj.telemetry.name;
|
||||||
|
expect(rowName).toBe(expectedName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show the correct values for the datum based on domain and range hints", async () => {
|
||||||
|
const range = mockObj.telemetry.telemetry.values.find((val) => {
|
||||||
|
return val.hints && val.hints.range !== undefined;
|
||||||
|
}).key;
|
||||||
|
const domain = mockObj.telemetry.telemetry.values.find((val) => {
|
||||||
|
return val.hints && val.hints.domain !== undefined;
|
||||||
|
}).key;
|
||||||
|
const mostRecentTelemetry = getLatestTelemetry(mockTelemetry, { timeFormat });
|
||||||
|
const rangeValue = mostRecentTelemetry[range];
|
||||||
|
const domainValue = utcTimeFormat(mostRecentTelemetry[domain]);
|
||||||
|
await Vue.nextTick();
|
||||||
|
const actualDomainValue = parent.querySelector(TABLE_BODY_FIRST_ROW_SECOND_DATA).innerText;
|
||||||
|
const actualRangeValue = parent.querySelector(TABLE_BODY_FIRST_ROW_THIRD_DATA).innerText;
|
||||||
|
expect(actualRangeValue).toBe(rangeValue);
|
||||||
|
expect(actualDomainValue).toBe(domainValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("The LAD Table Set", () => {
|
||||||
|
const ladTableSetKey = 'LadTableSet';
|
||||||
|
|
||||||
|
let openmct,
|
||||||
|
ladPlugin,
|
||||||
|
parent,
|
||||||
|
child,
|
||||||
|
telemetryCount = 3,
|
||||||
|
timeFormat = 'utc',
|
||||||
|
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
|
||||||
|
mockObj = getMockObjects({
|
||||||
|
objectKeyStrings: ['ladTable', 'ladTableSet', 'telemetry']
|
||||||
|
}),
|
||||||
|
bounds = {
|
||||||
|
start: 0,
|
||||||
|
end: 4
|
||||||
|
};
|
||||||
|
// add mock telemetry to lad table and lad table to lad table set (composition)
|
||||||
|
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
|
||||||
|
mockObj.ladTableSet.composition.push(mockObj.ladTable.identifier);
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
const appHolder = document.createElement('div');
|
||||||
|
appHolder.style.width = '640px';
|
||||||
|
appHolder.style.height = '480px';
|
||||||
|
|
||||||
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
parent = document.createElement('div');
|
||||||
|
child = document.createElement('div');
|
||||||
|
parent.appendChild(child);
|
||||||
|
|
||||||
|
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
||||||
|
|
||||||
|
ladPlugin = new LadPlugin();
|
||||||
|
openmct.install(ladPlugin);
|
||||||
|
|
||||||
|
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
||||||
|
|
||||||
|
openmct.time.bounds({ start: bounds.start, end: bounds.end });
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.start(appHolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should provide a lad table set view only for lad table set objects", () => {
|
||||||
|
let applicableViews = openmct.objectViews.get(mockObj.ladTableSet),
|
||||||
|
ladTableSetView = applicableViews.find(
|
||||||
|
(viewProvider) => viewProvider.key === ladTableSetKey
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(applicableViews.length).toEqual(1);
|
||||||
|
expect(ladTableSetView).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('composition', () => {
|
||||||
|
let ladTableSetCompositionCollection;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ladTableSetCompositionCollection = openmct.composition.get(mockObj.ladTableSet);
|
||||||
|
ladTableSetCompositionCollection.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should accept lad table objects", () => {
|
||||||
|
expect(() => {
|
||||||
|
ladTableSetCompositionCollection.add(mockObj.ladTable);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reject non lad table objects", () => {
|
||||||
|
expect(()=> {
|
||||||
|
ladTableSetCompositionCollection.add(mockObj.telemetry);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("table view", () => {
|
||||||
|
let applicableViews,
|
||||||
|
ladTableSetViewProvider,
|
||||||
|
ladTableSetView,
|
||||||
|
otherObj = getMockObjects({
|
||||||
|
objectKeyStrings: ['ladTable'],
|
||||||
|
overwrite: {
|
||||||
|
ladTable: {
|
||||||
|
name: "New LAD Table Object",
|
||||||
|
identifier: { namespace: "", key: "another-lad-object" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add another lad table (with telemetry object) object to the lad table set for multi row test
|
||||||
|
otherObj.ladTable.composition.push(mockObj.telemetry.identifier);
|
||||||
|
mockObj.ladTableSet.composition.push(otherObj.ladTable.identifier);
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
let telemetryRequestResolve,
|
||||||
|
ladObjectResolve,
|
||||||
|
anotherLadObjectResolve;
|
||||||
|
let telemetryRequestPromise = new Promise((resolve) => {
|
||||||
|
telemetryRequestResolve = resolve;
|
||||||
|
}),
|
||||||
|
ladObjectPromise = new Promise((resolve) => {
|
||||||
|
ladObjectResolve = resolve;
|
||||||
|
}),
|
||||||
|
anotherLadObjectPromise = new Promise((resolve) => {
|
||||||
|
anotherLadObjectResolve = resolve;
|
||||||
|
})
|
||||||
|
openmct.telemetry.request.and.callFake(() => {
|
||||||
|
telemetryRequestResolve(mockTelemetry);
|
||||||
|
return telemetryRequestPromise;
|
||||||
|
});
|
||||||
|
openmct.objects.get.and.callFake((obj) => {
|
||||||
|
if(obj.key === 'lad-object') {
|
||||||
|
ladObjectResolve(mockObj.ladObject);
|
||||||
|
return ladObjectPromise;
|
||||||
|
} else if(obj.key === 'another-lad-object') {
|
||||||
|
anotherLadObjectResolve(otherObj.ladObject);
|
||||||
|
return anotherLadObjectPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({});
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.time.bounds({ start: bounds.start, end: bounds.end });
|
||||||
|
|
||||||
|
applicableViews = openmct.objectViews.get(mockObj.ladTableSet);
|
||||||
|
ladTableSetViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableSetKey);
|
||||||
|
ladTableSetView = ladTableSetViewProvider.view(mockObj.ladTableSet, [mockObj.ladTableSet]);
|
||||||
|
ladTableSetView.show(child, true);
|
||||||
|
|
||||||
|
await Promise.all([telemetryRequestPromise, ladObjectPromise, anotherLadObjectPromise]);
|
||||||
|
await Vue.nextTick();
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show one row per lad table object in the composition", () => {
|
||||||
|
const rowCount = parent.querySelectorAll(LAD_SET_TABLE_HEADERS).length;
|
||||||
|
expect(rowCount).toBe(mockObj.ladTableSet.composition.length);
|
||||||
|
pending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,230 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import {
|
||||||
|
getAllSearchParams,
|
||||||
|
setAllSearchParams
|
||||||
|
} from 'utils/openmctLocation';
|
||||||
|
|
||||||
|
const TIME_EVENTS = ['bounds', 'timeSystem', 'clock', 'clockOffsets'];
|
||||||
|
const SEARCH_MODE = 'tc.mode';
|
||||||
|
const SEARCH_TIME_SYSTEM = 'tc.timeSystem';
|
||||||
|
const SEARCH_START_BOUND = 'tc.startBound';
|
||||||
|
const SEARCH_END_BOUND = 'tc.endBound';
|
||||||
|
const SEARCH_START_DELTA = 'tc.startDelta';
|
||||||
|
const SEARCH_END_DELTA = 'tc.endDelta';
|
||||||
|
const MODE_FIXED = 'fixed';
|
||||||
|
|
||||||
|
export default class URLTimeSettingsSynchronizer {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.isUrlUpdateInProgress = false;
|
||||||
|
|
||||||
|
this.initialize = this.initialize.bind(this);
|
||||||
|
this.destroy = this.destroy.bind(this);
|
||||||
|
this.updateTimeSettings = this.updateTimeSettings.bind(this);
|
||||||
|
this.setUrlFromTimeApi = this.setUrlFromTimeApi.bind(this);
|
||||||
|
|
||||||
|
openmct.on('start', this.initialize);
|
||||||
|
openmct.on('destroy', this.destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.updateTimeSettings();
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', this.updateTimeSettings);
|
||||||
|
TIME_EVENTS.forEach(event => {
|
||||||
|
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
window.removeEventListener('hashchange', this.updateTimeSettings);
|
||||||
|
this.openmct.off('start', this.initialize);
|
||||||
|
this.openmct.off('destroy', this.destroy);
|
||||||
|
|
||||||
|
TIME_EVENTS.forEach(event => {
|
||||||
|
this.openmct.time.off(event, this.setUrlFromTimeApi);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeSettings() {
|
||||||
|
// Prevent from triggering self
|
||||||
|
if (!this.isUrlUpdateInProgress) {
|
||||||
|
let timeParameters = this.parseParametersFromUrl();
|
||||||
|
|
||||||
|
|
||||||
|
if (this.areTimeParametersValid(timeParameters)) {
|
||||||
|
this.setTimeApiFromUrl(timeParameters);
|
||||||
|
} else {
|
||||||
|
this.setUrlFromTimeApi();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.isUrlUpdateInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseParametersFromUrl() {
|
||||||
|
let searchParams = getAllSearchParams();
|
||||||
|
|
||||||
|
let mode = searchParams.get(SEARCH_MODE);
|
||||||
|
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
|
||||||
|
|
||||||
|
let startBound = parseInt(searchParams.get(SEARCH_START_BOUND), 10);
|
||||||
|
let endBound = parseInt(searchParams.get(SEARCH_END_BOUND), 10);
|
||||||
|
let bounds = {
|
||||||
|
start: startBound,
|
||||||
|
end: endBound
|
||||||
|
};
|
||||||
|
|
||||||
|
let startOffset = parseInt(searchParams.get(SEARCH_START_DELTA), 10);
|
||||||
|
let endOffset = parseInt(searchParams.get(SEARCH_END_DELTA), 10);
|
||||||
|
let clockOffsets = {
|
||||||
|
start: 0 - startOffset,
|
||||||
|
end: endOffset
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
timeSystem,
|
||||||
|
bounds,
|
||||||
|
clockOffsets
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeApiFromUrl(timeParameters) {
|
||||||
|
if (timeParameters.mode === 'fixed') {
|
||||||
|
if (this.openmct.time.timeSystem().key !== timeParameters.timeSystem) {
|
||||||
|
this.openmct.time.timeSystem(
|
||||||
|
timeParameters.timeSystem,
|
||||||
|
timeParameters.bounds
|
||||||
|
);
|
||||||
|
} else if (!this.areStartAndEndEqual(this.openmct.time.bounds(), timeParameters.bounds)) {
|
||||||
|
this.openmct.time.bounds(timeParameters.bounds);
|
||||||
|
}
|
||||||
|
if (this.openmct.time.clock()) {
|
||||||
|
this.openmct.time.stopClock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.openmct.time.clock() ||
|
||||||
|
this.openmct.time.clock().key !== timeParameters.mode) {
|
||||||
|
this.openmct.time.clock(timeParameters.mode, timeParameters.clockOffsets);
|
||||||
|
} else if (!this.areStartAndEndEqual(this.openmct.time.clockOffsets(), timeParameters.clockOffsets)) {
|
||||||
|
this.openmct.time.clockOffsets(timeParameters.clockOffsets);
|
||||||
|
}
|
||||||
|
if (!this.openmct.time.timeSystem() ||
|
||||||
|
this.openmct.time.timeSystem().key !== timeParameters.timeSystem) {
|
||||||
|
this.openmct.time.timeSystem(timeParameters.timeSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setUrlFromTimeApi() {
|
||||||
|
let searchParams = getAllSearchParams();
|
||||||
|
let clock = this.openmct.time.clock();
|
||||||
|
let bounds = this.openmct.time.bounds();
|
||||||
|
let clockOffsets = this.openmct.time.clockOffsets();
|
||||||
|
|
||||||
|
if (clock === undefined) {
|
||||||
|
searchParams.set(SEARCH_MODE, MODE_FIXED);
|
||||||
|
searchParams.set(SEARCH_START_BOUND, bounds.start);
|
||||||
|
searchParams.set(SEARCH_END_BOUND, bounds.end);
|
||||||
|
|
||||||
|
searchParams.delete(SEARCH_START_DELTA);
|
||||||
|
searchParams.delete(SEARCH_END_DELTA);
|
||||||
|
} else {
|
||||||
|
searchParams.set(SEARCH_MODE, clock.key);
|
||||||
|
|
||||||
|
if (clockOffsets !== undefined) {
|
||||||
|
searchParams.set(SEARCH_START_DELTA, 0 - clockOffsets.start);
|
||||||
|
searchParams.set(SEARCH_END_DELTA, clockOffsets.end);
|
||||||
|
} else {
|
||||||
|
searchParams.delete(SEARCH_START_DELTA);
|
||||||
|
searchParams.delete(SEARCH_END_DELTA);
|
||||||
|
}
|
||||||
|
searchParams.delete(SEARCH_START_BOUND);
|
||||||
|
searchParams.delete(SEARCH_END_BOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
|
||||||
|
this.isUrlUpdateInProgress = true;
|
||||||
|
setAllSearchParams(searchParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
areTimeParametersValid(timeParameters) {
|
||||||
|
let isValid = false;
|
||||||
|
|
||||||
|
if (this.isModeValid(timeParameters.mode) &&
|
||||||
|
this.isTimeSystemValid(timeParameters.timeSystem)) {
|
||||||
|
|
||||||
|
if (timeParameters.mode === 'fixed') {
|
||||||
|
isValid = this.areStartAndEndValid(timeParameters.bounds);
|
||||||
|
} else {
|
||||||
|
isValid = this.areStartAndEndValid(timeParameters.clockOffsets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
areStartAndEndValid(bounds) {
|
||||||
|
return bounds !== undefined &&
|
||||||
|
bounds.start !== undefined &&
|
||||||
|
bounds.start !== null &&
|
||||||
|
bounds.end !== undefined &&
|
||||||
|
bounds.start !== null &&
|
||||||
|
!isNaN(bounds.start) &&
|
||||||
|
!isNaN(bounds.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
isTimeSystemValid(timeSystem) {
|
||||||
|
let isValid = timeSystem !== undefined;
|
||||||
|
if (isValid) {
|
||||||
|
let timeSystemObject = this.openmct.time.timeSystems.get(timeSystem);
|
||||||
|
isValid = timeSystemObject !== undefined;
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
isModeValid(mode) {
|
||||||
|
let isValid = false;
|
||||||
|
|
||||||
|
if (mode !== undefined &&
|
||||||
|
mode !== null) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
if (mode.toLowerCase() === MODE_FIXED) {
|
||||||
|
isValid = true;
|
||||||
|
} else {
|
||||||
|
isValid = this.openmct.time.clocks.get(mode) !== undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
areStartAndEndEqual(firstBounds, secondBounds) {
|
||||||
|
return firstBounds.start === secondBounds.start &&
|
||||||
|
firstBounds.end === secondBounds.end;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, 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.
|
||||||
*
|
*
|
||||||
@ -19,15 +19,10 @@
|
|||||||
* 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 URLTimeSettingsSynchronizer from "./URLTimeSettingsSynchronizer.js";
|
||||||
|
|
||||||
define(
|
export default function () {
|
||||||
[],
|
return function install(openmct) {
|
||||||
function () {
|
return new URLTimeSettingsSynchronizer(openmct);
|
||||||
|
|
||||||
function NotificationIndicator() {}
|
|
||||||
|
|
||||||
NotificationIndicator.template = 'notificationIndicatorTemplate';
|
|
||||||
|
|
||||||
return NotificationIndicator;
|
|
||||||
}
|
}
|
||||||
);
|
}
|
307
src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js
Normal file
307
src/plugins/URLTimeSettingsSynchronizer/pluginSpec.js
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
describe("The URLTimeSettingsSynchronizer", () => {
|
||||||
|
let openmct;
|
||||||
|
let testClock;
|
||||||
|
beforeAll(() => resetApplicationState());
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||||
|
testClock = jasmine.createSpyObj("testClock", ["start", "stop", "tick", "currentValue", "on", "off"]);
|
||||||
|
testClock.key = "test-clock";
|
||||||
|
testClock.currentValue.and.returnValue(0);
|
||||||
|
|
||||||
|
openmct.time.addClock(testClock);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => resetApplicationState(openmct));
|
||||||
|
|
||||||
|
describe("realtime mode", () => {
|
||||||
|
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
|
||||||
|
//Test expected initial conditions
|
||||||
|
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
||||||
|
|
||||||
|
openmct.time.clock('local', {start: -1000, end: 100});
|
||||||
|
|
||||||
|
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
|
||||||
|
|
||||||
|
//Test that expected initial conditions are no longer true
|
||||||
|
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||||
|
});
|
||||||
|
it("when offsets are set via the time API, they are immediately reflected in the URL", () => {
|
||||||
|
//Test expected initial conditions
|
||||||
|
expect(window.location.hash.includes('tc.startDelta')).toBe(false);
|
||||||
|
expect(window.location.hash.includes('tc.endDelta')).toBe(false);
|
||||||
|
|
||||||
|
openmct.time.clock('local', {start: -1000, end: 100});
|
||||||
|
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
|
||||||
|
|
||||||
|
openmct.time.clockOffsets({start: -2000, end: 200});
|
||||||
|
expect(window.location.hash.includes('tc.startDelta=2000')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endDelta=200')).toBe(true);
|
||||||
|
|
||||||
|
//Test that expected initial conditions are no longer true
|
||||||
|
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||||
|
});
|
||||||
|
describe("when set in the url", () => {
|
||||||
|
it("will change from fixed to realtime mode when the mode changes", () => {
|
||||||
|
expectLocationToBeInFixedMode();
|
||||||
|
return switchToRealtimeMode().then(() => {
|
||||||
|
let clock = openmct.time.clock();
|
||||||
|
|
||||||
|
expect(clock).toBeDefined();
|
||||||
|
expect(clock.key).toBe('local');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("the clock is correctly set in the API from the URL parameters", () => {
|
||||||
|
return switchToRealtimeMode().then(() => {
|
||||||
|
let resolveFunction;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
|
||||||
|
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||||
|
//detected in the API.
|
||||||
|
openmct.time.on('clock', resolveFunction);
|
||||||
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.mode=local', 'tc.mode=test-clock');
|
||||||
|
window.location.hash = hash;
|
||||||
|
}).then(() => {
|
||||||
|
let clock = openmct.time.clock();
|
||||||
|
expect(clock).toBeDefined();
|
||||||
|
expect(clock.key).toBe('test-clock');
|
||||||
|
openmct.time.off('clock', resolveFunction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("the clock offsets are correctly set in the API from the URL parameters", () => {
|
||||||
|
return switchToRealtimeMode().then(() => {
|
||||||
|
let resolveFunction;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||||
|
//detected in the API.
|
||||||
|
openmct.time.on('clockOffsets', resolveFunction);
|
||||||
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.startDelta=1000', 'tc.startDelta=2000');
|
||||||
|
hash = hash.replace('tc.endDelta=100', 'tc.endDelta=200');
|
||||||
|
window.location.hash = hash;
|
||||||
|
}).then(() => {
|
||||||
|
let clockOffsets = openmct.time.clockOffsets();
|
||||||
|
expect(clockOffsets).toBeDefined();
|
||||||
|
expect(clockOffsets.start).toBe(-2000);
|
||||||
|
expect(clockOffsets.end).toBe(200);
|
||||||
|
openmct.time.off('clockOffsets', resolveFunction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("the time system is correctly set in the API from the URL parameters", () => {
|
||||||
|
return switchToRealtimeMode().then(() => {
|
||||||
|
let resolveFunction;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
|
||||||
|
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||||
|
//detected in the API.
|
||||||
|
openmct.time.on('timeSystem', resolveFunction);
|
||||||
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
|
||||||
|
window.location.hash = hash;
|
||||||
|
}).then(() => {
|
||||||
|
let timeSystem = openmct.time.timeSystem();
|
||||||
|
expect(timeSystem).toBeDefined();
|
||||||
|
expect(timeSystem.key).toBe('local');
|
||||||
|
openmct.time.off('timeSystem', resolveFunction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("fixed timespan mode", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
openmct.time.stopClock();
|
||||||
|
openmct.time.timeSystem('utc', {start: 0, end: 1});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when bounds are set via the time API, they are immediately reflected in the URL", ()=>{
|
||||||
|
//Test expected initial conditions
|
||||||
|
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
|
||||||
|
|
||||||
|
openmct.time.bounds({start: 10, end: 20});
|
||||||
|
|
||||||
|
expect(window.location.hash.includes('tc.startBound=10')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endBound=20')).toBe(true);
|
||||||
|
|
||||||
|
//Test that expected initial conditions are no longer true
|
||||||
|
expect(window.location.hash.includes('tc.startBound=0')).toBe(false);
|
||||||
|
expect(window.location.hash.includes('tc.endBound=1')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when time system is set via the time API, it is immediately reflected in the URL", ()=>{
|
||||||
|
//Test expected initial conditions
|
||||||
|
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(true);
|
||||||
|
|
||||||
|
openmct.time.timeSystem('local', {start: 20, end: 30});
|
||||||
|
|
||||||
|
expect(window.location.hash.includes('tc.timeSystem=local')).toBe(true);
|
||||||
|
|
||||||
|
//Test that expected initial conditions are no longer true
|
||||||
|
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(false);
|
||||||
|
});
|
||||||
|
describe("when set in the url", () => {
|
||||||
|
it("time system changes are reflected in the API", () => {
|
||||||
|
let resolveFunction;
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let timeSystem = openmct.time.timeSystem();
|
||||||
|
resolveFunction = resolve;
|
||||||
|
|
||||||
|
expect(timeSystem.key).toBe('utc');
|
||||||
|
window.location.hash = window.location.hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
|
||||||
|
|
||||||
|
openmct.time.on('timeSystem', resolveFunction);
|
||||||
|
}).then(() => {
|
||||||
|
let timeSystem = openmct.time.timeSystem();
|
||||||
|
expect(timeSystem.key).toBe('local');
|
||||||
|
|
||||||
|
openmct.time.off('timeSystem', resolveFunction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("mode can be changed from realtime to fixed", () => {
|
||||||
|
return switchToRealtimeMode().then(() => {
|
||||||
|
expectLocationToBeInRealtimeMode();
|
||||||
|
|
||||||
|
expect(openmct.time.clock()).toBeDefined();
|
||||||
|
}).then(switchToFixedMode).then(() => {
|
||||||
|
let clock = openmct.time.clock();
|
||||||
|
expect(clock).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("bounds are correctly set in the API from the URL parameters", () => {
|
||||||
|
let resolveFunction;
|
||||||
|
|
||||||
|
expectLocationToBeInFixedMode();
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
openmct.time.on('bounds', resolveFunction);
|
||||||
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.startBound=0', 'tc.startBound=222')
|
||||||
|
.replace('tc.endBound=1', 'tc.endBound=333');
|
||||||
|
window.location.hash = hash;
|
||||||
|
}).then(() => {
|
||||||
|
let bounds = openmct.time.bounds();
|
||||||
|
|
||||||
|
expect(bounds).toBeDefined();
|
||||||
|
expect(bounds.start).toBe(222);
|
||||||
|
expect(bounds.end).toBe(333);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("bounds are correctly set in the API from the URL parameters where only the end bound changes", () => {
|
||||||
|
let resolveFunction;
|
||||||
|
|
||||||
|
expectLocationToBeInFixedMode();
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
openmct.time.on('bounds', resolveFunction);
|
||||||
|
let hash = window.location.hash;
|
||||||
|
hash = hash.replace('tc.endBound=1', 'tc.endBound=333');
|
||||||
|
window.location.hash = hash;
|
||||||
|
}).then(() => {
|
||||||
|
let bounds = openmct.time.bounds();
|
||||||
|
|
||||||
|
expect(bounds).toBeDefined();
|
||||||
|
expect(bounds.start).toBe(0);
|
||||||
|
expect(bounds.end).toBe(333);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setRealtimeLocationParameters() {
|
||||||
|
let hash = window.location.hash.toString()
|
||||||
|
.replace('tc.mode=fixed', 'tc.mode=local')
|
||||||
|
.replace('tc.startBound=0', 'tc.startDelta=1000')
|
||||||
|
.replace('tc.endBound=1', 'tc.endDelta=100');
|
||||||
|
|
||||||
|
window.location.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFixedLocationParameters() {
|
||||||
|
let hash = window.location.hash.toString()
|
||||||
|
.replace('tc.mode=local', 'tc.mode=fixed')
|
||||||
|
.replace('tc.timeSystem=utc', 'tc.timeSystem=local')
|
||||||
|
.replace('tc.startDelta=1000', 'tc.startBound=50')
|
||||||
|
.replace('tc.endDelta=100', 'tc.endBound=60');
|
||||||
|
|
||||||
|
window.location.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchToRealtimeMode() {
|
||||||
|
let resolveFunction;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
openmct.time.on('clock', resolveFunction);
|
||||||
|
setRealtimeLocationParameters();
|
||||||
|
}).then(() => {
|
||||||
|
openmct.time.off('clock', resolveFunction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchToFixedMode() {
|
||||||
|
let resolveFunction;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolveFunction = resolve;
|
||||||
|
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
|
||||||
|
//detected in the API.
|
||||||
|
openmct.time.on('clock', resolveFunction);
|
||||||
|
setFixedLocationParameters();
|
||||||
|
}).then(() => {
|
||||||
|
openmct.time.off('clock', resolveFunction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectLocationToBeInRealtimeMode() {
|
||||||
|
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectLocationToBeInFixedMode() {
|
||||||
|
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
|
||||||
|
expect(window.location.hash.includes('tc.mode=local')).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
@ -26,6 +26,7 @@ import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
|||||||
import { evaluateResults } from './utils/evaluator';
|
import { evaluateResults } from './utils/evaluator';
|
||||||
import { getLatestTimestamp } from './utils/time';
|
import { getLatestTimestamp } from './utils/time';
|
||||||
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
||||||
|
import {TRIGGER_CONJUNCTION, TRIGGER_LABEL} from "./utils/constants";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* conditionConfiguration = {
|
* conditionConfiguration = {
|
||||||
@ -41,13 +42,14 @@ import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
|||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export default class ConditionClass extends EventEmitter {
|
export default class Condition extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
|
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
|
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
|
||||||
* @param openmct
|
* @param openmct
|
||||||
|
* @param conditionManager
|
||||||
*/
|
*/
|
||||||
constructor(conditionConfiguration, openmct, conditionManager) {
|
constructor(conditionConfiguration, openmct, conditionManager) {
|
||||||
super();
|
super();
|
||||||
@ -62,6 +64,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
this.createCriteria(conditionConfiguration.configuration.criteria);
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
||||||
}
|
}
|
||||||
this.trigger = conditionConfiguration.configuration.trigger;
|
this.trigger = conditionConfiguration.configuration.trigger;
|
||||||
|
this.description = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getResult(datum) {
|
getResult(datum) {
|
||||||
@ -70,15 +73,18 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.criteria.forEach(criterion => {
|
if (this.isTelemetryUsed(datum.id)) {
|
||||||
if (this.isAnyOrAllTelemetry(criterion)) {
|
|
||||||
criterion.getResult(datum, this.conditionManager.telemetryObjects);
|
|
||||||
} else {
|
|
||||||
criterion.getResult(datum);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
this.criteria.forEach(criterion => {
|
||||||
|
if (this.isAnyOrAllTelemetry(criterion)) {
|
||||||
|
criterion.getResult(datum, this.conditionManager.telemetryObjects);
|
||||||
|
} else {
|
||||||
|
criterion.getResult(datum);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isAnyOrAllTelemetry(criterion) {
|
isAnyOrAllTelemetry(criterion) {
|
||||||
@ -106,7 +112,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
return {
|
return {
|
||||||
id: criterionConfiguration.id || uuid(),
|
id: criterionConfiguration.id || uuid(),
|
||||||
telemetry: criterionConfiguration.telemetry || '',
|
telemetry: criterionConfiguration.telemetry || '',
|
||||||
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
|
telemetryObjects: this.conditionManager.telemetryObjects,
|
||||||
operation: criterionConfiguration.operation || '',
|
operation: criterionConfiguration.operation || '',
|
||||||
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
|
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
|
||||||
metadata: criterionConfiguration.metadata || ''
|
metadata: criterionConfiguration.metadata || ''
|
||||||
@ -117,6 +123,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
criterionConfigurations.forEach((criterionConfiguration) => {
|
criterionConfigurations.forEach((criterionConfiguration) => {
|
||||||
this.addCriterion(criterionConfiguration);
|
this.addCriterion(criterionConfiguration);
|
||||||
});
|
});
|
||||||
|
this.updateDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCriteria(criterionConfigurations) {
|
updateCriteria(criterionConfigurations) {
|
||||||
@ -124,10 +131,11 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
this.createCriteria(criterionConfigurations);
|
this.createCriteria(criterionConfigurations);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTelemetry() {
|
updateTelemetryObjects() {
|
||||||
this.criteria.forEach((criterion) => {
|
this.criteria.forEach((criterion) => {
|
||||||
criterion.updateTelemetry(this.conditionManager.telemetryObjects);
|
criterion.updateTelemetryObjects(this.conditionManager.telemetryObjects);
|
||||||
});
|
});
|
||||||
|
this.updateDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,6 +183,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
criterion.unsubscribe();
|
criterion.unsubscribe();
|
||||||
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
this.criteria.splice(found.index, 1, newCriterion);
|
this.criteria.splice(found.index, 1, newCriterion);
|
||||||
|
this.updateDescription();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +196,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
});
|
});
|
||||||
criterion.destroy();
|
criterion.destroy();
|
||||||
this.criteria.splice(found.index, 1);
|
this.criteria.splice(found.index, 1);
|
||||||
|
this.updateDescription();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -197,9 +207,30 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
let found = this.findCriterion(criterion.id);
|
let found = this.findCriterion(criterion.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
this.criteria[found.index] = criterion.data;
|
this.criteria[found.index] = criterion.data;
|
||||||
|
this.updateDescription();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDescription() {
|
||||||
|
const triggerDescription = this.getTriggerDescription();
|
||||||
|
let description = '';
|
||||||
|
this.criteria.forEach((criterion, index) => {
|
||||||
|
if (!index) {
|
||||||
|
description = `Match if ${triggerDescription.prefix}`;
|
||||||
|
}
|
||||||
|
description = `${description} ${criterion.getDescription()} ${(index < this.criteria.length - 1) ? triggerDescription.conjunction : ''}`;
|
||||||
|
});
|
||||||
|
this.description = description;
|
||||||
|
this.conditionManager.updateConditionDescription(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTriggerDescription() {
|
||||||
|
return {
|
||||||
|
conjunction: TRIGGER_CONJUNCTION[this.trigger],
|
||||||
|
prefix: `${TRIGGER_LABEL[this.trigger]}: `
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
requestLADConditionResult() {
|
requestLADConditionResult() {
|
||||||
let latestTimestamp;
|
let latestTimestamp;
|
||||||
let criteriaResults = {};
|
let criteriaResults = {};
|
||||||
|
@ -57,7 +57,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
endpoint,
|
endpoint,
|
||||||
this.telemetryReceived.bind(this, endpoint)
|
this.telemetryReceived.bind(this, endpoint)
|
||||||
);
|
);
|
||||||
this.updateConditionTelemetry();
|
this.updateConditionTelemetryObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribeFromTelemetry(endpointIdentifier) {
|
unsubscribeFromTelemetry(endpointIdentifier) {
|
||||||
@ -70,11 +70,11 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.subscriptions[id]();
|
this.subscriptions[id]();
|
||||||
delete this.subscriptions[id];
|
delete this.subscriptions[id];
|
||||||
delete this.telemetryObjects[id];
|
delete this.telemetryObjects[id];
|
||||||
this.removeConditionTelemetry();
|
this.removeConditionTelemetryObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.conditionClassCollection = [];
|
this.conditions = [];
|
||||||
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
this.initCondition(conditionConfiguration, index);
|
this.initCondition(conditionConfiguration, index);
|
||||||
@ -82,13 +82,14 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConditionTelemetry() {
|
updateConditionTelemetryObjects() {
|
||||||
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
|
this.conditions.forEach((condition) => condition.updateTelemetryObjects());
|
||||||
}
|
}
|
||||||
|
|
||||||
removeConditionTelemetry() {
|
removeConditionTelemetryObjects() {
|
||||||
let conditionsChanged = false;
|
let conditionsChanged = false;
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, conditionIndex) => {
|
||||||
|
let conditionChanged = false;
|
||||||
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
|
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
|
||||||
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
|
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
|
||||||
if (!isAnyAllTelemetry) {
|
if (!isAnyAllTelemetry) {
|
||||||
@ -100,10 +101,14 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
criterion.metadata = '';
|
criterion.metadata = '';
|
||||||
criterion.input = [];
|
criterion.input = [];
|
||||||
criterion.operation = '';
|
criterion.operation = '';
|
||||||
conditionsChanged = true;
|
conditionChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (conditionChanged) {
|
||||||
|
this.updateCondition(conditionConfiguration, conditionIndex);
|
||||||
|
conditionsChanged = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (conditionsChanged) {
|
if (conditionsChanged) {
|
||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
@ -111,18 +116,24 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateCondition(conditionConfiguration, index) {
|
updateCondition(conditionConfiguration, index) {
|
||||||
let condition = this.conditionClassCollection[index];
|
let condition = this.conditions[index];
|
||||||
condition.update(conditionConfiguration);
|
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
|
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
|
||||||
|
condition.update(conditionConfiguration);
|
||||||
|
this.persistConditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConditionDescription(condition) {
|
||||||
|
const found = this.conditionSetDomainObject.configuration.conditionCollection.find(conditionConfiguration => (conditionConfiguration.id === condition.id));
|
||||||
|
found.summary = condition.description;
|
||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
initCondition(conditionConfiguration, index) {
|
initCondition(conditionConfiguration, index) {
|
||||||
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
this.conditionClassCollection.splice(index + 1, 0, condition);
|
this.conditions.splice(index + 1, 0, condition);
|
||||||
} else {
|
} else {
|
||||||
this.conditionClassCollection.unshift(condition);
|
this.conditions.unshift(condition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,15 +192,15 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeCondition(index) {
|
removeCondition(index) {
|
||||||
let condition = this.conditionClassCollection[index];
|
let condition = this.conditions[index];
|
||||||
condition.destroy();
|
condition.destroy();
|
||||||
this.conditionClassCollection.splice(index, 1);
|
this.conditions.splice(index, 1);
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
findConditionById(id) {
|
findConditionById(id) {
|
||||||
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
|
return this.conditions.find(condition => condition.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
reorderConditions(reorderPlan) {
|
reorderConditions(reorderPlan) {
|
||||||
@ -234,14 +245,14 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requestLADConditionSetOutput() {
|
requestLADConditionSetOutput() {
|
||||||
if (!this.conditionClassCollection.length) {
|
if (!this.conditions.length) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.compositionLoad.then(() => {
|
return this.compositionLoad.then(() => {
|
||||||
let latestTimestamp;
|
let latestTimestamp;
|
||||||
let conditionResults = {};
|
let conditionResults = {};
|
||||||
const conditionRequests = this.conditionClassCollection
|
const conditionRequests = this.conditions
|
||||||
.map(condition => condition.requestLADConditionResult());
|
.map(condition => condition.requestLADConditionResult());
|
||||||
|
|
||||||
return Promise.all(conditionRequests)
|
return Promise.all(conditionRequests)
|
||||||
@ -281,7 +292,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
isTelemetryUsed(endpoint) {
|
isTelemetryUsed(endpoint) {
|
||||||
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
||||||
|
|
||||||
for(const condition of this.conditionClassCollection) {
|
for(const condition of this.conditions) {
|
||||||
if (condition.isTelemetryUsed(id)) {
|
if (condition.isTelemetryUsed(id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -300,7 +311,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
let timestamp = {};
|
let timestamp = {};
|
||||||
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
|
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
|
||||||
|
|
||||||
this.conditionClassCollection.forEach(condition => {
|
this.conditions.forEach(condition => {
|
||||||
condition.getResult(normalizedDatum);
|
condition.getResult(normalizedDatum);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -364,7 +375,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.stopObservingForChanges();
|
this.stopObservingForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.conditionClassCollection.forEach((condition) => {
|
this.conditions.forEach((condition) => {
|
||||||
condition.destroy();
|
condition.destroy();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ describe('ConditionManager', () => {
|
|||||||
|
|
||||||
it('creates a conditionCollection with a default condition', function () {
|
it('creates a conditionCollection with a default condition', function () {
|
||||||
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
|
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
|
||||||
let defaultConditionId = conditionMgr.conditionClassCollection[0].id;
|
let defaultConditionId = conditionMgr.conditions[0].id;
|
||||||
expect(defaultConditionId).toEqual(mockCondition.id);
|
expect(defaultConditionId).toEqual(mockCondition.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,28 +36,36 @@ describe("The condition", function () {
|
|||||||
|
|
||||||
beforeEach (() => {
|
beforeEach (() => {
|
||||||
conditionManager = jasmine.createSpyObj('conditionManager',
|
conditionManager = jasmine.createSpyObj('conditionManager',
|
||||||
['on']
|
['on', 'updateConditionDescription']
|
||||||
);
|
);
|
||||||
mockTelemetryReceived = jasmine.createSpy('listener');
|
mockTelemetryReceived = jasmine.createSpy('listener');
|
||||||
conditionManager.on('telemetryReceived', mockTelemetryReceived);
|
conditionManager.on('telemetryReceived', mockTelemetryReceived);
|
||||||
|
conditionManager.updateConditionDescription.and.returnValue(function () {});
|
||||||
|
|
||||||
testTelemetryObject = {
|
testTelemetryObject = {
|
||||||
identifier:{ namespace: "", key: "test-object"},
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
type: "test-object",
|
type: "test-object",
|
||||||
name: "Test Object",
|
name: "Test Object",
|
||||||
telemetry: {
|
telemetry: {
|
||||||
values: [{
|
valueMetadatas: [{
|
||||||
key: "some-key",
|
key: "some-key",
|
||||||
name: "Some attribute",
|
name: "Some attribute",
|
||||||
|
hints: {
|
||||||
|
range: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
name: "Time",
|
||||||
|
format: "utc",
|
||||||
hints: {
|
hints: {
|
||||||
domain: 1
|
domain: 1
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
key: "some-other-key",
|
key: "testSource",
|
||||||
name: "Another attribute",
|
source: "value",
|
||||||
hints: {
|
name: "Test",
|
||||||
range: 1
|
format: "string"
|
||||||
}
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -71,7 +79,7 @@ describe("The condition", function () {
|
|||||||
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']);
|
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']);
|
||||||
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
openmct.telemetry.subscribe.and.returnValue(function () {});
|
openmct.telemetry.subscribe.and.returnValue(function () {});
|
||||||
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
|
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
|
||||||
|
|
||||||
mockTimeSystems = {
|
mockTimeSystems = {
|
||||||
key: 'utc'
|
key: 'utc'
|
||||||
@ -136,4 +144,38 @@ describe("The condition", function () {
|
|||||||
expect(result).toBeTrue();
|
expect(result).toBeTrue();
|
||||||
expect(conditionObj.criteria.length).toEqual(0);
|
expect(conditionObj.criteria.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("gets the result of a condition when new telemetry data is received", function () {
|
||||||
|
conditionObj.getResult({
|
||||||
|
value: '0',
|
||||||
|
utc: 'Hi',
|
||||||
|
id: testTelemetryObject.identifier.key
|
||||||
|
});
|
||||||
|
expect(conditionObj.result).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gets the result of a condition when new telemetry data is received", function () {
|
||||||
|
conditionObj.getResult({
|
||||||
|
value: '1',
|
||||||
|
utc: 'Hi',
|
||||||
|
id: testTelemetryObject.identifier.key
|
||||||
|
});
|
||||||
|
expect(conditionObj.result).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the old result new telemetry data is not used by it", function () {
|
||||||
|
conditionObj.getResult({
|
||||||
|
value: '0',
|
||||||
|
utc: 'Hi',
|
||||||
|
id: testTelemetryObject.identifier.key
|
||||||
|
});
|
||||||
|
expect(conditionObj.result).toBeTrue();
|
||||||
|
|
||||||
|
conditionObj.getResult({
|
||||||
|
value: '1',
|
||||||
|
utc: 'Hi',
|
||||||
|
id: '1234'
|
||||||
|
});
|
||||||
|
expect(conditionObj.result).toBeTrue();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -29,6 +29,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
if (suppressSubscriptionOnEdit) {
|
if (suppressSubscriptionOnEdit) {
|
||||||
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
|
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
|
||||||
|
this.isEditing = this.openmct.editor.editing;
|
||||||
}
|
}
|
||||||
if (styleConfiguration) {
|
if (styleConfiguration) {
|
||||||
this.initialize(styleConfiguration);
|
this.initialize(styleConfiguration);
|
||||||
@ -156,7 +157,6 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
delete this.stopProvidingTelemetry;
|
delete this.stopProvidingTelemetry;
|
||||||
this.conditionSetIdentifier = undefined;
|
this.conditionSetIdentifier = undefined;
|
||||||
this.isEditing = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
||||||
<span class="c-condition__summary">
|
<span class="c-condition__summary">
|
||||||
<template v-if="!canEvaluateCriteria">
|
<template v-if="!condition.isDefault && !canEvaluateCriteria">
|
||||||
Define criteria
|
Define criteria
|
||||||
</template>
|
</template>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
@ -250,7 +250,7 @@ export default {
|
|||||||
keys.forEach((trigger) => {
|
keys.forEach((trigger) => {
|
||||||
triggerOptions.push({
|
triggerOptions.push({
|
||||||
value: TRIGGER[trigger],
|
value: TRIGGER[trigger],
|
||||||
label: TRIGGER_LABEL[TRIGGER[trigger]]
|
label: `when ${TRIGGER_LABEL[TRIGGER[trigger]]}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return triggerOptions;
|
return triggerOptions;
|
||||||
|
@ -152,7 +152,8 @@ export default {
|
|||||||
},
|
},
|
||||||
observeForChanges() {
|
observeForChanges() {
|
||||||
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
|
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
|
||||||
this.conditionCollection = newConditionCollection;
|
//this forces children to re-render
|
||||||
|
this.conditionCollection = newConditionCollection.map(condition => condition);
|
||||||
this.updateDefaultCondition();
|
this.updateDefaultCondition();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -200,7 +201,7 @@ export default {
|
|||||||
this.$emit('telemetryUpdated', this.telemetryObjs);
|
this.$emit('telemetryUpdated', this.telemetryObjs);
|
||||||
},
|
},
|
||||||
removeTelemetryObject(identifier) {
|
removeTelemetryObject(identifier) {
|
||||||
let index = _.findIndex(this.telemetryObjs, (obj) => {
|
let index = this.telemetryObjs.findIndex(obj => {
|
||||||
let objId = this.openmct.objects.makeKeyString(obj.identifier);
|
let objId = this.openmct.objects.makeKeyString(obj.identifier);
|
||||||
let id = this.openmct.objects.makeKeyString(identifier);
|
let id = this.openmct.objects.makeKeyString(identifier);
|
||||||
return objId === id;
|
return objId === id;
|
||||||
|
@ -27,20 +27,20 @@
|
|||||||
>
|
>
|
||||||
{{ condition.configuration.name }}
|
{{ condition.configuration.name }}
|
||||||
</span>
|
</span>
|
||||||
<span v-for="(criterionDescription, index) in criterionDescriptions"
|
<span class="c-style__condition-desc__text"
|
||||||
:key="criterionDescription"
|
v-if="!condition.isDefault"
|
||||||
class="c-style__condition-desc__text"
|
|
||||||
>
|
>
|
||||||
<template v-if="!index">When</template>
|
{{ description }}
|
||||||
{{ criterionDescription }}
|
</span>
|
||||||
<template v-if="index < (criterionDescriptions.length-1)">{{ triggerDescription }}</template>
|
<span class="c-style__condition-desc__text"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
Match if no other condition is matched
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { TRIGGER } from "@/plugins/condition/utils/constants";
|
|
||||||
import { OPERATIONS } from "@/plugins/condition/utils/operations";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ConditionDescription',
|
name: 'ConditionDescription',
|
||||||
@ -59,95 +59,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
computed: {
|
||||||
return {
|
description() {
|
||||||
criterionDescriptions: [],
|
return this.condition ? this.condition.summary : '';
|
||||||
triggerDescription: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
condition: {
|
|
||||||
handler(val) {
|
|
||||||
this.getConditionDescription();
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getConditionDescription();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getTriggerDescription(trigger) {
|
|
||||||
let description = '';
|
|
||||||
switch(trigger) {
|
|
||||||
case TRIGGER.ANY:
|
|
||||||
case TRIGGER.XOR:
|
|
||||||
description = 'or';
|
|
||||||
break;
|
|
||||||
case TRIGGER.ALL:
|
|
||||||
case TRIGGER.NOT: description = 'and';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return description;
|
|
||||||
},
|
|
||||||
getConditionDescription() {
|
|
||||||
if (this.condition) {
|
|
||||||
this.triggerDescription = this.getTriggerDescription(this.condition.configuration.trigger);
|
|
||||||
this.criterionDescriptions = [];
|
|
||||||
this.condition.configuration.criteria.forEach((criterion, index) => {
|
|
||||||
this.getCriterionDescription(criterion, index);
|
|
||||||
});
|
|
||||||
if (this.condition.isDefault) {
|
|
||||||
this.criterionDescriptions.splice(0, 0, 'all else fails');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.criterionDescriptions = [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getCriterionDescription(criterion, index) {
|
|
||||||
if (!criterion.telemetry) {
|
|
||||||
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
|
||||||
this.criterionDescriptions.splice(index, 0, description);
|
|
||||||
} else if (criterion.telemetry === 'all' || criterion.telemetry === 'any') {
|
|
||||||
const telemetryDescription = criterion.telemetry === 'all' ? 'All telemetry' : 'Any telemetry';
|
|
||||||
let description = `${telemetryDescription} ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
|
||||||
this.criterionDescriptions.splice(index, 0, description);
|
|
||||||
} else {
|
|
||||||
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
|
||||||
if (telemetryObject.type === 'unknown') {
|
|
||||||
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
|
||||||
this.criterionDescriptions.splice(index, 0, description);
|
|
||||||
} else {
|
|
||||||
let metadataValue = criterion.metadata;
|
|
||||||
let inputValue = criterion.input;
|
|
||||||
if (criterion.metadata) {
|
|
||||||
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
|
||||||
|
|
||||||
const metadataObj = this.telemetryMetadata.valueMetadatas.find((metadata) => metadata.key === criterion.metadata);
|
|
||||||
if (metadataObj) {
|
|
||||||
if (metadataObj.name) {
|
|
||||||
metadataValue = metadataObj.name;
|
|
||||||
}
|
|
||||||
if(metadataObj.enumerations && inputValue.length) {
|
|
||||||
if (metadataObj.enumerations[inputValue[0]] && metadataObj.enumerations[inputValue[0]].string) {
|
|
||||||
inputValue = [metadataObj.enumerations[inputValue[0]].string];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let description = `${telemetryObject.name} ${metadataValue} ${this.getOperatorText(criterion.operation, inputValue)}`;
|
|
||||||
if (this.criterionDescriptions[index]) {
|
|
||||||
this.criterionDescriptions[index] = description;
|
|
||||||
} else {
|
|
||||||
this.criterionDescriptions.splice(index, 0, description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getOperatorText(operationName, values) {
|
|
||||||
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
|
||||||
return found ? found.getDescription(values) : '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,22 +66,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getCriterionErrors(criterion, index) {
|
getCriterionErrors(criterion, index) {
|
||||||
if (!criterion.telemetry) {
|
//It is sufficient to check for absence of telemetry here since the condition manager ensures that telemetry for a criterion is set if it exists
|
||||||
|
const isInvalidTelemetry = !criterion.telemetry && (criterion.telemetry !== 'all' && criterion.telemetry !== 'any');
|
||||||
|
if (isInvalidTelemetry) {
|
||||||
this.conditionErrors.push({
|
this.conditionErrors.push({
|
||||||
message: ERROR.TELEMETRY_NOT_FOUND,
|
message: ERROR.TELEMETRY_NOT_FOUND,
|
||||||
additionalInfo: ''
|
additionalInfo: ''
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
if (criterion.telemetry !== 'all' && criterion.telemetry !== 'any') {
|
|
||||||
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
|
||||||
if (telemetryObject.type === 'unknown') {
|
|
||||||
this.conditionErrors.push({
|
|
||||||
message: ERROR.TELEMETRY_NOT_FOUND,
|
|
||||||
additionalInfo: criterion.telemetry ? `Key: ${this.openmct.objects.makeKeyString(criterion.telemetry)}` : ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
<input v-model="criterion.input[inputIndex]"
|
<input v-model="criterion.input[inputIndex]"
|
||||||
class="c-cdef__control__input"
|
class="c-cdef__control__input"
|
||||||
:type="setInputType"
|
:type="setInputType"
|
||||||
@blur="persist"
|
@change="persist"
|
||||||
>
|
>
|
||||||
<span v-if="inputIndex < inputCount-1">and</span>
|
<span v-if="inputIndex < inputCount-1">and</span>
|
||||||
</span>
|
</span>
|
||||||
@ -108,6 +108,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { OPERATIONS } from '../utils/operations';
|
import { OPERATIONS } from '../utils/operations';
|
||||||
import { INPUT_TYPES } from '../utils/operations';
|
import { INPUT_TYPES } from '../utils/operations';
|
||||||
|
import {TRIGGER_CONJUNCTION} from "../utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -143,8 +144,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
setRowLabel: function () {
|
setRowLabel: function () {
|
||||||
let operator = this.trigger === 'all' ? 'and ': 'or ';
|
let operator = TRIGGER_CONJUNCTION[this.trigger];
|
||||||
return (this.index !== 0 ? operator : '') + 'when';
|
return (this.index !== 0 ? operator : '') + ' when';
|
||||||
},
|
},
|
||||||
filteredOps: function () {
|
filteredOps: function () {
|
||||||
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
|
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
|
||||||
@ -178,17 +179,18 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
checkTelemetry() {
|
checkTelemetry() {
|
||||||
if(this.criterion.telemetry) {
|
if(this.criterion.telemetry) {
|
||||||
if (this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all') {
|
const isAnyAllTelemetry = this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all';
|
||||||
this.updateMetadataOptions();
|
const telemetryForCriterionExists = this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier));
|
||||||
|
if (!isAnyAllTelemetry &&
|
||||||
|
!telemetryForCriterionExists) {
|
||||||
|
//telemetry being used was removed. So reset this criterion.
|
||||||
|
this.criterion.telemetry = '';
|
||||||
|
this.criterion.metadata = '';
|
||||||
|
this.criterion.input = [];
|
||||||
|
this.criterion.operation = '';
|
||||||
|
this.persist();
|
||||||
} else {
|
} else {
|
||||||
if (!this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier))) {
|
this.updateMetadataOptions();
|
||||||
//telemetry being used was removed. So reset this criterion.
|
|
||||||
this.criterion.telemetry = '';
|
|
||||||
this.criterion.metadata = '';
|
|
||||||
this.criterion.input = [];
|
|
||||||
this.criterion.operation = '';
|
|
||||||
this.persist();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -221,19 +223,17 @@ export default {
|
|||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
if (this.criterion.telemetry) {
|
if (this.criterion.telemetry) {
|
||||||
const telemetry = (this.criterion.telemetry === 'all' || this.criterion.telemetry === 'any') ? this.telemetry : [{
|
let telemetryObjects = this.telemetry;
|
||||||
identifier: this.criterion.telemetry
|
if (this.criterion.telemetry !== 'all' && this.criterion.telemetry !== 'any') {
|
||||||
}];
|
const found = this.telemetry.find(telemetryObj => (this.openmct.objects.areIdsEqual(telemetryObj.identifier, this.criterion.telemetry)));
|
||||||
|
telemetryObjects = found ? [found] : [];
|
||||||
let telemetryPromises = telemetry.map((telemetryObject) => this.openmct.objects.get(telemetryObject.identifier));
|
}
|
||||||
Promise.all(telemetryPromises).then(telemetryObjects => {
|
this.telemetryMetadataOptions = [];
|
||||||
this.telemetryMetadataOptions = [];
|
telemetryObjects.forEach(telemetryObject => {
|
||||||
telemetryObjects.forEach(telemetryObject => {
|
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
this.addMetaDataOptions(telemetryMetadata.values());
|
||||||
this.addMetaDataOptions(telemetryMetadata.values());
|
|
||||||
});
|
|
||||||
this.updateOperations();
|
|
||||||
});
|
});
|
||||||
|
this.updateOperations();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addMetaDataOptions(options) {
|
addMetaDataOptions(options) {
|
||||||
|
@ -108,6 +108,7 @@ import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
|
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ConditionalStylesView',
|
name: 'ConditionalStylesView',
|
||||||
@ -288,7 +289,7 @@ export default {
|
|||||||
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;
|
||||||
if (_.isEmpty(domainObjectStyles[this.itemId])) {
|
if (isEmpty(domainObjectStyles[this.itemId])) {
|
||||||
delete domainObjectStyles[this.itemId];
|
delete domainObjectStyles[this.itemId];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -299,7 +300,7 @@ export default {
|
|||||||
domainObjectStyles.styles = undefined;
|
domainObjectStyles.styles = undefined;
|
||||||
delete domainObjectStyles.styles;
|
delete domainObjectStyles.styles;
|
||||||
}
|
}
|
||||||
if (_.isEmpty(domainObjectStyles)) {
|
if (isEmpty(domainObjectStyles)) {
|
||||||
domainObjectStyles = undefined;
|
domainObjectStyles = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +338,7 @@ export default {
|
|||||||
delete domainObjectStyles[this.itemId];
|
delete domainObjectStyles[this.itemId];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (_.isEmpty(domainObjectStyles)) {
|
if (isEmpty(domainObjectStyles)) {
|
||||||
domainObjectStyles = undefined;
|
domainObjectStyles = undefined;
|
||||||
}
|
}
|
||||||
this.persist(domainObjectStyles);
|
this.persist(domainObjectStyles);
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
import StyleEditor from "./StyleEditor.vue";
|
import StyleEditor from "./StyleEditor.vue";
|
||||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
|
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MultiSelectStylesView',
|
name: 'MultiSelectStylesView',
|
||||||
@ -178,7 +179,7 @@ export default {
|
|||||||
domainObjectStyles[itemId] = undefined;
|
domainObjectStyles[itemId] = undefined;
|
||||||
delete domainObjectStyles[this.itemId];
|
delete domainObjectStyles[this.itemId];
|
||||||
|
|
||||||
if (_.isEmpty(domainObjectStyles)) {
|
if (isEmpty(domainObjectStyles)) {
|
||||||
domainObjectStyles = undefined;
|
domainObjectStyles = undefined;
|
||||||
}
|
}
|
||||||
this.persist(this.domainObject, domainObjectStyles);
|
this.persist(this.domainObject, domainObjectStyles);
|
||||||
@ -239,7 +240,7 @@ export default {
|
|||||||
if (this.isStaticAndConditionalStyles) {
|
if (this.isStaticAndConditionalStyles) {
|
||||||
this.removeConditionalStyles(domainObjectStyles, item.id);
|
this.removeConditionalStyles(domainObjectStyles, item.id);
|
||||||
}
|
}
|
||||||
if (_.isEmpty(itemStaticStyle)) {
|
if (isEmpty(itemStaticStyle)) {
|
||||||
itemStaticStyle = undefined;
|
itemStaticStyle = undefined;
|
||||||
domainObjectStyles[item.id] = undefined;
|
domainObjectStyles[item.id] = undefined;
|
||||||
} else {
|
} else {
|
||||||
|
601
src/plugins/condition/components/inspector/StylesView.vue
Normal file
601
src/plugins/condition/components/inspector/StylesView.vue
Normal file
@ -0,0 +1,601 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-inspector__styles c-inspect-styles">
|
||||||
|
<div v-if="isStaticAndConditionalStyles"
|
||||||
|
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
|
||||||
|
>
|
||||||
|
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
|
||||||
|
</div>
|
||||||
|
<template v-if="!conditionSetDomainObject">
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Object Style
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content">
|
||||||
|
<div v-if="staticStyle"
|
||||||
|
class="c-inspect-styles__style"
|
||||||
|
>
|
||||||
|
<style-editor class="c-inspect-styles__editor"
|
||||||
|
:style-item="staticStyle"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
:mixed-styles="mixedStyles"
|
||||||
|
@persist="updateStaticStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
id="addConditionSet"
|
||||||
|
class="c-button c-button--major c-toggle-styling-button labeled"
|
||||||
|
@click="addConditionSet"
|
||||||
|
>
|
||||||
|
<span class="c-cs-button__label">Use Conditional Styling...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Conditional Object Styles
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||||
|
<a v-if="conditionSetDomainObject"
|
||||||
|
class="c-object-label icon-conditional"
|
||||||
|
:href="navigateToPath"
|
||||||
|
@click="navigateOrPreview"
|
||||||
|
>
|
||||||
|
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
||||||
|
</a>
|
||||||
|
<template v-if="isEditing">
|
||||||
|
<button
|
||||||
|
id="changeConditionSet"
|
||||||
|
class="c-button labeled"
|
||||||
|
@click="addConditionSet"
|
||||||
|
>
|
||||||
|
<span class="c-button__label">Change...</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="c-click-icon icon-x"
|
||||||
|
title="Remove conditional styles"
|
||||||
|
@click="removeConditionSet"
|
||||||
|
></button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="conditionsLoaded"
|
||||||
|
class="c-inspect-styles__conditions"
|
||||||
|
>
|
||||||
|
<div v-for="(conditionStyle, index) in conditionalStyles"
|
||||||
|
:key="index"
|
||||||
|
class="c-inspect-styles__condition"
|
||||||
|
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
|
||||||
|
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
|
||||||
|
>
|
||||||
|
<condition-error :show-label="true"
|
||||||
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
|
/>
|
||||||
|
<condition-description :show-label="true"
|
||||||
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
|
/>
|
||||||
|
<style-editor class="c-inspect-styles__editor"
|
||||||
|
:style-item="conditionStyle"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
@persist="updateConditionalStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import StyleEditor from "./StyleEditor.vue";
|
||||||
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
|
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
|
||||||
|
import ConditionSetSelectorDialog from "@/plugins/condition/components/inspector/ConditionSetSelectorDialog.vue";
|
||||||
|
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||||
|
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'StylesView',
|
||||||
|
components: {
|
||||||
|
StyleEditor,
|
||||||
|
ConditionError,
|
||||||
|
ConditionDescription
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'openmct',
|
||||||
|
'selection'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
staticStyle: undefined,
|
||||||
|
isEditing: this.openmct.editor.isEditing(),
|
||||||
|
mixedStyles: [],
|
||||||
|
isStaticAndConditionalStyles: false,
|
||||||
|
conditionalStyles: [],
|
||||||
|
conditionSetDomainObject: undefined,
|
||||||
|
conditions: undefined,
|
||||||
|
conditionsLoaded: false,
|
||||||
|
navigateToPath: '',
|
||||||
|
selectedConditionId: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.removeListeners();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.items = [];
|
||||||
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
|
this.isMultipleSelection = this.selection.length > 1;
|
||||||
|
this.getObjectsAndItemsFromSelection();
|
||||||
|
if (!this.isMultipleSelection) {
|
||||||
|
let objectStyles = this.getObjectStyles();
|
||||||
|
this.initializeStaticStyle(objectStyles);
|
||||||
|
if (objectStyles && objectStyles.conditionSetIdentifier) {
|
||||||
|
this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
|
||||||
|
this.conditionalStyles = objectStyles.styles;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.initializeStaticStyle();
|
||||||
|
}
|
||||||
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getObjectStyles() {
|
||||||
|
let objectStyles;
|
||||||
|
if (this.domainObjectsById) {
|
||||||
|
const domainObject = Object.values(this.domainObjectsById)[0];
|
||||||
|
if (domainObject.configuration && domainObject.configuration.objectStyles) {
|
||||||
|
objectStyles = domainObject.configuration.objectStyles;
|
||||||
|
}
|
||||||
|
} else if (this.items.length) {
|
||||||
|
const itemId = this.items[0].id;
|
||||||
|
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) {
|
||||||
|
objectStyles = this.domainObject.configuration.objectStyles[itemId];
|
||||||
|
}
|
||||||
|
} else if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
||||||
|
objectStyles = this.domainObject.configuration.objectStyles;
|
||||||
|
}
|
||||||
|
return objectStyles;
|
||||||
|
},
|
||||||
|
setEditState(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
if (this.isEditing) {
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableConditionSetNav() {
|
||||||
|
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
||||||
|
(objectPath) => {
|
||||||
|
this.objectPath = objectPath;
|
||||||
|
this.navigateToPath = '#/browse/' + this.objectPath
|
||||||
|
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
navigateOrPreview(event) {
|
||||||
|
// If editing, display condition set in Preview overlay; otherwise nav to it while browsing
|
||||||
|
if (this.openmct.editor.isEditing()) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.previewAction.invoke(this.objectPath);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isItemType(type, item) {
|
||||||
|
return item && (item.type === type);
|
||||||
|
},
|
||||||
|
hasConditionalStyle(domainObject, layoutItem) {
|
||||||
|
const id = layoutItem ? layoutItem.id : undefined;
|
||||||
|
return getConditionSetIdentifierForItem(domainObject, id) !== undefined;
|
||||||
|
},
|
||||||
|
getObjectsAndItemsFromSelection() {
|
||||||
|
let domainObject;
|
||||||
|
let subObjects = [];
|
||||||
|
let itemsWithConditionalStyles = 0;
|
||||||
|
|
||||||
|
//multiple selection
|
||||||
|
let itemInitialStyles = [];
|
||||||
|
let itemStyle;
|
||||||
|
this.selection.forEach((selectionItem) => {
|
||||||
|
const item = selectionItem[0].context.item;
|
||||||
|
const layoutItem = selectionItem[0].context.layoutItem;
|
||||||
|
const isChildItem = selectionItem.length > 1;
|
||||||
|
if (!isChildItem) {
|
||||||
|
domainObject = item;
|
||||||
|
itemStyle = getApplicableStylesForItem(item);
|
||||||
|
if (this.hasConditionalStyle(item)) {
|
||||||
|
itemsWithConditionalStyles += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.canHide = true;
|
||||||
|
domainObject = selectionItem[1].context.item;
|
||||||
|
if (item && !layoutItem || this.isItemType('subobject-view', layoutItem)) {
|
||||||
|
subObjects.push(item);
|
||||||
|
itemStyle = getApplicableStylesForItem(item);
|
||||||
|
if (this.hasConditionalStyle(item)) {
|
||||||
|
itemsWithConditionalStyles += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
|
||||||
|
this.items.push({
|
||||||
|
id: layoutItem.id,
|
||||||
|
applicableStyles: itemStyle
|
||||||
|
});
|
||||||
|
if (this.hasConditionalStyle(item, layoutItem)) {
|
||||||
|
itemsWithConditionalStyles += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemInitialStyles.push(itemStyle);
|
||||||
|
});
|
||||||
|
this.isStaticAndConditionalStyles = this.isMultipleSelection && itemsWithConditionalStyles;
|
||||||
|
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
|
||||||
|
this.initialStyles = styles;
|
||||||
|
this.mixedStyles = mixedStyles;
|
||||||
|
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.removeListeners();
|
||||||
|
if (this.domainObject) {
|
||||||
|
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||||
|
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
subObjects.forEach(this.registerListener);
|
||||||
|
},
|
||||||
|
updateDomainObjectItemStyles(newItems) {
|
||||||
|
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (this.isKeyItemId(key)) {
|
||||||
|
if (!(newItems.find(item => item.id === key))) {
|
||||||
|
this.removeItemStyles(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isKeyItemId(key) {
|
||||||
|
return (key !== 'styles') &&
|
||||||
|
(key !== 'staticStyle') &&
|
||||||
|
(key !== 'defaultConditionId') &&
|
||||||
|
(key !== 'selectedConditionId') &&
|
||||||
|
(key !== 'conditionSetIdentifier');
|
||||||
|
},
|
||||||
|
registerListener(domainObject) {
|
||||||
|
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
|
if (!this.domainObjectsById) {
|
||||||
|
this.domainObjectsById = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.domainObjectsById[id]) {
|
||||||
|
this.domainObjectsById[id] = domainObject;
|
||||||
|
this.observeObject(domainObject, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
observeObject(domainObject, id) {
|
||||||
|
let unobserveObject = this.openmct.objects.observe(domainObject, '*', (newObject) => {
|
||||||
|
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
|
||||||
|
});
|
||||||
|
this.unObserveObjects.push(unobserveObject);
|
||||||
|
},
|
||||||
|
removeListeners() {
|
||||||
|
if (this.stopObserving) {
|
||||||
|
this.stopObserving();
|
||||||
|
}
|
||||||
|
if (this.stopObservingItems) {
|
||||||
|
this.stopObservingItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.unObserveObjects) {
|
||||||
|
this.unObserveObjects.forEach((unObserveObject) => {
|
||||||
|
unObserveObject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.unObserveObjects = [];
|
||||||
|
},
|
||||||
|
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 : '';
|
||||||
|
},
|
||||||
|
initialize(conditionSetDomainObject) {
|
||||||
|
//If there are new conditions in the conditionSet we need to set those styles to default
|
||||||
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
this.enableConditionSetNav();
|
||||||
|
this.initializeConditionalStyles();
|
||||||
|
},
|
||||||
|
initializeConditionalStyles() {
|
||||||
|
if (!this.conditions) {
|
||||||
|
this.conditions = {};
|
||||||
|
}
|
||||||
|
let conditionalStyles = [];
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
|
if (conditionConfiguration.isDefault) {
|
||||||
|
this.selectedConditionId = conditionConfiguration.id;
|
||||||
|
}
|
||||||
|
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
||||||
|
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
||||||
|
if (foundStyle) {
|
||||||
|
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
|
||||||
|
conditionalStyles.push(foundStyle);
|
||||||
|
} else {
|
||||||
|
conditionalStyles.splice(index, 0, {
|
||||||
|
conditionId: conditionConfiguration.id,
|
||||||
|
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
|
||||||
|
this.conditionalStyles = conditionalStyles;
|
||||||
|
this.conditionsLoaded = true;
|
||||||
|
this.getAndPersistStyles(null, this.selectedConditionId);
|
||||||
|
if (!this.isEditing) {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//TODO: Double check how this works for single styles
|
||||||
|
initializeStaticStyle(objectStyles) {
|
||||||
|
let staticStyle = objectStyles && objectStyles.staticStyle;
|
||||||
|
if (staticStyle) {
|
||||||
|
this.staticStyle = {
|
||||||
|
style: Object.assign({}, this.initialStyles, staticStyle.style)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.staticStyle = {
|
||||||
|
style: Object.assign({}, this.initialStyles)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItemStyles(itemId) {
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
if (itemId && domainObjectStyles[itemId]) {
|
||||||
|
delete domainObjectStyles[itemId];
|
||||||
|
|
||||||
|
if (Object.keys(domainObjectStyles).length <= 0) {
|
||||||
|
domainObjectStyles = undefined;
|
||||||
|
}
|
||||||
|
this.persist(this.domainObject, domainObjectStyles);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findStyleByConditionId(id) {
|
||||||
|
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
||||||
|
},
|
||||||
|
getCondition(id) {
|
||||||
|
return this.conditions ? this.conditions[id] : {};
|
||||||
|
},
|
||||||
|
addConditionSet() {
|
||||||
|
let conditionSetDomainObject;
|
||||||
|
const handleItemSelection = (item) => {
|
||||||
|
if (item) {
|
||||||
|
conditionSetDomainObject = item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dismissDialog = (overlay, initialize) => {
|
||||||
|
overlay.dismiss();
|
||||||
|
if (initialize && conditionSetDomainObject) {
|
||||||
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
this.conditionalStyles = [];
|
||||||
|
this.initializeConditionalStyles();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let vm = new Vue({
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct
|
||||||
|
},
|
||||||
|
components: {ConditionSetSelectorDialog},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
handleItemSelection
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>'
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
let overlay = this.openmct.overlays.overlay({
|
||||||
|
element: vm.$el,
|
||||||
|
size: 'small',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
emphasis: 'true',
|
||||||
|
callback: () => dismissDialog(overlay, true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cancel',
|
||||||
|
callback: () => dismissDialog(overlay, false)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onDestroy: () => vm.$destroy()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeConditionSet() {
|
||||||
|
this.conditionSetDomainObject = undefined;
|
||||||
|
this.conditionalStyles = [];
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
if (this.domainObjectsById) {
|
||||||
|
const domainObjects = Object.values(this.domainObjectsById);
|
||||||
|
domainObjects.forEach(domainObject => {
|
||||||
|
let objectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
||||||
|
this.removeConditionalStyles(objectStyles);
|
||||||
|
if (objectStyles && Object.keys(objectStyles).length <= 0) {
|
||||||
|
objectStyles = undefined;
|
||||||
|
}
|
||||||
|
this.persist(domainObject, objectStyles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.items.length) {
|
||||||
|
this.items.forEach((item) => {
|
||||||
|
const itemId = item.id;
|
||||||
|
this.removeConditionalStyles(domainObjectStyles, itemId);
|
||||||
|
if (domainObjectStyles[itemId] && Object.keys(domainObjectStyles[itemId]).length <= 0) {
|
||||||
|
delete domainObjectStyles[itemId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.removeConditionalStyles(domainObjectStyles);
|
||||||
|
}
|
||||||
|
if (domainObjectStyles && Object.keys(domainObjectStyles).length <= 0) {
|
||||||
|
domainObjectStyles = undefined;
|
||||||
|
}
|
||||||
|
this.persist(this.domainObject, domainObjectStyles);
|
||||||
|
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeConditionalStyles(domainObjectStyles, itemId) {
|
||||||
|
if (itemId && domainObjectStyles[itemId]) {
|
||||||
|
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles[itemId].conditionSetIdentifier;
|
||||||
|
domainObjectStyles[itemId].selectedConditionId = undefined;
|
||||||
|
domainObjectStyles[itemId].defaultConditionId = undefined;
|
||||||
|
domainObjectStyles[itemId].styles = undefined;
|
||||||
|
delete domainObjectStyles[itemId].styles;
|
||||||
|
} else {
|
||||||
|
domainObjectStyles.conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles.conditionSetIdentifier;
|
||||||
|
domainObjectStyles.selectedConditionId = undefined;
|
||||||
|
domainObjectStyles.defaultConditionId = undefined;
|
||||||
|
domainObjectStyles.styles = undefined;
|
||||||
|
delete domainObjectStyles.styles;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateStaticStyle(staticStyle, property) {
|
||||||
|
//update the static style for each of the layoutItems as well as each sub object item
|
||||||
|
this.staticStyle = staticStyle;
|
||||||
|
this.removeConditionSet();
|
||||||
|
this.getAndPersistStyles(property);
|
||||||
|
},
|
||||||
|
updateConditionalStyle(conditionStyle, property) {
|
||||||
|
let foundStyle = this.findStyleByConditionId(conditionStyle.conditionId);
|
||||||
|
if (foundStyle) {
|
||||||
|
foundStyle.style = conditionStyle.style;
|
||||||
|
this.selectedConditionId = foundStyle.conditionId;
|
||||||
|
this.getAndPersistStyles(property);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAndPersistStyles(property, defaultConditionId) {
|
||||||
|
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items, defaultConditionId));
|
||||||
|
if (this.domainObjectsById) {
|
||||||
|
const domainObjects = Object.values(this.domainObjectsById);
|
||||||
|
domainObjects.forEach(domainObject => {
|
||||||
|
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property, null, defaultConditionId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!this.items.length && !this.domainObjectsById) {
|
||||||
|
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, null, defaultConditionId));
|
||||||
|
}
|
||||||
|
this.isStaticAndConditionalStyles = false;
|
||||||
|
if (property) {
|
||||||
|
let foundIndex = this.mixedStyles.indexOf(property);
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
this.mixedStyles.splice(foundIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getDomainObjectStyle(domainObject, property, items, defaultConditionId) {
|
||||||
|
let objectStyle = {
|
||||||
|
styles: this.conditionalStyles,
|
||||||
|
staticStyle: this.staticStyle,
|
||||||
|
selectedConditionId: this.selectedConditionId
|
||||||
|
};
|
||||||
|
if (defaultConditionId) {
|
||||||
|
objectStyle.defaultConditionId = defaultConditionId;
|
||||||
|
}
|
||||||
|
if (this.conditionSetDomainObject) {
|
||||||
|
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
|
||||||
|
}
|
||||||
|
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
||||||
|
|
||||||
|
if (items) {
|
||||||
|
items.forEach(item => {
|
||||||
|
let itemStaticStyle = {};
|
||||||
|
let itemConditionalStyle = { styles: []};
|
||||||
|
if (!this.conditionSetDomainObject) {
|
||||||
|
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
|
||||||
|
itemStaticStyle = Object.assign({}, domainObjectStyles[item.id].staticStyle.style);
|
||||||
|
}
|
||||||
|
if (item.applicableStyles[property] !== undefined) {
|
||||||
|
itemStaticStyle[property] = this.staticStyle.style[property];
|
||||||
|
}
|
||||||
|
if (Object.keys(itemStaticStyle).length <= 0) {
|
||||||
|
itemStaticStyle = undefined;
|
||||||
|
}
|
||||||
|
domainObjectStyles[item.id] = { staticStyle: { style: itemStaticStyle } };
|
||||||
|
} else {
|
||||||
|
objectStyle.styles.forEach((conditionalStyle, index) => {
|
||||||
|
let style = {};
|
||||||
|
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
|
||||||
|
style[key] = conditionalStyle.style[key];
|
||||||
|
});
|
||||||
|
itemConditionalStyle.styles.push({
|
||||||
|
...conditionalStyle,
|
||||||
|
style
|
||||||
|
});
|
||||||
|
});
|
||||||
|
domainObjectStyles[item.id] = {
|
||||||
|
...domainObjectStyles[item.id],
|
||||||
|
...objectStyle,
|
||||||
|
...itemConditionalStyle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
domainObjectStyles = {
|
||||||
|
...domainObjectStyles,
|
||||||
|
...objectStyle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObjectStyles;
|
||||||
|
},
|
||||||
|
applySelectedConditionStyle(conditionId) {
|
||||||
|
this.selectedConditionId = conditionId;
|
||||||
|
this.getAndPersistStyles();
|
||||||
|
},
|
||||||
|
persist(domainObject, style) {
|
||||||
|
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -23,6 +23,7 @@
|
|||||||
import TelemetryCriterion from './TelemetryCriterion';
|
import TelemetryCriterion from './TelemetryCriterion';
|
||||||
import { evaluateResults } from "../utils/evaluator";
|
import { evaluateResults } from "../utils/evaluator";
|
||||||
import { getLatestTimestamp } from '../utils/time';
|
import { getLatestTimestamp } from '../utils/time';
|
||||||
|
import { getOperatorText } from "@/plugins/condition/utils/operations";
|
||||||
|
|
||||||
export default class AllTelemetryCriterion extends TelemetryCriterion {
|
export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
|||||||
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetryObjects(telemetryObjects) {
|
||||||
this.telemetryObjects = { ...telemetryObjects };
|
this.telemetryObjects = { ...telemetryObjects };
|
||||||
this.removeTelemetryDataCache();
|
this.removeTelemetryDataCache();
|
||||||
}
|
}
|
||||||
@ -159,6 +160,25 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDescription() {
|
||||||
|
const telemetryDescription = this.telemetry === 'all' ? 'all telemetry' : 'any telemetry';
|
||||||
|
let metadataValue = this.metadata;
|
||||||
|
let inputValue = this.input;
|
||||||
|
if (this.metadata) {
|
||||||
|
const telemetryObjects = Object.values(this.telemetryObjects);
|
||||||
|
for (let i=0; i < telemetryObjects.length; i++) {
|
||||||
|
const telemetryObject = telemetryObjects[i];
|
||||||
|
const metadataObject = this.getMetaDataObject(telemetryObject, this.metadata);
|
||||||
|
if (metadataObject) {
|
||||||
|
metadataValue = this.getMetadataValueFromMetaData(metadataObject) || this.metadata;
|
||||||
|
inputValue = this.getInputValueFromMetaData(metadataObject, this.input) || this.input;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${telemetryDescription} ${metadataValue} ${getOperatorText(this.operation, inputValue)}`;
|
||||||
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
delete this.telemetryObjects;
|
delete this.telemetryObjects;
|
||||||
delete this.telemetryDataCache;
|
delete this.telemetryDataCache;
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
import { OPERATIONS } from '../utils/operations';
|
import { OPERATIONS, getOperatorText } from '../utils/operations';
|
||||||
|
|
||||||
export default class TelemetryCriterion extends EventEmitter {
|
export default class TelemetryCriterion extends EventEmitter {
|
||||||
|
|
||||||
@ -49,15 +49,15 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
|
|
||||||
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
|
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
|
||||||
|
this.updateTelemetryObjects(this.telemetryDomainObjectDefinition.telemetryObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
isValid() {
|
||||||
return this.telemetryObject && this.metadata && this.operation;
|
return this.telemetryObject && this.metadata && this.operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetryObjects(telemetryObjects) {
|
||||||
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +153,51 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMetaDataObject(telemetryObject, metadata) {
|
||||||
|
let metadataObject;
|
||||||
|
if (metadata) {
|
||||||
|
const telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
|
metadataObject = telemetryMetadata.valueMetadatas.find((valueMetadata) => valueMetadata.key === metadata);
|
||||||
|
}
|
||||||
|
return metadataObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputValueFromMetaData(metadataObject, input) {
|
||||||
|
let inputValue;
|
||||||
|
if (metadataObject) {
|
||||||
|
if(metadataObject.enumerations && input.length) {
|
||||||
|
const enumeration = metadataObject.enumerations[input[0]];
|
||||||
|
if (enumeration !== undefined && enumeration.string) {
|
||||||
|
inputValue = [enumeration.string];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMetadataValueFromMetaData(metadataObject) {
|
||||||
|
let metadataValue;
|
||||||
|
if (metadataObject) {
|
||||||
|
if (metadataObject.name) {
|
||||||
|
metadataValue = metadataObject.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metadataValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDescription(criterion, index) {
|
||||||
|
let description;
|
||||||
|
if (!this.telemetry || !this.telemetryObject || (this.telemetryObject.type === 'unknown')) {
|
||||||
|
description = `Unknown ${this.metadata} ${getOperatorText(this.operation, this.input)}`;
|
||||||
|
} else {
|
||||||
|
const metadataObject = this.getMetaDataObject(this.telemetryObject, this.metadata);
|
||||||
|
const metadataValue = this.getMetadataValueFromMetaData(metadataObject) || this.metadata;
|
||||||
|
const inputValue = this.getInputValueFromMetaData(metadataObject, this.input) || this.input;
|
||||||
|
description = `${this.telemetryObject.name} ${metadataValue} ${getOperatorText(this.operation, inputValue)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
delete this.telemetryObject;
|
delete this.telemetryObject;
|
||||||
|
@ -83,7 +83,7 @@ describe("The telemetry criterion", function () {
|
|||||||
operation: 'textContains',
|
operation: 'textContains',
|
||||||
metadata: 'value',
|
metadata: 'value',
|
||||||
input: ['Hell'],
|
input: ['Hell'],
|
||||||
telemetryObject: testTelemetryObject
|
telemetryObjects: {[testTelemetryObject.identifier.key]: testTelemetryObject}
|
||||||
};
|
};
|
||||||
|
|
||||||
mockListener = jasmine.createSpy('listener');
|
mockListener = jasmine.createSpy('listener');
|
||||||
@ -109,13 +109,4 @@ describe("The telemetry criterion", function () {
|
|||||||
});
|
});
|
||||||
expect(telemetryCriterion.result).toBeTrue();
|
expect(telemetryCriterion.result).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
// it("does not return a result on new data from irrelavant telemetry providers", function () {
|
|
||||||
// telemetryCriterion.getResult({
|
|
||||||
// value: 'Hello',
|
|
||||||
// utc: 'Hi',
|
|
||||||
// id: '1234'
|
|
||||||
// });
|
|
||||||
// expect(telemetryCriterion.result).toBeFalse();
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
@ -20,25 +20,28 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { createOpenMct } from "testTools";
|
import { createOpenMct, resetApplicationState } from "utils/testing";
|
||||||
import ConditionPlugin from "./plugin";
|
import ConditionPlugin from "./plugin";
|
||||||
|
import StylesView from "./components/inspector/StylesView.vue";
|
||||||
let openmct = createOpenMct();
|
import Vue from 'vue';
|
||||||
openmct.install(new ConditionPlugin());
|
import {getApplicableStylesForItem} from "./utils/styleUtils";
|
||||||
|
|
||||||
let conditionSetDefinition;
|
|
||||||
let mockConditionSetDomainObject;
|
|
||||||
let element;
|
|
||||||
let child;
|
|
||||||
|
|
||||||
describe('the plugin', function () {
|
describe('the plugin', function () {
|
||||||
|
let conditionSetDefinition;
|
||||||
|
let mockConditionSetDomainObject;
|
||||||
|
let element;
|
||||||
|
let child;
|
||||||
|
let openmct;
|
||||||
|
|
||||||
beforeAll((done) => {
|
beforeAll(() => {
|
||||||
|
resetApplicationState(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct();
|
||||||
|
openmct.install(new ConditionPlugin());
|
||||||
|
|
||||||
conditionSetDefinition = openmct.types.get('conditionSet').definition;
|
conditionSetDefinition = openmct.types.get('conditionSet').definition;
|
||||||
const appHolder = document.createElement('div');
|
|
||||||
appHolder.style.width = '640px';
|
|
||||||
appHolder.style.height = '480px';
|
|
||||||
|
|
||||||
element = document.createElement('div');
|
element = document.createElement('div');
|
||||||
child = document.createElement('div');
|
child = document.createElement('div');
|
||||||
@ -55,7 +58,11 @@ describe('the plugin', function () {
|
|||||||
conditionSetDefinition.initialize(mockConditionSetDomainObject);
|
conditionSetDefinition.initialize(mockConditionSetDomainObject);
|
||||||
|
|
||||||
openmct.on('start', done);
|
openmct.on('start', done);
|
||||||
openmct.start(appHolder);
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
resetApplicationState(openmct);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mockConditionSetObject = {
|
let mockConditionSetObject = {
|
||||||
@ -94,4 +101,259 @@ describe('the plugin', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('the condition set usage for multiple display layout items', () => {
|
||||||
|
let displayLayoutItem;
|
||||||
|
let lineLayoutItem;
|
||||||
|
let boxLayoutItem;
|
||||||
|
let selection;
|
||||||
|
let component;
|
||||||
|
let styleViewComponentObject;
|
||||||
|
const conditionSetDomainObject = {
|
||||||
|
"configuration":{
|
||||||
|
"conditionTestData":[
|
||||||
|
{
|
||||||
|
"telemetry":"",
|
||||||
|
"metadata":"",
|
||||||
|
"input":""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditionCollection":[
|
||||||
|
{
|
||||||
|
"id":"39584410-cbf9-499e-96dc-76f27e69885d",
|
||||||
|
"configuration":{
|
||||||
|
"name":"Unnamed Condition",
|
||||||
|
"output":"Sine > 0",
|
||||||
|
"trigger":"all",
|
||||||
|
"criteria":[
|
||||||
|
{
|
||||||
|
"id":"85fbb2f7-7595-42bd-9767-a932266c5225",
|
||||||
|
"telemetry":{
|
||||||
|
"namespace":"",
|
||||||
|
"key":"be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||||
|
},
|
||||||
|
"operation":"greaterThan",
|
||||||
|
"input":[
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"metadata":"sin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"35400132-63b0-425c-ac30-8197df7d5862",
|
||||||
|
"telemetry":"any",
|
||||||
|
"operation":"enumValueIs",
|
||||||
|
"input":[
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"metadata":"state"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary":"Match if all criteria are met: Sine Wave Generator Sine > 0 and any telemetry State is OFF "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"isDefault":true,
|
||||||
|
"id":"2532d90a-e0d6-4935-b546-3123522da2de",
|
||||||
|
"configuration":{
|
||||||
|
"name":"Default",
|
||||||
|
"output":"Default",
|
||||||
|
"trigger":"all",
|
||||||
|
"criteria":[
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary":""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"composition":[
|
||||||
|
{
|
||||||
|
"namespace":"",
|
||||||
|
"key":"be0ba97f-b510-4f40-a18d-4ff121d5ea1a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"namespace":"",
|
||||||
|
"key":"077ffa67-e78f-4e99-80e0-522ac33a3888"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"telemetry":{
|
||||||
|
},
|
||||||
|
"name":"Condition Set",
|
||||||
|
"type":"conditionSet",
|
||||||
|
"identifier":{
|
||||||
|
"namespace":"",
|
||||||
|
"key":"863012c1-f6ca-4ab0-aed7-fd43d5e4cd12"
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
const staticStyle = {
|
||||||
|
"style":{
|
||||||
|
"backgroundColor":"#717171",
|
||||||
|
"border":"1px solid #00ffff"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const conditionalStyle = {
|
||||||
|
"conditionId":"39584410-cbf9-499e-96dc-76f27e69885d",
|
||||||
|
"style":{
|
||||||
|
"isStyleInvisible":"",
|
||||||
|
"backgroundColor":"#717171",
|
||||||
|
"border":"1px solid #ffff00"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
displayLayoutItem = {
|
||||||
|
"composition":[
|
||||||
|
],
|
||||||
|
"configuration":{
|
||||||
|
"items":[
|
||||||
|
{
|
||||||
|
"fill":"#717171",
|
||||||
|
"stroke":"",
|
||||||
|
"x":1,
|
||||||
|
"y":1,
|
||||||
|
"width":10,
|
||||||
|
"height":5,
|
||||||
|
"type":"box-view",
|
||||||
|
"id":"89b88746-d325-487b-aec4-11b79afff9e8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x":18,
|
||||||
|
"y":9,
|
||||||
|
"x2":23,
|
||||||
|
"y2":4,
|
||||||
|
"stroke":"#717171",
|
||||||
|
"type":"line-view",
|
||||||
|
"id":"57d49a28-7863-43bd-9593-6570758916f0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"layoutGrid":[
|
||||||
|
10,
|
||||||
|
10
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name":"Display Layout",
|
||||||
|
"type":"layout",
|
||||||
|
"identifier":{
|
||||||
|
"namespace":"",
|
||||||
|
"key":"c5e636c1-6771-4c9c-b933-8665cab189b3"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lineLayoutItem = {
|
||||||
|
"x":18,
|
||||||
|
"y":9,
|
||||||
|
"x2":23,
|
||||||
|
"y2":4,
|
||||||
|
"stroke":"#717171",
|
||||||
|
"type":"line-view",
|
||||||
|
"id":"57d49a28-7863-43bd-9593-6570758916f0"
|
||||||
|
};
|
||||||
|
boxLayoutItem = {
|
||||||
|
"fill": "#717171",
|
||||||
|
"stroke": "",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"width": 10,
|
||||||
|
"height": 5,
|
||||||
|
"type": "box-view",
|
||||||
|
"id": "89b88746-d325-487b-aec4-11b79afff9e8"
|
||||||
|
};
|
||||||
|
selection = [
|
||||||
|
[{
|
||||||
|
context: {
|
||||||
|
"layoutItem": lineLayoutItem,
|
||||||
|
"index":1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
"item": displayLayoutItem,
|
||||||
|
"supportsMultiSelect":true
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
context: {
|
||||||
|
"layoutItem": boxLayoutItem,
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
item: displayLayoutItem,
|
||||||
|
"supportsMultiSelect":true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
let viewContainer = document.createElement('div');
|
||||||
|
child.append(viewContainer);
|
||||||
|
component = new Vue({
|
||||||
|
provide: {
|
||||||
|
openmct: openmct,
|
||||||
|
selection: selection
|
||||||
|
},
|
||||||
|
el: viewContainer,
|
||||||
|
components: {
|
||||||
|
StylesView
|
||||||
|
},
|
||||||
|
template: '<styles-view/>'
|
||||||
|
});
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
styleViewComponentObject = component.$root.$children[0];
|
||||||
|
styleViewComponentObject.setEditState(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes the items in the view', () => {
|
||||||
|
expect(styleViewComponentObject.items.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes conditional styles', () => {
|
||||||
|
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
styleViewComponentObject.conditionalStyles = [];
|
||||||
|
styleViewComponentObject.initializeConditionalStyles();
|
||||||
|
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates applicable conditional styles', () => {
|
||||||
|
styleViewComponentObject.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
styleViewComponentObject.conditionalStyles = [];
|
||||||
|
styleViewComponentObject.initializeConditionalStyles();
|
||||||
|
expect(styleViewComponentObject.conditionalStyles.length).toBe(2);
|
||||||
|
styleViewComponentObject.updateConditionalStyle(conditionalStyle, 'border');
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
|
||||||
|
[boxLayoutItem, lineLayoutItem].forEach((item) => {
|
||||||
|
const itemStyles = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].styles;
|
||||||
|
expect(itemStyles.length).toBe(2);
|
||||||
|
const foundStyle = itemStyles.find((style) => {
|
||||||
|
return style.conditionId === conditionalStyle.conditionId;
|
||||||
|
});
|
||||||
|
expect(foundStyle).toBeDefined();
|
||||||
|
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
||||||
|
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
||||||
|
Object.keys(foundStyle.style).forEach((key) => {
|
||||||
|
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
||||||
|
expect(foundStyle.style[key]).toEqual(conditionalStyle.style[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates applicable static styles', () => {
|
||||||
|
styleViewComponentObject.updateStaticStyle(staticStyle, 'border');
|
||||||
|
return Vue.nextTick().then(() => {
|
||||||
|
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
|
||||||
|
[boxLayoutItem, lineLayoutItem].forEach((item) => {
|
||||||
|
const itemStyle = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].staticStyle;
|
||||||
|
expect(itemStyle).toBeDefined();
|
||||||
|
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);
|
||||||
|
const applicableStylesKeys = Object.keys(applicableStyles).concat(['isStyleInvisible']);
|
||||||
|
Object.keys(itemStyle.style).forEach((key) => {
|
||||||
|
expect(applicableStylesKeys.indexOf(key)).toBeGreaterThan(-1);
|
||||||
|
expect(itemStyle.style[key]).toEqual(staticStyle.style[key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -28,10 +28,17 @@ export const TRIGGER = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TRIGGER_LABEL = {
|
export const TRIGGER_LABEL = {
|
||||||
'any': 'when any criteria are met',
|
'any': 'any criteria are met',
|
||||||
'all': 'when all criteria are met',
|
'all': 'all criteria are met',
|
||||||
'not': 'when no criteria are met',
|
'not': 'no criteria are met',
|
||||||
'xor': 'when only one criteria is met'
|
'xor': 'only one criterion is met'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TRIGGER_CONJUNCTION = {
|
||||||
|
'any': 'or',
|
||||||
|
'all': 'and',
|
||||||
|
'not': 'and',
|
||||||
|
'xor': 'or'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const STYLE_CONSTANTS = {
|
export const STYLE_CONSTANTS = {
|
||||||
|
@ -35,7 +35,7 @@ export const evaluateResults = (results, trigger) => {
|
|||||||
|
|
||||||
function matchAll(results) {
|
function matchAll(results) {
|
||||||
for (const result of results) {
|
for (const result of results) {
|
||||||
if (!result) {
|
if (result !== true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ function matchAll(results) {
|
|||||||
|
|
||||||
function matchAny(results) {
|
function matchAny(results) {
|
||||||
for (const result of results) {
|
for (const result of results) {
|
||||||
if (result) {
|
if (result === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ function matchAny(results) {
|
|||||||
function matchExact(results, target) {
|
function matchExact(results, target) {
|
||||||
let matches = 0;
|
let matches = 0;
|
||||||
for (const result of results) {
|
for (const result of results) {
|
||||||
if (result) {
|
if (result === true) {
|
||||||
matches++;
|
matches++;
|
||||||
}
|
}
|
||||||
if (matches > target) {
|
if (matches > target) {
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
* 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)));
|
||||||
@ -252,12 +250,12 @@ export const OPERATIONS = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'valueIs',
|
name: 'isOneOf',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
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 === _.trim(value.toString()));
|
return values.some((value) => lhsValue === value.toString().trim());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -269,12 +267,12 @@ export const OPERATIONS = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'valueIsNot',
|
name: 'isNotOneOf',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
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 === _.trim(value.toString()));
|
const found = values.some((value) => lhsValue === value.toString().trim());
|
||||||
return !found;
|
return !found;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -292,3 +290,8 @@ export const INPUT_TYPES = {
|
|||||||
'string': 'text',
|
'string': 'text',
|
||||||
'number': 'number'
|
'number': 'number'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getOperatorText = (operationName, values) => {
|
||||||
|
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
||||||
|
return found ? found.getDescription(values) : '';
|
||||||
|
};
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { OPERATIONS } from "./operations";
|
import { OPERATIONS } from "./operations";
|
||||||
let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIs');
|
let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'isOneOf');
|
||||||
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot');
|
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'isNotOneOf');
|
||||||
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
|
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
|
||||||
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
|
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
|
||||||
let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs');
|
let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs');
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
* 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 isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
const NONE_VALUE = '__no_value';
|
const NONE_VALUE = '__no_value';
|
||||||
|
|
||||||
const styleProps = {
|
const styleProps = {
|
||||||
@ -122,12 +124,25 @@ export const getConditionalStyleForItem = (domainObject, id) => {
|
|||||||
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
|
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
|
||||||
return domainObjectStyles[id].styles;
|
return domainObjectStyles[id].styles;
|
||||||
}
|
}
|
||||||
} else if (domainObjectStyles.staticStyle) {
|
} else if (domainObjectStyles.conditionSetIdentifier) {
|
||||||
return domainObjectStyles.styles;
|
return domainObjectStyles.styles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getConditionSetIdentifierForItem = (domainObject, id) => {
|
||||||
|
let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
|
||||||
|
if (domainObjectStyles) {
|
||||||
|
if (id) {
|
||||||
|
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
|
||||||
|
return domainObjectStyles[id].conditionSetIdentifier;
|
||||||
|
}
|
||||||
|
} else if (domainObjectStyles.conditionSetIdentifier) {
|
||||||
|
return domainObjectStyles.conditionSetIdentifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//Returns either existing static styles or uses SVG defaults if available
|
//Returns either existing static styles or uses SVG defaults if available
|
||||||
export const getApplicableStylesForItem = (domainObject, item) => {
|
export const getApplicableStylesForItem = (domainObject, item) => {
|
||||||
const type = item && item.type;
|
const type = item && item.type;
|
||||||
@ -154,7 +169,7 @@ export const getApplicableStylesForItem = (domainObject, item) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getStylesWithoutNoneValue = (style) => {
|
export const getStylesWithoutNoneValue = (style) => {
|
||||||
if (_.isEmpty(style) || !style) {
|
if (isEmpty(style) || !style) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let styleObj = {};
|
let styleObj = {};
|
||||||
|
@ -29,11 +29,15 @@ define([
|
|||||||
function isTelemetryObject(selectionPath) {
|
function isTelemetryObject(selectionPath) {
|
||||||
let selectedObject = selectionPath[0].context.item;
|
let selectedObject = selectionPath[0].context.item;
|
||||||
let parentObject = selectionPath[1].context.item;
|
let parentObject = selectionPath[1].context.item;
|
||||||
|
let selectedLayoutItem = selectionPath[0].context.layoutItem;
|
||||||
|
|
||||||
return parentObject &&
|
return parentObject &&
|
||||||
parentObject.type === 'layout' &&
|
parentObject.type === 'layout' &&
|
||||||
selectedObject &&
|
selectedObject &&
|
||||||
|
selectedLayoutItem &&
|
||||||
|
selectedLayoutItem.type === 'telemetry-view' &&
|
||||||
openmct.telemetry.isTelemetryObject(selectedObject) &&
|
openmct.telemetry.isTelemetryObject(selectedObject) &&
|
||||||
!options.showAsView.includes(selectedObject.type)
|
!options.showAsView.includes(selectedObject.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, 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.
|
||||||
*
|
*
|
||||||
@ -41,38 +41,84 @@ define(['lodash'], function (_) {
|
|||||||
},
|
},
|
||||||
toolbar: function (selectedObjects) {
|
toolbar: function (selectedObjects) {
|
||||||
const DIALOG_FORM = {
|
const DIALOG_FORM = {
|
||||||
'text': {
|
'text': {
|
||||||
name: "Text Element Properties",
|
name: "Text Element Properties",
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
rows: [
|
rows: [
|
||||||
{
|
{
|
||||||
key: "text",
|
key: "text",
|
||||||
control: "textfield",
|
control: "textfield",
|
||||||
name: "Text",
|
name: "Text",
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
'image': {
|
||||||
|
name: "Image Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "url",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Image URL",
|
||||||
|
"cssClass": "l-input-lg",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'image': {
|
viewTypes = {
|
||||||
name: "Image Properties",
|
'telemetry-view': {
|
||||||
sections: [
|
value: 'telemetry-view',
|
||||||
{
|
name: 'Alphanumeric',
|
||||||
rows: [
|
class: 'icon-alphanumeric'
|
||||||
{
|
},
|
||||||
key: "url",
|
'telemetry.plot.overlay': {
|
||||||
control: "textfield",
|
value: 'telemetry.plot.overlay',
|
||||||
name: "Image URL",
|
name: 'Overlay Plot',
|
||||||
"cssClass": "l-input-lg",
|
class: "icon-plot-overlay"
|
||||||
required: true
|
},
|
||||||
}
|
'telemetry.plot.stacked': {
|
||||||
]
|
value: "telemetry.plot.stacked",
|
||||||
}
|
name: "Stacked Plot",
|
||||||
|
class: "icon-plot-stacked"
|
||||||
|
},
|
||||||
|
'table': {
|
||||||
|
value: 'table',
|
||||||
|
name: 'Table',
|
||||||
|
class: 'icon-tabular-realtime'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applicableViews = {
|
||||||
|
'telemetry-view': [
|
||||||
|
viewTypes['telemetry.plot.overlay'],
|
||||||
|
viewTypes.table
|
||||||
|
],
|
||||||
|
'telemetry.plot.overlay': [
|
||||||
|
viewTypes['telemetry.plot.stacked'],
|
||||||
|
viewTypes.table,
|
||||||
|
viewTypes['telemetry-view']
|
||||||
|
],
|
||||||
|
'table': [
|
||||||
|
viewTypes['telemetry.plot.overlay'],
|
||||||
|
viewTypes['telemetry.plot.stacked'],
|
||||||
|
viewTypes['telemetry-view']
|
||||||
|
],
|
||||||
|
'telemetry-view-multi': [
|
||||||
|
viewTypes['telemetry.plot.overlay'],
|
||||||
|
viewTypes['telemetry.plot.stacked'],
|
||||||
|
viewTypes.table
|
||||||
|
],
|
||||||
|
'telemetry.plot.overlay-multi': [
|
||||||
|
viewTypes['telemetry.plot.stacked']
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
function getUserInput(form) {
|
function getUserInput(form) {
|
||||||
return openmct.$injector.get('dialogService').getUserInput(form, {});
|
return openmct.$injector.get('dialogService').getUserInput(form, {});
|
||||||
@ -415,6 +461,100 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDuplicateButton(selectedParent, selectionPath, selection) {
|
||||||
|
return {
|
||||||
|
control: "button",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
icon: "icon-duplicate",
|
||||||
|
title: "Duplicate the selected object",
|
||||||
|
method: function () {
|
||||||
|
let duplicateItem = selectionPath[1].context.duplicateItem;
|
||||||
|
|
||||||
|
duplicateItem(selection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPropertyFromPath(object, path) {
|
||||||
|
let splitPath = path.split('.'),
|
||||||
|
property = Object.assign({}, object);
|
||||||
|
|
||||||
|
while (splitPath.length && property) {
|
||||||
|
property = property[splitPath.shift()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
function areAllViews(type, path, selection) {
|
||||||
|
let allTelemetry = true;
|
||||||
|
|
||||||
|
selection.forEach(selectedItem => {
|
||||||
|
let selectedItemContext = selectedItem[0].context;
|
||||||
|
|
||||||
|
if (getPropertyFromPath(selectedItemContext, path) !== type) {
|
||||||
|
allTelemetry = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return allTelemetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewSwitcherMenu(selectedParent, selectionPath, selection) {
|
||||||
|
if (selection.length === 1) {
|
||||||
|
let displayLayoutContext = selectionPath[1].context,
|
||||||
|
selectedItemContext = selectionPath[0].context,
|
||||||
|
selectedItemType = selectedItemContext.item.type;
|
||||||
|
|
||||||
|
if (selectedItemContext.layoutItem.type === 'telemetry-view') {
|
||||||
|
selectedItemType = 'telemetry-view';
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewOptions = applicableViews[selectedItemType];
|
||||||
|
|
||||||
|
if (viewOptions) {
|
||||||
|
return {
|
||||||
|
control: "menu",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
icon: "icon-object",
|
||||||
|
title: "Switch the way this telemetry is displayed",
|
||||||
|
options: viewOptions,
|
||||||
|
method: function (option) {
|
||||||
|
displayLayoutContext.switchViewType(selectedItemContext, option.value, selection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (selection.length > 1) {
|
||||||
|
if (areAllViews('telemetry-view', 'layoutItem.type', selection)) {
|
||||||
|
let displayLayoutContext = selectionPath[1].context;
|
||||||
|
|
||||||
|
return {
|
||||||
|
control: "menu",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
icon: "icon-object",
|
||||||
|
title: "Merge into a telemetry table or plot",
|
||||||
|
options: applicableViews['telemetry-view-multi'],
|
||||||
|
method: function (option) {
|
||||||
|
displayLayoutContext.mergeMultipleTelemetryViews(selection, option.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (areAllViews('telemetry.plot.overlay', 'item.type', selection)) {
|
||||||
|
let displayLayoutContext = selectionPath[1].context;
|
||||||
|
|
||||||
|
return {
|
||||||
|
control: "menu",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
icon: "icon-object",
|
||||||
|
title: "Merge into a stacked plot",
|
||||||
|
options: applicableViews['telemetry.plot.overlay-multi'],
|
||||||
|
method: function (option) {
|
||||||
|
displayLayoutContext.mergeMultipleOverlayPlots(selection, option.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getSeparator() {
|
function getSeparator() {
|
||||||
return {
|
return {
|
||||||
control: "separator"
|
control: "separator"
|
||||||
@ -435,12 +575,14 @@ define(['lodash'], function (_) {
|
|||||||
'add-menu': [],
|
'add-menu': [],
|
||||||
'text': [],
|
'text': [],
|
||||||
'url': [],
|
'url': [],
|
||||||
|
'viewSwitcher': [],
|
||||||
'toggle-frame': [],
|
'toggle-frame': [],
|
||||||
'display-mode': [],
|
'display-mode': [],
|
||||||
'telemetry-value': [],
|
'telemetry-value': [],
|
||||||
'style': [],
|
'style': [],
|
||||||
'text-style': [],
|
'text-style': [],
|
||||||
'position': [],
|
'position': [],
|
||||||
|
'duplicate': [],
|
||||||
'remove': []
|
'remove': []
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -471,6 +613,9 @@ define(['lodash'], function (_) {
|
|||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
if (toolbar.viewSwitcher.length === 0) {
|
||||||
|
toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)];
|
||||||
|
}
|
||||||
} else if (layoutItem.type === 'telemetry-view') {
|
} else if (layoutItem.type === 'telemetry-view') {
|
||||||
if (toolbar['display-mode'].length === 0) {
|
if (toolbar['display-mode'].length === 0) {
|
||||||
toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)];
|
toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)];
|
||||||
@ -495,6 +640,9 @@ define(['lodash'], function (_) {
|
|||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
if (toolbar.viewSwitcher.length === 0) {
|
||||||
|
toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)];
|
||||||
|
}
|
||||||
} else if (layoutItem.type === 'text-view') {
|
} else if (layoutItem.type === 'text-view') {
|
||||||
if (toolbar['text-style'].length === 0) {
|
if (toolbar['text-style'].length === 0) {
|
||||||
toolbar['text-style'] = [
|
toolbar['text-style'] = [
|
||||||
@ -556,6 +704,9 @@ define(['lodash'], function (_) {
|
|||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(toolbar.duplicate.length === 0) {
|
||||||
|
toolbar.duplicate = [getDuplicateButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let toolbarArray = Object.values(toolbar);
|
let toolbarArray = Object.values(toolbar);
|
||||||
|
@ -91,6 +91,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
|
},
|
||||||
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
:is="item.type"
|
:is="item.type"
|
||||||
v-for="(item, index) in layoutItems"
|
v-for="(item, index) in layoutItems"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
:ref="`layout-item-${item.id}`"
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:init-select="initSelectIndex === index"
|
:init-select="initSelectIndex === index"
|
||||||
@ -68,7 +69,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
|
||||||
import SubobjectView from './SubobjectView.vue'
|
import SubobjectView from './SubobjectView.vue'
|
||||||
import TelemetryView from './TelemetryView.vue'
|
import TelemetryView from './TelemetryView.vue'
|
||||||
import BoxView from './BoxView.vue'
|
import BoxView from './BoxView.vue'
|
||||||
@ -76,6 +76,7 @@ import TextView from './TextView.vue'
|
|||||||
import LineView from './LineView.vue'
|
import LineView from './LineView.vue'
|
||||||
import ImageView from './ImageView.vue'
|
import ImageView from './ImageView.vue'
|
||||||
import EditMarquee from './EditMarquee.vue'
|
import EditMarquee from './EditMarquee.vue'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
const ITEM_TYPE_VIEW_MAP = {
|
const ITEM_TYPE_VIEW_MAP = {
|
||||||
'subobject-view': SubobjectView,
|
'subobject-view': SubobjectView,
|
||||||
@ -92,6 +93,7 @@ const ORDERS = {
|
|||||||
bottom: Number.NEGATIVE_INFINITY
|
bottom: Number.NEGATIVE_INFINITY
|
||||||
};
|
};
|
||||||
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
||||||
|
const DUPLICATE_OFFSET = 3;
|
||||||
|
|
||||||
let components = ITEM_TYPE_VIEW_MAP;
|
let components = ITEM_TYPE_VIEW_MAP;
|
||||||
components['edit-marquee'] = EditMarquee;
|
components['edit-marquee'] = EditMarquee;
|
||||||
@ -301,9 +303,9 @@ export default {
|
|||||||
if (this.isTelemetry(domainObject)) {
|
if (this.isTelemetry(domainObject)) {
|
||||||
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
|
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
|
||||||
} else {
|
} else {
|
||||||
let identifier = this.openmct.objects.makeKeyString(domainObject.identifier);
|
let keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
if (!this.objectViewMap[identifier]) {
|
if (!this.objectViewMap[keyString]) {
|
||||||
this.addItem('subobject-view', domainObject, droppedObjectPosition);
|
this.addItem('subobject-view', domainObject, droppedObjectPosition);
|
||||||
} else {
|
} else {
|
||||||
let prompt = this.openmct.overlays.dialog({
|
let prompt = this.openmct.overlays.dialog({
|
||||||
@ -365,7 +367,8 @@ export default {
|
|||||||
let count = this.telemetryViewMap[keyString] || 0;
|
let count = this.telemetryViewMap[keyString] || 0;
|
||||||
this.telemetryViewMap[keyString] = ++count;
|
this.telemetryViewMap[keyString] = ++count;
|
||||||
} else if (item.type === "subobject-view") {
|
} else if (item.type === "subobject-view") {
|
||||||
this.objectViewMap[keyString] = true;
|
let count = this.objectViewMap[keyString] || 0;
|
||||||
|
this.objectViewMap[keyString] = ++count;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeItem(selectedItems) {
|
removeItem(selectedItems) {
|
||||||
@ -384,17 +387,25 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
let keyString = this.openmct.objects.makeKeyString(item.identifier),
|
||||||
|
telemetryViewCount = this.telemetryViewMap[keyString],
|
||||||
|
objectViewCount = this.objectViewMap[keyString];
|
||||||
|
|
||||||
if (item.type === 'telemetry-view') {
|
if (item.type === 'telemetry-view') {
|
||||||
let count = --this.telemetryViewMap[keyString];
|
telemetryViewCount = --this.telemetryViewMap[keyString];
|
||||||
|
|
||||||
if (count === 0) {
|
if (telemetryViewCount === 0) {
|
||||||
delete this.telemetryViewMap[keyString];
|
delete this.telemetryViewMap[keyString];
|
||||||
this.removeFromComposition(keyString);
|
|
||||||
}
|
}
|
||||||
} else if (item.type === 'subobject-view') {
|
} else if (item.type === 'subobject-view') {
|
||||||
delete this.objectViewMap[keyString];
|
objectViewCount = --this.objectViewMap[keyString];
|
||||||
|
|
||||||
|
if (objectViewCount === 0) {
|
||||||
|
delete this.objectViewMap[keyString];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!telemetryViewCount && !objectViewCount) {
|
||||||
this.removeFromComposition(keyString);
|
this.removeFromComposition(keyString);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -411,12 +422,12 @@ export default {
|
|||||||
this.layoutItems.forEach(this.trackItem);
|
this.layoutItems.forEach(this.trackItem);
|
||||||
},
|
},
|
||||||
addChild(child) {
|
addChild(child) {
|
||||||
let identifier = this.openmct.objects.makeKeyString(child.identifier);
|
let keyString = this.openmct.objects.makeKeyString(child.identifier);
|
||||||
if (this.isTelemetry(child)) {
|
if (this.isTelemetry(child)) {
|
||||||
if (!this.telemetryViewMap[identifier]) {
|
if (!this.telemetryViewMap[keyString] && !this.objectViewMap[keyString]) {
|
||||||
this.addItem('telemetry-view', child);
|
this.addItem('telemetry-view', child);
|
||||||
}
|
}
|
||||||
} else if (!this.objectViewMap[identifier]) {
|
} else if (!this.objectViewMap[keyString]) {
|
||||||
this.addItem('subobject-view', child);
|
this.addItem('subobject-view', child);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -512,9 +523,183 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTelemetryFormat(item, format) {
|
updateTelemetryFormat(item, format) {
|
||||||
let index = _.findIndex(this.layoutItems, item);
|
let index = this.layoutItems.findIndex(item);
|
||||||
item.format = format;
|
item.format = format;
|
||||||
this.mutate(`configuration.items[${index}]`, item);
|
this.mutate(`configuration.items[${index}]`, item);
|
||||||
|
},
|
||||||
|
createNewDomainObject(domainObject, composition, viewType, nameExtension, model) {
|
||||||
|
let identifier = {
|
||||||
|
key: uuid(),
|
||||||
|
namespace: domainObject.identifier.namespace
|
||||||
|
},
|
||||||
|
type = this.openmct.types.get(viewType),
|
||||||
|
parentKeyString = this.openmct.objects.makeKeyString(this.internalDomainObject.identifier),
|
||||||
|
objectName = nameExtension ? `${domainObject.name}-${nameExtension}` : domainObject.name,
|
||||||
|
object = {};
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
object = _.cloneDeep(model);
|
||||||
|
} else {
|
||||||
|
object.type = viewType;
|
||||||
|
type.definition.initialize(object);
|
||||||
|
object.composition.push(...composition);
|
||||||
|
}
|
||||||
|
|
||||||
|
object.name = objectName;
|
||||||
|
object.identifier = identifier;
|
||||||
|
object.location = parentKeyString;
|
||||||
|
|
||||||
|
this.openmct.objects.mutate(object, 'persisted', Date.now());
|
||||||
|
|
||||||
|
return object;
|
||||||
|
},
|
||||||
|
convertToTelemetryView(identifier, position) {
|
||||||
|
this.openmct.objects.get(identifier).then((domainObject) => {
|
||||||
|
this.composition.add(domainObject);
|
||||||
|
this.addItem('telemetry-view', domainObject, position);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dispatchMultipleSelection(selectItemsArray) {
|
||||||
|
let event = new MouseEvent('click', {
|
||||||
|
bubbles: true,
|
||||||
|
shiftKey: true,
|
||||||
|
cancelable: true,
|
||||||
|
view: window
|
||||||
|
})
|
||||||
|
|
||||||
|
selectItemsArray.forEach((id) => {
|
||||||
|
let refId = `layout-item-${id}`,
|
||||||
|
component = this.$refs[refId] && this.$refs[refId][0];
|
||||||
|
|
||||||
|
if (component) {
|
||||||
|
component.immediatelySelect = event;
|
||||||
|
component.$el.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
duplicateItem(selectedItems) {
|
||||||
|
let objectStyles = this.internalDomainObject.configuration.objectStyles || {},
|
||||||
|
selectItemsArray = [],
|
||||||
|
newDomainObjectsArray = [];
|
||||||
|
|
||||||
|
selectedItems.forEach(selectedItem => {
|
||||||
|
let layoutItem = selectedItem[0].context.layoutItem,
|
||||||
|
domainObject = selectedItem[0].context.item,
|
||||||
|
layoutItemStyle = objectStyles[layoutItem.id],
|
||||||
|
copy = _.cloneDeep(layoutItem);
|
||||||
|
|
||||||
|
copy.id = uuid();
|
||||||
|
selectItemsArray.push(copy.id);
|
||||||
|
|
||||||
|
let offsetKeys = ['x', 'y'];
|
||||||
|
|
||||||
|
if (copy.type === 'line-view') {
|
||||||
|
offsetKeys = offsetKeys.concat(['x2', 'y2']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy.type === 'subobject-view') {
|
||||||
|
let newDomainObject = this.createNewDomainObject(domainObject, domainObject.composition, domainObject.type, 'duplicate', domainObject);
|
||||||
|
|
||||||
|
newDomainObjectsArray.push(newDomainObject);
|
||||||
|
copy.identifier = newDomainObject.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetKeys.forEach(key => {
|
||||||
|
copy[key] += DUPLICATE_OFFSET
|
||||||
|
});
|
||||||
|
|
||||||
|
if (layoutItemStyle) {
|
||||||
|
objectStyles[copy.id] = layoutItemStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trackItem(copy);
|
||||||
|
this.layoutItems.push(copy);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.openmct.objects.mutate(this.internalDomainObject, "configuration.items", this.layoutItems);
|
||||||
|
this.openmct.objects.mutate(this.internalDomainObject, "configuration.objectStyles", objectStyles);
|
||||||
|
this.$el.click(); //clear selection;
|
||||||
|
|
||||||
|
newDomainObjectsArray.forEach(domainObject => {
|
||||||
|
this.composition.add(domainObject);
|
||||||
|
});
|
||||||
|
this.dispatchMultipleSelection(selectItemsArray);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mergeMultipleTelemetryViews(selection, viewType) {
|
||||||
|
let identifiers = selection.map(selectedItem => {
|
||||||
|
return selectedItem[0].context.layoutItem.identifier;
|
||||||
|
}),
|
||||||
|
firstDomainObject = selection[0][0].context.item,
|
||||||
|
firstLayoutItem = selection[0][0].context.layoutItem,
|
||||||
|
position = [firstLayoutItem.x, firstLayoutItem.y],
|
||||||
|
mockDomainObject = {
|
||||||
|
name: 'Merged Telemetry Views',
|
||||||
|
identifier: firstDomainObject.identifier
|
||||||
|
},
|
||||||
|
newDomainObject = this.createNewDomainObject(mockDomainObject, identifiers, viewType);
|
||||||
|
|
||||||
|
this.composition.add(newDomainObject);
|
||||||
|
this.addItem('subobject-view', newDomainObject, position);
|
||||||
|
this.removeItem(selection);
|
||||||
|
this.initSelectIndex = this.layoutItems.length - 1;
|
||||||
|
},
|
||||||
|
mergeMultipleOverlayPlots(selection, viewType) {
|
||||||
|
let overlayPlots = selection.map(selectedItem => selectedItem[0].context.item),
|
||||||
|
overlayPlotIdentifiers = overlayPlots.map(overlayPlot => overlayPlot.identifier),
|
||||||
|
firstOverlayPlot = overlayPlots[0],
|
||||||
|
firstLayoutItem = selection[0][0].context.layoutItem,
|
||||||
|
position = [firstLayoutItem.x, firstLayoutItem.y],
|
||||||
|
mockDomainObject = {
|
||||||
|
name: 'Merged Overlay Plots',
|
||||||
|
identifier: firstOverlayPlot.identifier
|
||||||
|
},
|
||||||
|
newDomainObject = this.createNewDomainObject(mockDomainObject, overlayPlotIdentifiers, viewType),
|
||||||
|
newDomainObjectKeyString = this.openmct.objects.makeKeyString(newDomainObject.identifier),
|
||||||
|
internalDomainObjectKeyString = this.openmct.objects.makeKeyString(this.internalDomainObject.identifier);
|
||||||
|
|
||||||
|
this.composition.add(newDomainObject);
|
||||||
|
this.addItem('subobject-view', newDomainObject, position);
|
||||||
|
|
||||||
|
overlayPlots.forEach(overlayPlot => {
|
||||||
|
if (overlayPlot.location === internalDomainObjectKeyString) {
|
||||||
|
this.openmct.objects.mutate(overlayPlot, 'location', newDomainObjectKeyString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.removeItem(selection);
|
||||||
|
this.initSelectIndex = this.layoutItems.length - 1;
|
||||||
|
},
|
||||||
|
switchViewType(context, viewType, selection) {
|
||||||
|
let domainObject = context.item,
|
||||||
|
layoutItem = context.layoutItem,
|
||||||
|
position = [layoutItem.x, layoutItem.y],
|
||||||
|
newDomainObject,
|
||||||
|
layoutType = 'subobject-view';
|
||||||
|
|
||||||
|
if (layoutItem.type === 'telemetry-view') {
|
||||||
|
newDomainObject = this.createNewDomainObject(domainObject, [domainObject.identifier], viewType);
|
||||||
|
} else {
|
||||||
|
if (viewType !== 'telemetry-view') {
|
||||||
|
newDomainObject = this.createNewDomainObject(domainObject, domainObject.composition, viewType);
|
||||||
|
} else {
|
||||||
|
domainObject.composition.forEach((identifier , index) => {
|
||||||
|
let positionX = position[0] + (index * DUPLICATE_OFFSET),
|
||||||
|
positionY = position[1] + (index * DUPLICATE_OFFSET);
|
||||||
|
|
||||||
|
this.convertToTelemetryView(identifier, [positionX, positionY]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newDomainObject) {
|
||||||
|
this.composition.add(newDomainObject);
|
||||||
|
this.addItem(layoutType, newDomainObject, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeItem(selection);
|
||||||
|
this.initSelectIndex = this.layoutItems.length - 1; //restore selection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
|
},
|
||||||
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutDrag from './../LayoutDrag'
|
import LayoutDrag from './../LayoutDrag'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
const START_HANDLE_QUADRANTS = {
|
const START_HANDLE_QUADRANTS = {
|
||||||
1: 'c-frame-edit__handle--sw',
|
1: 'c-frame-edit__handle--sw',
|
||||||
@ -193,6 +194,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
|
},
|
||||||
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -61,7 +61,7 @@ function hasFrameByDefault(type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
makeDefinition(openmct, gridSize, domainObject, position, viewKey) {
|
||||||
let defaultDimensions = getDefaultDimensions(gridSize);
|
let defaultDimensions = getDefaultDimensions(gridSize);
|
||||||
position = position || DEFAULT_POSITION;
|
position = position || DEFAULT_POSITION;
|
||||||
|
|
||||||
@ -71,7 +71,8 @@ export default {
|
|||||||
x: position[0],
|
x: position[0],
|
||||||
y: position[1],
|
y: position[1],
|
||||||
identifier: domainObject.identifier,
|
identifier: domainObject.identifier,
|
||||||
hasFrame: hasFrameByDefault(domainObject.type)
|
hasFrame: hasFrameByDefault(domainObject.type),
|
||||||
|
viewKey
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'objectPath'],
|
inject: ['openmct', 'objectPath'],
|
||||||
@ -109,6 +110,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
|
},
|
||||||
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -131,7 +139,8 @@ export default {
|
|||||||
childContext.index = this.index;
|
childContext.index = this.index;
|
||||||
this.context = childContext;
|
this.context = childContext;
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
this.removeSelectable = this.openmct.selection.selectable(
|
||||||
this.$el, this.context, this.initSelect);
|
this.$el, this.context, this.immediatelySelect || this.initSelect);
|
||||||
|
delete this.immediatelySelect;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,10 @@ export default {
|
|||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
},
|
},
|
||||||
item(newItem) {
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.context.layoutItem = newItem;
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -242,7 +246,8 @@ export default {
|
|||||||
updateTelemetryFormat: this.updateTelemetryFormat
|
updateTelemetryFormat: this.updateTelemetryFormat
|
||||||
};
|
};
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
this.removeSelectable = this.openmct.selection.selectable(
|
||||||
this.$el, this.context, this.initSelect);
|
this.$el, this.context, this.immediatelySelect || this.initSelect);
|
||||||
|
delete this.immediatelySelect;
|
||||||
},
|
},
|
||||||
updateTelemetryFormat(format) {
|
updateTelemetryFormat(format) {
|
||||||
this.$emit('formatChanged', this.item, format);
|
this.$emit('formatChanged', this.item, format);
|
||||||
|
@ -91,6 +91,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
this.context.index = newIndex;
|
||||||
|
},
|
||||||
|
item(newItem) {
|
||||||
|
if (!this.context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.layoutItem = newItem;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import Layout from './components/DisplayLayout.vue'
|
import Layout from './components/DisplayLayout.vue'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import objectUtils from '../../api/objects/object-utils.js'
|
import objectUtils from 'objectUtils'
|
||||||
import DisplayLayoutType from './DisplayLayoutType.js'
|
import DisplayLayoutType from './DisplayLayoutType.js'
|
||||||
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
|
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
|
||||||
import AlphaNumericFormatViewProvider from './AlphanumericFormatViewProvider.js'
|
import AlphaNumericFormatViewProvider from './AlphanumericFormatViewProvider.js'
|
||||||
@ -66,8 +66,12 @@ export default function DisplayLayoutPlugin(options) {
|
|||||||
supportsMultiSelect: true,
|
supportsMultiSelect: true,
|
||||||
addElement: component && component.$refs.displayLayout.addElement,
|
addElement: component && component.$refs.displayLayout.addElement,
|
||||||
removeItem: component && component.$refs.displayLayout.removeItem,
|
removeItem: component && component.$refs.displayLayout.removeItem,
|
||||||
orderItem: component && component.$refs.displayLayout.orderItem
|
orderItem: component && component.$refs.displayLayout.orderItem,
|
||||||
}
|
duplicateItem: component && component.$refs.displayLayout.duplicateItem,
|
||||||
|
switchViewType: component && component.$refs.displayLayout.switchViewType,
|
||||||
|
mergeMultipleTelemetryViews: component && component.$refs.displayLayout.mergeMultipleTelemetryViews,
|
||||||
|
mergeMultipleOverlayPlots: component && component.$refs.displayLayout.mergeMultipleOverlayPlots
|
||||||
|
};
|
||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
component.$destroy();
|
component.$destroy();
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import FilterField from './FilterField.vue';
|
import FilterField from './FilterField.vue';
|
||||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -102,7 +103,7 @@ export default {
|
|||||||
hasActiveFilters() {
|
hasActiveFilters() {
|
||||||
// Should be true when the user has entered any filter values.
|
// Should be true when the user has entered any filter values.
|
||||||
return Object.values(this.persistedFilters).some(comparator => {
|
return Object.values(this.persistedFilters).some(comparator => {
|
||||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
return (typeof(comparator) === 'object' && !isEmpty(comparator));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FilterObject from './FilterObject.vue';
|
import FilterObject from './FilterObject.vue';
|
||||||
import GlobalFilters from './GlobalFilters.vue'
|
import GlobalFilters from './GlobalFilters.vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
const FILTER_VIEW_TITLE = 'Filters applied';
|
const FILTER_VIEW_TITLE = 'Filters applied';
|
||||||
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';
|
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import compositionLoader from './composition-loader';
|
import compositionLoader from './composition-loader';
|
||||||
import ListItem from './ListItem.vue';
|
import ListItem from './ListItem.vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {ListItem},
|
components: {ListItem},
|
||||||
|
@ -66,7 +66,6 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
date: '',
|
|
||||||
filters : {
|
filters : {
|
||||||
brightness: 100,
|
brightness: 100,
|
||||||
contrast: 100
|
contrast: 100
|
||||||
@ -78,22 +77,39 @@ export default {
|
|||||||
imageHistory: [],
|
imageHistory: [],
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
isPaused: false,
|
isPaused: false,
|
||||||
|
metadata: {},
|
||||||
requestCount: 0,
|
requestCount: 0,
|
||||||
timeFormat: ''
|
timeFormat: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// set
|
||||||
this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
this.subscribe(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
|
this.imageFormat = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]);
|
||||||
|
// initialize
|
||||||
|
this.timeKey = this.openmct.time.timeSystem().key;
|
||||||
|
this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey));
|
||||||
|
// listen
|
||||||
|
this.openmct.time.on('bounds', this.boundsChange);
|
||||||
|
this.openmct.time.on('timeSystem', this.timeSystemChange);
|
||||||
|
// kickoff
|
||||||
|
this.subscribe();
|
||||||
|
this.requestHistory();
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
this.scrollToRight();
|
this.scrollToRight();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.stopListening();
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
delete this.unsubscribe;
|
||||||
|
}
|
||||||
|
this.openmct.time.off('bounds', this.boundsChange);
|
||||||
|
this.openmct.time.off('timeSystem', this.timeSystemChange);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
datumMatchesMostRecent(datum) {
|
datumIsNotValid(datum) {
|
||||||
if (this.imageHistory.length === 0) {
|
if (this.imageHistory.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -103,7 +119,14 @@ export default {
|
|||||||
const lastHistoryTime = this.timeFormat.format(this.imageHistory.slice(-1)[0]);
|
const lastHistoryTime = this.timeFormat.format(this.imageHistory.slice(-1)[0]);
|
||||||
const lastHistoryURL = this.imageFormat.format(this.imageHistory.slice(-1)[0]);
|
const lastHistoryURL = this.imageFormat.format(this.imageHistory.slice(-1)[0]);
|
||||||
|
|
||||||
return (datumTime === lastHistoryTime) && (datumURL === lastHistoryURL);
|
// datum is not valid if it matches the last datum in history,
|
||||||
|
// or it is before the last datum in the history
|
||||||
|
const datumTimeCheck = this.timeFormat.parse(datum);
|
||||||
|
const historyTimeCheck = this.timeFormat.parse(this.imageHistory.slice(-1)[0]);
|
||||||
|
const matchesLast = (datumTime === lastHistoryTime) && (datumURL === lastHistoryURL);
|
||||||
|
const isStale = datumTimeCheck < historyTimeCheck;
|
||||||
|
|
||||||
|
return matchesLast || isStale;
|
||||||
},
|
},
|
||||||
getImageUrl(datum) {
|
getImageUrl(datum) {
|
||||||
return datum ?
|
return datum ?
|
||||||
@ -147,21 +170,6 @@ export default {
|
|||||||
|
|
||||||
return this.isPaused;
|
return this.isPaused;
|
||||||
},
|
},
|
||||||
requestHistory(bounds) {
|
|
||||||
this.requestCount++;
|
|
||||||
this.imageHistory = [];
|
|
||||||
const requestId = this.requestCount;
|
|
||||||
this.openmct.telemetry
|
|
||||||
.request(this.domainObject, bounds)
|
|
||||||
.then((values = []) => {
|
|
||||||
if (this.requestCount > requestId) {
|
|
||||||
return Promise.resolve('Stale request');
|
|
||||||
}
|
|
||||||
|
|
||||||
values.forEach(this.updateHistory);
|
|
||||||
this.updateValues(values[values.length - 1]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
scrollToRight() {
|
scrollToRight() {
|
||||||
if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) {
|
if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) {
|
||||||
return;
|
return;
|
||||||
@ -188,40 +196,56 @@ export default {
|
|||||||
image.selected = true;
|
image.selected = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stopListening() {
|
boundsChange(bounds, isTick) {
|
||||||
if (this.unsubscribe) {
|
if(!isTick) {
|
||||||
this.unsubscribe();
|
this.requestHistory();
|
||||||
delete this.unsubscribe;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
subscribe(domainObject) {
|
requestHistory() {
|
||||||
this.date = ''
|
let bounds = this.openmct.time.bounds();
|
||||||
this.imageUrl = '';
|
this.requestCount++;
|
||||||
this.openmct.objects.get(this.keystring)
|
const requestId = this.requestCount;
|
||||||
.then((object) => {
|
this.imageHistory = [];
|
||||||
const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.openmct.telemetry
|
||||||
this.timeKey = this.openmct.time.timeSystem().key;
|
.request(this.domainObject, bounds)
|
||||||
this.timeFormat = this.openmct.telemetry.getValueFormatter(metadata.value(this.timeKey));
|
.then((values = []) => {
|
||||||
this.imageFormat = this.openmct.telemetry.getValueFormatter(metadata.valuesForHints(['image'])[0]);
|
if (this.requestCount === requestId) {
|
||||||
this.unsubscribe = this.openmct.telemetry
|
values.forEach(this.updateHistory, false);
|
||||||
.subscribe(this.domainObject, (datum) => {
|
this.updateValues(values[values.length - 1]);
|
||||||
this.updateHistory(datum);
|
}
|
||||||
this.updateValues(datum);
|
});
|
||||||
});
|
},
|
||||||
|
timeSystemChange(system) {
|
||||||
|
// reset timesystem dependent variables
|
||||||
|
this.timeKey = system.key;
|
||||||
|
this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey));
|
||||||
|
},
|
||||||
|
subscribe() {
|
||||||
|
this.unsubscribe = this.openmct.telemetry
|
||||||
|
.subscribe(this.domainObject, (datum) => {
|
||||||
|
let parsedTimestamp = this.timeFormat.parse(datum[this.timeKey]),
|
||||||
|
bounds = this.openmct.time.bounds();
|
||||||
|
|
||||||
this.requestHistory(this.openmct.time.bounds());
|
if(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
|
||||||
|
this.updateHistory(datum);
|
||||||
|
this.updateValues(datum);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
unselectAllImages() {
|
unselectAllImages() {
|
||||||
this.imageHistory.forEach(image => image.selected = false);
|
this.imageHistory.forEach(image => image.selected = false);
|
||||||
},
|
},
|
||||||
updateHistory(datum) {
|
updateHistory(datum, updateValues = true) {
|
||||||
if (this.datumMatchesMostRecent(datum)) {
|
if (this.datumIsNotValid(datum)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = _.sortedIndex(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
|
const index = _.sortedIndexBy(this.imageHistory, datum, this.timeFormat.format.bind(this.timeFormat));
|
||||||
this.imageHistory.splice(index, 0, datum);
|
this.imageHistory.splice(index, 0, datum);
|
||||||
|
|
||||||
|
if(updateValues) {
|
||||||
|
this.updateValues(datum);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateValues(datum) {
|
updateValues(datum) {
|
||||||
if (this.isPaused) {
|
if (this.isPaused) {
|
||||||
|
@ -169,11 +169,10 @@ export default {
|
|||||||
|
|
||||||
const bounds = this.openmct.time.bounds();
|
const bounds = this.openmct.time.bounds();
|
||||||
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
|
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
|
||||||
&& this.embed.bounds.end !== bounds.end;
|
|| this.embed.bounds.end !== bounds.end;
|
||||||
const isFixedTimespanMode = !this.openmct.time.clock();
|
const isFixedTimespanMode = !this.openmct.time.clock();
|
||||||
|
|
||||||
window.location.href = link;
|
this.openmct.time.stopClock();
|
||||||
|
|
||||||
let message = '';
|
let message = '';
|
||||||
if (isTimeBoundChanged) {
|
if (isTimeBoundChanged) {
|
||||||
this.openmct.time.bounds({
|
this.openmct.time.bounds({
|
||||||
@ -187,7 +186,11 @@ export default {
|
|||||||
message = 'Time bound values changed to fixed timespan mode';
|
message = 'Time bound values changed to fixed timespan mode';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openmct.notifications.alert(message);
|
if (message.length) {
|
||||||
|
this.openmct.notifications.alert(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = link;
|
||||||
},
|
},
|
||||||
formatTime(unixTime, timeFormat) {
|
formatTime(unixTime, timeFormat) {
|
||||||
return Moment.utc(unixTime).format(timeFormat);
|
return Moment.utc(unixTime).format(timeFormat);
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="notifications.length > 0"
|
||||||
|
class="c-indicator c-indicator--clickable icon-bell"
|
||||||
|
:class="[severityClass]"
|
||||||
|
>
|
||||||
|
<span class="c-indicator__label">
|
||||||
|
<button @click="toggleNotificationsList(true)">
|
||||||
|
{{ notificationsCountMessage(notifications.length) }}
|
||||||
|
</button>
|
||||||
|
<button @click="dismissAllNotifications()">
|
||||||
|
Clear All
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<span class="c-indicator__count">{{ notifications.length }}</span>
|
||||||
|
|
||||||
|
<notifications-list
|
||||||
|
v-if="showNotificationsOverlay"
|
||||||
|
:notifications="notifications"
|
||||||
|
@close="toggleNotificationsList"
|
||||||
|
@clear-all="dismissAllNotifications"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NotificationsList from './NotificationsList.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
NotificationsList
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
notifications: this.openmct.notifications.notifications,
|
||||||
|
highest: this.openmct.notifications.highest,
|
||||||
|
showNotificationsOverlay: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
severityClass() {
|
||||||
|
return `s-status-${this.highest.severity}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.notifications.on('notification', this.updateNotifications);
|
||||||
|
this.openmct.notifications.on('dismiss-all', this.updateNotifications);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
dismissAllNotifications() {
|
||||||
|
this.openmct.notifications.dismissAllNotifications();
|
||||||
|
},
|
||||||
|
toggleNotificationsList(flag) {
|
||||||
|
this.showNotificationsOverlay = flag;
|
||||||
|
},
|
||||||
|
updateNotifications() {
|
||||||
|
this.notifications = this.openmct.notifications.notifications;
|
||||||
|
this.highest = this.openmct.notifications.highest;
|
||||||
|
},
|
||||||
|
notificationsCountMessage(count) {
|
||||||
|
if (count > 1) {
|
||||||
|
return `${count} Notifications`;
|
||||||
|
} else {
|
||||||
|
return `${count} Notification`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="c-message"
|
||||||
|
:class="'message-severity-' + notification.model.severity"
|
||||||
|
>
|
||||||
|
<div class="c-ne__time-and-content">
|
||||||
|
<div class="c-ne__time">
|
||||||
|
<span>{{ notification.model.timestamp }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="c-ne__content">
|
||||||
|
<div class="w-message-contents">
|
||||||
|
<div class="c-message__top-bar">
|
||||||
|
<div class="c-message__title">{{ notification.model.message }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="message-body">
|
||||||
|
<progress-bar
|
||||||
|
v-if="isProgressNotification"
|
||||||
|
:model="progressObject"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-overlay__button-bar">
|
||||||
|
<button
|
||||||
|
v-for="(dialogOption, index) in notification.model.options"
|
||||||
|
:key="index"
|
||||||
|
class="c-button"
|
||||||
|
@click="dialogOption.callback()"
|
||||||
|
>
|
||||||
|
{{ dialogOption.label }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="notification.model.primaryOption"
|
||||||
|
class="c-button c-button--major"
|
||||||
|
@click="notification.model.primaryOption.callback()"
|
||||||
|
>
|
||||||
|
{{ notification.model.primaryOption.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ProgressBar from '../../../ui/components/ProgressBar.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ProgressBar
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
notification: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isProgressNotification: false,
|
||||||
|
progressPerc: this.notification.model.progressPerc,
|
||||||
|
progressText: this.notification.model.progressText
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
progressObject() {
|
||||||
|
return {
|
||||||
|
progressPerc: this.progressPerc,
|
||||||
|
progressText: this.progressText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.notification.model.progressPerc) {
|
||||||
|
this.isProgressNotification = true;
|
||||||
|
this.notification.on('progress', this.updateProgressBar)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateProgressBar(progressPerc, progressText) {
|
||||||
|
this.progressPerc = progressPerc;
|
||||||
|
this.progressText = progressText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="t-message-list c-overlay__contents">
|
||||||
|
<div class="c-overlay__top-bar">
|
||||||
|
<div class="c-overlay__dialog-title">Notifications</div>
|
||||||
|
<div class="c-overlay__dialog-hint">
|
||||||
|
{{ notificationsCountDisplayMessage(notifications.length) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-messages c-overlay__messages">
|
||||||
|
<notification-message
|
||||||
|
v-for="notification in notifications"
|
||||||
|
:key="notification.model.timestamp"
|
||||||
|
:notification="notification"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NotificationMessage from './NotificationMessage.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
NotificationMessage
|
||||||
|
},
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
notifications: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openOverlay();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openOverlay() {
|
||||||
|
this.overlay = this.openmct.overlays.overlay({
|
||||||
|
element: this.$el,
|
||||||
|
size: 'large',
|
||||||
|
dismissable: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'Clear All Notifications',
|
||||||
|
emphasis: true,
|
||||||
|
callback:() => {
|
||||||
|
this.$emit('clear-all');
|
||||||
|
this.overlay.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onDestroy: () => {
|
||||||
|
this.$emit('close', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
notificationsCountDisplayMessage(count) {
|
||||||
|
if (count > 1 || count === 0) {
|
||||||
|
return `Displaying ${count} notifications`;
|
||||||
|
} else {
|
||||||
|
return `Displaying ${count} notification`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user