Compare commits

...

397 Commits

Author SHA1 Message Date
Andrew Henry
dc7b9a1e61 Build in production mode 2020-06-04 09:07:45 -07:00
Andrew Henry
2df62ef925 Modified plotly import 2020-06-03 19:22:16 -07:00
Andrew Henry
bdc3fdd7bb Switched plotly dependencies 2020-06-03 19:14:38 -07:00
Andrew Henry
b040af1e28 Restored plugin, suppressed plotly import 2020-06-03 18:52:18 -07:00
Andrew Henry
aed4a5ba2a Removed plotly plugin altogether: 2020-06-03 18:08:13 -07:00
Andrew Henry
32287f9108 Disable plotly plot plugin 2020-06-03 18:01:52 -07:00
Andrew Henry
c8bbeb0cae Removed plotly css 2020-06-03 17:59:20 -07:00
Andrew Henry
6b74a133d8 Upgraded karma 2020-06-03 17:41:42 -07:00
Andrew Henry
ceaf4c2ef0 Cleared circle cache 2020-06-03 16:29:55 -07:00
Andrew Henry
982b69e7e1 Don't rebuild sass every time 2020-06-03 16:25:08 -07:00
Andrew Henry
8e5182732d Used cross-platform instead of export 2020-06-03 16:24:13 -07:00
Andrew Henry
2d0b92cc38 Removed no-sandbox flag 2020-06-03 16:23:17 -07:00
Andrew Henry
75e846ea3f Added missing new line 2020-06-03 16:22:08 -07:00
Andrew Henry
1b5dee981d Reverted resource class change 2020-06-03 16:20:49 -07:00
Andrew Henry
4fee7f73e7 Fixed circle config 2020-06-03 16:19:49 -07:00
Andrew Henry
4308a4c9cf Removed report files 2020-06-03 16:17:23 -07:00
Andrew Henry
abbffcfbf1 Merge branch 'plotly-test' of https://github.com/nasa/openmct into plotly-test 2020-06-03 16:16:00 -07:00
Andrew Henry
01710876fb Increase circle resource class 2020-06-03 16:15:52 -07:00
Joel McKinnon
9e3d97c85d added no-sandbox flag 2020-06-03 13:33:59 -07:00
Joel McKinnon
519075001e bumped docker config to node 13 2020-06-03 13:12:45 -07:00
Joel McKinnon
96a48dd9ee moved rebuild node-sass later 2020-06-03 13:08:00 -07:00
Joel McKinnon
550daae76f add rebuild node-sass to config 2020-06-03 13:03:36 -07:00
Joel McKinnon
b8fcb8ff14 change circleci config to node12 2020-06-03 12:45:46 -07:00
Joel McKinnon
373ddd0bf5 updated circleci to use node13 2020-06-03 11:47:00 -07:00
Joel McKinnon
d62cc6b3ee bumped karma-jasmine to ^2.0.0 2020-06-03 11:11:07 -07:00
Joel McKinnon
5116d38437 added test memory increase 2020-06-03 11:03:36 -07:00
Joel McKinnon
29771f2722 Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-02 16:52:34 -07:00
Joel McKinnon
7bfe4bb25c restored karma-jasmine to original 2020-06-02 16:31:04 -07:00
Joel McKinnon
510c637081 locked karma-jasmine dependency 2020-06-02 16:01:02 -07:00
Joel McKinnon
02b537580c Merge pull request #3057 from nasa/code-standards-update
Updated code standard
2020-06-02 13:31:21 -07:00
Joel McKinnon
d7c65fec4c changed jasmine-core to ^2.0.0 2020-06-02 13:11:18 -07:00
Andrew Henry
7073b0717f Merge branch 'master' into code-standards-update 2020-06-02 13:05:32 -07:00
Andrew Henry
d6bb1b2a12 Added note about requiring a single return statement. 2020-06-02 13:05:15 -07:00
Joel McKinnon
626e2d8e80 changed jasmine-core to 2.0.0 to test circle-ci specfilter issue 2020-06-02 12:56:51 -07:00
Joel McKinnon
1fe673c1f5 Merge branch 'plotly-test' of https://github.com/nasa/openmct into plotly-test 2020-06-02 12:44:35 -07:00
Joel McKinnon
d9b00574e7 Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-02 12:38:39 -07:00
Joel McKinnon
7c48b3ba9a Merge branch 'master' into plotly-test 2020-06-02 12:29:46 -07:00
Jamie V
c256696790 Image view should react to time conductor changes - 2712 (#2934)
Image view reacts to time conductor changes
Fixes #2712
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-06-02 12:27:55 -07:00
Joel McKinnon
b6e589eed4 removed telemetry from initialization config 2020-06-01 14:46:54 -07:00
Shefali Joshi
d5480e7524 Multiple item conditional styles (#3017)
Conditional Styles for multiple items #3076

* Preview condition styles on selecting that condition or one of it's styles
Do not evaluate conditional styles in edit mode

* Conditions styling tweaked

- Condition match `is-current` styling for browse and edit modes;
- Disallowed pointer events on Conditions to prevent selection in
Inspector when not editing;

* Highlight current condition in conditionSet view

* Addresses review comments.

* Condition matching highlighting tweaked

- Enable match highlighting during Editing;
- Tweaks to `is-current` styling;

* Don't reset the callback on destroy

* Combine multiple and single selection styling of objects

* Fix issue with applying styles in edit mode

* Fix item styles bug

* Remove comment

* Adds back visibility toggle

* Set isEditing on initialization.

* Addresses review comments - removes use of lodash.

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-06-01 12:40:40 -07:00
Deep Tailor
ab463e93fe Refactor Notifications to use Vue, and add clear all option (#3068)
[Notifications] Need a clear all notifications option #3006

* create new notifications indicator in vue, and sunset old one

* add notifications list overlay

* working notifications

* add support for progress notifications

* update percentage on mounted

* Markup cleanups in new Vue Notifications files

- Removed unneeded markup and class wrappers;
- Removed unneeded inline styling;

* remove example notifications

* fix lint errors

* make reviewer requested changes, remove old not needed files, add test

* update testTools to testUtils

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-06-01 11:45:33 -07:00
Joel McKinnon
fb1813c14b change in legend position, trace names 2020-06-01 07:40:24 -07:00
Joel McKinnon
cce834f873 changed colors, added legend, fixed removeTelemetry 2020-05-29 15:14:05 -07:00
Joel McKinnon
ca60d02614 added name to plugin function, changed colors 2020-05-29 13:38:52 -07:00
Joel McKinnon
9520c09b49 fixed updateData so plot is extended in fixed timespan 2020-05-29 12:02:31 -07:00
Joel McKinnon
0d70717b35 addressed review comments 2020-05-29 09:55:37 -07:00
Joel McKinnon
f18da542d6 added updateRange function, removed length arg from updateData, fixed unsubscribe 2020-05-29 09:37:56 -07:00
Joel McKinnon
e7c38d473b added unsubscribe, subscribeTo methods 2020-05-28 15:26:30 -07:00
Joel McKinnon
2f8db31a33 added dynamic layout for fixed, local clock 2020-05-28 15:25:30 -07:00
Joel McKinnon
1c58d8c85f implemented Plotly.relayout to set x-axis correctly for local clock 2020-05-28 15:25:05 -07:00
Joel McKinnon
baf426055c added back if in updateData 2020-05-28 15:23:04 -07:00
Joel McKinnon
aa3af520ea refactor of refreshData 2020-05-28 15:21:15 -07:00
Joel McKinnon
4a43893ccc debugging local clock issues 2020-05-28 15:21:01 -07:00
Joel McKinnon
77f50a41c9 added some comments 2020-05-28 15:20:38 -07:00
Joel McKinnon
e4cd5f441f added requestHistory 2020-05-28 15:19:44 -07:00
Joel McKinnon
a9cfb002fb added listeners and bounds methods 2020-05-28 15:15:39 -07:00
Joel McKinnon
b603a5b722 WIP... 2020-05-28 15:13:35 -07:00
Joel McKinnon
be165761c7 WIP: adding bounds functions 2020-05-28 15:13:10 -07:00
Joel McKinnon
da88cf58cc add, remove multiple traces, remove hover effects 2020-05-28 15:12:48 -07:00
Joel McKinnon
bc2bd53f9b WIP 2020-05-28 15:12:11 -07:00
Joel McKinnon
6b7fd5f22a import RemoveAction 2020-05-28 15:09:01 -07:00
Joel McKinnon
4ee214a142 add code to set Y-axis and plot multiple telemetry types 2020-05-28 15:07:35 -07:00
Joel McKinnon
4af24db38a changed mode and type, dropped old data as data extends 2020-05-28 15:06:24 -07:00
Joel McKinnon
8db27809ef rendering a plotly plot from SWG 2020-05-28 15:04:36 -07:00
Joel McKinnon
3116b1addd wip: added telemetry provider 2020-05-28 14:58:24 -07:00
Joel McKinnon
5f67c45b50 working on viewProvider 2020-05-28 14:57:52 -07:00
Joel McKinnon
8158afc29a added hardcoded test plot 2020-05-28 14:57:13 -07:00
Joel McKinnon
13b3acb7e0 basic plotly plugin structure 2020-05-28 14:55:06 -07:00
Nikhil
8363c65312 [Notebook] display bounds change notification only if bounds changed (#3062) Resolves (#3051)
* display bounds change notification only if bounds changed

* avoid empty notification messages.
2020-05-28 12:29:32 -07:00
Deep Tailor
04598b6cf1 Bug fixes for plots (#3019) (#3069)
* prevent plots from breaking when more than one NaN is received.

* fix y axis label issue

* dont emit a viewport change event when marquee doesnt happen
2020-05-28 09:58:22 -07:00
Joel McKinnon
43628ad9d6 Lodash upgrade and cleanup (#2990)
* Upgrades lodash
* Replaces some usage of lodash with native functions.
* Adds linting to catch cases where native functions could be used instead of lodash functions
* Renamed testTools to testUtils

Co-authored-by: Joshi <simplyrender@gmail.com>
Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-27 10:59:02 -07:00
Andrew Henry
67bea86bc8 Added headless mode start option (#3065)
* Added headless mode start option. Fixes #3064
2020-05-26 11:39:55 -07:00
Shefali Joshi
4eb4cbfffc Merge pull request #3020 from nasa/updated-checklists
Add details about pull requests to contributing guide
2020-05-19 09:30:05 -07:00
Shefali Joshi
eda01abcbc Merge branch 'master' into updated-checklists 2020-05-18 12:12:26 -07:00
Shefali Joshi
2fa29124bf Merge branch 'master' into code-standards-update 2020-05-18 11:45:27 -07:00
David Tsay
694b8f4666 provide format for name metadata (#3054) 2020-05-18 11:40:05 -07:00
Andrew Henry
33faeafa98 New proposed ESLint rules that require no code changes 2020-05-16 16:19:36 -07:00
Andrew Henry
a40ff07353 Updated guidance on anonymous functions 2020-05-16 16:11:42 -07:00
Andrew Henry
41dc9c794d Fixed capitalization on CONST 2020-05-16 16:08:43 -07:00
Andrew Henry
f1faf3965d Changed reference to constructors to classes 2020-05-16 16:07:49 -07:00
Andrew Henry
da2ecbbcad Fixed formatting issues, removed outdated example code. 2020-05-16 16:06:59 -07:00
Andrew Henry
32c892fe98 Updated code standards 2020-05-16 15:56:30 -07:00
David Tsay
bbb271a678 clarify value hints (#2673)
remove confusing comments regarding domain - input and range - output
2020-05-15 14:48:25 -07:00
Deep Tailor
fec1438806 dont emit a viewport change event when marquee doesnt happen (#3036)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-15 14:35:13 -07:00
Nikhil
28f19ec310 [Notebook] Clicking on an embed in a notebook entry does not result in bounds change #3034 (#3042) 2020-05-14 14:56:41 -07:00
Shefali Joshi
f934454c25 Fixes computation of result when telemetry is not used by an object (#3040)
* Resolves issue #30307
Don't compute the result of a condition if telemetry datum is not being used by that condition

* Adds tests for condition results
2020-05-13 14:30:45 -07:00
Andrew Henry
eb49ffae02 Update CONTRIBUTING.md 2020-05-13 09:21:00 -07:00
Andrew Henry
5751012872 Update CONTRIBUTING.md 2020-05-13 09:20:03 -07:00
Andrew Henry
aa041e04cf Merge remote-tracking branch 'origin/update-contributing-guidelines' into updated-checklists 2020-05-11 17:13:53 -07:00
Andrew Henry
24e7ea143a Added more details on the process around pull requests 2020-05-11 17:12:25 -07:00
Andrew Henry
79d5d9c4d0 Update CONTRIBUTING.md 2020-05-11 16:15:17 -07:00
Andrew Henry
b5bfdc4418 Update CONTRIBUTING.md 2020-05-11 15:56:49 -07:00
Andrew Henry
59730c60ec Update CONTRIBUTING.md 2020-05-11 15:54:25 -07:00
Shefali Joshi
4a87a5d847 Show object styles in preview modal (#3018)
* Adds conditional styles to Preview window
2020-05-11 14:25:39 -07:00
Deep Tailor
421c09ec2c Allow users to lazy load Tabs (#2958)
* lazy load tabs

* remove listener on destroy

* fix lint error

* Store current tab position on domainObject

* remove lodash dependency and use keystring
2020-05-08 10:36:13 -07:00
Nikhil
0679b246b8 [Notebook]: Remove default section/page from localstorage on notebook delete (#2900)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-07 16:13:32 -07:00
Deep Tailor
83f9c6c528 improve plot gestures - and clean up (#3013) 2020-05-06 10:33:59 -07:00
Andrew Henry
a5f3ba6259 Use evalAsync instead of digest() (#3001)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-05-05 12:23:41 -07:00
Shefali Joshi
a70facf0c8 Merge pull request #3000 from nasa/upgrade-moment
Upgrade moment to 2.25.3
2020-05-05 11:01:39 -07:00
Andrew Henry
447fe94325 Merge branch 'upgrade-moment' of https://github.com/nasa/openmct into upgrade-moment 2020-05-05 10:36:43 -07:00
Andrew Henry
8e2b666766 Upgraded moment version to 2.25.3 2020-05-05 10:36:33 -07:00
Shefali Joshi
dcbfbdbb89 Merge branch 'master' into upgrade-moment 2020-05-05 10:12:50 -07:00
Shefali Joshi
4c76bf34ab Highlight currently winning Condition in Condition Set Edit and Read-only views (#2936)
* Preview condition styles on selecting that condition or one of it's styles

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-05-05 09:55:42 -07:00
Andrew Henry
81b7a9d3e0 Merge branch 'master' into upgrade-moment 2020-05-01 16:47:26 -07:00
Andrew Henry
dc573c479c Upgrade moment to 2.24.0 2020-05-01 16:25:49 -07:00
Nikhil
23303c910e Don't allow recursive Preview actions #2775 (#2869)
* Don't allow recursive Preview actions #2775

* actionsToBeIncluded and actionsToBeSkipped passed in as options object.

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

This reverts commit f501d0b4ba.

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

This reverts commit 5563cbea3a.

* Don't allow recursive Preview actions

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-01 09:33:10 -07:00
Nikhil
3282934cf6 fix test coverage. (#2951)
* fix test coverage.

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

* renamed test:coverage and test:debug.

* circle-ci to use test:coverage.

* reduced test coverage thresholds.

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

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

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

* removed stopclock call.

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

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

* do not send telemetryObjects to telemetry api request cal

* normalize data on requests

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

* request and subscription data cache should be mutually exclusive

use latest timestamp for any/all requests

* do not add prop to datum

remove unnecessary if check

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-28 13:02:23 -07:00
Joel McKinnon
a37c686993 Merge pull request #2968 from nasa/revert-2885-lodash-upgrade-test
Revert "Lodash upgrade"
2020-04-24 11:55:44 -07:00
Deep Tailor
f12166097c Revert "Lodash upgrade (#2885)"
This reverts commit d103a22fa0.
2020-04-24 11:53:31 -07:00
Joel McKinnon
d103a22fa0 Lodash upgrade (#2885)
* upgraded lodash, changed method names
* native implementations as requested
2020-04-23 10:38:44 -07:00
Deep Tailor
04a60cfcbb fixes #2713 (#2928)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-22 15:22:11 -07:00
Joel McKinnon
8d723960f4 removed acorn from package.json (#2906)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:50:45 -07:00
Joel McKinnon
6d3cd2c699 Upgrade angular from 1.4.14 to 1.7.9 (#2955)
* successfully upgraded to v1.6 with $compileProvider.preAssignBindingsEnabled(true)
* removed $compileProvider.preAssignBindingsEnabled(true), wrapped constructors for plot and chart inside onInit function
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:34:12 -07:00
Andrew Henry
87bf94fe0a Updated year in index.html (#2930) 2020-04-21 15:28:10 -07:00
Joel McKinnon
af93823b6f Elasticsearch support for change to typeless API (#2941)
* added elasticsearch to bundlemap
2020-04-21 15:23:43 -07:00
Shefali Joshi
4a39ddf425 Check for any and all criteria (#2948) 2020-04-16 15:01:14 -07:00
Shefali Joshi
83c273b976 Remove telemetry from criteria when not editing a condition set (#2933) 2020-04-16 12:32:32 -07:00
Shefali Joshi
7dd81beb03 Remove telemetry data cache if a telemetry endpoint is removed (#2916) 2020-04-10 16:49:29 -07:00
David Tsay
1842d3923c [Conditionals] Only provide telemetry for incoming telemetry that is used (#2914)
* Ensures that results for a specific datapoint are evaluated atomically.

* Removes timestamp based evalutation from conditionManager

* Remove unused code

* remove generating timestamp for telemetry data

* get results directly instead of using events

* remove unused listeners, events, and helpers

* linting

* remove commented code

* telemetry criterion stores its own result

* refactor all/any telemetry criterion to use new evaluator

* tie in requests and eliminate unused code

* use current timesystem to compare latest

* scope function names

* AllTelemetryCriterion extends TelemetryCriterion

* fix telemetrycriterion and unit testing

* fix unit tests

* check if telemetry is used at condition manager level

* move check to condition manager

* remove whitespace

Co-authored-by: Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 16:45:09 -07:00
Shefali Joshi
26838635b6 Ensures correct results are returned for conditions and criteria for a given telemetry datapoint (#2904)
* Ensures that results for a specific datapoint are evaluated atomically.
* Remove generating timestamp for telemetry data
* Get results directly instead of using events
* Refactor all/any telemetry criterion to use new evaluator
* Use current timesystem to compare latest
* AllTelemetryCriterion extends TelemetryCriterion

Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 15:57:38 -07:00
Nikhil
11f2c35bb2 [Notebook]: Entries filter #2820 (#2864)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 15:33:50 -07:00
Shefali Joshi
766f48c1ba Handles static and mixed styles for multiple items in a layout (#2907)
* Show non specific styles when updating multiple item styles
* Save sub object styles to it's domain object
* Layout UI tweak
* Fixes flexible layout bug.
* Fixes font size bug in telemetry view
* Fixes issues with newly places TVOs including transparent properties.
* Fixes #2908
* Say NO to 'transparent' === '__no_value'
- Fixes #2895;
* Ensure styles are correctly applied to domain objects and drawing objects when selected individually
* Ensure none treatment is correctly applied to objects when multple selecting
* Fix intial box border
* Tweaks to c-text-view layout
- Vertically center text;
- Normalize padding;
- Overflow: hidden;

* Tweaks to Clock and Timer layout
- Fixes #2893;
- Vertically center text;
- Normalize padding;
- Overflow: hidden;
- `position: absolute` when in Layout;

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 15:22:47 -07:00
Nikhil
da7b93f9b3 Notebook context menu (#2888)
Notebook popup menu fix
Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-10 15:17:01 -07:00
Charles Hacskaylo
99c095a69f Fixed condition-improve-reorder branch (#2912)
* wip: changing to condition as drag target

* wip

* wip

* wip

* fixed dragging issues

* fixed dragging classes and added temp border on condition with dragging class

* Conditionals sanding and shimming

- CSS and `all-dragging`;

* wip

* fixed drag end issue and changed dragging class to go on parent condition h

* drag with counter

* wip

* wip

* wip

* return to logic in ConditionCollection.vue

* wip

* completed js part with highlighted c-condition-h on dragover

* restored grippy as draggable elem, improved isValidTarget

* fixed drag text bug

* added moveIndex prop in Condition.vue

* Conditionals drag reorder styling

- Moved `.is-drag-target` class up to conditions-h element;
- Renamed `.all-dragging` to `is-active-dragging`;
- Styling for `__drop-target` elements;

* fixed incorrect default for moveIndex in condition collection, unnecessary reset in condition

* fixed downward move reorder

* removed prevent from dragenter and drag leave, changed @blur to @change for name and output fields

* removed console log

* Repair merge-damaged conditionals.scss

- Manual merge from latest master;

* Test data layout tweaked

- Prevent c-cs__test-data__controls from collapsing;

Co-authored-by: Joel McKinnon <joel.g.mckinnon@nasa.gov>
Co-authored-by: Joel McKinnon <JoelMcKinnon@users.noreply.github.com>
2020-04-10 15:04:04 -07:00
Charles Hacskaylo
f885e83505 Merge pull request #2911 from nasa/revert-2818-condition-improve-reorder
Revert "Condition improve reorder"
2020-04-10 14:35:41 -07:00
Deep Tailor
928bc4c68a Revert "Condition improve reorder (#2818)"
This reverts commit 46fedc1a30.
2020-04-10 14:32:48 -07:00
Shefali Joshi
d5539c7ae4 Merge pull request #2898 from nasa/fix-enum-comparison
Fixes enum comparisons
2020-04-10 14:03:05 -07:00
Shefali Joshi
c86a104fb6 Merge branch 'master' into fix-enum-comparison 2020-04-10 13:54:04 -07:00
Joel McKinnon
46fedc1a30 Condition improve reorder (#2818)
* wip: changing to condition as drag target

* wip

* wip

* wip

* fixed dragging issues

* fixed dragging classes and added temp border on condition with dragging class

* Conditionals sanding and shimming

- CSS and `all-dragging`;

* wip

* fixed drag end issue and changed dragging class to go on parent condition h

* drag with counter

* wip

* wip

* wip

* return to logic in ConditionCollection.vue

* wip

* completed js part with highlighted c-condition-h on dragover

* restored grippy as draggable elem, improved isValidTarget

* fixed drag text bug

* added moveIndex prop in Condition.vue

* Conditionals drag reorder styling

- Moved `.is-drag-target` class up to conditions-h element;
- Renamed `.all-dragging` to `is-active-dragging`;
- Styling for `__drop-target` elements;

* fixed incorrect default for moveIndex in condition collection, unnecessary reset in condition

* fixed downward move reorder

* removed prevent from dragenter and drag leave, changed @blur to @change for name and output fields

* removed console log

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-04-10 10:02:33 -07:00
Joshi
3b6ef9b44b Refactor duplicate code into functions 2020-04-09 15:33:52 -07:00
Joshi
c68edd9b7d Fixes enum comparisons
Adds check for undefined
2020-04-09 09:14:31 -07:00
Shefali Joshi
11574b7c40 Merge pull request #2861 from nasa/fix-telemetryview-styles
Styles for telemetry are stored on their container domain objects
2020-04-08 15:19:15 -07:00
Shefali Joshi
abc2cd2413 Merge branch 'master' into fix-telemetryview-styles 2020-04-08 14:40:54 -07:00
David Tsay
5d74882646 Merge pull request #2871 from nasa/dave/conditionals-own-results
[Conditionals] Request and subscription results are mutually exclusive
2020-04-08 14:21:31 -07:00
David Tsay
9fe7f230e6 Merge branch 'master' into dave/conditionals-own-results 2020-04-08 14:16:50 -07:00
Shefali Joshi
de4c5b3729 Disables conditional and static styles for hyperlinks and summary widgets. (#2887) 2020-04-08 12:30:59 -07:00
Deep Tailor
2a7901914a Merge branch 'master' into fix-telemetryview-styles 2020-04-08 11:57:33 -07:00
Joshi
73b0fc6f79 Removes preventNone as per conversation with UX designer. 2020-04-08 11:46:12 -07:00
Charles Hacskaylo
ddef16795c Conditionals and Notebook UI fixes (#2868)
- Significant fixes for Safari-compatible Flex layout in Condition Set
view;
- Changed visual approach to current-value section;
- Firefox scrollbar coloring
- Fix layout issues in Firefox;
- Consolidate Conditionals styles into single scss file;
- Fix test datum elements layout, better wrapping;
- Better approach to presence/absence of URL property in Condition
Widget;
- Fixes #2853;
- Fix errors in URL property handling in Condition Widget;
- Fixes #2853;
- Fixes #2867 - hide the View Switcher when an object is being edited;
- Refined titling on View Switcher and Notebook menu button;
- Cleaned up styles in l-browse-bar and moved into
ui/layout/layout.scss;
- Removed styles/_layout.scss;
- Hide the main view Edit button when in mobile
2020-04-08 09:36:23 -07:00
David Tsay
d188b9a056 do not mutate function args for criteria results either 2020-04-07 13:22:03 -07:00
Joshi
f510f3edd0 Removes missed code 2020-04-07 11:59:24 -07:00
Joshi
e05b0bb562 Address review comments:
Fixes telemetry view visibility and styling issue
Removes none option for border and background styles for drawing objects
2020-04-07 11:34:48 -07:00
David Tsay
713c5e9fb7 Merge branch 'master' into dave/conditionals-own-results 2020-04-06 18:56:08 -07:00
David Tsay
17bca04560 do not mutate function args 2020-04-06 18:53:48 -07:00
Charles Hacskaylo
61bdadc33c Fixes #2878 (#2879)
- Remove background from `elementStatusColors` mixin;
- Change Indicators to use more correct style of `.s-status-on`;
2020-04-06 15:36:52 -07:00
David Tsay
e0c5bca47d fix unit tests 2020-04-06 14:25:55 -07:00
Joshi
cdc7c1af64 Removes unused data 2020-04-06 14:04:11 -07:00
Joshi
3158baa998 Merge branch 'fix-telemetryview-styles' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-06 14:02:45 -07:00
Joshi
698508fde4 Use the subobject view type to determine where styles should be saved 2020-04-06 14:02:06 -07:00
Shefali Joshi
68a96989e1 Merge branch 'master' into fix-telemetryview-styles 2020-04-06 13:57:41 -07:00
Joshi
46a6a43234 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-06 13:56:30 -07:00
David Tsay
d41fc27b55 subscriptions should use latest timestamp 2020-04-06 11:17:59 -07:00
David Tsay
24bb96cc90 WIP detach request data from subscription data
extract getLatestTimestamp function
2020-04-03 18:39:26 -07:00
David Tsay
483ee173d6 pass correct args into off listener 2020-04-02 16:34:40 -07:00
Joel McKinnon
469e93d916 Merge pull request #2865 from nasa/criterion-delete
Criterion delete
2020-04-02 10:51:12 -07:00
Joshi
f96dfcc942 Reassign ids for criteria when we clone conditions 2020-04-02 10:45:37 -07:00
Joshi
063a6c0e51 Merge branch 'criterion-delete' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:40:57 -07:00
Joshi
7c289d76b6 Revert object.assign change 2020-04-02 10:40:39 -07:00
Joshi
c617a440eb Merge branch 'master' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:40:32 -07:00
Joshi
8f81a45b9b Removes coverage branch 2020-04-02 10:30:23 -07:00
Joshi
666459be87 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-02 10:30:07 -07:00
Shefali Joshi
53df89aa5d Merge branch 'master' into criterion-delete 2020-04-02 10:26:57 -07:00
Shefali Joshi
8f553f6327 Merge pull request #2848 from nasa/fix-any-telemetry
Fix LAD requests for any/all telemetry
2020-04-02 10:23:26 -07:00
Joshi
f91a64483b Adds missing id during create condition 2020-04-02 10:18:41 -07:00
Joshi
de8d63c09d Merge branch 'master' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:11:42 -07:00
Shefali Joshi
58b4a6ebf5 Merge branch 'master' into fix-any-telemetry 2020-04-02 10:06:37 -07:00
Shefali Joshi
9d45080526 Merge pull request #2858 from nasa/dave/conditionals-request-default
request LAD for default condition
2020-04-02 10:05:42 -07:00
Joshi
d3fe2a6811 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-01 15:52:55 -07:00
Joshi
97b37edce4 Store telemetry styles on their container domain objects. 2020-04-01 15:51:40 -07:00
Joel McKinnon
d3443518d6 reverted changes unrelated to id 2020-04-01 15:08:31 -07:00
Joel McKinnon
c33314a4bf added uuid to addCriteria, cloneCriteria, changed key of v-for to criteria.id 2020-04-01 14:56:32 -07:00
David Tsay
59eb034ab4 Merge branch 'master' into dave/conditionals-request-default 2020-04-01 14:42:21 -07:00
Nikhil
67d53fb62b [Notebook]: When notebook is not default new entry button does not add new entry #2841 (#2855)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-01 14:28:52 -07:00
Nikhil
0fd637c0e9 [Notebook]: Error in nextTick: "TypeError: Cannot read property 'focus' of null" #2845 (#2857) 2020-04-01 14:22:11 -07:00
Joel McKinnon
169cc6617a added uuid() in conditionManager 2020-04-01 13:58:58 -07:00
David Tsay
a946325e95 request LAD for default condition 2020-04-01 13:58:27 -07:00
Joel McKinnon
1beba78111 removed call to checkTelemetry in computed prop, removed if in persist(), changed key in v-for for criteria 2020-04-01 13:34:17 -07:00
Joel McKinnon
2edfeaa606 added checkTelemetry call in inputCount computed prop 2020-04-01 12:15:02 -07:00
Joel McKinnon
2a1f9fd063 removed recursive call 2020-04-01 11:10:10 -07:00
Joel McKinnon
63fe92f8ea Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into criterion-delete 2020-04-01 10:37:01 -07:00
Joel McKinnon
35f303ffa4 changed inputCount to computed property 2020-04-01 10:35:49 -07:00
Joshi
dd70bb470f Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-01 10:18:30 -07:00
Nikhil
250fee125a Merge pull request #2854 from nasa/notebook-preview-action-fix
[Notebook] Embed preview action fix
2020-04-01 10:13:01 -07:00
Joshi
956029c786 Merge branch 'fix-any-telemetry' of https://github.com/nasa/openmct into fix-any-telemetry 2020-04-01 10:11:30 -07:00
Joshi
b9ab599c35 Fix description and errors for all/any telemetry 2020-04-01 10:10:31 -07:00
Deep Tailor
5a690932e9 Merge branch 'master' of https://github.com/nasa/openmct into notebook-preview-action-fix 2020-04-01 09:59:57 -07:00
Deep Tailor
b00757150e use objectPathJSON 2020-04-01 09:58:30 -07:00
Joshi
8515a411bd Merge branch 'master' of https://github.com/nasa/openmct into fix-any-telemetry 2020-04-01 09:55:55 -07:00
Shefali Joshi
3034ec016a Merge branch 'master' into fix-any-telemetry 2020-04-01 09:55:03 -07:00
David Tsay
a81af1ce34 check format first (#2851)
default to number
2020-03-31 21:04:52 -07:00
Deep Tailor
64c5725687 pass correct object path, and remove notebook snapshot button from preview 2020-03-31 20:59:20 -07:00
Joshi
72a3248123 Fix LAD requests for any/all telemetry 2020-03-31 16:48:59 -07:00
Shefali Joshi
ee4a81bdfd Conditionals feature (#2830)
Introduces conditional styling feature.
2020-03-31 15:56:06 -07:00
Joshi
b90eb80584 Addresses review comments: Rename file, change copyright year, remove commented out code 2020-03-31 15:50:30 -07:00
Charles Hacskaylo
ebaf702c59 Topic conditionals master merge (#2833)
- Restore updated symbols font file for Condition Widget icon glyph;
- Change "All telemetry | Any telemetry" to all lowercase;
- Fix cursor styles for Condition Widget;
- Fix Safari layout problems with Condition Set/section styles;
2020-03-31 15:36:50 -07:00
Shefali Joshi
3956cd1c06 Merge pull request #2829 from nasa/topic-conditionals-master-merge
Topic conditionals master merge
2020-03-31 14:25:16 -07:00
Joshi
064cf6747e Adds copyright text 2020-03-31 14:21:30 -07:00
Joshi
8512b634c7 Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-31 12:54:12 -07:00
David Tsay
4220a8a68a [Conditionals] default condition handling (#2827)
* Sisplay default condition output by... default
2020-03-31 12:32:06 -07:00
Nikhil
e7e5116773 [Notebook] V2.0 development #2666 (#2755)
* Notebook v2.0
Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-03-31 12:11:11 -07:00
David Tsay
079201273e Merge branch 'master' into topic-conditionals 2020-03-31 12:02:37 -07:00
Shefali Joshi
e4c9f156a7 Merge pull request #2800 from nasa/any-all-telemetry
Any all telemetry option for conditions.
2020-03-31 11:49:23 -07:00
Joshi
42eeeea374 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into any-all-telemetry 2020-03-31 11:00:00 -07:00
Charles Hacskaylo
7b060509f5 c-object-label changes brought over from topic-conditionals (#2823)
- SCSS mods;
- Markup in select objects
2020-03-30 17:30:53 -07:00
Deep Tailor
3ca9e9ae56 When view params change, get a new copy of the domainObject (#2813)
Update browseObject on object change
2020-03-30 17:27:10 -07:00
Joel McKinnon
984bede43b Removed image properties button from display layouts toolbar (#2809)
Removed image properties button from display layouts toolbar
2020-03-30 16:58:46 -07:00
Deep Tailor
87d838c690 [Tables] Use parsed data to sort (#2785)
* Use parsed values when sorting
2020-03-30 16:57:20 -07:00
Joshi
072bf361de Store styles for telemetry on domain objects 2020-03-30 16:20:22 -07:00
Shefali Joshi
4e39d9fb84 Test data for condition sets (#2807)
* Test Data
* Persist testData and apply it to the conditionSet
* Do not persist the applied flag, but persist test data
Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-03-30 15:45:07 -07:00
Joshi
14eaf4e899 Addresses comments: Fixes dropdown logic 2020-03-30 12:17:49 -07:00
Shefali Joshi
a31d10e708 Adds NOT and XOR triggers for conditions (#2816)
* Adds XOR and NOT triggers for conditions
* Adds unit tests and fixes linting issues
2020-03-30 12:09:50 -07:00
Charles Hacskaylo
f9e88321a3 Condition Widget refinements (#2814)
* New Condition Widget
* Add new glyphs and symbols font files
2020-03-30 11:59:27 -07:00
David Tsay
9e12203b86 Merge branch 'master' into topic-conditionals 2020-03-30 11:19:45 -07:00
David Tsay
a5326a7b95 Merge pull request #2810 from nasa/toolbar-fixes-32720
[Toolbar] fix errors when navigated away from a layout that uses toolbar
2020-03-30 11:13:17 -07:00
Shefali Joshi
84874f22e6 Cast number inputs to a Number so that operations evaluate correctly (#2799)
* Cast number inputs to a Number so that operations evaluate correctly
2020-03-30 10:37:55 -07:00
David Tsay
d00e8b68a5 Merge pull request #2802 from nasa/clone-condition-issues
Clone condition, criteria issues
2020-03-30 09:40:00 -07:00
Joel McKinnon
2907d6d79c moved opy to cloneCondition 2020-03-30 07:13:15 -07:00
Joel McKinnon
a5a4bb87c5 Removed extra if statement, copied only config 2020-03-30 07:06:03 -07:00
David Tsay
389589d7f7 Merge pull request #2806 from nasa/preserve-user-input
Preserve user input when changing criterion comparison
2020-03-29 15:59:31 -07:00
David Tsay
c02cbd1ba7 Merge pull request #2804 from nasa/criterion-type-change
Criterion field change causing incorrect input type
2020-03-29 15:46:18 -07:00
Joel McKinnon
90b475e17b addressed review comments 2020-03-29 14:03:49 -07:00
Joshi
89a298f5b3 Removes name string fix 2020-03-27 15:25:24 -07:00
Joshi
f990a14a3c Remove duplicate metadata options 2020-03-27 15:22:50 -07:00
Deep Tailor
6b00af6ece fix errors when navigated away from a layout that uses toolbar 2020-03-27 10:15:09 -07:00
Joshi
5ec8ac95c1 Fixes linting issues 2020-03-27 09:42:18 -07:00
Joel McKinnon
5f061001d6 fixed updateOperations, clearDependentFields, removed unnecessary ref 2020-03-27 08:52:23 -07:00
Joel McKinnon
ef3c4ccf47 clear dependent fields when field selector is changed 2020-03-27 08:09:04 -07:00
Joel McKinnon
1a86204637 used lodash cloneDeep for criteria, conditions 2020-03-27 07:31:41 -07:00
Joshi
0a1959df38 Fixes linting issues 2020-03-26 23:00:59 -07:00
Joshi
03a690a158 Fixes import 2020-03-26 22:56:39 -07:00
Joshi
459a055455 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into any-all-telemetry 2020-03-26 22:55:44 -07:00
Joshi
502d29dd25 Add all and any options for telemetry 2020-03-26 16:44:44 -07:00
Joel McKinnon
bf947a8835 reverted to espresso (#2797) 2020-03-26 15:12:43 -07:00
Shefali Joshi
f0fd0a9cc7 Use parsed telemetry data values for condition evaluations (#2783)
* Use parsed telemetry data values for condition evaluations

* Addresses comments - handles undefined value and implements format and validate formatter methods

* Merge topic-conditionals
2020-03-26 15:11:29 -07:00
Joel McKinnon
7282792da1 fixed refs and ev bugs 2020-03-26 14:57:55 -07:00
Joshi
ec0291c54d Implement any telemetry and all telemetry options 2020-03-26 14:01:02 -07:00
Charles Hacskaylo
4413c29abb Merge pull request #2793 from nasa/new-widget
Add Condition Widget
2020-03-26 13:26:47 -07:00
Joshi
91e1a144ed Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-26 13:22:59 -07:00
Shefali Joshi
53440c31d5 Handles remove telemetry from condition set - this resets criterion properties (#2791)
Handles remove condition from a condition set that's being used in a conditional style.
Fixes a small bug with stye rule manager request lad results
2020-03-26 13:12:22 -07:00
charlesh88
5128af2531 New Condition Widget, WIP
- Remove temp test styling;
2020-03-26 13:08:44 -07:00
Shefali Joshi
eedc0f13bc adds formatting for strings (#2792) 2020-03-26 13:08:28 -07:00
Shefali Joshi
4172fdf1d5 Merge pull request #2788 from nasa/criterion-enum-persist
Criterion enum persist
2020-03-26 12:58:25 -07:00
Joel McKinnon
7c200df4c4 resolved merge conflict 2020-03-26 12:39:50 -07:00
charlesh88
d1b28e079a Merge branch 'topic-conditionals' into new-widget 2020-03-26 12:34:56 -07:00
David Tsay
23ec838643 Merge pull request #2764 from nasa/condition-persist
Telemetry stickiness issue
2020-03-26 12:34:37 -07:00
Joel McKinnon
271c619c63 Merge pull request #2789 from nasa/fix-description
Fixes description for enums
2020-03-26 12:23:40 -07:00
Joel McKinnon
0552769670 change back to refs 2020-03-26 11:30:00 -07:00
David Tsay
99a9f5b3ba Merge pull request #2790 from nasa/telemetry-metadata-manager-fix-32620
[Metadata] Fails when trying to apply reasonable defaults to an undefined values array
2020-03-26 10:41:29 -07:00
Deep Tailor
4b535ade31 metadataManager should check for a values array
before calling map on it
2020-03-26 10:25:04 -07:00
Shefali Joshi
de8f8088e2 Merge pull request #2786 from nasa/issue-2773
[Conditions] cleanup subscriptions in style rule manager
2020-03-26 09:53:16 -07:00
Joshi
32c16416d3 Fixes description for enums 2020-03-26 09:45:44 -07:00
Shefali Joshi
0cf27c349b Merge pull request #2768 from nasa/conditionset-multi-value-operations
Implements two new operations - is one of and is not one of
2020-03-26 09:43:08 -07:00
Joshi
015aa8c637 Checks for input before processing it. 2020-03-26 09:37:52 -07:00
Joel McKinnon
4a07ddbefc added persist call to change enum input 2020-03-26 09:03:32 -07:00
Joel McKinnon
a81009541c Merge branch 'condition-persist' of https://github.com/nasa/openmct into criterion-enum-persist 2020-03-26 08:22:59 -07:00
Joel McKinnon
1b680cfaca moved persist call into change event method and removed check on $ref 2020-03-26 08:10:35 -07:00
David Tsay
311ff003c0 Merge pull request #2784 from nasa/fix-conditionals
Fixes error while getting metadata when telemetry is not yet available for a criterion
2020-03-25 21:39:45 -07:00
charlesh88
0cae61444d New Condition Widget, WIP
- Refine base styling;
- Fix so that edited properties are reactive;
2020-03-25 19:47:21 -07:00
charlesh88
28a603def8 New Condition Widget, WIP
- Add condition-widget.scss file;
- Add type definitions to control display behaviors in layouts;
- Refine widget structure;
2020-03-25 18:24:14 -07:00
David Tsay
d2b7407674 add request for LAD data 2020-03-25 17:41:33 -07:00
David Tsay
fff89a6384 delete unsubscribe method on destroy 2020-03-25 17:36:27 -07:00
charlesh88
953b95f79c Merge branch 'topic-conditionals' into new-widget 2020-03-25 17:22:43 -07:00
charlesh88
6ff5ce78e1 Merge remote-tracking branch 'origin' into new-widget 2020-03-25 17:16:23 -07:00
charlesh88
0857fd95a7 New Condition Widget, WIP
- Rename `c-object-view` to `u-angular-object-view-wrapper` and fix to
allow style settings to be applied to it;
2020-03-25 17:15:56 -07:00
Charles Hacskaylo
a1f2608e7c Merge pull request #2782 from nasa/conditionals-ch-fixes-032420
Misc Conditionals fixes
2020-03-25 17:14:08 -07:00
charlesh88
b7cea7b955 New Condition Widget, WIP
- Normalize usage of `c-object-label` across components;
2020-03-25 16:34:11 -07:00
charlesh88
32da19a486 New Condition Widget, WIP
- Add new Vue components, mod configs;
2020-03-25 16:19:46 -07:00
Joshi
39d7dc8372 Adds unit tests for isOneOf and isNotOneOf 2020-03-25 15:50:16 -07:00
Joel McKinnon
f76f537be7 removed extraneous emits and handlers 2020-03-25 15:27:48 -07:00
Joshi
a681d67e05 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionset-multi-value-operations 2020-03-25 14:14:23 -07:00
Joshi
0a634eb490 Fixes unit tests 2020-03-25 14:08:33 -07:00
Joshi
fcca8fa8d9 Fixes error while getting metadata when telemetry is not yet available for a criterion 2020-03-25 13:44:26 -07:00
Joel McKinnon
829eecf1ae resolved merge conflicts 2020-03-25 13:24:43 -07:00
Joshi
dc7f83754a Adds parse treatment for string format.
Adds format property to sine wave generator's name metadata
2020-03-25 12:05:24 -07:00
David Tsay
51498c0e75 Merge pull request #2781 from nasa/global-filters-no-request-bug-fix
Global Filters not re-requesting on value change
2020-03-25 11:57:30 -07:00
David Tsay
6dbdebbe05 Merge branch 'master' into global-filters-no-request-bug-fix 2020-03-25 11:54:45 -07:00
David Tsay
c994227d5d Merge pull request #2769 from nasa/telemetry-table-duplicate-request-fix
[Telemetry Tables] Duplicate requests when users change time system
2020-03-25 11:42:34 -07:00
Deep Tailor
761ca7ad56 remove console log 2020-03-25 11:16:25 -07:00
charlesh88
7f49a7bc99 Conditionals fixes and tweaks
- Fixes #2772;
- Remove styling toolbar icons in Display Layouts;
- Fix regression error in tree items that was only allowing clicks on
the name to navigate;
2020-03-25 11:12:54 -07:00
Deep Tailor
ca022b8a28 make a copy of filters before checking isEqual
- to prevent using same pointer
2020-03-25 11:11:17 -07:00
Joshi
6f500d0d0b Resolves ##2741
Uses parsed telemetry data values for comparisons
2020-03-25 10:45:28 -07:00
charlesh88
07d101ac1c Conditionals fixes and tweaks
- Legacy wrapper fixes to allow color styling to be applied as expected;
- New `$mainViewPad` constant;
- Fix `c-control-bar` element spacing in plots;
- TODO: check for regressions, particularly with plot views;
- Fixes #2772: better `is-style-invisible` css for `c-style-thumbs`;
2020-03-24 23:45:25 -07:00
charlesh88
cdf0dd0c10 Conditionals fixes and tweaks
- Fixes multiple items in #2772;
- Fix missing wrapping markup, convert empty span to template;
- Add INPUT_TYPES object to allow proper setting of 'text' input type;
- Fix padding on <selects> for better inline alignment with other input
types;
2020-03-24 19:41:31 -07:00
charlesh88
c27c347d29 Conditionals fixes and tweaks
- Fixes #2778;
2020-03-24 18:17:13 -07:00
Joshi
dc54eef2c9 Resolves #2776
Adds null checks for telemetry and domain object configuration
2020-03-24 15:40:33 -07:00
Shefali Joshi
57a68a24de Apply condition sets to drawing objects in a layout + Static styling for objects. (#2767)
* Hardcoded prototype - conditional styles for display layout or generator domain objects only.
Needs Architectural review

* Updates to ConditionalStylesView

* Adds background colors list

* Show conditional styles for subObjectViews and telemetryViews

* Uses telemetry provider to determine which style is active
Removes hardcoded conditionSet work used for prototype

* Fixes failing test

* Add default styles to conditionalStyles when a condition set is added to the domain object.

* Use EventEmitter alias instead of eventEmitter3 in imports
Change variable name for better readability and clarity
Remove unused code

* Change StyleRuleManager to accept conditionStyle objects instead of domainObjects

* Uses a map for conditional styles instead of a list in order to improve performance

* Use in-built api to check for identifier equality
Pass missing arguments

* Removes unnecessary object get call.

* Conditionals styling WIP

- Tweaks for layout/overflow issues;
- Better header alignment;
- Much better approach to "accordion" spacing;

* Adds conditional styles for drawing objects

* Fixes small bugs while adding or removing nested conditional styling

* Removes hard coded conditionSetIdentifier

* Fixes small conditionManager bug

* WIP - Adds condition set selection dialog

* WIP - Add condition set

* Styling for tree dialog in selector

- New `.c-selector` class;
- Markup added to tree-item.vue to fix missing `c-object-label` wrapper;

* Adds image url to styles inspector view

* Adds highlight for selected conditionSet in selection dialog.

* Conditionals Inspector styling WIP

- Simplified Inspector markup;
- Refined `.c-inspector` layout strategy;
- Refined `.l-multipane` classes for better layout and scroll handling;
- TODOs: styling for 'styles' section; unit test, find regressions, fix;

* Read-only mode changes for the styles inspector

* Adds basic styling for styles inspector view

* Removes unused code

* Displays condition set name

* Display description for conditions

* Conditionals Inspector styling WIP

- Add new ObjectName component to Inspector;
- Add supporting styles;

* Conditionals Inspector styling WIP

- Refactor for better usage of c-object-label;
- Refactor `c-properties` classes to `c-inspect-properties`;

* Conditionals Inspector styling WIP

- Introduce `c-inspect-styles` class;
- Markup refined in ConditionalStylesView.vue;

* Conditionals Inspector styling WIP

- Fix unintended regression in `c-object-label`;

* Makes inspector view tabs sticky.
Shows errors if either telemetry or conditions are missing
Refresh conditionStyles when styles inspector is loaded to show new conditions that might be added to the conditionSet

* Conditionals styling WIP

- Added title to Condition Set selector dialog;

* Conditionals styling WIP

- CSS and markup changes;

* Conditionals styling WIP

- Markup changes;

* Componentize conditional style editor, condition description and condition error

* Conditionals styling WIP

- Conditional styling elements in Inspector, significant refinements;
- Condition description formatting;
- Significant markup simplification in StyleEditor.vue;
- Moved `.c-inspect-styles` into conditional-styles.scss;

* Conditionals styling WIP

- Added menu positioning css;

* Conditionals styling WIP

- Display images in StyleEditor `c-style-thumb` element;

* Use the condition description component to show the condition description in condition set views

* Conditionals styling WIP

- New `u-alert` and `u-error` classes and mixin;
- New $colorError theme constants;

* Conditionals styling WIP

- Fixed background: cover or c-style-thumbs;

* Conditionals styling WIP

- Add navigate-or-preview functionality to attached Condition Set used
for conditional styling (thank you Deep!);
- Better theme-compliant box-shadowing on c-style-thumb;
- Added default color property to cClickIcon SCSS mixin;

* Fixes ConditionDescription and ConditionErrors views

* Allow static styles for domain Objects and display layout items

* Consolidate handling of 'none' style to the object styles mixin

* Adds visibility option to child items

* Conditionals styling WIP

- Add `is-style-invisible` class;
- Better color approach for table headers to support domain object
styling;

* Change visibility to isInvisible class

* Fixes visibility for object views and telemetry views

* Fixes selection of condition set from tree view

* Conditionals styling WIP

- Added new $glyph-icon-eye-disabled font symbol and related class;
- Modded glyphs used for visibility toggle button;
- Modded StyleEditor thumb preview to reflect visibility setting;
- Tweaked `is-style-invisible` opacity property;
- Code cleanup;

* Conditionals styling WIP

- Display disabled toolbar for Inspector styles in Browse mode;
- Better approach to 'is-style-invisible' `c-style-thumb` with new
bgCheckerboard mixin;
- Moved `$controlDisabledOpacity` into a theme constant;
- Refined spacing in Inspector grid;

* Fixes linting issues

* Fixes telemetry metadata name

* criterion descriptions is now an array of strings

* Fixes spacing

* Removes commented out code.

* Remove v-if

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-03-24 13:09:21 -07:00
Shefali Joshi
2c1b4b4cfc Merge pull request #2759 from nasa/dave/conditions-telemetry
[Conditions] Request, Subscribe to, and Provide Telemetry
2020-03-24 11:43:09 -07:00
David Tsay
437e8a0263 always load composition 2020-03-24 11:15:58 -07:00
Joel McKinnon
0a2e912091 removed refs from dom elements and added index to call to isLastCriterion 2020-03-24 09:43:54 -07:00
Joel McKinnon
87513a14b7 removed log 2020-03-23 17:47:35 -07:00
Joel McKinnon
168c040f3c fix sticky telemetry selection issue 2020-03-23 16:16:58 -07:00
David Tsay
78487a48f6 restrict conditionManager instances to one per domainObject 2020-03-23 16:04:37 -07:00
David Tsay
a5a197680d remove debug 2020-03-23 15:58:01 -07:00
David Tsay
d42bd44485 remove logging statement 2020-03-23 15:24:24 -07:00
David Tsay
77b705ecc8 add telemetry object back for criterion 2020-03-23 15:21:51 -07:00
David Tsay
48af39a584 Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-23 14:58:32 -07:00
Deep Tailor
ec978f3a35 when a time system is changed, it emits a timeSystem update as well as a bounds update, this caused telemetry table to request data twice when users changed time systems 2020-03-23 14:21:46 -07:00
David Tsay
f13714e0c4 rename telemetry subscription calls 2020-03-23 13:53:19 -07:00
Joshi
42ac3ef9af Implements two new operations - is one of and is not one of that evaluates to true if telemetry matches one of many values 2020-03-23 13:21:22 -07:00
David Tsay
26ffe8efde remove comment 2020-03-23 13:05:48 -07:00
David Tsay
87b4000b12 Merge pull request #2763 from nasa/dave/conditions-views
[Conditions] Enable dropping Condition Sets into overlay plots
2020-03-23 12:44:48 -07:00
Joshi
f790c9bd39 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into topic-conditionals 2020-03-23 11:55:33 -07:00
Joshi
096c9688d5 Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-23 11:55:12 -07:00
David Tsay
1f19f480ce fix unit tests 2020-03-20 17:38:29 -07:00
Joel McKinnon
44f48a3e2a moved persist calls to events, used emit to save name and fieldname props 2020-03-20 17:37:48 -07:00
David Tsay
60fce4a003 remove completed TODO comments 2020-03-20 17:08:55 -07:00
David Tsay
f04b5b689e suppress plot view on view condition sets 2020-03-20 17:08:23 -07:00
David Tsay
05d981768e change string output to enum for plot views 2020-03-20 17:07:51 -07:00
David Tsay
e4a6c21101 load composition using less code 2020-03-20 15:08:45 -07:00
David Tsay
d51dd8b7d0 fix broken tests 2020-03-20 15:00:24 -07:00
David Tsay
aed5377ad2 use existing var instead of recalculating
on listener should be off in destroy()
2020-03-20 11:44:05 -07:00
Shefali Joshi
459b2060a5 Merge pull request #2727 from nasa/swg-randomness
Adds customizable randomness factor to sine wave generator
2020-03-20 10:25:17 -07:00
David Tsay
cbeb25c583 request telemetry from all conditions
if conditions have no criteria (isDefault) then nothing will be requested
2020-03-20 10:16:02 -07:00
David Tsay
b38a9ad4ce exclude default condition when requesting telemetry 2020-03-20 10:12:02 -07:00
David Tsay
ecf3e19f16 remove listener on destroy 2020-03-19 20:30:27 -07:00
David Tsay
3b82fd5d8b Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-19 20:28:40 -07:00
David Tsay
e08b4ff0ab Merge pull request #2751 from nasa/fix-enum-metadata
Handles enum meta data for criteria
2020-03-19 20:18:35 -07:00
David Tsay
1f3ec77bf1 Merge branch 'topic-conditionals' into fix-enum-metadata 2020-03-19 20:15:05 -07:00
David Tsay
3f61db2067 pass telemetry down to criteria. criteria listens to corresponding endpoints. 2020-03-19 17:30:02 -07:00
Shefali Joshi
ce1fdbddda Merge pull request #2757 from nasa/conditions-set-cleanup
[Conditions] Condition Set cleanup
2020-03-19 15:19:13 -07:00
Joshi
5332d136b7 Fix small bug with inputCount 2020-03-19 15:16:12 -07:00
Joshi
983ed7f0e7 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into fix-enum-metadata 2020-03-19 14:06:25 -07:00
David Tsay
11978cd869 remove straggler call
linting fix
2020-03-19 10:22:48 -07:00
David Tsay
00d1b5e69f avoid multiple instantiations of condtionManager if possible 2020-03-19 10:18:57 -07:00
David Tsay
6731283cf8 Merge pull request #2734 from nasa/condition-description
Condition dynamic summary description based on criteria
2020-03-18 15:25:19 -07:00
David Tsay
6b4cd25417 Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-18 10:58:17 -07:00
Joel McKinnon
0cd2799d00 added isLastCriterion comp method 2020-03-18 10:24:43 -07:00
David Tsay
243b9cac24 subscribe to telemetry 2020-03-18 10:21:53 -07:00
Joel McKinnon
316e0f24cf added fieldName prop to telemetry object, computed props for canEvaluateCriteria, getConjunction, simplified getRule method 2020-03-18 10:18:42 -07:00
Joshi
05f94edb49 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into topic-conditionals 2020-03-18 09:29:10 -07:00
Joshi
e22458f09e Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-18 09:28:53 -07:00
Joel McKinnon
cc2df8401b vue style render of description 2020-03-17 17:36:55 -07:00
Shefali Joshi
43a82ec05f Conditional styles for drawing objects (#2740)
* Hardcoded prototype - conditional styles for display layout or generator domain objects only.
* Adds background colors list
* Show conditional styles for subObjectViews and telemetryViews
* Uses telemetry provider to determine which style is active
* Removes hardcoded conditionSet work used for prototype
* Add default styles to conditionalStyles when a condition set is added to the domain object.
* Use EventEmitter alias instead of eventEmitter3 in imports
* Change StyleRuleManager to accept conditionStyle objects instead of domainObjects
* Uses a map for conditional styles instead of a list in order to improve performance
* Use in-built api to check for identifier equality
* Removes unnecessary object get call.
* Adds conditional styles for drawing objects
* Removes hard coded conditionSetIdentifier
* Fixes small conditionManager bug
2020-03-17 14:42:15 -07:00
David Tsay
fe2e29d69b Merge pull request #2753 from nasa/remove-ConditionSetView-telemetry
Remove telemetry subscription from conditionSet edit view
2020-03-17 13:49:25 -07:00
David Tsay
60aecfe27e Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-17 12:00:57 -07:00
Joshi
5d21a8b6fe Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into remove-ConditionSetView-telemetry 2020-03-17 11:06:51 -07:00
Shefali Joshi
500ab52476 Merge pull request #2754 from nasa/conditions-observables
[Conditions] Remove uneccessary update function
2020-03-17 11:05:46 -07:00
Joel McKinnon
b0bb723357 resolve merge, address review comments 2020-03-17 10:31:19 -07:00
David Tsay
b7fffeab1c conditionManager controls subscriptions 2020-03-16 21:57:42 -07:00
David Tsay
e339d743ed remove uneccessary update call 2020-03-16 16:01:12 -07:00
David Tsay
84f0d49d6f code is documentation 2020-03-16 15:35:13 -07:00
David Tsay
090e89d524 remove testing aid 2020-03-16 15:34:12 -07:00
David Tsay
90dd53e954 Merge branch 'topic-conditionals' into dave/condition-telemetry-request 2020-03-16 15:31:19 -07:00
Joshi
6ab60ab52e ConditionSet view listens to a listener from conditionCollection to display current output. 2020-03-16 15:00:16 -07:00
Joshi
8975bc8c55 Handles enum meta data for criteria
Ensures metadata with sources are also handled correctly
2020-03-16 13:36:33 -07:00
David Tsay
55e5c49f6e Merge pull request #2747 from nasa/conditions-do-refactor
Conditions refactor - not domain objects anymore
2020-03-16 10:59:31 -07:00
Joshi
f090f7ffe7 Removes comment. 2020-03-16 10:51:33 -07:00
Joshi
94b5617e63 Since we're observing for changes to the conditionSet domain object, we don't need to re-fetch it with persist. 2020-03-16 10:50:11 -07:00
David Tsay
41c79c6032 [Conditions] ConditionManager should observe its own mutations (#2748)
* change blur listener to change listener to watch for immediate updates to input

* move observe ConditionManager logic into ConditionManager
2020-03-16 10:14:46 -07:00
Joshi
83c648cc26 Addresses comments 2020-03-13 14:24:47 -07:00
Joshi
76e7fec1a0 Ensures the conditionManager is in sync with outside changes so that composition is not thrown away. 2020-03-13 14:08:14 -07:00
Joshi
09bfd80f69 Fix bug with telemetry disappearing 2020-03-13 12:50:06 -07:00
David Tsay
15a7d03e74 rename function for clarity 2020-03-13 09:36:17 -07:00
David Tsay
1dc9743484 remove unecessary get call 2020-03-13 09:26:46 -07:00
Joshi
8f05c57d1a Fixes clone condition bug 2020-03-13 09:09:50 -07:00
Joshi
81caa27cba Fixes failing tests 2020-03-12 14:38:39 -07:00
Joshi
74a7ef2565 Fix bug with removeCondition 2020-03-12 14:06:43 -07:00
Joshi
649575fd2d Moves domain object observe logic to the condition set telemetry provider 2020-03-12 13:29:50 -07:00
Joel McKinnon
b75b7a958a added input types and fixed bug for isDefined, isUndefined comparisons 2020-03-12 13:14:54 -07:00
Joshi
625b39d722 Fix typo 2020-03-12 13:06:56 -07:00
Joshi
65f80f4c45 Resubscribe to the conditionSet telemetry provider when the condition set domain object changes. 2020-03-12 13:05:03 -07:00
Joshi
02cd3048c8 Removed conditions as domain objects 2020-03-12 12:33:04 -07:00
Joel McKinnon
63feaef988 reinitialize rule string in forEach 2020-03-11 15:22:17 -07:00
David Tsay
6095872682 remove logging 2020-03-10 10:08:53 -07:00
David Tsay
dba55867f4 fix broken unit tests 2020-03-10 09:59:12 -07:00
David Tsay
0da80c2a67 move var declaration around for clarity 2020-03-10 09:58:57 -07:00
David Tsay
084df5329a Merge branch 'topic-conditionals' into dave/condition-telemetry-request 2020-03-09 16:33:55 -07:00
David Tsay
49ff0c79db tidy up condition manager use in telemetry provider 2020-03-09 16:15:53 -07:00
David Tsay
7a4b967a01 condition sets with no telemetry points should not provide telemetry 2020-03-09 13:28:37 -07:00
Andrew Henry
ddfa611c44 Merge branch 'master' into swg-randomness 2020-03-09 13:11:31 -07:00
Joel McKinnon
efca7c8e58 added descriptions to conditions 2020-03-09 12:42:44 -07:00
Joel McKinnon
8900072239 added computed property and method to get description 2020-03-09 11:50:11 -07:00
David Tsay
a7e57c62f4 linting fixes (#2733) 2020-03-09 11:15:15 -07:00
David Tsay
24bade2284 provide correct data to getLatestTimestamp call 2020-03-09 10:59:05 -07:00
Deep Tailor
5fcc4eebe1 Add a re-calculate column width button (#2719)
* Add a recalculate Column width button

* Tweaks to telemetry table for recalculateColumnWidths

- Recalc button now hidden if isAutosizeEnabled === false;
- Recalc button label, title edited for clarity;
- Normalized button titles for other table buttons;
- Fixed `.c-separator` height issue;

* toggle between expand and autosize

* Tweaked button title text

* remove nested loop

* fix lint errors

* remove unecessary promise and use clientWidth instead of offsetWidth

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-03-09 10:31:26 -07:00
Charles Hacskaylo
27a09239e3 Integrate Conditionals styling into topic-conditionals (try 2) (#2730)
* Styling for Conditionals WIP

- Condition Set markup and styling WIP;

* Styling for Conditionals WIP

- Condition Set markup and styling WIP;

* Styling for Conditionals WIP

- Main layout and container styling refinement, simplification and
normalization;

* Styling for Conditionals WIP

- Begin styling for individual condition elements;

* Styling for Conditionals WIP

- Condition styling, very initial;

* Conditionals styling WIP

- Redo work done previously and lost due to merging;
- Overall layout in edit mode;
- Styling for hint element;

* Conditionals styling WIP

- Major progress on Conditionals edit-view styling;
- Grid layout WIP in condition element;
- Added new `.c-grippy` class;

* Conditionals styling WIP

- discreteItem theme constants refined, add
`$colorDiscreteItemCurrentBg` color value;
- `.c-grippy` enhanced;
- Condition layout significantly refined;

* Conditionals styling WIP

- Styling for browse view in Condition Set;
- Refined alignment and styles for condition header;

* Conditionals styling WIP

- Cleanups;
- Significant improvements to flex layout;
- Test Data layout and element formatting;

* Conditionals styling WIP

- Better approach to condition set hinting;

* Conditionals styling WIP

- Merge and integrate changes from Joel;
- 'Add Criteria' button now disabled until telemetry has been added;
- Fix JS configuration error with help from Joel;

* Conditionals styling WIP

- Much better flex approach to sections layout;
- Sanding and shimming;

* Conditionals styling WIP

- Fixed some linting;

* Conditionals styling WIP

- Tweaks for layouts issues;
2020-03-09 10:08:39 -07:00
Nikhil
8d86c914a1 Merge pull request #2732 from nasa/minmax-fix-382020
Revert minMax to minmax
2020-03-09 10:05:42 -07:00
Deep Tailor
fab04519c6 fix minmax typo 2020-03-08 20:11:04 -07:00
David Tsay
4a5e106709 request LAD for conditions provides telemetry 2020-03-06 17:33:21 -08:00
David Tsay
cf9336dae9 WIP receive criterion results and compute condition results 2020-03-06 11:01:15 -08:00
David Tsay
7f32d196e4 WIP request gets to telemetry criterion before erroring 2020-03-05 23:11:20 -08:00
David Tsay
897d05276a WIP continue to add support for LAD request
TODO conditionCollection needs to load before condition requests can be made
2020-03-05 16:42:31 -08:00
David Tsay
3e6509ce6f WIP add support for LAD request 2020-03-05 12:14:27 -08:00
Andrew Henry
9b8b63a0d8 Revert record size limit to 5000 2020-03-05 08:56:47 -08:00
Andrew Henry
a1b1fa464e Adds customizable randomness factor to sine wave generator. Fixes #2726 2020-03-05 08:55:05 -08:00
274 changed files with 11845 additions and 4229 deletions

View File

@@ -2,7 +2,7 @@ version: 2
jobs:
build:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:13-browsers
environment:
CHROME_BIN: "/usr/bin/google-chrome"
steps:
@@ -11,7 +11,7 @@ jobs:
name: Update npm
command: 'sudo npm install -g npm@latest'
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
key: dependency-cache-node-13-{{ checksum "package.json" }}
- run:
name: Installing dependencies (npm install)
command: npm install
@@ -20,8 +20,8 @@ jobs:
paths:
- node_modules
- run:
name: npm run test
command: npm run test
name: npm run test:coverage
command: npm run test:coverage
- run:
name: npm run lint
command: npm run lint

View File

@@ -1,3 +1,4 @@
const LEGACY_FILES = ["platform/**", "example/**"];
module.exports = {
"env": {
"browser": true,
@@ -10,7 +11,8 @@ module.exports = {
},
"extends": [
"eslint:recommended",
"plugin:vue/recommended"
"plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible"
],
"parser": "vue-eslint-parser",
"parserOptions": {
@@ -22,6 +24,9 @@ module.exports = {
}
},
"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",
"curly": "error",
"eqeqeq": "error",
@@ -66,6 +71,56 @@ module.exports = {
],
"dot-notation": "error",
"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": [
"error",
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"
}
}
]
};

1
.gitignore vendored
View File

@@ -38,3 +38,4 @@ protractor/logs
npm-debug.log
package-lock.json
report.*.json

4
API.md
View File

@@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints. Keys in this thi
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.
* `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.
* `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`: 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.
##### The Time Conductor and Telemetry

View File

@@ -103,7 +103,7 @@ the name chosen could not be mistaken for a topic or master branch.
### Merging
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,
and the pull request should include a completed author checklist, also
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 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 requests __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 requests __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
Contributions to Open MCT are expected to meet the following standards.
@@ -122,89 +131,96 @@ changes.
### Code Standards
JavaScript sources in Open MCT must satisfy JSLint under its default
settings. This is verified by the command line build.
JavaScript sources in Open MCT must satisfy the ESLint rules defined in
this repository. This is verified by the command line build.
#### Code Guidelines
JavaScript sources in Open MCT should:
* 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. Heres 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. Functions should only have a single return statement.
1. Avoid the use of "magic" values.
eg.
```JavaScript
Const UNAUTHORIZED = 401
if (responseCode === UNAUTHORIZED)
```
is preferable to
```JavaScript
if (responseCode === 401)
```
1. Dont use the ternary operator. Yes it's terse, but there's probably a clearer way of writing it.
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,
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
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?
3. Command line build passes?
4. Changes have been smoke-tested?
5. Testing instructions included?
### Reviewer Checklist
@@ -299,3 +316,4 @@ checklist).
2. Appropriate unit tests included?
3. Code style and in-line documentation are appropriate?
4. Commit messages meet standards?
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)

View File

@@ -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
Administration. All rights reserved.
@@ -18,4 +18,4 @@
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.
-->
-->

View File

@@ -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
* Administration. All rights reserved.
*
@@ -18,4 +18,4 @@
* 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.
*****************************************************************************/
*****************************************************************************/

View File

@@ -1,80 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
/
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/0</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Wed Dec 11 2019 13:15:10 GMT-0800 (Pacific Standard Time)
</div>
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="sorter.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

View File

@@ -1,158 +0,0 @@
var addSorting = (function () {
"use strict";
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() { return document.querySelector('.coverage-summary'); }
// returns the thead element of the summary table
function getTableHeader() { return getTable().querySelector('thead tr'); }
// returns the tbody element of the summary table
function getTableBody() { return getTable().querySelector('tbody'); }
// returns the th element for nth column
function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function (a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function (a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function () {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i =0 ; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function () {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData(cols);
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

View File

@@ -9,7 +9,8 @@ define([
values: [
{
key: "name",
name: "Name"
name: "Name",
format: "string"
},
{
key: "utc",
@@ -49,7 +50,8 @@ define([
values: [
{
key: "name",
name: "Name"
name: "Name",
format: "string"
},
{
key: "utc",
@@ -98,7 +100,7 @@ define([
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend(
return Object.assign(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]

View File

@@ -31,6 +31,7 @@ define([
period: 10,
offset: 0,
dataRateInHz: 1,
randomness: 0,
phase: 0
};
@@ -52,7 +53,8 @@ define([
'period',
'offset',
'dataRateInHz',
'phase'
'phase',
'randomness'
];
request = request || {};

View File

@@ -65,8 +65,8 @@
name: data.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase)
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
}
});
nextStep += step;
@@ -99,6 +99,7 @@
var offset = request.offset;
var dataRateInHz = request.dataRateInHz;
var phase = request.phase;
var randomness = request.randomness;
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
@@ -110,8 +111,8 @@
name: request.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, period, amplitude, offset, phase),
cos: cos(nextStep, period, amplitude, offset, phase)
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
cos: cos(nextStep, period, amplitude, offset, phase, randomness)
});
}
self.postMessage({
@@ -120,14 +121,14 @@
});
}
function cos(timestamp, period, amplitude, offset, phase) {
function cos(timestamp, period, amplitude, offset, phase, randomness) {
return amplitude *
Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
function sin(timestamp, period, amplitude, offset, phase) {
function sin(timestamp, period, amplitude, offset, phase, randomness) {
return amplitude *
Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
function sendError(error, message) {

View File

@@ -122,6 +122,17 @@ define([
"telemetry",
"phase"
]
},
{
name: "Randomness",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "randomness",
required: true,
property: [
"telemetry",
"randomness"
]
}
],
initialize: function (object) {
@@ -130,7 +141,8 @@ define([
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0
phase: 0,
randomness: 0
};
}
});

View File

@@ -52,6 +52,7 @@ define([
return {
name: name,
utc: Math.floor(timestamp / 5000) * 5000,
local: Math.floor(timestamp / 5000) * 5000,
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
};
}
@@ -78,7 +79,7 @@ define([
},
request: function (domainObject, options) {
var start = options.start;
var end = options.end;
var end = Math.min(options.end, Date.now());
var data = [];
while (start <= end && data.length < 5000) {
data.push(pointForTimestamp(start, domainObject.name));
@@ -118,6 +119,14 @@ define([
name: 'Time',
key: 'utc',
format: 'utc',
hints: {
domain: 2
}
},
{
name: 'Local Time',
key: 'local',
format: 'local-format',
hints: {
domain: 1
}

View File

@@ -1,14 +1,14 @@
<template>
<div class="example">{{ msg }}</div>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
data() {
return {
msg: 'Hello world!'
}
}
}
}
</script>

View File

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

View File

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

View File

@@ -2,10 +2,8 @@
"name": "openmct",
"version": "1.0.0-snapshot",
"description": "The Open MCT core platform",
"dependencies": {},
"devDependencies": {
"acorn": "6.2.0",
"angular": "1.4.14",
"angular": "1.7.9",
"angular-route": "1.4.14",
"babel-eslint": "8.2.6",
"comma-separated-values": "^3.6.4",
@@ -25,6 +23,7 @@
"d3-time-format": "2.1.x",
"eslint": "5.2.0",
"eslint-plugin-vue": "^6.0.0",
"eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
"eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0",
"express": "^4.13.1",
@@ -39,26 +38,28 @@
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine-core": "^3.1.0",
"jsdoc": "^3.3.2",
"karma": "^2.0.3",
"karma-chrome-launcher": "^2.2.0",
"karma": "5.0.9",
"karma-chrome-launcher": "3.1.0",
"karma-cli": "^1.0.1",
"karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-html-reporter": "^0.2.7",
"karma-jasmine": "^1.1.2",
"karma-jasmine": "^2.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.0",
"location-bar": "^3.0.1",
"lodash": "^3.10.1",
"lodash": "^4.17.12",
"markdown-toc": "^0.11.7",
"marked": "^0.3.5",
"mini-css-extract-plugin": "^0.4.1",
"minimist": "^1.1.1",
"moment": "^2.11.1",
"moment": "2.25.3",
"moment-duration-format": "^2.2.2",
"moment-timezone": "^0.5.21",
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"node-sass": "^4.9.2",
"painterro": "^0.2.65",
"plotly.js": "^1.54.1",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
@@ -76,14 +77,16 @@
"zepto": "^1.2.0"
},
"scripts": {
"clean": "rm -rf ./dist",
"start": "node app.js",
"lint": "eslint platform example src/**/*.{js,vue} openmct.js",
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
"lint": "eslint platform example src --ext .js,.vue openmct.js",
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
"build:prod": "cross-env NODE_ENV=production webpack",
"build:dev": "webpack",
"build:watch": "webpack --watch",
"test": "karma start --single-run",
"test-debug": "cross-env NODE_ENV=debug karma start --no-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:coverage": "cross-env NODE_ENV=production COVERAGE=true NODE_OPTIONS=\"--max-old-space-size=4096\" karma start --single-run",
"test:watch": "karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",

View File

@@ -6,6 +6,12 @@
ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
</div>
<button
ng-if="ngModel.dialog.topBarButton"
class="c-button c-button--major"
ng-click="ngModel.topBarButton.onClick">
{{ ngModel.topBarButton.label }}
</button>
</div>
<div class="w-messages c-overlay__messages">
<mct-include
@@ -16,7 +22,7 @@
<button ng-repeat="dialogAction in ngModel.dialog.actions"
class="c-button c-button--major"
ng-click="dialogAction.action()">
{{dialogAction.label}}
{{ dialogAction.label }}
</button>
</div>
</div>

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define(
['../../../../../src/api/objects/object-utils'],
['objectUtils'],
function (objectUtils) {
/**

View File

@@ -21,44 +21,15 @@
*****************************************************************************/
define([
"./src/NotificationIndicatorController",
"./src/NotificationIndicator",
"./src/NotificationService",
"./res/notification-indicator.html"
"./src/NotificationService"
], function (
NotificationIndicatorController,
NotificationIndicator,
NotificationService,
notificationIndicatorTemplate
NotificationService
) {
return {
name:"platform/commonUI/notification",
definition: {
"extensions": {
"templates": [
{
"key": "notificationIndicatorTemplate",
"template": notificationIndicatorTemplate
}
],
"controllers": [
{
"key": "NotificationIndicatorController",
"implementation": NotificationIndicatorController,
"depends": [
"$scope",
"openmct",
"dialogService"
]
}
],
"indicators": [
{
"implementation": NotificationIndicator,
"priority": "fallback"
}
],
"services": [
{
"key": "notificationService",

View File

@@ -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>

View File

@@ -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;
}
);

View File

@@ -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();
});
});
}
);

View File

@@ -26,7 +26,7 @@
* @namespace platform/containment
*/
define(
['../../../src/api/objects/object-utils'],
['objectUtils'],
function (objectUtils) {
function PersistableCompositionPolicy(openmct) {

View File

@@ -81,7 +81,7 @@ define(
baseContext = context || {};
}
var actionContext = _.extend({}, baseContext);
var actionContext = Object.assign({}, baseContext);
actionContext.domainObject = this.domainObject;
return this.actionService.getActions(actionContext);

View File

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

View File

@@ -121,7 +121,7 @@ define(['lodash'], function (_) {
*/
ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
this.externalIdentifiers.push(this.getId(child));
var index = _.findIndex(parent.composition, function (id) {
var index = parent.composition.findIndex(id => {
return _.isEqual(child.identifier, id);
});
var copyOfChild = this.copyObject(child);

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* 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

View File

@@ -33,7 +33,7 @@ define(
var CONNECTED = {
text: "Connected",
glyphClass: "ok",
statusClass: "s-status-ok",
statusClass: "s-status-on",
description: "Connected to the domain object database."
},
DISCONNECTED = {

View File

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

View File

@@ -32,7 +32,7 @@ define(
var CONNECTED = {
text: "Connected",
glyphClass: "ok",
statusClass: "s-status-ok",
statusClass: "s-status-on",
description: "Connected to the domain object database."
},
DISCONNECTED = {

View File

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

View File

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

View File

@@ -25,7 +25,7 @@
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
*/
define([
'../../../../src/api/objects/object-utils',
'objectUtils',
'lodash'
], function (
objectUtils,
@@ -191,7 +191,7 @@ define([
}
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);
});

View File

@@ -25,7 +25,7 @@
*/
define(
[
'../../../src/api/objects/object-utils',
'objectUtils',
'lodash'
],
function (
@@ -235,7 +235,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var sourceMap = _.keyBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
telemetryAPI.legacyProvider;
@@ -300,7 +300,7 @@ define(
var defaultRange = metadata.valuesForHints(['range'])[0];
defaultRange = defaultRange ? defaultRange.key : undefined;
var sourceMap = _.indexBy(metadata.values(), 'key');
var sourceMap = _.keyBy(metadata.values(), 'key');
var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
telemetryAPI.legacyProvider;

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, United States Government
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -28,7 +28,7 @@ define([
'./api/api',
'./api/overlays/OverlayAPI',
'./selection/Selection',
'./api/objects/object-utils',
'objectUtils',
'./plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin',
'./plugins/buildInfo/plugin',
@@ -249,7 +249,7 @@ define([
this.legacyRegistry = new BundleRegistry();
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.TelemetryTable());
@@ -265,6 +265,8 @@ define([
this.install(this.plugins.ImportExport());
this.install(this.plugins.WebPage());
this.install(this.plugins.Condition());
this.install(this.plugins.ConditionWidget());
this.install(this.plugins.NotificationIndicator());
}
MCT.prototype = Object.create(EventEmitter.prototype);
@@ -349,17 +351,13 @@ define([
* @param {HTMLElement} [domElement] the DOM element in which to run
* 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) {
this.install(this.plugins.DisplayLayout({
showAsView: ['summary-widget']
}));
}
if (!domElement) {
domElement = document.body;
}
this.element = domElement;
this.legacyExtension('runs', {
@@ -399,24 +397,31 @@ define([
// something has depended upon objectService. Cool, right?
this.$injector.get('objectService');
var appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
if (!isHeadlessMode) {
var appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
this.layout = appLayout.$refs.layout;
Browse(this);
this.layout = appLayout.$refs.layout;
Browse(this);
}
this.router.start();
this.emit('start');
}.bind(this));
};
MCT.prototype.startHeadless = function () {
let unreachableNode = document.createElement('div');
return this.start(unreachableNode, true);
}
/**
* Install a plugin in MCT.
*

View File

@@ -21,11 +21,11 @@
*****************************************************************************/
define([
'./MCT',
'./plugins/plugins',
'legacyRegistry'
], function (MCT, plugins, legacyRegistry) {
xdescribe("MCT", function () {
'legacyRegistry',
'testUtils'
], function (plugins, legacyRegistry, testUtils) {
describe("MCT", function () {
var openmct;
var mockPlugin;
var mockPlugin2;
@@ -38,7 +38,7 @@ define([
mockListener = jasmine.createSpy('listener');
oldBundles = legacyRegistry.list();
openmct = new MCT();
openmct = testUtils.createOpenMct();
openmct.install(mockPlugin);
openmct.install(mockPlugin2);
@@ -63,8 +63,11 @@ define([
});
describe("start", function () {
beforeEach(function () {
openmct.start();
let appHolder;
beforeEach(function (done) {
appHolder = document.createElement("div");
openmct.on('start', done);
openmct.start(appHolder);
});
it("calls plugins for configuration", function () {
@@ -75,25 +78,51 @@ define([
it("emits a start event", function () {
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 () {
var testAssetPath;
beforeEach(function () {
testAssetPath = "some/path";
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
openmct.setAssetPath(testAssetPath);
});
it("internally configures the path for assets", function () {
expect(openmct.legacyExtension).toHaveBeenCalledWith(
'constants',
{
key: "ASSETS_PATH",
value: testAssetPath
}
);
it("configures the path for assets", function () {
testAssetPath = "some/path/";
openmct.setAssetPath(testAssetPath);
expect(openmct.getAssetPath()).toBe(testAssetPath);
});
it("adds a trailing /", function () {
testAssetPath = "some/path";
openmct.setAssetPath(testAssetPath);
expect(openmct.getAssetPath()).toBe(testAssetPath + "/");
});
});
});

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (objectUtils) {
function ActionDialogDecorator(mct, actionService) {
this.mct = mct;

View File

@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['../../api/objects/object-utils'], function (objectUtils) {
define(['objectUtils'], function (objectUtils) {
function AdapterCapability(domainObject) {
this.domainObject = domainObject;
}

View File

@@ -24,7 +24,7 @@
* Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
*/
define([
'../../api/objects/object-utils',
'objectUtils',
'../../../platform/core/src/capabilities/ContextualDomainObject'
], function (objectUtils, ContextualDomainObject) {
function AlternateCompositionCapability($injector, domainObject) {

View File

@@ -31,6 +31,7 @@ define([
var capability = viewConstructor(domainObject);
var oldInvoke = capability.invoke.bind(capability);
/* eslint-disable you-dont-need-lodash-underscore/map */
capability.invoke = function () {
var availableViews = oldInvoke();
var newDomainObject = capability
@@ -52,6 +53,8 @@ define([
.map('view')
.value();
};
/* eslint-enable you-dont-need-lodash-underscore/map */
return capability;
};
}

View File

@@ -22,7 +22,7 @@
define([
'../capabilities/AlternateCompositionCapability',
'../../api/objects/object-utils'
'objectUtils'
], function (
AlternateCompositionCapability,
objectUtils

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
utils
) {

View File

@@ -78,7 +78,7 @@ define([
};
TimeSettingsURLHandler.prototype.parseQueryParams = function () {
var searchParams = _.pick(this.$location.search(), _.values(SEARCH));
var searchParams = _.pick(this.$location.search(), Object.values(SEARCH));
var parsedParams = {
clock: searchParams[SEARCH.MODE],
timeSystem: searchParams[SEARCH.TIME_SYSTEM]

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
utils
) {

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'../../api/objects/object-utils'
'objectUtils'
], function (
objectUtils
) {

View File

@@ -1,7 +1,7 @@
define([
'./LegacyViewProvider',
'./TypeInspectorViewProvider',
'../../api/objects/object-utils'
'objectUtils'
], function (
LegacyViewProvider,
TypeInspectorViewProvider,

View File

@@ -70,7 +70,7 @@ define([
* @memberof module:openmct.CompositionAPI#
*/
CompositionAPI.prototype.get = function (domainObject) {
var provider = _.find(this.registry, function (p) {
var provider = this.registry.find(p => {
return p.appliesTo(domainObject);
});

View File

@@ -122,7 +122,7 @@ define([
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;
});

View File

@@ -22,7 +22,7 @@
define([
'lodash',
'../objects/object-utils'
'objectUtils'
], function (
_,
objectUtils
@@ -143,7 +143,7 @@ define([
var keyString = objectUtils.makeKeyString(domainObject.identifier);
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;
});
@@ -196,8 +196,8 @@ define([
* @private
*/
DefaultCompositionProvider.prototype.includes = function (parent, childId) {
return parent.composition.findIndex(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
return parent.composition.some(composee =>
this.publicAPI.objects.areIdsEqual(composee, childId));
};
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {

View File

@@ -128,6 +128,11 @@ export default class NotificationAPI extends EventEmitter {
return this._notify(notificationModel);
}
dismissAllNotifications() {
this.notifications = [];
this.emit('dismiss-all');
}
/**
* Minimize a notification. The notification will still be available
* from the notification list. Typically notifications with a

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
define([
'./object-utils.js',
'objectUtils',
'lodash'
], function (
utils,

View File

@@ -22,7 +22,7 @@
define([
'lodash',
'./object-utils',
'objectUtils',
'./MutableObject',
'./RootRegistry',
'./RootObjectProvider',

View File

@@ -43,7 +43,7 @@ define([
}
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 () {
return key;
});

View File

@@ -1,5 +1,5 @@
define([
'../object-utils'
'objectUtils'
], function (
objectUtils
) {

View File

@@ -85,9 +85,9 @@ define([
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
valueMetadata.max = Math.max(valueMetadata.values);
valueMetadata.min = Math.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
@@ -103,7 +103,7 @@ define([
var metadata = domainObject.telemetry || {};
if (this.typeHasTelemetry(domainObject)) {
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
_.extend(metadata, typeMetadata);
Object.assign(metadata, typeMetadata);
if (!metadata.values) {
metadata.values = valueMetadatasFromOldFormat(metadata);
}

View File

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

View File

@@ -57,13 +57,13 @@ define([
if (valueMetadata.format === 'enum') {
if (!valueMetadata.values) {
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
}
if (!valueMetadata.hasOwnProperty('max')) {
valueMetadata.max = _.max(valueMetadata.values) + 1;
valueMetadata.max = Math.max(valueMetadata.values) + 1;
}
if (!valueMetadata.hasOwnProperty('min')) {
valueMetadata.min = _.min(valueMetadata.values) - 1;
valueMetadata.min = Math.min(valueMetadata.values) - 1;
}
}
@@ -81,7 +81,7 @@ define([
function TelemetryMetadataManager(metadata) {
this.metadata = metadata;
this.valueMetadatas = this.metadata.values.map(applyReasonableDefaults);
this.valueMetadatas = this.metadata.values ? this.metadata.values.map(applyReasonableDefaults) : [];
}
/**
@@ -121,7 +121,7 @@ define([
return metadata.hints[hint];
}
});
return _.sortByAll(matchingMetadata, ...iteratees);
return _.sortBy(matchingMetadata, ...iteratees);
};
TelemetryMetadataManager.prototype.getFilterableValues = function () {

View File

@@ -81,6 +81,24 @@ define([
return printj.sprintf(formatString, baseFormat.call(this, value));
};
}
if (valueMetadata.format === 'string') {
this.formatter.parse = function (value) {
if (value === undefined) {
return '';
}
if (typeof value === 'string') {
return value;
} else {
return value.toString();
}
};
this.formatter.format = function (value) {
return value;
};
this.formatter.validate = function (value) {
return typeof value === 'string';
};
}
}
TelemetryValueFormatter.prototype.parse = function (datum) {

View File

@@ -21,22 +21,24 @@
*****************************************************************************/
<template>
<table class="c-table c-lad-table">
<thead>
<tr>
<th>Name</th>
<th>Timestamp</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<lad-row
v-for="item in items"
:key="item.key"
:domain-object="item.domainObject"
/>
</tbody>
</table>
<div class="c-lad-table-wrapper">
<table class="c-table c-lad-table">
<thead>
<tr>
<th>Name</th>
<th>Timestamp</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<lad-row
v-for="item in items"
:key="item.key"
:domain-object="item.domainObject"
/>
</tbody>
</table>
</div>
</template>
<script>
@@ -73,7 +75,7 @@ export default {
this.items.push(item);
},
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);
},

View File

@@ -102,7 +102,7 @@ export default {
this.compositions.push({composition, addCallback, removeCallback});
},
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];
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
@@ -130,7 +130,7 @@ export default {
removeSecondary(primary) {
return (identifier) => {
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);

View File

@@ -30,7 +30,7 @@ define(
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
// PENDING: Still trying to connect, and haven't failed yet.
var CONNECTED = {
statusClass: "s-status-ok"
statusClass: "s-status-on"
},
PENDING = {
statusClass: "s-status-warning-lo"

View File

@@ -122,7 +122,7 @@ define(
it("indicates success if connection is nominal", function () {
jasmine.clock().tick(pluginOptions.interval + 1);
ajaxOptions.success();
expect(indicatorElement.classList.contains('s-status-ok')).toBe(true);
expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
});
it("indicates an error when the server cannot be reached", function () {

View File

@@ -23,16 +23,14 @@
import EventEmitter from 'EventEmitter';
import uuid from 'uuid';
import TelemetryCriterion from "./criterion/TelemetryCriterion";
import { TRIGGER } from "./utils/constants";
import {computeCondition} from "./utils/evaluator";
import { evaluateResults } from './utils/evaluator';
import { getLatestTimestamp } from './utils/time';
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
/*
* conditionConfiguration = {
* identifier: {
* key: '',
* namespace: ''
* },
* trigger: 'any'/'all',
* id: uuid,
* trigger: 'any'/'all'/'not','xor',
* criteria: [
* {
* telemetry: '',
@@ -48,44 +46,70 @@ export default class ConditionClass extends EventEmitter {
/**
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
* @constructor
* @param conditionConfiguration: {identifier: {domainObject.identifier},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
*/
constructor(conditionConfiguration, openmct) {
constructor(conditionConfiguration, openmct, conditionManager) {
super();
this.openmct = openmct;
this.id = this.openmct.objects.makeKeyString(conditionConfiguration.identifier);
this.conditionManager = conditionManager;
this.id = conditionConfiguration.id;
this.criteria = [];
this.criteriaResults = {};
this.result = undefined;
this.timeSystems = this.openmct.time.getAllTimeSystems();
if (conditionConfiguration.configuration.criteria) {
this.createCriteria(conditionConfiguration.configuration.criteria);
}
this.trigger = conditionConfiguration.configuration.trigger;
this.openmct.objects.get(this.id).then(obj => this.observeForChanges(obj));
}
observeForChanges(conditionDO) {
this.stopObservingForChanges = this.openmct.objects.observe(conditionDO, '*', this.update.bind(this));
getResult(datum) {
if (!datum || !datum.id) {
console.log('no data received');
return;
}
if (this.isTelemetryUsed(datum.id)) {
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);
}
}
update(newDomainObject) {
this.updateTrigger(newDomainObject.configuration.trigger);
this.updateCriteria(newDomainObject.configuration.criteria);
isAnyOrAllTelemetry(criterion) {
return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any'));
}
isTelemetryUsed(id) {
return this.criteria.some(criterion => {
return this.isAnyOrAllTelemetry(criterion) || criterion.telemetryObjectIdAsString === id;
});
}
update(conditionConfiguration) {
this.updateTrigger(conditionConfiguration.configuration.trigger);
this.updateCriteria(conditionConfiguration.configuration.criteria);
}
updateTrigger(trigger) {
if (this.trigger !== trigger) {
this.trigger = trigger;
this.handleConditionUpdated();
}
}
generateCriterion(criterionConfiguration) {
return {
id: uuid(),
id: criterionConfiguration.id || uuid(),
telemetry: criterionConfiguration.telemetry || '',
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
operation: criterionConfiguration.operation || '',
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
metadata: criterionConfiguration.metadata || ''
@@ -103,14 +127,24 @@ export default class ConditionClass extends EventEmitter {
this.createCriteria(criterionConfigurations);
}
updateTelemetry() {
this.criteria.forEach((criterion) => {
criterion.updateTelemetry(this.conditionManager.telemetryObjects);
});
}
/**
* adds criterion to the condition.
*/
addCriterion(criterionConfiguration) {
let criterion;
let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
let criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
if (criterionConfiguration.telemetry && (criterionConfiguration.telemetry === 'any' || criterionConfiguration.telemetry === 'all')) {
criterion = new AllTelemetryCriterion(criterionConfigurationWithId, this.openmct);
} else {
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
}
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
if (!this.criteria) {
this.criteria = [];
}
@@ -139,22 +173,11 @@ export default class ConditionClass extends EventEmitter {
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
let criterion = found.item;
criterion.unsubscribe();
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
this.criteria.splice(found.index, 1, newCriterion);
if (this.criteriaResults[criterion.id] !== undefined) {
delete this.criteriaResults[criterion.id];
}
}
}
removeCriterion(id) {
if (this.destroyCriterion(id)) {
this.handleConditionUpdated();
}
}
@@ -162,15 +185,12 @@ export default class ConditionClass extends EventEmitter {
let found = this.findCriterion(id);
if (found) {
let criterion = found.item;
criterion.destroy();
// TODO this is passing the wrong args
criterion.off('criterionUpdated', (result) => {
this.handleCriterionUpdated(id, result);
criterion.off('criterionUpdated', (obj) => {
this.handleCriterionUpdated(obj);
});
criterion.destroy();
this.criteria.splice(found.index, 1);
if (this.criteriaResults[criterion.id] !== undefined) {
delete this.criteriaResults[criterion.id];
}
return true;
}
return false;
@@ -180,38 +200,38 @@ export default class ConditionClass extends EventEmitter {
let found = this.findCriterion(criterion.id);
if (found) {
this.criteria[found.index] = criterion.data;
this.subscribe();
// TODO nothing is listening to this
this.emitEvent('conditionUpdated', {
trigger: this.trigger,
criteria: this.criteria
}
}
requestLADConditionResult() {
let latestTimestamp;
let criteriaResults = {};
const criteriaRequests = this.criteria
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
return Promise.all(criteriaRequests)
.then(results => {
results.forEach(resultObj => {
const { id, data, data: { result } } = resultObj;
if (this.findCriterion(id)) {
criteriaResults[id] = !!result;
}
latestTimestamp = getLatestTimestamp(
latestTimestamp,
data,
this.timeSystems,
this.openmct.time.timeSystem()
);
});
return {
id: this.id,
data: Object.assign(
{},
latestTimestamp,
{ result: evaluateResults(Object.values(criteriaResults), this.trigger) }
)
};
});
}
}
handleCriterionResult(eventData) {
const id = eventData.id;
if (this.findCriterion(id)) {
this.criteriaResults[id] = eventData.data.result;
}
this.handleConditionUpdated(eventData.data);
}
subscribe() {
// TODO it looks like on any single criterion update subscriptions fire for all criteria
this.criteria.forEach((criterion) => {
criterion.subscribe();
})
}
handleConditionUpdated(datum) {
// trigger an updated event so that consumers can react accordingly
this.evaluate();
this.emitEvent('conditionResultUpdated',
Object.assign({}, datum, { result: this.result })
);
}
getCriteria() {
@@ -227,21 +247,7 @@ export default class ConditionClass extends EventEmitter {
return success;
}
evaluate() {
this.result = computeCondition(this.criteriaResults, this.trigger === TRIGGER.ALL);
}
emitEvent(eventName, data) {
this.emit(eventName, {
id: this.id,
data: data
});
}
destroy() {
if (typeof this.stopObservingForChanges === 'function') {
this.stopObservingForChanges();
}
this.destroyCriteria();
}
}

View File

@@ -21,107 +21,131 @@
*****************************************************************************/
import Condition from "./Condition";
import { getLatestTimestamp } from './utils/time';
import uuid from "uuid";
import EventEmitter from 'EventEmitter';
export default class ConditionManager extends EventEmitter {
constructor(domainObject, openmct) {
constructor(conditionSetDomainObject, openmct) {
super();
this.openmct = openmct;
this.domainObject = domainObject;
this.timeAPI = this.openmct.time;
this.latestTimestamp = {};
this.instantiate = this.openmct.$injector.get('instantiate');
this.conditionSetDomainObject = conditionSetDomainObject;
this.timeSystems = this.openmct.time.getAllTimeSystems();
this.composition = this.openmct.composition.get(conditionSetDomainObject);
this.composition.on('add', this.subscribeToTelemetry, this);
this.composition.on('remove', this.unsubscribeFromTelemetry, this);
this.compositionLoad = this.composition.load();
this.subscriptions = {};
this.telemetryObjects = {};
this.testData = {conditionTestData: [], applied: false};
this.initialize();
this.stopObservingForChanges = this.openmct.objects.observe(this.conditionSetDomainObject, '*', (newDomainObject) => {
this.conditionSetDomainObject = newDomainObject;
});
}
subscribeToTelemetry(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
if (this.subscriptions[id]) {
console.log('subscription already exists');
return;
}
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
this.subscriptions[id] = this.openmct.telemetry.subscribe(
endpoint,
this.telemetryReceived.bind(this, endpoint)
);
this.updateConditionTelemetry();
}
unsubscribeFromTelemetry(endpointIdentifier) {
const id = this.openmct.objects.makeKeyString(endpointIdentifier);
if (!this.subscriptions[id]) {
console.log('no subscription to remove');
return;
}
this.subscriptions[id]();
delete this.subscriptions[id];
delete this.telemetryObjects[id];
this.removeConditionTelemetry();
}
initialize() {
this.conditionResults = {};
this.observeForChanges(this.domainObject);
this.conditionCollection = [];
if (this.domainObject.configuration.conditionCollection.length) {
this.domainObject.configuration.conditionCollection.forEach((conditionConfigurationId, index) => {
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.initCondition(conditionConfiguration, index)
});
this.conditionClassCollection = [];
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.initCondition(conditionConfiguration, index);
});
} else {
this.addCondition(true);
}
}
observeForChanges(domainObject) {
//TODO: Observe only the conditionCollection property instead of the whole domainObject
this.stopObservingForChanges = this.openmct.objects.observe(domainObject, '*', this.handleConditionCollectionUpdated.bind(this));
updateConditionTelemetry() {
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
}
handleConditionCollectionUpdated(newDomainObject) {
let oldConditionIdentifiers = this.domainObject.configuration.conditionCollection.map((conditionConfigurationId) => {
return this.openmct.objects.makeKeyString(conditionConfigurationId);
});
let newConditionIdentifiers = newDomainObject.configuration.conditionCollection.map((conditionConfigurationId) => {
return this.openmct.objects.makeKeyString(conditionConfigurationId);
});
this.domainObject = newDomainObject;
//check for removed conditions
oldConditionIdentifiers.forEach((identifier, index) => {
if (newConditionIdentifiers.indexOf(identifier) < 0) {
this.removeCondition(identifier);
}
});
let newConditionCount = this.domainObject.configuration.conditionCollection.length - this.conditionCollection.length;
for (let i = 0; i < newConditionCount; i++) {
let conditionConfigurationId = this.domainObject.configuration.conditionCollection[i];
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.initCondition(conditionConfiguration, i);
removeConditionTelemetry() {
let conditionsChanged = false;
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
if (!isAnyAllTelemetry) {
const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
});
if (!found) {
criterion.telemetry = '';
criterion.metadata = '';
criterion.input = [];
criterion.operation = '';
conditionsChanged = true;
}
}
});
});
if (conditionsChanged) {
this.persistConditions();
}
}
updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration);
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
this.persistConditions();
}
initCondition(conditionConfiguration, index) {
let condition = new Condition(conditionConfiguration, this.openmct);
condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
let condition = new Condition(conditionConfiguration, this.openmct, this);
if (index !== undefined) {
this.conditionCollection.splice(index + 1, 0, condition);
this.conditionClassCollection.splice(index + 1, 0, condition);
} else {
this.conditionCollection.unshift(condition);
}
//There are no criteria for a default condition and hence no subscriptions.
//Hence the conditionResult must be manually triggered for it.
if (conditionConfiguration.isDefault) {
this.handleConditionResult();
this.conditionClassCollection.unshift(condition);
}
}
createConditionDomainObject(isDefault, conditionConfiguration) {
createCondition(conditionConfiguration) {
let conditionObj;
if (conditionConfiguration) {
conditionObj = {
...conditionConfiguration,
name: `Copy of ${conditionConfiguration.name}`,
identifier: {
...this.domainObject.identifier,
key: uuid()
id: uuid(),
configuration: {
...conditionConfiguration.configuration,
name: `Copy of ${conditionConfiguration.configuration.name}`
}
};
} else {
conditionObj = {
isDefault: isDefault,
type: 'condition',
identifier: {
...this.domainObject.identifier,
key: uuid()
},
id: uuid(),
configuration: {
name: isDefault ? 'Default' : 'Unnamed Condition',
name: 'Unnamed Condition',
output: 'false',
trigger: 'all',
criteria: isDefault ? [] : [{
criteria: [{
id: uuid(),
telemetry: '',
operation: '',
input: [],
@@ -131,134 +155,216 @@ export default class ConditionManager extends EventEmitter {
summary: ''
};
}
let conditionDomainObjectKeyString = this.openmct.objects.makeKeyString(conditionObj.identifier);
let newDomainObject = this.instantiate(conditionObj, conditionDomainObjectKeyString);
return newDomainObject.useCapability('adapter');
return conditionObj;
}
addCondition(isDefault, index) {
this.createAndSaveConditionDomainObject(!!isDefault, index);
addCondition() {
this.createAndSaveCondition();
}
cloneCondition(conditionConfigurationId, index) {
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.createAndSaveConditionDomainObject(false, index, conditionConfiguration);
});
cloneCondition(conditionConfiguration, index) {
let clonedConfig = JSON.parse(JSON.stringify(conditionConfiguration));
clonedConfig.configuration.criteria.forEach((criterion) => criterion.id = uuid());
this.createAndSaveCondition(index, clonedConfig);
}
createAndSaveConditionDomainObject(isDefault, index, conditionConfiguration) {
let newConditionDomainObject = this.createConditionDomainObject(isDefault, conditionConfiguration);
//persist the condition domain object so that we can do an openmct.objects.get on it and only persist the identifier in the conditionCollection of conditionSet
this.openmct.objects.mutate(newConditionDomainObject, 'created', new Date());
createAndSaveCondition(index, conditionConfiguration) {
const newCondition = this.createCondition(conditionConfiguration);
if (index !== undefined) {
this.domainObject.configuration.conditionCollection.splice(index + 1, 0, newConditionDomainObject.identifier);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index + 1, 0, newCondition);
} else {
this.domainObject.configuration.conditionCollection.unshift(newConditionDomainObject.identifier);
this.conditionSetDomainObject.configuration.conditionCollection.unshift(newCondition);
}
this.persist();
this.initCondition(newCondition, index);
this.persistConditions();
}
removeCondition(identifier) {
let found = this.findConditionById(identifier);
if (found) {
let index = found.index;
let condition = this.conditionCollection[index];
let conditionIdAsString = condition.id;
condition.destroyCriteria();
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
this.conditionCollection.splice(index, 1);
this.domainObject.configuration.conditionCollection.splice(index, 1);
if (this.conditionResults[conditionIdAsString] !== undefined) {
delete this.conditionResults[conditionIdAsString];
}
this.persist();
this.handleConditionResult();
}
removeCondition(index) {
let condition = this.conditionClassCollection[index];
condition.destroy();
this.conditionClassCollection.splice(index, 1);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
this.persistConditions();
}
findConditionById(identifier) {
let found;
for (let i=0, ii=this.conditionCollection.length; i < ii; i++) {
if (this.conditionCollection[i].id === this.openmct.objects.makeKeyString(identifier)) {
found = {
item: this.conditionCollection[i],
index: i
};
break;
}
}
return found;
findConditionById(id) {
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
}
//this.$set(this.conditionCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
reorderConditions(reorderPlan) {
let oldConditions = Array.from(this.domainObject.configuration.conditionCollection);
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
let newCollection = [];
reorderPlan.forEach((reorderEvent) => {
let item = oldConditions[reorderEvent.oldIndex];
newCollection.push(item);
this.domainObject.configuration.conditionCollection = newCollection;
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
});
this.persist();
this.persistConditions();
}
handleConditionResult(resultObj) {
let conditionCollection = this.domainObject.configuration.conditionCollection;
let currentConditionIdentifier = conditionCollection[conditionCollection.length-1];
if (resultObj) {
let idAsString = this.openmct.objects.makeKeyString(resultObj.id);
if (this.findConditionById(idAsString)) {
this.conditionResults[idAsString] = resultObj.data.result;
}
this.updateTimestamp(resultObj.data);
}
getCurrentCondition() {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length-1];
for (let i = 0; i < conditionCollection.length - 1; i++) {
let conditionIdAsString = this.openmct.objects.makeKeyString(conditionCollection[i]);
if (this.conditionResults[conditionIdAsString]) {
const condition = this.findConditionById(conditionCollection[i].id)
if (condition.result) {
//first condition to be true wins
currentConditionIdentifier = conditionCollection[i];
currentCondition = conditionCollection[i];
break;
}
}
this.openmct.objects.get(currentConditionIdentifier).then((obj) => {
this.emit('conditionSetResultUpdated',
Object.assign(
{
output: obj.configuration.output,
id: this.domainObject.identifier,
conditionId: currentConditionIdentifier
},
this.latestTimestamp
)
)
});
return currentCondition;
}
updateTimestamp(timestamp) {
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
if (!this.latestTimestamp[timeSystem.key]
|| timestamp[timeSystem.key] > this.latestTimestamp[timeSystem.key]
) {
this.latestTimestamp[timeSystem.key] = timestamp[timeSystem.key];
getCurrentConditionLAD(conditionResults) {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length-1];
for (let i = 0; i < conditionCollection.length - 1; i++) {
if (conditionResults[conditionCollection[i].id]) {
//first condition to be true wins
currentCondition = conditionCollection[i];
break;
}
}
return currentCondition;
}
requestLADConditionSetOutput() {
if (!this.conditionClassCollection.length) {
return Promise.resolve([]);
}
return this.compositionLoad.then(() => {
let latestTimestamp;
let conditionResults = {};
const conditionRequests = this.conditionClassCollection
.map(condition => condition.requestLADConditionResult());
return Promise.all(conditionRequests)
.then((results) => {
results.forEach(resultObj => {
const { id, data, data: { result } } = resultObj;
if (this.findConditionById(id)) {
conditionResults[id] = !!result;
}
latestTimestamp = getLatestTimestamp(
latestTimestamp,
data,
this.timeSystems,
this.openmct.time.timeSystem()
);
});
if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
return [];
}
const currentCondition = this.getCurrentConditionLAD(conditionResults);
const currentOutput = Object.assign(
{
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id
},
latestTimestamp
);
return [currentOutput];
});
});
}
persist() {
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionCollection', this.domainObject.configuration.conditionCollection);
isTelemetryUsed(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditionClassCollection) {
if (condition.isTelemetryUsed(id)) {
return true;
}
}
return false;
}
telemetryReceived(endpoint, datum) {
if (!this.isTelemetryUsed(endpoint)) {
return;
}
const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
const timeSystemKey = this.openmct.time.timeSystem().key;
let timestamp = {};
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
this.conditionClassCollection.forEach(condition => {
condition.getResult(normalizedDatum);
});
const currentCondition = this.getCurrentCondition();
this.emit('conditionSetResultUpdated',
Object.assign(
{
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id
},
timestamp
)
)
}
getTestData(metadatum) {
let data = undefined;
if (this.testData.applied) {
const found = this.testData.conditionTestInputs.find((testInput) => (testInput.metadata === metadatum.source));
if (found) {
data = found.value;
}
}
return data;
}
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const testValue = this.getTestData(metadatum);
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
return datum;
}, {});
normalizedDatum.id = id;
return normalizedDatum;
}
updateTestData(testData) {
this.testData = testData;
this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionTestData', this.testData.conditionTestInputs);
}
persistConditions() {
this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionCollection', this.conditionSetDomainObject.configuration.conditionCollection);
}
destroy() {
if (typeof this.stopObservingForChanges === 'function') {
this.composition.off('add', this.subscribeToTelemetry, this);
this.composition.off('remove', this.unsubscribeFromTelemetry, this);
Object.values(this.subscriptions).forEach(unsubscribe => unsubscribe());
delete this.subscriptions;
if(this.stopObservingForChanges) {
this.stopObservingForChanges();
}
this.conditionCollection.forEach((condition) => {
condition.off('conditionResultUpdated', this.handleConditionResult);
this.conditionClassCollection.forEach((condition) => {
condition.destroy();
})
}

View File

@@ -27,6 +27,13 @@ describe('ConditionManager', () => {
let conditionMgr;
let mockListener;
let openmct = {};
let mockCondition = {
isDefault: true,
id: '1234-5678',
configuration: {
criteria: []
}
};
let conditionSetDomainObject = {
identifier: {
namespace: "",
@@ -35,17 +42,14 @@ describe('ConditionManager', () => {
type: "conditionSet",
location: "mine",
configuration: {
conditionCollection: []
}
};
let mockConditionDomainObject = {
isDefault: true,
type: 'condition',
identifier: {
namespace: '',
key: '1234-5678'
conditionCollection: [
mockCondition
]
}
};
let mockComposition;
let loader;
let mockTimeSystems;
function mockAngularComponents() {
let mockInjector = jasmine.createSpyObj('$injector', ['get']);
@@ -55,7 +59,7 @@ describe('ConditionManager', () => {
let mockDomainObject = {
useCapability: function () {
return mockConditionDomainObject;
return mockCondition;
}
};
mockInstantiate.and.callFake(function () {
@@ -70,29 +74,60 @@ describe('ConditionManager', () => {
openmct.$injector = mockInjector;
}
beforeAll(function () {
beforeEach(function () {
mockAngularComponents();
mockListener = jasmine.createSpy('mockListener');
loader = {};
loader.promise = new Promise(function (resolve, reject) {
loader.resolve = resolve;
loader.reject = reject;
});
mockComposition = jasmine.createSpyObj('compositionCollection', [
'load',
'on',
'off'
]);
mockComposition.load.and.callFake(() => {
setTimeout(() => {
loader.resolve();
});
return loader.promise;
});
mockComposition.on('add', mockListener);
mockComposition.on('remove', mockListener);
openmct.composition = jasmine.createSpyObj('compositionAPI', [
'get'
]);
openmct.composition.get.and.returnValue(mockComposition);
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', 'observe', 'mutate']);
openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) {
resolve(conditionSetDomainObject);
}), new Promise(function (resolve, reject) {
resolve(mockConditionDomainObject);
resolve(mockCondition);
}));
openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key);
openmct.objects.observe.and.returnValue(function () {});
openmct.objects.mutate.and.returnValue(function () {});
mockTimeSystems = {
key: 'utc'
};
openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
mockListener = jasmine.createSpy('mockListener');
conditionMgr.on('conditionSetResultUpdated', mockListener);
conditionMgr.on('telemetryReceived', mockListener);
});
it('creates a conditionCollection with a default condition', function () {
expect(conditionMgr.domainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionIdentifier = conditionMgr.domainObject.configuration.conditionCollection[0];
expect(defaultConditionIdentifier).toEqual(mockConditionDomainObject.identifier);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionId = conditionMgr.conditionClassCollection[0].id;
expect(defaultConditionId).toEqual(mockCondition.id);
});
});

View File

@@ -28,7 +28,6 @@ describe('ConditionSetCompositionPolicy', () => {
let testTelemetryObject;
let openmct = {};
let parentDomainObject;
let composition;
beforeAll(function () {
testTelemetryObject = {
@@ -57,7 +56,6 @@ describe('ConditionSetCompositionPolicy', () => {
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']);
policy = new ConditionSetCompositionPolicy(openmct);
parentDomainObject = {};
composition = {};
});
it('returns true for object types that are not conditionSets', function () {

View File

@@ -1,3 +1,25 @@
/*****************************************************************************
* 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 ConditionSetMetadataProvider {
constructor(openmct) {
this.openmct = openmct;
@@ -21,15 +43,33 @@ export default class ConditionSetMetadataProvider {
}
getMetadata(domainObject) {
const enumerations = domainObject.configuration.conditionCollection
.map((condition, index) => {
return {
string: condition.configuration.output,
value: index
};
});
return {
values: this.getDomains().concat([
{
name: 'Output',
key: 'output',
format: 'string',
key: "state",
source: "output",
name: "State",
format: "enum",
enumerations: enumerations,
hints: {
range: 1
}
},
{
key: "output",
name: "Value",
format: "string",
hints: {
range: 2
}
}
])
};

View File

@@ -1,29 +1,86 @@
/*****************************************************************************
* 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 ConditionManager from './ConditionManager'
export default class ConditionSetTelemetryProvider {
constructor(openmct) {
this.openmct = openmct;
this.conditionManagerPool = {};
}
isTelemetryObject(domainObject) {
return domainObject.type === 'conditionSet';
}
supportsRequest(domainObject, options) {
return false;
supportsRequest(domainObject) {
return domainObject.type === 'conditionSet';
}
supportsSubscribe(domainObject) {
return domainObject.type === 'conditionSet';
}
request(domainObject) {
let conditionManager = this.getConditionManager(domainObject);
return conditionManager.requestLADConditionSetOutput()
.then(latestOutput => {
return latestOutput;
});
}
subscribe(domainObject, callback) {
let conditionManager = new ConditionManager(domainObject, this.openmct);
let conditionManager = this.getConditionManager(domainObject);
conditionManager.on('conditionSetResultUpdated', callback);
return function unsubscribe() {
conditionManager.off('conditionSetResultUpdated');
conditionManager.destroy();
return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
}
/**
* returns conditionManager instance for corresponding domain object
* creates the instance if it is not yet created
* @private
*/
getConditionManager(domainObject) {
const id = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.conditionManagerPool[id]) {
this.conditionManagerPool[id] = new ConditionManager(domainObject, this.openmct);
}
return this.conditionManagerPool[id];
}
/**
* cleans up and destroys conditionManager instance for corresponding domain object id
* can be called manually for views that only request but do not subscribe to data
*/
destroyConditionManager(id) {
if (this.conditionManagerPool[id]) {
this.conditionManagerPool[id].off('conditionSetResultUpdated');
this.conditionManagerPool[id].destroy();
delete this.conditionManagerPool[id];
}
}
}

View File

@@ -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
* Administration. All rights reserved.
*
@@ -20,14 +20,14 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
function ConditionSetViewPolicy() {
}
function NotificationIndicator() {}
NotificationIndicator.template = 'notificationIndicatorTemplate';
return NotificationIndicator;
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
if (domainObject.getModel().type === 'conditionSet') {
return view.key === 'conditionSet.view';
}
);
return true;
}
export default ConditionSetViewPolicy;

View File

@@ -30,7 +30,7 @@ export default class ConditionSetViewProvider {
this.openmct = openmct;
this.name = 'Conditions View';
this.key = 'conditionSet.view';
this.cssClass = 'icon-conditional'; // TODO: replace with class for new icon
this.cssClass = 'icon-conditional';
}
canView(domainObject) {

View File

@@ -25,35 +25,52 @@ import {TRIGGER} from "./utils/constants";
import TelemetryCriterion from "./criterion/TelemetryCriterion";
let openmct = {},
mockListener,
testConditionDefinition,
testTelemetryObject,
conditionObj;
conditionObj,
conditionManager,
mockTelemetryReceived,
mockTimeSystems;
describe("The condition", function () {
beforeEach (() => {
mockListener = jasmine.createSpy('listener');
conditionManager = jasmine.createSpyObj('conditionManager',
['on']
);
mockTelemetryReceived = jasmine.createSpy('listener');
conditionManager.on('telemetryReceived', mockTelemetryReceived);
testTelemetryObject = {
identifier:{ namespace: "", key: "test-object"},
type: "test-object",
name: "Test Object",
telemetry: {
values: [{
key: "some-key",
name: "Some attribute",
key: "value",
name: "Value",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
}, {
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
key: "testSource",
source: "value",
name: "Test",
format: "string"
}]
}
};
conditionManager.telemetryObjects = {
"test-object": testTelemetryObject
};
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) {
resolve(testTelemetryObject);
@@ -63,13 +80,23 @@ describe("The condition", function () {
openmct.telemetry.subscribe.and.returnValue(function () {});
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
mockTimeSystems = {
key: 'utc'
};
openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
testConditionDefinition = {
id: '123-456',
configuration: {
name: 'mock condition',
output: 'mock output',
trigger: TRIGGER.ANY,
criteria: [
{
id: '1234-5678-9999-0000',
operation: 'equalTo',
input: false,
input: ['0'],
metadata: 'value',
telemetry: testTelemetryObject.identifier
}
@@ -79,14 +106,12 @@ describe("The condition", function () {
conditionObj = new Condition(
testConditionDefinition,
openmct
openmct,
conditionManager
);
conditionObj.on('conditionUpdated', mockListener);
});
it("generates criteria with an id", function () {
it("generates criteria with the correct properties", function () {
const testCriterion = testConditionDefinition.configuration.criteria[0];
let criterion = conditionObj.generateCriterion(testCriterion);
expect(criterion.id).toBeDefined();
@@ -118,4 +143,38 @@ describe("The condition", function () {
expect(result).toBeTrue();
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();
});
});

View File

@@ -23,19 +23,44 @@
import EventEmitter from 'EventEmitter';
export default class StyleRuleManager extends EventEmitter {
constructor(conditionalStyleConfiguration, openmct) {
constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
super();
this.openmct = openmct;
if (conditionalStyleConfiguration && conditionalStyleConfiguration.conditionSetIdentifier) {
this.initialize(conditionalStyleConfiguration);
this.callback = callback;
if (suppressSubscriptionOnEdit) {
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
this.isEditing = this.openmct.editor.editing;
}
if (styleConfiguration) {
this.initialize(styleConfiguration);
if (styleConfiguration.conditionSetIdentifier) {
this.subscribeToConditionSet();
} else {
this.applyStaticStyle();
}
}
}
toggleSubscription(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetIdentifier) {
this.applySelectedConditionStyle();
}
} else if (this.conditionSetIdentifier) {
this.subscribeToConditionSet();
}
}
initialize(conditionalStyleConfiguration) {
this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
this.defaultStyle = conditionalStyleConfiguration.defaultStyle;
this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
initialize(styleConfiguration) {
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
this.staticStyle = styleConfiguration.staticStyle;
this.selectedConditionId = styleConfiguration.selectedConditionId;
this.defaultConditionId = styleConfiguration.defaultConditionId;
this.updateConditionStylesMap(styleConfiguration.styles || []);
}
subscribeToConditionSet() {
@@ -43,20 +68,31 @@ export default class StyleRuleManager extends EventEmitter {
this.stopProvidingTelemetry();
}
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
this.openmct.telemetry.request(conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
});
}
updateConditionalStyleConfig(conditionalStyleConfiguration) {
if (!conditionalStyleConfiguration || !conditionalStyleConfiguration.conditionSetIdentifier) {
updateObjectStyleConfig(styleConfiguration) {
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
this.initialize(styleConfiguration || {});
this.destroy();
} else {
let isNewConditionSet = !this.conditionSetIdentifier ||
this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, conditionalStyleConfiguration.conditionSetIdentifier);
this.initialize(conditionalStyleConfiguration);
//Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) {
this.subscribeToConditionSet();
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
this.initialize(styleConfiguration);
if (this.isEditing) {
this.applySelectedConditionStyle();
} else {
//Only resubscribe if the conditionSet has changed.
if (isNewConditionSet) {
this.subscribeToConditionSet();
}
}
}
}
@@ -64,38 +100,62 @@ export default class StyleRuleManager extends EventEmitter {
updateConditionStylesMap(conditionStyles) {
let conditionStyleMap = {};
conditionStyles.forEach((conditionStyle) => {
const identifier = this.openmct.objects.makeKeyString(conditionStyle.conditionIdentifier);
conditionStyleMap[identifier] = conditionStyle.style;
if (conditionStyle.conditionId) {
conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
} else {
conditionStyleMap.static = conditionStyle.style;
}
});
this.conditionalStyleMap = conditionStyleMap;
}
handleConditionSetResultUpdated(resultData) {
let identifier = this.openmct.objects.makeKeyString(resultData.conditionId);
let foundStyle = this.conditionalStyleMap[identifier];
let foundStyle = this.conditionalStyleMap[resultData.conditionId];
if (foundStyle) {
if (foundStyle !== this.currentStyle) {
this.currentStyle = foundStyle;
}
this.updateDomainObjectStyle();
} else {
if (this.currentStyle !== this.defaultStyle) {
this.currentStyle = this.defaultStyle;
}
this.applyStaticStyle();
}
this.updateDomainObjectStyle();
}
updateDomainObjectStyle() {
this.emit('conditionalStyleUpdated', this.currentStyle)
if (this.callback) {
this.callback(Object.assign({}, this.currentStyle));
}
}
applySelectedConditionStyle() {
const conditionId = this.selectedConditionId || this.defaultConditionId;
if (!conditionId) {
this.applyStaticStyle();
} else if (this.conditionalStyleMap[conditionId]) {
this.currentStyle = this.conditionalStyleMap[conditionId];
this.updateDomainObjectStyle();
}
}
applyStaticStyle() {
if (this.staticStyle) {
this.currentStyle = this.staticStyle.style;
} else {
if (this.currentStyle) {
Object.keys(this.currentStyle).forEach(key => {
this.currentStyle[key] = '__no_value';
});
}
}
this.updateDomainObjectStyle();
}
destroy() {
this.currentStyle = this.defaultStyle;
this.updateDomainObjectStyle();
this.applyStaticStyle();
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
delete this.stopProvidingTelemetry;
this.conditionSetIdentifier = undefined;
}

View File

@@ -1,177 +1,188 @@
/*****************************************************************************
* 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.
*****************************************************************************/
* 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 v-if="isEditing">
<div v-if="domainObject"
class="c-c-editui__conditions c-c-container__container c-c__drag-wrapper"
:class="['widget-condition', { 'widget-condition--current': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }]"
<div class="c-condition-h"
:class="{ 'is-drag-target': draggingOver }"
@dragover.prevent
@drop.prevent="dropCondition($event, conditionIndex)"
@dragenter="dragEnter($event, conditionIndex)"
@dragleave="dragLeave($event, conditionIndex)"
>
<div class="c-condition-h__drop-target"></div>
<div v-if="isEditing"
:class="{'is-current': condition.id === currentConditionId}"
class="c-condition c-condition--edit"
>
<div class="title-bar">
<span class="c-c__menu-hamburger"
:class="{ 'is-enabled': !domainObject.isDefault }"
:draggable="!domainObject.isDefault"
<!-- Edit view -->
<div class="c-condition__header">
<span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
title="Drag to reorder conditions"
:class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
:draggable="!condition.isDefault"
@dragstart="dragStart"
@dragstop="dragStop"
@dragover.stop
@dragend="dragEnd"
></span>
<span
class="is-enabled flex-elem"
:class="['c-c__disclosure-triangle', { 'c-c__disclosure-triangle--expanded': expanded }]"
@click="expanded = !expanded"
<span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
:class="{ 'c-disclosure-triangle--expanded': expanded }"
@click="expanded = !expanded"
></span>
<div class="condition-summary">
<span class="condition-name">{{ domainObject.configuration.name }}</span>
<!-- TODO: description should be derived from criteria -->
<span class="condition-description">{{ domainObject.configuration.name }}</span>
<span class="c-condition__name">{{ condition.configuration.name }}</span>
<span class="c-condition__summary">
<template v-if="!canEvaluateCriteria">
Define criteria
</template>
<span v-else>
<condition-description :show-label="false"
:condition="condition"
/>
</span>
</span>
<div class="c-condition__buttons">
<button v-if="!condition.isDefault"
class="c-click-icon c-condition__duplicate-button icon-duplicate"
title="Duplicate this condition"
@click="cloneCondition"
></button>
<button v-if="!condition.isDefault"
class="c-click-icon c-condition__delete-button icon-trash"
title="Delete this condition"
@click="removeCondition"
></button>
</div>
<span v-if="!domainObject.isDefault"
class="is-enabled c-c__duplicate"
@click="cloneCondition"
></span>
<span v-if="!domainObject.isDefault"
class="is-enabled c-c__trash"
@click="removeCondition"
></span>
</div>
<div v-if="expanded"
class="condition-config-edit widget-condition-content c-sw-editui__conditions-wrapper holder widget-conditions-wrapper flex-elem expanded"
class="c-condition__definition c-cdef"
>
<div id="conditionArea"
class="c-c-editui__condition widget-conditions"
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Condition Name</span>
<span class="c-cdef__controls">
<input v-model="condition.configuration.name"
class="t-condition-input__name"
type="text"
@change="persist"
>
</span>
<span class="c-cdef__label">Output</span>
<span class="c-cdef__controls">
<span class="c-cdef__control">
<select v-model="selectedOutputSelection"
@change="setOutputValue"
>
<option v-for="option in outputOptions"
:key="option"
:value="option"
>
{{ initCap(option) }}
</option>
</select>
</span>
<span class="c-cdef__control">
<input v-if="selectedOutputSelection === outputOptions[2]"
v-model="condition.configuration.output"
class="t-condition-name-input"
type="text"
@change="persist"
>
</span>
</span>
<div v-if="!condition.isDefault"
class="c-cdef__match-and-criteria"
>
<div class="c-c-condition">
<div class="c-c-condition__ui l-compact-form l-widget-condition has-local-controls">
<div>
<ul class="t-widget-condition-config">
<li>
<label>Condition Name</label>
<span class="controls">
<input v-model="domainObject.configuration.name"
class="t-condition-input__name"
type="text"
@blur="persist"
>
</span>
</li>
<li>
<label>Output</label>
<span class="controls">
<select v-model="selectedOutputSelection"
@change="setOutputValue"
>
<option value="">- Select Output -</option>
<option v-for="option in outputOptions"
:key="option"
:value="option"
>
{{ initCap(option) }}
</option>
</select>
<input v-if="selectedOutputSelection === outputOptions[2]"
v-model="domainObject.configuration.output"
class="t-condition-name-input"
type="text"
@blur="persist"
>
</span>
</li>
</ul>
<div v-if="!domainObject.isDefault"
class="widget-condition-content expanded"
>
<ul class="t-widget-condition-config">
<li class="has-local-controls t-condition">
<label>Match when</label>
<span class="controls">
<select v-model="domainObject.configuration.trigger"
@change="persist"
>
<option value="all">all criteria are met</option>
<option value="any">any criteria are met</option>
</select>
</span>
</li>
</ul>
<ul v-if="telemetry.length"
class="t-widget-condition-config"
>
<li v-for="(criterion, index) in domainObject.configuration.criteria"
:key="index"
class="has-local-controls t-condition"
>
<Criterion :telemetry="telemetry"
:criterion="criterion"
:index="index"
:trigger="domainObject.configuration.trigger"
:is-default="domainObject.configuration.criteria.length === 1"
@persist="persist"
/>
<div class="c-c__criterion-controls">
<span class="is-enabled c-c__duplicate"
@click="cloneCriterion(index)"
></span>
<span v-if="!(domainObject.configuration.criteria.length === 1)"
class="is-enabled c-c__trash"
@click="removeCriterion(index)"
></span>
</div>
</li>
</ul>
<div class="holder c-c-button-wrapper align-left">
<span class="c-c-label-spacer"></span>
<button
class="c-c-button c-c-button--minor add-criteria-button"
@click="addCriteria"
>
<span class="c-c-button__label">Add Criteria</span>
</button>
</div>
</div>
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Match</span>
<span class="c-cdef__controls">
<select v-model="condition.configuration.trigger"
@change="persist"
>
<option v-for="option in triggers"
:key="option.value"
:value="option.value"
> {{ option.label }}</option>
</select>
</span>
<template v-if="telemetry.length || condition.configuration.criteria.length">
<div v-for="(criterion, index) in condition.configuration.criteria"
:key="criterion.id"
class="c-cdef__criteria"
>
<Criterion :telemetry="telemetry"
:criterion="criterion"
:index="index"
:trigger="condition.configuration.trigger"
:is-default="condition.configuration.criteria.length === 1"
@persist="persist"
/>
<div class="c-cdef__criteria__buttons">
<button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
title="Duplicate this criteria"
@click="cloneCriterion(index)"
></button>
<button v-if="!(condition.configuration.criteria.length === 1)"
class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
title="Delete this criteria"
@click="removeCriterion(index)"
></button>
</div>
</div>
</template>
<div class="c-cdef__separator c-row-separator"></div>
<div class="c-cdef__controls"
:disabled="!telemetry.length"
>
<button
class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
@click="addCriteria"
>
<span class="c-button__label">Add Criteria</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<div v-if="domainObject"
id="conditionArea"
class="c-cs-ui__conditions"
:class="['widget-condition', { 'widget-condition--current': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }]"
<div v-else
class="c-condition c-condition--browse"
:class="{'is-current': condition.id === currentConditionId}"
>
<div class="title-bar">
<span class="condition-name">
{{ domainObject.configuration.name }}
<!-- Browse view -->
<div class="c-condition__header">
<span class="c-condition__name">
{{ condition.configuration.name }}
</span>
<span class="condition-output">
Output: {{ domainObject.configuration.output }}
<span class="c-condition__output">
Output: {{ condition.configuration.output }}
</span>
</div>
<div class="condition-config">
<span class="condition-description">
{{ domainObject.configuration.description }}
</span>
<div class="c-condition__summary">
<condition-description :show-label="false"
:condition="condition"
/>
</div>
</div>
</div>
@@ -179,18 +190,22 @@
<script>
import Criterion from './Criterion.vue';
import ConditionDescription from "./ConditionDescription.vue";
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
import uuid from 'uuid';
export default {
inject: ['openmct'],
components: {
Criterion
Criterion,
ConditionDescription
},
props: {
conditionIdentifier: {
type: Object,
required: true
currentConditionId: {
type: String,
default: ''
},
currentConditionIdentifier: {
condition: {
type: Object,
required: true
},
@@ -206,34 +221,64 @@ export default {
type: Array,
required: true,
default: () => []
},
isDragging: {
type: Boolean,
default: false
},
moveIndex: {
type: Number,
default: 0
}
},
data() {
return {
domainObject: this.domainObject,
currentCriteria: this.currentCriteria,
expanded: true,
trigger: 'all',
selectedOutputSelection: '',
outputOptions: ['false', 'true', 'string'],
criterionIndex: 0
criterionIndex: 0,
draggingOver: false,
isDefault: this.condition.isDefault
};
},
computed: {
triggers() {
const keys = Object.keys(TRIGGER);
const triggerOptions = [];
keys.forEach((trigger) => {
triggerOptions.push({
value: TRIGGER[trigger],
label: TRIGGER_LABEL[TRIGGER[trigger]]
});
});
return triggerOptions;
},
canEvaluateCriteria: function () {
let criteria = this.condition.configuration.criteria;
if (criteria.length) {
let lastCriterion = criteria[criteria.length - 1];
if (lastCriterion.telemetry &&
lastCriterion.operation &&
(lastCriterion.input.length ||
lastCriterion.operation === 'isDefined' ||
lastCriterion.operation === 'isUndefined')) {
return true;
}
}
return false;
}
},
destroyed() {
this.destroy();
},
mounted() {
this.openmct.objects.get(this.conditionIdentifier).then((domainObject => {
this.domainObject = domainObject;
this.initialize();
}));
this.setOutputSelection();
},
methods: {
initialize() {
this.setOutputSelection();
},
setOutputSelection() {
let conditionOutput = this.domainObject.configuration.output;
let conditionOutput = this.condition.configuration.output;
if (conditionOutput) {
if (conditionOutput !== 'false' && conditionOutput !== 'true') {
this.selectedOutputSelection = 'string';
@@ -244,62 +289,89 @@ export default {
},
setOutputValue() {
if (this.selectedOutputSelection === 'string') {
this.domainObject.configuration.output = '';
this.condition.configuration.output = '';
} else {
this.domainObject.configuration.output = this.selectedOutputSelection;
this.condition.configuration.output = this.selectedOutputSelection;
}
this.persist();
},
addCriteria() {
const criteriaObject = {
id: uuid(),
telemetry: '',
operation: '',
input: '',
metadata: ''
};
this.domainObject.configuration.criteria.push(criteriaObject);
this.condition.configuration.criteria.push(criteriaObject);
},
dragStart(e) {
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
e.dataTransfer.effectAllowed = "copyMove";
e.dataTransfer.setDragImage(e.target.closest('.c-c-container__container'), 0, 0);
e.dataTransfer.setDragImage(e.target.closest('.c-condition-h'), 0, 0);
this.$emit('setMoveIndex', this.conditionIndex);
},
dragStop(e) {
e.dataTransfer.clearData();
dragEnd(event) {
this.dragStarted = false;
event.dataTransfer.clearData();
this.$emit('dragComplete');
},
dropCondition(event, targetIndex) {
if (!this.isDragging) { return }
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
if (this.isValidTarget(targetIndex)) {
this.dragElement = undefined;
this.draggingOver = false;
this.$emit('dropCondition', targetIndex);
}
},
dragEnter(event, targetIndex) {
if (!this.isDragging) { return }
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
if (this.isValidTarget(targetIndex)) {
this.dragElement = event.target.parentElement;
this.draggingOver = true;
}
},
dragLeave(event) {
if (event.target.parentElement === this.dragElement) {
this.draggingOver = false;
this.dragElement = undefined;
}
},
isValidTarget(targetIndex) {
return this.moveIndex !== targetIndex;
},
destroy() {
},
removeCondition(ev) {
this.$emit('removeCondition', this.conditionIdentifier);
this.$emit('removeCondition', this.conditionIndex);
},
cloneCondition(ev) {
this.$emit('cloneCondition', {
identifier: this.conditionIdentifier,
index: Number(ev.target.closest('.widget-condition').getAttribute('data-condition-index'))
condition: this.condition,
index: this.conditionIndex
});
},
removeCriterion(index) {
this.domainObject.configuration.criteria.splice(index, 1);
this.persist()
this.condition.configuration.criteria.splice(index, 1);
this.persist();
},
cloneCriterion(index) {
const clonedCriterion = {...this.domainObject.configuration.criteria[index]};
this.domainObject.configuration.criteria.splice(index + 1, 0, clonedCriterion);
this.persist()
},
hasTelemetry(identifier) {
// TODO: check parent domainObject.composition.hasTelemetry
return this.currentCriteria && identifier;
const clonedCriterion = JSON.parse(JSON.stringify(this.condition.configuration.criteria[index]));
clonedCriterion.id = uuid();
this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
this.persist();
},
persist() {
this.openmct.objects.mutate(this.domainObject, 'configuration', this.domainObject.configuration);
this.$emit('updateCondition', {
condition: this.condition,
index: this.conditionIndex
});
},
initCap: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1)
initCap(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}
</script>

View File

@@ -22,60 +22,55 @@
<template>
<section id="conditionCollection"
class="c-cs__ui_section"
:class="{ 'is-expanded': expanded }"
>
<div class="c-cs__ui__header">
<span class="c-cs__ui__header-label">Conditions</span>
<div class="c-cs__header c-section__header">
<span
class="is-enabled flex-elem"
:class="['c-cs__disclosure-triangle', { 'c-cs__disclosure-triangle--expanded': expanded }]"
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
:class="{ 'c-disclosure-triangle--expanded': expanded }"
@click="expanded = !expanded"
></span>
<div class="c-cs__header-label c-section__label">Conditions</div>
</div>
<div v-if="expanded"
class="c-cs__ui_content"
class="c-cs__content"
>
<div v-show="isEditing"
class="help"
class="hint"
:class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
>
<span v-if="!telemetryObjs.length">Drag telemetry into Condition Set in order to add conditions.</span>
<span v-else>The first condition to match is the one that wins. Drag conditions to rearrange.</span>
<template v-if="!telemetryObjs.length">Drag telemetry into this Condition Set to configure Conditions and add criteria.</template>
<template v-else>The first condition to match is the one that is applied. Drag conditions to reorder.</template>
</div>
<div class="holder add-condition-button-wrapper align-left">
<button
v-show="isEditing"
id="addCondition"
class="c-cs-button c-cs-button--major add-condition-button"
:class="{ 'is-disabled': !telemetryObjs.length}"
:disabled="!telemetryObjs.length"
@click="addCondition"
>
<span class="c-cs-button__label">Add Condition</span>
</button>
</div>
<div class="c-c__condition-collection">
<ul class="c-c__container-holder">
<li v-for="(conditionIdentifier, index) in conditionCollection"
:key="conditionIdentifier.key"
>
<div v-if="isEditing"
class="c-c__drag-ghost"
@drop.prevent="dropCondition"
@dragenter="dragEnter"
@dragleave="dragLeave"
@dragover.prevent
></div>
<Condition :condition-identifier="conditionIdentifier"
:current-condition-identifier="currentConditionIdentifier"
:condition-index="index"
:telemetry="telemetryObjs"
:is-editing="isEditing"
@removeCondition="removeCondition"
@cloneCondition="cloneCondition"
@setMoveIndex="setMoveIndex"
/>
</li>
</ul>
<button
v-show="isEditing"
id="addCondition"
class="c-button c-button--major icon-plus labeled"
@click="addCondition"
>
<span class="c-cs-button__label">Add Condition</span>
</button>
<div class="c-cs__conditions-h"
:class="{ 'is-active-dragging': isDragging }"
>
<Condition v-for="(condition, index) in conditionCollection"
:key="condition.id"
:condition="condition"
:current-condition-id="currentConditionId"
:condition-index="index"
:telemetry="telemetryObjs"
:is-editing="isEditing"
:move-index="moveIndex"
:is-dragging="isDragging"
@updateCondition="updateCondition"
@removeCondition="removeCondition"
@cloneCondition="cloneCondition"
@setMoveIndex="setMoveIndex"
@dragComplete="dragComplete"
@dropCondition="dropCondition"
/>
</div>
</div>
</section>
@@ -91,28 +86,51 @@ export default {
Condition
},
props: {
isEditing: Boolean
isEditing: Boolean,
testData: {
type: Object,
required: true,
default: () => {
return {
applied: false,
conditionTestInputs: []
}
}
}
},
data() {
return {
expanded: true,
parentKeyString: this.openmct.objects.makeKeyString(this.domainObject.identifier),
conditionCollection: [],
conditionResults: {},
conditions: [],
currentConditionIdentifier: this.currentConditionIdentifier || {},
telemetryObjs: [],
moveIndex: Number,
isDragging: false
moveIndex: undefined,
isDragging: false,
defaultOutput: undefined,
dragCounter: 0,
currentConditionId: ''
};
},
watch: {
defaultOutput(newOutput, oldOutput) {
this.$emit('updateDefaultOutput', newOutput);
},
testData: {
handler() {
this.updateTestData();
},
deep: true
}
},
destroyed() {
this.composition.off('add', this.addTelemetryObject);
this.composition.off('remove', this.removeTelemetryObject);
if(this.conditionManager) {
this.conditionManager.off('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
this.conditionManager.destroy();
}
if (typeof this.stopObservingForChanges === 'function') {
if (this.stopObservingForChanges) {
this.stopObservingForChanges();
}
},
@@ -122,22 +140,32 @@ export default {
this.composition.on('remove', this.removeTelemetryObject);
this.composition.load();
this.conditionCollection = this.domainObject.configuration.conditionCollection;
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
this.observeForChanges();
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
this.conditionManager.on('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
this.updateDefaultCondition();
},
methods: {
handleConditionSetResultUpdated(data) {
this.currentConditionId = data.conditionId;
this.$emit('conditionSetResultUpdated', data)
},
observeForChanges() {
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, '*', (newDomainObject) => {
this.conditionCollection = newDomainObject.configuration.conditionCollection;
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
this.conditionCollection = newConditionCollection;
this.updateDefaultCondition();
});
},
updateDefaultCondition() {
const defaultCondition = this.domainObject.configuration.conditionCollection
.find(conditionConfiguration => conditionConfiguration.isDefault);
this.defaultOutput = defaultCondition.configuration.output;
},
setMoveIndex(index) {
this.moveIndex = index;
this.isDragging = true;
},
dropCondition(e) {
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
dropCondition(targetIndex) {
const oldIndexArr = Object.keys(this.conditionCollection);
const move = function (arr, old_index, new_index) {
while (old_index < 0) {
@@ -163,25 +191,16 @@ export default {
}
this.reorder(reorderPlan);
e.target.classList.remove("dragging");
},
dragComplete() {
this.isDragging = false;
},
dragEnter(e) {
if (!this.isDragging) { return }
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
if (this.moveIndex === targetIndex) { return }
e.target.classList.add("dragging");
},
dragLeave(e) {
e.target.classList.remove("dragging");
},
addTelemetryObject(domainObject) {
this.telemetryObjs.push(domainObject);
this.$emit('telemetryUpdated', this.telemetryObjs);
},
removeTelemetryObject(identifier) {
let index = _.findIndex(this.telemetryObjs, (obj) => {
let index = this.telemetryObjs.findIndex(obj => {
let objId = this.openmct.objects.makeKeyString(obj.identifier);
let id = this.openmct.objects.makeKeyString(identifier);
return objId === id;
@@ -190,20 +209,23 @@ export default {
this.telemetryObjs.splice(index, 1);
}
},
addCondition(event, isDefault, index) {
this.conditionManager.addCondition(!!isDefault, index);
addCondition() {
this.conditionManager.addCondition();
},
updateCurrentCondition(identifier) {
this.currentConditionIdentifier = identifier;
updateCondition(data) {
this.conditionManager.updateCondition(data.condition, data.index);
},
removeCondition(identifier) {
this.conditionManager.removeCondition(identifier);
removeCondition(index) {
this.conditionManager.removeCondition(index);
},
reorder(reorderPlan) {
this.conditionManager.reorderConditions(reorderPlan);
},
cloneCondition(condition) {
this.conditionManager.cloneCondition(condition.identifier, condition.index);
cloneCondition(data) {
this.conditionManager.cloneCondition(data.condition, data.index);
},
updateTestData() {
this.conditionManager.updateTestData(this.testData);
}
}
}

View File

@@ -0,0 +1,154 @@
/*****************************************************************************
* 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-style__condition-desc">
<span v-if="showLabel && condition"
class="c-style__condition-desc__name c-condition__name"
>
{{ condition.configuration.name }}
</span>
<span v-for="(criterionDescription, index) in criterionDescriptions"
:key="criterionDescription"
class="c-style__condition-desc__text"
>
<template v-if="!index">When</template>
{{ criterionDescription }}
<template v-if="index < (criterionDescriptions.length-1)">{{ triggerDescription }}</template>
</span>
</div>
</template>
<script>
import { TRIGGER } from "@/plugins/condition/utils/constants";
import { OPERATIONS } from "@/plugins/condition/utils/operations";
export default {
name: 'ConditionDescription',
inject: [
'openmct'
],
props: {
showLabel: {
type: Boolean,
default: false
},
condition: {
type: Object,
default() {
return undefined;
}
}
},
data() {
return {
criterionDescriptions: [],
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) : '';
}
}
}
</script>

View File

@@ -0,0 +1,89 @@
/*****************************************************************************
* 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 v-if="conditionErrors.length"
class="c-condition__errors"
>
<div v-for="(error, index) in conditionErrors"
:key="index"
class="u-alert u-alert--block u-alert--with-icon"
>{{ error.message.errorText }} {{ error.additionalInfo }}
</div>
</div>
</template>
<script>
import { ERROR } from "@/plugins/condition/utils/constants";
export default {
name: 'ConditionError',
inject: [
'openmct'
],
props: {
condition: {
type: Object,
default() {
return undefined;
}
}
},
data() {
return {
conditionErrors: []
}
},
mounted() {
this.getConditionErrors();
},
methods: {
getConditionErrors() {
if (this.condition) {
this.condition.configuration.criteria.forEach((criterion, index) => {
this.getCriterionErrors(criterion, index);
});
}
},
getCriterionErrors(criterion, index) {
if (!criterion.telemetry) {
this.conditionErrors.push({
message: ERROR.TELEMETRY_NOT_FOUND,
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)}` : ''
});
}
});
}
}
}
}
}
</script>

View File

@@ -21,23 +21,34 @@
*****************************************************************************/
<template>
<div class="c-cs-edit w-condition-set">
<div class="c-sw-edit__ui holder">
<section id="current-output">
<div class="c-cs__ui__header">
<span class="c-cs__ui__header-label">Current Output</span>
</div>
<div class="c-cs__ui_content">
<span v-if="currentConditionOutput"
class="current-output"
>
<div class="c-cs">
<section class="c-cs__current-output c-section">
<div class="c-cs__content c-cs__current-output-value">
<span class="c-cs__current-output-value__label">Current Output</span>
<span class="c-cs__current-output-value__value">
<template v-if="currentConditionOutput">
{{ currentConditionOutput }}
</span>
<span v-else>No output selected</span>
</div>
</section>
<TestData :is-editing="isEditing" />
<ConditionCollection :is-editing="isEditing" />
</template>
<template v-else>
{{ defaultConditionOutput }}
</template>
</span>
</div>
</section>
<div class="c-cs__test-data-and-conditions-w">
<TestData class="c-cs__test-data"
:is-editing="isEditing"
:test-data="testData"
:telemetry="telemetryObjs"
@updateTestData="updateTestData"
/>
<ConditionCollection class="c-cs__conditions"
:is-editing="isEditing"
:test-data="testData"
@conditionSetResultUpdated="updateCurrentOutput"
@updateDefaultOutput="updateDefaultOutput"
@telemetryUpdated="updateTelemetry"
/>
</div>
</div>
</template>
@@ -57,25 +68,31 @@ export default {
},
data() {
return {
currentConditionOutput: ''
currentConditionOutput: '',
defaultConditionOutput: '',
telemetryObjs: [],
testData: {}
}
},
mounted() {
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.provideTelemetry();
},
beforeDestroy() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
this.testData = {
applied: false,
conditionTestInputs: this.domainObject.configuration.conditionTestData || []
};
},
methods: {
updateCurrentOutput(currentConditionResult) {
this.currentConditionOutput = currentConditionResult.output;
},
provideTelemetry() {
this.stopProvidingTelemetry = this.openmct.telemetry
.subscribe(this.domainObject, output => { this.updateCurrentOutput(output); });
updateDefaultOutput(output) {
this.currentConditionOutput = output;
},
updateTelemetry(telemetryObjs) {
this.telemetryObjs = telemetryObjs;
},
updateTestData(testData) {
this.testData = testData;
}
}
};

View File

@@ -1,12 +1,38 @@
/*****************************************************************************
* 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>
<label>{{ setRowLabel }}</label>
<span class="t-configuration">
<span class="controls">
<select v-model="criterion.telemetry"
<div class="u-contents">
<div class="c-cdef__separator c-row-separator"></div>
<span class="c-cdef__label">{{ setRowLabel }}</span>
<span class="c-cdef__controls">
<span class="c-cdef__control">
<select ref="telemetrySelect"
v-model="criterion.telemetry"
@change="updateMetadataOptions"
>
<option value="">- Select Telemetry -</option>
<option value="all">all telemetry</option>
<option value="any">any telemetry</option>
<option v-for="telemetryOption in telemetry"
:key="telemetryOption.identifier.key"
:value="telemetryOption.identifier"
@@ -16,9 +42,10 @@
</select>
</span>
<span v-if="criterion.telemetry"
class="controls"
class="c-cdef__control"
>
<select v-model="criterion.metadata"
<select ref="metadataSelect"
v-model="criterion.metadata"
@change="updateOperations"
>
<option value="">- Select Field -</option>
@@ -31,10 +58,10 @@
</select>
</span>
<span v-if="criterion.telemetry && criterion.metadata"
class="controls"
class="c-cdef__control"
>
<select v-model="criterion.operation"
@change="updateOperationInputVisibility"
@change="updateInputVisibilityAndValues"
>
<option value="">- Select Comparison -</option>
<option v-for="option in filteredOps"
@@ -44,15 +71,34 @@
{{ option.text }}
</option>
</select>
<span v-for="(item, inputIndex) in inputCount"
:key="inputIndex"
>
<input v-model="criterion.input[inputIndex]"
class="t-condition-name-input"
type="text"
@blur="persist"
<template v-if="!enumerations.length">
<span v-for="(item, inputIndex) in inputCount"
:key="inputIndex"
class="c-cdef__control__inputs"
>
<span v-if="inputIndex < inputCount-1">and</span>
<input v-model="criterion.input[inputIndex]"
class="c-cdef__control__input"
:type="setInputType"
@blur="persist"
>
<span v-if="inputIndex < inputCount-1">and</span>
</span>
</template>
<span v-else>
<span v-if="inputCount && criterion.operation"
class="c-cdef__control"
>
<select v-model="criterion.input[0]"
@change="persist"
>
<option v-for="option in enumerations"
:key="option.string"
:value="option.value.toString()"
>
{{ option.string }}
</option>
</select>
</span>
</span>
</span>
</span>
@@ -61,6 +107,7 @@
<script>
import { OPERATIONS } from '../utils/operations';
import { INPUT_TYPES } from '../utils/operations';
export default {
inject: ['openmct'],
@@ -85,12 +132,13 @@ export default {
},
data() {
return {
telemetryMetadata: {},
telemetryMetadataOptions: {},
telemetryMetadataOptions: [],
operations: OPERATIONS,
inputCount: 0,
rowLabel: '',
operationFormat: ''
operationFormat: '',
enumerations: [],
inputTypes: INPUT_TYPES
}
},
computed: {
@@ -99,69 +147,145 @@ export default {
return (this.index !== 0 ? operator : '') + 'when';
},
filteredOps: function () {
return [...this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1)];
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
},
setInputType: function () {
let type = '';
for (let i = 0; i < this.filteredOps.length; i++) {
if (this.criterion.operation === this.filteredOps[i].name) {
if (this.filteredOps[i].appliesTo.length) {
type = this.inputTypes[this.filteredOps[i].appliesTo[0]];
} else {
type = 'text'
}
break;
}
}
return type;
}
},
watch: {
telemetry: {
handler(newTelemetry, oldTelemetry) {
this.checkTelemetry();
},
deep: true
}
},
mounted() {
this.updateMetadataOptions();
},
methods: {
getOperationFormat() {
this.telemetryMetadata.valueMetadatas.forEach((value, index) => {
if (value.key === this.criterion.metadata) {
let valueMetadata = this.telemetryMetadataOptions[index];
if (valueMetadata.enumerations !== undefined) {
this.operationFormat = 'enum';
} else if (valueMetadata.hints.hasOwnProperty('range')) {
this.operationFormat = 'number';
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
this.operationFormat = 'number';
} else if (valueMetadata.key === 'name') {
this.operationFormat = 'string';
} else {
this.operationFormat = 'string';
checkTelemetry() {
if(this.criterion.telemetry) {
if (this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all') {
this.updateMetadataOptions();
} else {
if (!this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier))) {
//telemetry being used was removed. So reset this criterion.
this.criterion.telemetry = '';
this.criterion.metadata = '';
this.criterion.input = [];
this.criterion.operation = '';
this.persist();
}
}
}
},
updateOperationFormat() {
this.enumerations = [];
let foundMetadata = this.telemetryMetadataOptions.find((value) => {
return value.key === this.criterion.metadata;
});
if (foundMetadata) {
if (foundMetadata.enumerations !== undefined) {
this.operationFormat = 'enum';
this.enumerations = foundMetadata.enumerations;
} else if (foundMetadata.format === 'string' || foundMetadata.format === 'number') {
this.operationFormat = foundMetadata.format;
} else if (foundMetadata.hints.hasOwnProperty('range')) {
this.operationFormat = 'number';
} else if (foundMetadata.hints.hasOwnProperty('domain')) {
this.operationFormat = 'number';
} else if (foundMetadata.key === 'name') {
this.operationFormat = 'string';
} else {
this.operationFormat = 'number';
}
}
this.updateInputVisibilityAndValues();
},
updateMetadataOptions(ev) {
if (ev) {
this.clearDependentFields(ev.target);
this.persist();
}
if (this.criterion.telemetry) {
const telemetry = (this.criterion.telemetry === 'all' || this.criterion.telemetry === 'any') ? this.telemetry : [{
identifier: this.criterion.telemetry
}];
let telemetryPromises = telemetry.map((telemetryObject) => this.openmct.objects.get(telemetryObject.identifier));
Promise.all(telemetryPromises).then(telemetryObjects => {
this.telemetryMetadataOptions = [];
telemetryObjects.forEach(telemetryObject => {
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
this.addMetaDataOptions(telemetryMetadata.values());
});
this.updateOperations();
});
}
},
addMetaDataOptions(options) {
if (!this.telemetryMetadataOptions) {
this.telemetryMetadataOptions = options;
}
options.forEach((option) => {
const found = this.telemetryMetadataOptions.find((metadataOption) => {
return (metadataOption.key && (metadataOption.key === option.key)) && (metadataOption.name && (metadataOption.name === option.name))
});
if (!found) {
this.telemetryMetadataOptions.push(option);
}
});
},
updateMetadataOptions(ev) {
if (ev) {this.clearInputs()}
if (this.criterion.telemetry) {
this.openmct.objects.get(this.criterion.telemetry).then((telemetryObject) => {
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
this.telemetryMetadataOptions = this.telemetryMetadata.values();
this.updateOperations();
this.updateOperationInputVisibility();
});
} else {
this.criterion.metadata = '';
}
},
updateOperations(ev) {
if (ev) {this.clearInputs()}
this.getOperationFormat();
this.persist();
},
updateOperationInputVisibility(ev) {
this.updateOperationFormat();
if (ev) {
this.criterion.input = [];
this.inputCount = 0;
this.clearDependentFields(ev.target);
this.persist();
}
},
updateInputVisibilityAndValues(ev) {
if (ev) {
this.clearDependentFields();
this.persist();
}
for (let i = 0; i < this.filteredOps.length; i++) {
if (this.criterion.operation === this.filteredOps[i].name) {
this.inputCount = this.filteredOps[i].inputCount;
if (!this.inputCount) {this.criterion.input = []}
}
}
this.persist();
if (!this.inputCount) {
this.criterion.input = [];
}
},
clearInputs() {
this.criterion.operation = '';
this.criterion.input = [];
this.inputCount = 0;
},
updateMetadataSelection() {
this.updateOperationInputVisibility();
clearDependentFields(el) {
if (el === this.$refs.telemetrySelect) {
this.criterion.metadata = '';
} else if (el === this.$refs.metadataSelect) {
if (!this.filteredOps.find(operation => operation.name === this.criterion.operation)) {
this.criterion.operation = '';
this.criterion.input = this.enumerations.length ? [this.enumerations[0].value.toString()] : [];
this.inputCount = 0;
}
} else {
if (this.enumerations.length && !this.criterion.input.length) {
this.criterion.input = [this.enumerations[0].value.toString()];
}
this.inputCount = 0;
}
},
persist() {
this.$emit('persist', this.criterion);

View File

@@ -23,54 +23,98 @@
<template>
<section v-show="isEditing"
id="test-data"
class="test-data"
:class="{ 'is-expanded': expanded }"
>
<div class="c-cs__ui__header">
<span class="c-cs__ui__header-label">Test Data</span>
<div class="c-cs__header c-section__header">
<span
class="is-enabled flex-elem"
:class="['c-cs__disclosure-triangle', { 'c-cs__disclosure-triangle--expanded': expanded }]"
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
:class="{ 'c-disclosure-triangle--expanded': expanded }"
@click="expanded = !expanded"
></span>
<div class="c-cs__header-label c-section__label">Test Data</div>
</div>
<div v-if="expanded"
class="c-cs__ui_content"
class="c-cs__content"
>
<label class="c-toggle-switch">
<input
type="checkbox"
:checked="isApplied"
@change="applyTestData"
>
<span class="c-toggle-switch__slider"></span>
<span>Apply Test Data</span>
</label>
<div class="t-test-data-config">
<div class="c-cs-editui__conditions widget-condition">
<form>
<label>
<span>Set</span>
<select>
<option>- Select Input -</option>
</select>
</label>
<span class="is-enabled flex-elem c-cs__duplicate"></span>
<span class="is-enabled flex-elem c-cs__trash"></span>
</form>
</div>
<div class="c-cs-editui__conditions widget-condition">
<form>
<label>
<span>Set</span>
<select>
<option>- Select Input -</option>
</select>
</label>
<span class="is-enabled c-cs__duplicate"></span>
<span class="is-enabled c-cs__trash"></span>
</form>
</div>
<div class="c-cs__test-data__controls c-cdef__controls"
:disabled="!telemetry.length"
>
<label class="c-toggle-switch">
<input
type="checkbox"
:checked="isApplied"
@change="applyTestData"
>
<span class="c-toggle-switch__slider"></span>
<span class="c-toggle-switch__label">Apply Test Data</span>
</label>
</div>
<div class="c-cs-tests">
<span v-for="(testInput, tIndex) in testInputs"
:key="tIndex"
class="c-test-datum c-cs-test"
>
<span class="c-cs-test__label">Set</span>
<span class="c-cs-test__controls">
<span class="c-cdef__control">
<select v-model="testInput.telemetry"
@change="updateMetadata(testInput)"
>
<option value="">- Select Telemetry -</option>
<option v-for="(telemetryOption, index) in telemetry"
:key="index"
:value="telemetryOption.identifier"
>
{{ telemetryOption.name }}
</option>
</select>
</span>
<span v-if="testInput.telemetry"
class="c-cdef__control"
>
<select v-model="testInput.metadata"
@change="updateTestData"
>
<option value="">- Select Field -</option>
<option v-for="(option, index) in telemetryMetadataOptions[getId(testInput.telemetry)]"
:key="index"
:value="option.key"
>
{{ option.name }}
</option>
</select>
</span>
<span v-if="testInput.metadata"
class="c-cdef__control__inputs"
>
<input v-model="testInput.value"
placeholder="Enter test input"
type="text"
class="c-cdef__control__input"
@change="updateTestData"
>
</span>
</span>
<div class="c-cs-test__buttons">
<button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
title="Duplicate this test datum"
@click="addTestInput(testInput)"
></button>
<button class="c-click-icon c-test-data__delete-button icon-trash"
title="Delete this test datum"
@click="removeTestInput(tIndex)"
></button>
</div>
</span>
</div>
<button
v-show="isEditing"
id="addTestDatum"
class="c-button c-button--major icon-plus labeled"
@click="addTestInput"
>
<span class="c-cs-button__label">Add Test Datum</span>
</button>
</div>
</section>
</template>
@@ -79,17 +123,113 @@
export default {
inject: ['openmct'],
props: {
isEditing: Boolean
isEditing: Boolean,
telemetry: {
type: Array,
required: true,
default: () => []
},
testData: {
type: Object,
required: true,
default: () => {
return {
applied: false,
conditionTestInputs: []
}
}
}
},
data() {
return {
expanded: true,
isApplied: true
isApplied: false,
testInputs: [],
telemetryMetadataOptions: {}
};
},
watch: {
isEditing(editing) {
if (!editing) {
this.resetApplied();
}
},
telemetry: {
handler() {
this.initializeMetadata();
},
deep: true
},
testData: {
handler() {
this.initialize();
},
deep: true
}
},
beforeDestroy() {
this.resetApplied();
},
mounted() {
this.initialize();
this.initializeMetadata();
},
methods: {
applyTestData(ev) {
this.$emit('change', ev.target.checked);
applyTestData() {
this.isApplied = !this.isApplied;
this.updateTestData();
},
initialize() {
if (this.testData && this.testData.conditionTestInputs) {
this.testInputs = this.testData.conditionTestInputs;
}
if (!this.testInputs.length) {
this.addTestInput();
}
},
initializeMetadata() {
this.telemetry.forEach((telemetryObject) => {
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
});
},
addTestInput(testInput) {
this.testInputs.push(Object.assign({
telemetry: '',
metadata: '',
input: ''
}, testInput));
},
removeTestInput(index) {
this.testInputs.splice(index, 1);
this.updateTestData();
},
getId(identifier) {
if (identifier) {
return this.openmct.objects.makeKeyString(identifier);
}
return [];
},
updateMetadata(testInput) {
if (testInput.telemetry) {
const id = this.openmct.objects.makeKeyString(testInput.telemetry);
if(this.telemetryMetadataOptions[id]) {
return;
}
let telemetryMetadata = this.openmct.telemetry.getMetadata(testInput);
this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
}
},
resetApplied() {
this.isApplied = false;
this.updateTestData();
},
updateTestData() {
this.$emit('updateTestData', {
applied: this.isApplied,
conditionTestInputs: this.testInputs
});
}
}
}

View File

@@ -1,155 +0,0 @@
.c-cs-edit {
padding: 0;
}
section {
padding-bottom: 5px;
}
.c-cs__ui__header {
background-color: #D0D1D3;
padding: 0.4em 0.6em;
text-transform: uppercase;
font-size: 0.8em;
font-weight: bold;
color: #7C7D80;
display: flex;
justify-content: stretch;
align-items: center;
}
.c-cs__ui__header-label {
display: inline-block;
width: 100%;
}
.c-cs__ui_content {
padding: 0.4em;
}
.c-cs-ui__label {
color: #333;
}
.c-cs__ui_content .help {
font-style: italic;
padding: 0.4em 0;
}
.current-output {
text-transform: uppercase;
font-weight: bold;
margin: 0.4em 0.6em;
}
.condition-output {
color: #999;
}
.condition-description {
color: #333;
}
.checkbox.custom {
display: flex;
align-items: center;
margin-left: 0.6em;
}
.checkbox.custom span {
display: inline-block;
margin-left: 0.6em;
}
.t-test-data-config {
margin-top: 5px;
}
.c-c__condition-collection.is-disabled {
opacity: 0.5;
}
.widget-condition form {
padding: 0.5em;
display: flex;
align-items: center;
justify-content: stretch;
}
.widget-condition form label {
flex-grow: 1;
margin-left: 0;
}
.widget-condition form input {
min-height: 24px;
}
.c-cs-button[class*="--major"],
.c-cs-button[class*='is-active'],
.c-cs-button--menu[class*="--major"],
.c-cs-button--menu[class*='is-active'] {
border: solid 1px #0B427C;
background-color: #4778A3;
padding: 0.2em 0.6em;
margin: 0.4em;
font-weight: bold;
color: #eee;
border-radius: 6px;
&.is-disabled {
opacity: 0.5;
}
}
.c-cs__disclosure-triangle,
.c-cs__menu-hamburger,
.c-cs__duplicate,
.c-cs__trash {
$d: 8px;
color: $colorDisclosureCtrl;
width: $d;
visibility: hidden;
&.is-enabled {
cursor: pointer;
visibility: visible;
&:hover {
color: $colorDisclosureCtrlHov;
}
&:before {
$s: .5;
display: block;
font-family: symbolsfont;
font-size: 1rem * $s;
}
}
}
.c-cs__disclosure-triangle {
&:before {
content: $glyph-icon-arrow-right;
}
&--expanded {
&:before {
transform: rotate(90deg);
}
}
}
.c-cs__duplicate {
margin-right: 0.3em;
&:before {
content: $glyph-icon-duplicate;
}
}
.c-cs__trash {
&:before {
content: $glyph-icon-trash;
}
}

View File

@@ -1,208 +0,0 @@
.widget-condition {
background-color: #eee;
margin: 0 0 0.33em;
border-radius: 3px;
&--current {
background-color: #DEECFA;
}
}
.c-c-editui__conditions.widget-condition {
margin: 0;
}
.c-c-button-wrapper {
border-top: solid 1px #ccc;
padding: 2px;
}
.c-c-label-spacer {
display: inline-block;
width: 90px;
}
.c-c-button[class*="--minor"],
.c-c-button[class*='is-active'],
.c-c-button--menu[class*="--minor"],
.c-c-button--menu[class*='is-active'] {
border: solid 1px #666;
background-color: #fff;
padding: 0.1em 0.4em;
margin: 0.4em;
font-weight: normal;
color: #666;
border-radius: 6px;
}
.title-bar {
display: flex;
align-items: center;
justify-content: stretch;
padding: 0.4em 0;
}
.title-bar span {
margin-right: 0.6em;
}
.title-bar span.c-c__duplicate,
.title-bar span.c-c__trash{
margin-right: 0;
margin-left: 0.3em;
}
.widget-condition > div {
margin: 0 0.4em;
}
.condition-name {
font-weight: bold;
}
.condition-summary .condition-description {
color: #999;
}
.condition-summary {
flex-grow: 1;
}
.condition-config {
padding: 0.3em 0;
}
.widget-condition form label {
flex-grow: 1;
margin-left: 0;
}
.l-widget-condition {
padding: 0;
}
.l-compact-form ul li {
padding: 0;
}
.widget-condition-content.expanded {
margin: 0 3px;
}
.widget-condition-content.expanded ul li {
border-top: solid 1px #ccc;
padding: 2px;
}
.l-compact-form ul li .controls {
display: inline-flex;
flex-grow: inherit;
padding: 2px 0;
}
.l-compact-form ul li > label {
display: block;
width: 90px;
min-width: 90px;
text-align: right;
line-height: 20px;
}
.l-compact-form ul li .controls input[type="text"],
.l-compact-form ul li .controls input[type="search"],
.l-compact-form ul li .controls input[type="number"],
.l-compact-form ul li .controls button,
.l-compact-form ul li .controls select {
min-height: 20px;
}
.condition-config-edit {
padding: 3px 0;
}
.c-c__disclosure-triangle,
.c-c__menu-hamburger,
.c-c__duplicate,
.c-c__trash {
$d: 8px;
color: $colorDisclosureCtrl;
width: $d;
visibility: hidden;
&.is-enabled {
cursor: pointer;
visibility: visible;
&:hover {
color: $colorDisclosureCtrlHov;
}
&:before {
$s: .5;
display: block;
font-family: symbolsfont;
font-size: 1rem * $s;
}
}
}
.c-c__disclosure-triangle {
&:before {
content: $glyph-icon-arrow-right;
}
&--expanded {
&:before {
transform: rotate(90deg);
}
}
}
.c-c__menu-hamburger {
&:active {
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
}
&:before {
content: $glyph-icon-menu-hamburger;
}
}
.c-c__duplicate {
&:before {
content: $glyph-icon-duplicate;
}
}
.c-c__trash {
&:before {
content: $glyph-icon-trash;
}
}
.c-c__drag-ghost {
width: 100%;
min-height: 0.33em;
&.dragging {
min-height: 2em;
border: solid 1px blue;
background-color: lightblue;
border-radius: 2px;
}
}
.c-c__criterion-controls {
width: 28px;
.c-c__duplicate,
.c-c__trash {
display: inline-block;
}
}

View File

@@ -0,0 +1,318 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/***************************** DRAGGING */
.is-active-dragging {
.c-condition-h__drop-target {
height: 3px;
margin-bottom: $interiorMarginSm;
}
}
.c-condition-h {
&__drop-target {
border-radius: $controlCr;
height: 0;
min-height: 0;
transition: background-color, height;
transition-duration: 150ms;
}
&.is-drag-target {
.c-condition > * {
pointer-events: none; // Keeps the JS drop handler from being intercepted by internal elements
}
.c-condition-h__drop-target {
background-color: rgba($colorKey, 0.7);
}
}
}
.c-cs {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
/************************** CONDITION SET LAYOUT */
&__current-output {
flex: 0 0 auto;
}
&__test-data-and-conditions-w {
display: flex;
flex-direction: column;
flex: 1 1 auto;
height: 100%;
overflow: hidden;
}
&__test-data,
&__conditions {
flex: 0 0 auto;
overflow: hidden;
}
&__test-data {
flex: 0 0 auto;
max-height: 50%;
&.is-expanded {
margin-bottom: $interiorMargin * 4;
}
}
&__conditions {
flex: 1 1 auto;
> * + * {
margin-top: $interiorMarginSm;
}
}
&__content {
display: flex;
flex-direction: column;
flex: 0 1 auto;
overflow: hidden;
> * {
flex: 0 0 auto;
overflow: hidden;
+ * {
margin-top: $interiorMarginSm;
}
}
.c-button {
align-self: start;
}
}
.is-editing & {
// Add some space to kick away from blue editing border indication
padding: $interiorMargin;
}
section {
display: flex;
flex-direction: column;
overflow: hidden;
}
&__conditions-h {
display: flex;
flex-direction: column;
flex: 1 1 auto;
overflow: auto;
padding-right: $interiorMarginSm;
> * + * {
margin-top: $interiorMarginSm;
}
}
.hint {
padding: $interiorMarginSm;
}
/************************** SPECIFIC ITEMS */
&__current-output-value {
flex-direction: row;
align-items: baseline;
padding: 0 $interiorMargin $interiorMarginLg $interiorMargin;
> * {
padding: $interiorMargin 0; // Must do this to align label and value
}
&__label {
color: $colorInspectorSectionHeaderFg;
opacity: 0.9;
text-transform: uppercase;
}
&__value {
$p: $interiorMargin * 3;
font-size: 1.25em;
margin-left: $interiorMargin;
padding-left: $p;
padding-right: $p;
background: rgba(black, 0.2);
border-radius: 5px;
}
}
}
/***************************** CONDITIONS AND TEST DATUM ELEMENTS */
.c-condition,
.c-test-datum {
@include discreteItem();
display: flex;
padding: $interiorMargin;
line-height: 170%; // Aligns text with controls like selects
}
.c-cdef,
.c-cs-test {
&__controls {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
> * > * {
margin-right: $interiorMarginSm;
}
}
&__buttons {
white-space: nowrap;
}
}
.c-condition {
border: 1px solid transparent;
flex-direction: column;
min-width: 400px;
> * + * {
margin-top: $interiorMarginSm;
}
&--browse {
.c-condition__summary {
border-top: 1px solid $colorInteriorBorder;
padding-top: $interiorMargin;
}
}
/***************************** HEADER */
&__header {
$h: 22px;
display: flex;
align-items: start;
align-content: stretch;
overflow: hidden;
min-height: $h;
line-height: $h;
> * {
flex: 0 0 auto;
+ * {
margin-left: $interiorMarginSm;
}
}
}
&__drag-grippy {
transform: translateY(50%);
}
&__name {
font-weight: bold;
align-self: baseline; // Fixes bold line-height offset problem
}
&__output,
&__summary {
flex: 1 1 auto;
}
&.is-current {
$c: $colorBodyFg;
border-color: rgba($c, 0.2);
background: rgba($c, 0.2);
}
}
/***************************** CONDITION DEFINITION, EDITING */
.c-cdef {
display: grid;
grid-row-gap: $interiorMarginSm;
grid-column-gap: $interiorMargin;
grid-auto-columns: min-content 1fr max-content;
align-items: start;
min-width: 150px;
margin-left: 29px;
overflow: hidden;
&__criteria,
&__match-and-criteria {
display: contents;
}
&__label {
grid-column: 1;
text-align: right;
white-space: nowrap;
}
&__separator {
grid-column: 1 / span 3;
}
&__controls {
align-items: flex-start;
grid-column: 2;
> * > * {
margin-right: $interiorMarginSm;
}
}
&__buttons {
grid-column: 3;
}
}
.c-c__drag-ghost {
width: 100%;
min-height: $interiorMarginSm;
&.dragging {
min-height: 5em;
background-color: lightblue;
border-radius: 2px;
}
}
/***************************** TEST DATA */
.c-cs__test-data {
&__controls {
flex: 0 0 auto;
}
}
.c-cs-tests {
flex: 0 1 auto;
overflow: auto;
padding-right: $interiorMarginSm;
> * + * {
margin-top: $interiorMarginSm;
}
}
.c-cs-test {
> * + * {
margin-left: $interiorMargin;
}
}

View File

@@ -0,0 +1,185 @@
/*****************************************************************************
* 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>
<li class="c-tree__item-h">
<div
class="c-tree__item"
:class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
@click="handleItemSelected(node.object, node)"
>
<view-control
v-model="expanded"
class="c-tree__item__view-control"
:enabled="hasChildren"
:propagate="false"
/>
<div class="c-tree__item__label c-object-label">
<div
class="c-tree__item__type-icon c-object-label__type-icon"
:class="typeClass"
></div>
<div class="c-tree__item__name c-object-label__name">{{ node.object.name }}</div>
</div>
</div>
<ul
v-if="expanded"
class="c-tree"
>
<li
v-if="isLoading && !loaded"
class="c-tree__item-h"
>
<div class="c-tree__item loading">
<span class="c-tree__item__label">Loading...</span>
</div>
</li>
<condition-set-dialog-tree-item
v-for="child in children"
:key="child.id"
:node="child"
:selected-item="selectedItem"
:handle-item-selected="handleItemSelected"
/>
</ul>
</li>
</template>
<script>
import viewControl from '@/ui/components/viewControl.vue';
export default {
name: 'ConditionSetDialogTreeItem',
inject: ['openmct'],
components: {
viewControl
},
props: {
node: {
type: Object,
required: true
},
selectedItem: {
type: Object,
default() {
return undefined;
}
},
handleItemSelected: {
type: Function,
default() {
return (item) => {};
}
}
},
data() {
return {
hasChildren: false,
isLoading: false,
loaded: false,
children: [],
expanded: false
}
},
computed: {
navigated() {
const itemId = this.selectedItem && this.selectedItem.itemId;
const isSelectedObject = itemId && this.openmct.objects.areIdsEqual(this.node.object.identifier, itemId);
if (isSelectedObject && this.node.objectPath && this.node.objectPath.length > 1) {
const isParent = this.openmct.objects.areIdsEqual(this.node.objectPath[1].identifier, this.selectedItem.parentId);
return isSelectedObject && isParent;
}
return isSelectedObject;
},
isAlias() {
let parent = this.node.objectPath[1];
if (!parent) {
return false;
}
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
return parentKeyString !== this.node.object.location;
},
typeClass() {
let type = this.openmct.types.get(this.node.object.type);
if (!type) {
return 'icon-object-unknown';
}
return type.definition.cssClass;
}
},
watch: {
expanded() {
if (!this.hasChildren) {
return;
}
if (!this.loaded && !this.isLoading) {
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addChild);
this.composition.on('remove', this.removeChild);
this.composition.load().then(this.finishLoading);
this.isLoading = true;
}
}
},
mounted() {
this.domainObject = this.node.object;
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
this.domainObject = newObject;
});
this.$once('hook:destroyed', removeListener);
if (this.openmct.composition.get(this.node.object)) {
this.hasChildren = true;
}
},
beforeDestroy() {
this.expanded = false;
},
destroyed() {
if (this.composition) {
this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild);
delete this.composition;
}
},
methods: {
addChild(child) {
this.children.push({
id: this.openmct.objects.makeKeyString(child.identifier),
object: child,
objectPath: [child].concat(this.node.objectPath),
navigateToParent: this.navigateToPath
});
},
removeChild(identifier) {
let removeId = this.openmct.objects.makeKeyString(identifier);
this.children = this.children
.filter(c => c.id !== removeId);
},
finishLoading() {
this.isLoading = false;
this.loaded = true;
}
}
}
</script>

View File

@@ -0,0 +1,172 @@
/*****************************************************************************
* 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="u-contents">
<div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">Select Condition Set</div>
</div>
<div class="c-overlay__contents-main c-selector c-tree-and-search">
<div class="c-tree-and-search__search">
<search ref="shell-search"
class="c-search"
:value="searchValue"
@input="searchTree"
@clear="searchTree"
/>
</div>
<!-- loading -->
<div v-if="isLoading"
class="c-tree-and-search__loading loading"
></div>
<!-- end loading -->
<div v-if="(allTreeItems.length === 0) || (searchValue && filteredTreeItems.length === 0)"
class="c-tree-and-search__no-results"
>
No results found
</div>
<!-- main tree -->
<ul v-if="!isLoading"
v-show="!searchValue"
class="c-tree-and-search__tree c-tree"
>
<condition-set-dialog-tree-item
v-for="treeItem in allTreeItems"
:key="treeItem.id"
:node="treeItem"
:selected-item="selectedItem"
:handle-item-selected="handleItemSelection"
/>
</ul>
<!-- end main tree -->
<!-- search tree -->
<ul v-if="searchValue"
class="c-tree-and-search__tree c-tree"
>
<condition-set-dialog-tree-item
v-for="treeItem in filteredTreeItems"
:key="treeItem.id"
:node="treeItem"
:selected-item="selectedItem"
:handle-item-selected="handleItemSelection"
/>
</ul>
<!-- end search tree -->
</div>
</div>
</template>
<script>
import search from '@/ui/components/search.vue';
import ConditionSetDialogTreeItem from './ConditionSetDialogTreeItem.vue';
export default {
inject: ['openmct'],
name: 'ConditionSetSelectorDialog',
components: {
search,
ConditionSetDialogTreeItem
},
data() {
return {
expanded: false,
searchValue: '',
allTreeItems: [],
filteredTreeItems: [],
isLoading: false,
selectedItem: undefined
}
},
mounted() {
this.searchService = this.openmct.$injector.get('searchService');
this.getAllChildren();
},
methods: {
getAllChildren() {
this.isLoading = true;
this.openmct.objects.get('ROOT')
.then(root => {
return this.openmct.composition.get(root).load()
})
.then(children => {
this.isLoading = false;
this.allTreeItems = children.map(c => {
return {
id: this.openmct.objects.makeKeyString(c.identifier),
object: c,
objectPath: [c],
navigateToParent: '/browse'
};
});
});
},
getFilteredChildren() {
this.searchService.query(this.searchValue).then(children => {
this.filteredTreeItems = children.hits.map(child => {
let context = child.object.getCapability('context'),
object = child.object.useCapability('adapter'),
objectPath = [],
navigateToParent;
if (context) {
objectPath = context.getPath().slice(1)
.map(oldObject => oldObject.useCapability('adapter'))
.reverse();
navigateToParent = '/browse/' + objectPath.slice(1)
.map((parent) => this.openmct.objects.makeKeyString(parent.identifier))
.join('/');
}
return {
id: this.openmct.objects.makeKeyString(object.identifier),
object,
objectPath,
navigateToParent
}
});
});
},
searchTree(value) {
this.searchValue = value;
if (this.searchValue !== '') {
this.getFilteredChildren();
}
},
handleItemSelection(item, node) {
if (item && item.type === 'conditionSet') {
const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
this.selectedItem = {
itemId: item.identifier,
parentId
};
this.$emit('conditionSetSelected', item);
}
}
}
}
</script>

View File

@@ -1,38 +0,0 @@
<template>
<div>
<div v-if="condition"
class="holder c-c-button-wrapper align-left"
>
<div>{{ condition.configuration.name }}</div>
</div>
</div>
</template>
<script>
export default {
components: {
},
inject: [
'openmct'
],
props: {
conditionIdentifier: {
type: Object,
required: true
}
},
data() {
return {
condition: null
}
},
destroyed() {
},
mounted() {
this.openmct.objects.get(this.conditionIdentifier).then((conditionDomainObject) => {
this.condition = conditionDomainObject;
});
}
}
</script>

View File

@@ -1,109 +1,457 @@
/*****************************************************************************
* 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>
<div v-if="!conditionalStyles.length"
class="holder c-c-button-wrapper align-left">
<button
class="c-c-button c-c-button--minor add-criteria-button"
@click="addConditionSet">
<span class="c-c-button__label">Use conditional styling</span>
</button>
</div>
<div v-else>
<div class="holder c-c-button-wrapper align-left">
<div class="c-inspector__styles c-inspect-styles">
<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"
@persist="updateStaticStyle"
/>
</div>
<button
class="c-c-button c-c-button--minor add-criteria-button"
@click="removeConditionSet">
<span class="c-c-button__label">Remove conditional styling</span>
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>
<ul>
<li v-for="conditionStyle in conditionalStyles"
:key="conditionStyle.conditionIdentifier.key">
<conditional-style :condition-identifier="conditionStyle.conditionIdentifier"
:condition-style="conditionStyle.style"/>
</li>
</ul>
</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 ConditionalStyle from "./ConditionalStyle.vue";
import StyleEditor from "./StyleEditor.vue";
import ConditionSetSelectorDialog from "./ConditionSetSelectorDialog.vue";
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import Vue from 'vue';
import PreviewAction from "@/ui/preview/PreviewAction.js";
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'ConditionalStylesView',
components: {
ConditionalStyle
ConditionDescription,
ConditionError,
StyleEditor
},
inject: [
'openmct',
'domainObject',
'layoutItem'
'selection'
],
data() {
return {
conditionalStyles: []
conditionalStyles: [],
staticStyle: undefined,
conditionSetDomainObject: undefined,
isEditing: this.openmct.editor.isEditing(),
conditions: undefined,
conditionsLoaded: false,
navigateToPath: '',
selectedConditionId: ''
}
},
destroyed() {
this.removeListeners();
},
mounted() {
if (this.layoutItem) {
//TODO: Handle layout items
}
if (this.domainObject.configuration) {
this.defautStyle = this.domainObject.configuration.defaultStyle;
if (this.domainObject.configuration.conditionalStyle) {
this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
this.itemId = '';
this.getDomainObjectFromSelection();
this.previewAction = new PreviewAction(this.openmct);
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
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: {
addConditionSet() {
//TODO: this.conditionSetIdentifier will be set by the UI before calling this
this.conditionSetIdentifier = {
namespace: '',
key: 'bb0f61ad-268d-4d3e-bb30-90ca4a2053c4'
};
this.initializeConditionalStyles();
isItemType(type, item) {
return item && (item.type === type);
},
removeConditionSet() {
this.conditionSetIdentifier = '';
this.conditionalStyles = [];
this.persist(undefined);
},
initializeConditionalStyles() {
const backgroundColors = [{backgroundColor: 'red'},{backgroundColor: 'orange'}, {backgroundColor: 'blue'}];
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
conditionSetDomainObject.configuration.conditionCollection.forEach((identifier, index) => {
this.conditionalStyles.push({
conditionIdentifier: identifier,
style: backgroundColors[index]
});
});
this.persist({
defaultStyle: this.defaultStyle || {backgroundColor: 'inherit'},
conditionSetIdentifier: this.conditionSetIdentifier,
styles: this.conditionalStyles
});
getDomainObjectFromSelection() {
let layoutItem;
let domainObject;
if (this.selection[0].length > 1) {
//If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
//The second item in the this.selection[0] list is the container object (usually a layout)
layoutItem = this.selection[0][0].context.layoutItem;
const item = this.selection[0][0].context.item;
this.canHide = true;
if (item &&
(!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
domainObject = item;
} else {
domainObject = this.selection[0][1].context.item;
if (layoutItem) {
this.itemId = layoutItem.id;
}
}
} else {
domainObject = this.selection[0][0].context.item;
}
this.domainObject = domainObject;
this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
this.$nextTick(() => {
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);
}
});
},
findStyleByConditionId(id) {
for(let i=0, ii=this.conditionalStyles.length; i < ii; i++) {
if (this.openmct.objects.makeKeyString(this.conditionalStyles[i].conditionIdentifier) === this.openmct.objects.makeKeyString(id)) {
removeListeners() {
if (this.stopObserving) {
this.stopObserving();
}
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
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();
},
setEditState(isEditing) {
this.isEditing = isEditing;
if (this.isEditing) {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
} else {
this.subscribeToConditionSet();
}
},
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 {
index: i,
item: this.conditionalStyles[i]
};
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()
});
},
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);
}
},
removeConditionSet() {
this.conditionSetDomainObject = undefined;
this.conditionalStyles = [];
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) {
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
domainObjectStyles[this.itemId].selectedConditionId = undefined;
domainObjectStyles[this.itemId].defaultConditionId = undefined;
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
domainObjectStyles[this.itemId].styles = undefined;
delete domainObjectStyles[this.itemId].styles;
if (isEmpty(domainObjectStyles[this.itemId])) {
delete domainObjectStyles[this.itemId];
}
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
updateDomainObjectItemStyles(newItems) {
//check that all items that have been styles still exist. Otherwise delete those styles
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
let itemsToRemove = [];
let keys = Object.keys(domainObjectStyles);
//TODO: Need an easier way to find which properties are itemIds
keys.forEach((key) => {
const keyIsItemId = (key !== 'styles') &&
(key !== 'staticStyle') &&
(key !== 'defaultConditionId') &&
(key !== 'selectedConditionId') &&
(key !== 'conditionSetIdentifier');
if (keyIsItemId) {
if (!(newItems.find(item => item.id === key))) {
itemsToRemove.push(key);
}
}
});
if (itemsToRemove.length) {
this.removeItemStyles(itemsToRemove, domainObjectStyles);
}
},
removeItemStyles(itemIds, domainObjectStyles) {
itemIds.forEach(itemId => {
if (domainObjectStyles[itemId]) {
domainObjectStyles[itemId] = undefined;
delete domainObjectStyles[this.itemId];
}
});
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(domainObjectStyles);
},
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.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
if (!this.isEditing) {
this.subscribeToConditionSet();
}
},
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
if (this.conditionSetDomainObject) {
this.openmct.telemetry.request(this.conditionSetDomainObject)
.then(output => {
if (output && output.length) {
this.handleConditionSetResultUpdated(output[0]);
}
});
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
}
},
handleConditionSetResultUpdated(resultData) {
this.selectedConditionId = resultData ? resultData.conditionId : '';
},
initializeStaticStyle(objectStyles) {
let staticStyle = objectStyles && objectStyles.staticStyle;
if (staticStyle) {
this.staticStyle = {
style: Object.assign({}, this.initialStyles, staticStyle.style)
};
} else {
this.staticStyle = {
style: Object.assign({}, this.initialStyles)
};
}
},
findStyleByConditionId(id) {
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
},
updateStaticStyle(staticStyle) {
this.staticStyle = staticStyle;
this.persist(this.getDomainObjectConditionalStyle());
},
updateConditionalStyle(conditionStyle) {
let found = this.findStyleByConditionId(conditionStyle.conditionId);
if (found) {
found.style = conditionStyle.style;
this.selectedConditionId = found.conditionId;
this.persist(this.getDomainObjectConditionalStyle());
}
},
getDomainObjectConditionalStyle(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 = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (this.itemId) {
domainObjectStyles[this.itemId] = objectStyle;
} else {
//we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
}
}
return domainObjectStyles;
},
updateConditionalStyle(conditionIdentifier, style) {
let found = this.findStyleByConditionId(conditionIdentifier);
if (found) {
this.conditionalStyles[found.index].style = style;
}
this.persist(undefined);
getCondition(id) {
return this.conditions ? this.conditions[id] : {};
},
persist(conditionalStyle) {
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle);
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.persist(this.getDomainObjectConditionalStyle());
},
persist(style) {
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
}
}
}

View File

@@ -0,0 +1,270 @@
/*****************************************************************************
* 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 class="c-inspect-styles__header">
Object Style
</div>
<div class="c-inspect-styles__content">
<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>
<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>
</div>
</div>
</template>
<script>
import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
import isEmpty from 'lodash/isEmpty';
export default {
name: 'MultiSelectStylesView',
components: {
StyleEditor
},
inject: [
'openmct',
'selection'
],
data() {
return {
staticStyle: undefined,
isEditing: this.openmct.editor.isEditing(),
mixedStyles: [],
isStaticAndConditionalStyles: false
}
},
destroyed() {
this.removeListeners();
},
mounted() {
this.items = [];
this.previewAction = new PreviewAction(this.openmct);
this.getObjectsAndItemsFromSelection();
this.initializeStaticStyle();
this.openmct.editor.on('isEditing', this.setEditState);
},
methods: {
isItemType(type, item) {
return item && (item.type === type);
},
hasConditionalStyles(domainObject, id) {
return getConditionalStyleForItem(domainObject, id) !== undefined;
},
getObjectsAndItemsFromSelection() {
let domainObject;
let subObjects = [];
//multiple selection
let itemInitialStyles = [];
let itemStyle;
this.selection.forEach((selectionItem) => {
const item = selectionItem[0].context.item;
const layoutItem = selectionItem[0].context.layoutItem;
if (item && this.isItemType('subobject-view', layoutItem)) {
subObjects.push(item);
itemStyle = getApplicableStylesForItem(item);
if (!this.isStaticAndConditionalStyles) {
this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
}
} else {
domainObject = selectionItem[1].context.item;
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
this.items.push({
id: layoutItem.id,
applicableStyles: itemStyle
});
if (!this.isStaticAndConditionalStyles) {
this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
}
}
itemInitialStyles.push(itemStyle);
});
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) {
//check that all items that have been styles still exist. Otherwise delete those styles
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
keys.forEach((key) => {
if ((key !== 'styles') &&
(key !== 'staticStyle') &&
(key !== 'conditionSetIdentifier')) {
if (!(newItems.find(item => item.id === key))) {
this.removeItemStyles(key);
}
}
});
},
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, '*', function (newObject) {
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
}.bind(this));
this.unObserveObjects.push(unobserveObject);
},
removeListeners() {
if (this.stopObserving) {
this.stopObserving();
}
if (this.stopObservingItems) {
this.stopObservingItems();
}
if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => {
unObserveObject();
});
}
this.unObserveObjects = [];
},
removeItemStyles(itemId) {
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
if (itemId && domainObjectStyles[itemId]) {
domainObjectStyles[itemId] = undefined;
delete domainObjectStyles[this.itemId];
if (isEmpty(domainObjectStyles)) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
}
},
removeConditionalStyles(domainObjectStyles, itemId) {
if (itemId) {
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
delete domainObjectStyles[itemId].conditionSetIdentifier;
domainObjectStyles[itemId].styles = undefined;
delete domainObjectStyles[itemId].styles;
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
},
setEditState(isEditing) {
this.isEditing = isEditing;
},
initializeStaticStyle() {
this.staticStyle = {
style: Object.assign({}, this.initialStyles)
};
},
updateStaticStyle(staticStyle, property) {
//update the static style for each of the layoutItems as well as each sub object item
this.staticStyle = staticStyle;
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
if (this.domainObjectsById) {
const keys = Object.keys(this.domainObjectsById);
keys.forEach(key => {
let domainObject = this.domainObjectsById[key];
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
});
}
this.isStaticAndConditionalStyles = false;
let foundIndex = this.mixedStyles.indexOf(property);
if (foundIndex > -1) {
this.mixedStyles.splice(foundIndex, 1);
}
},
getDomainObjectStyle(domainObject, property, items) {
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
if (items) {
items.forEach(item => {
let itemStaticStyle = {};
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
}
Object.keys(item.applicableStyles).forEach(key => {
if (property === key) {
itemStaticStyle[key] = this.staticStyle.style[key];
}
});
if (this.isStaticAndConditionalStyles) {
this.removeConditionalStyles(domainObjectStyles, item.id);
}
if (isEmpty(itemStaticStyle)) {
itemStaticStyle = undefined;
domainObjectStyles[item.id] = undefined;
} else {
domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
}
});
} else {
if (!domainObjectStyles.staticStyle) {
domainObjectStyles.staticStyle = {
style: {}
}
}
if (this.isStaticAndConditionalStyles) {
this.removeConditionalStyles(domainObjectStyles);
}
domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
}
return domainObjectStyles;
},
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
}
}
}
</script>

View File

@@ -0,0 +1,215 @@
/*****************************************************************************
* 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-style">
<span :class="[
{ 'is-style-invisible': styleItem.style.isStyleInvisible },
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
]"
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
class="c-style-thumb"
>
<span class="c-style-thumb__text"
:class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
>
ABC
</span>
</span>
<span class="c-toolbar">
<toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
class="c-style__toolbar-button--border-color u-menu-to--center"
:options="borderColorOption"
@change="updateStyleValue"
/>
<toolbar-color-picker v-if="hasProperty(styleItem.style.backgroundColor)"
class="c-style__toolbar-button--background-color u-menu-to--center"
:options="backgroundColorOption"
@change="updateStyleValue"
/>
<toolbar-color-picker v-if="hasProperty(styleItem.style.color)"
class="c-style__toolbar-button--color u-menu-to--center"
:options="colorOption"
@change="updateStyleValue"
/>
<toolbar-button v-if="hasProperty(styleItem.style.imageUrl)"
class="c-style__toolbar-button--image-url"
:options="imageUrlOption"
@change="updateStyleValue"
/>
<toolbar-toggle-button v-if="hasProperty(styleItem.style.isStyleInvisible)"
class="c-style__toolbar-button--toggle-visible"
:options="isStyleInvisibleOption"
@change="updateStyleValue"
/>
</span>
</div>
</template>
<script>
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
import {getStylesWithoutNoneValue} from "@/plugins/condition/utils/styleUtils";
export default {
name: 'StyleEditor',
components: {
ToolbarButton,
ToolbarColorPicker,
ToolbarToggleButton
},
inject: [
'openmct'
],
props: {
isEditing: {
type: Boolean
},
mixedStyles: {
type: Array,
default() {
return [];
}
},
styleItem: {
type: Object,
required: true
}
},
computed: {
itemStyle() {
return getStylesWithoutNoneValue(this.styleItem.style);
},
borderColorOption() {
let value = this.styleItem.style.border.replace('1px solid ', '');
return {
icon: 'icon-line-horz',
title: STYLE_CONSTANTS.borderColorTitle,
value: this.normalizeValueForSwatch(value),
property: 'border',
isEditing: this.isEditing,
nonSpecific: this.mixedStyles.indexOf('border') > -1
}
},
backgroundColorOption() {
let value = this.styleItem.style.backgroundColor;
return {
icon: 'icon-paint-bucket',
title: STYLE_CONSTANTS.backgroundColorTitle,
value: this.normalizeValueForSwatch(value),
property: 'backgroundColor',
isEditing: this.isEditing,
nonSpecific: this.mixedStyles.indexOf('backgroundColor') > -1
}
},
colorOption() {
let value = this.styleItem.style.color;
return {
icon: 'icon-font',
title: STYLE_CONSTANTS.textColorTitle,
value: this.normalizeValueForSwatch(value),
property: 'color',
isEditing: this.isEditing,
nonSpecific: this.mixedStyles.indexOf('color') > -1
}
},
imageUrlOption() {
return {
icon: 'icon-image',
title: STYLE_CONSTANTS.imagePropertiesTitle,
dialog: {
name: "Image Properties",
sections: [
{
rows: [
{
key: "url",
control: "textfield",
name: "Image URL",
"cssClass": "l-input-lg"
}
]
}
]
},
property: 'imageUrl',
formKeys: ['url'],
value: {url: this.styleItem.style.imageUrl},
isEditing: this.isEditing,
nonSpecific: this.mixedStyles.indexOf('imageUrl') > -1
}
},
isStyleInvisibleOption() {
return {
value: this.styleItem.style.isStyleInvisible,
property: 'isStyleInvisible',
isEditing: this.isEditing,
options: [
{
value: '',
icon: 'icon-eye-disabled',
title: STYLE_CONSTANTS.visibilityHidden
},
{
value: STYLE_CONSTANTS.isStyleInvisible,
icon: 'icon-eye-open',
title: STYLE_CONSTANTS.visibilityVisible
}
]
}
}
},
methods: {
hasProperty(property) {
return property !== undefined;
},
normalizeValueForSwatch(value) {
if (value && value.indexOf('__no_value') > -1) {
return value.replace('__no_value', 'transparent');
}
return value;
},
normalizeValueForStyle(value) {
if (value && value === 'transparent') {
return '__no_value';
}
return value;
},
updateStyleValue(value, item) {
value = this.normalizeValueForStyle(value);
if (item.property === 'border') {
value = '1px solid ' + value;
}
if (value && (value.url !== undefined)) {
this.styleItem.style[item.property] = value.url;
} else {
this.styleItem.style[item.property] = value;
}
this.$emit('persist', this.styleItem, item.property);
}
}
}
</script>

View 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 (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 (Object.keys(domainObjectStyles[itemId]).length <= 0) {
delete domainObjectStyles[itemId];
}
});
} else {
this.removeConditionalStyles(domainObjectStyles);
}
if (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 = domainObjectStyles[item.id].staticStyle.style;
}
if (item.applicableStyles[property]) {
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>

View File

@@ -0,0 +1,149 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/********************************************* INSPECTOR STYLES TAB */
.c-inspect-styles {
> * + * {
margin-top: $interiorMargin;
}
&__content,
&__conditions,
&__condition {
> * + * {
margin-top: $interiorMargin;
}
}
&__content {
display: flex;
flex-direction: column;
}
&__condition-set {
display: flex;
flex-direction: row;
align-items: center;
.c-object-label {
flex: 1 1 auto;
}
.c-button {
flex: 0 0 auto;
}
}
&__style,
&__condition {
padding: $interiorMargin;
}
&__condition {
@include discreteItem();
border: 1px solid transparent;
pointer-events: none; // Prevent selecting when the object isn't being edited
&.is-current {
$c: $colorBodyFg;
border-color: rgba($c, 0.2);
background: rgba($c, 0.2);
}
.is-editing & {
cursor: pointer;
pointer-events: initial;
transition: $transOut;
&:hover {
background: rgba($colorBodyFg, 0.1);
transition: $transIn;
}
&.is-current {
$c: $editUIColorBg;
border-color: $c;
background: rgba($c, 0.1);
}
}
}
.c-style {
padding: 2px; // Allow a bit of room for thumb box-shadow
&__condition-desc {
@include ellipsize();
}
}
}
.c-inspect-styles__style {
.is-editing & {
border-bottom: 1px solid $colorInteriorBorder;
}
}
.l-shell:not(.is-editing) .c-inspect-styles {
.c-toolbar {
// Disabled-look toolbar when not editing
pointer-events: none;
cursor: inherit;
// Hide control buttons, like image URL
[class*='--image-url'] {
display: none;
}
// Make buttons look disabled by knocking back icon, not swatch element
.c-icon-button {
&:before {
opacity: $controlDisabledOpacity;
}
}
}
}
.c-toggle-styling-button {
display: none;
.is-editing & {
display: block;
align-self: flex-end;
}
}
.is-style-invisible {
display: none !important;
.is-editing & {
display: block !important;
opacity: 0.2;
}
&.c-style-thumb {
display: block !important;
background-color: transparent !important;
border-color: transparent !important;
@include bgCheckerboard($size: 10px, $imp: true);
opacity: 1;
}
}

View File

@@ -0,0 +1,166 @@
/*****************************************************************************
* 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 TelemetryCriterion from './TelemetryCriterion';
import { evaluateResults } from "../utils/evaluator";
import { getLatestTimestamp } from '../utils/time';
export default class AllTelemetryCriterion extends TelemetryCriterion {
/**
* Subscribes/Unsubscribes to telemetry and emits the result
* of operations performed on the telemetry data returned and a given input value.
* @constructor
* @param telemetryDomainObjectDefinition {id: uuid, operation: enum, input: Array, metadata: string, key: {domainObject.identifier} }
* @param openmct
*/
constructor(telemetryDomainObjectDefinition, openmct) {
super(telemetryDomainObjectDefinition, openmct);
}
initialize() {
this.telemetryObjects = { ...this.telemetryDomainObjectDefinition.telemetryObjects };
this.telemetryDataCache = {};
}
isValid() {
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
}
updateTelemetry(telemetryObjects) {
this.telemetryObjects = { ...telemetryObjects };
this.removeTelemetryDataCache();
}
removeTelemetryDataCache() {
const telemetryCacheIds = Object.keys(this.telemetryDataCache);
Object.values(this.telemetryObjects).forEach(telemetryObject => {
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
const foundIndex = telemetryCacheIds.indexOf(id);
if (foundIndex > -1) {
telemetryCacheIds.splice(foundIndex, 1);
}
});
telemetryCacheIds.forEach(id => {
delete (this.telemetryDataCache[id]);
});
}
formatData(data, telemetryObjects) {
if (data) {
this.telemetryDataCache[data.id] = this.computeResult(data);
}
let keys = Object.keys(telemetryObjects);
keys.forEach((key) => {
let telemetryObject = telemetryObjects[key];
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
if (this.telemetryDataCache[id] === undefined) {
this.telemetryDataCache[id] = false;
}
});
const datum = {
result: evaluateResults(Object.values(this.telemetryDataCache), this.telemetry)
};
if (data) {
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
datum[timeSystem.key] = data[timeSystem.key]
});
}
return datum;
}
getResult(data, telemetryObjects) {
const validatedData = this.isValid() ? data : {};
if (validatedData) {
this.telemetryDataCache[validatedData.id] = this.computeResult(validatedData);
}
Object.values(telemetryObjects).forEach(telemetryObject => {
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
if (this.telemetryDataCache[id] === undefined) {
this.telemetryDataCache[id] = false;
}
});
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
}
requestLAD(telemetryObjects) {
const options = {
strategy: 'latest',
size: 1
};
if (!this.isValid()) {
return this.formatData({}, telemetryObjects);
}
let keys = Object.keys(Object.assign({}, telemetryObjects));
const telemetryRequests = keys
.map(key => this.openmct.telemetry.request(
telemetryObjects[key],
options
));
let telemetryDataCache = {};
return Promise.all(telemetryRequests)
.then(telemetryRequestsResults => {
let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => {
const latestDatum = results.length ? results[results.length - 1] : {};
const datumId = keys[index];
const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
latestTimestamp = getLatestTimestamp(
latestTimestamp,
normalizedDatum,
timeSystems,
timeSystem
);
});
const datum = {
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
...latestTimestamp
};
return {
id: this.id,
data: datum
};
});
}
destroy() {
delete this.telemetryObjects;
delete this.telemetryDataCache;
}
}

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
import EventEmitter from 'EventEmitter';
import {OPERATIONS} from '../utils/operations';
import { OPERATIONS } from '../utils/operations';
export default class TelemetryCriterion extends EventEmitter {
@@ -36,37 +36,89 @@ export default class TelemetryCriterion extends EventEmitter {
super();
this.openmct = openmct;
this.objectAPI = this.openmct.objects;
this.telemetryAPI = this.openmct.telemetry;
this.timeAPI = this.openmct.time;
this.telemetryDomainObjectDefinition = telemetryDomainObjectDefinition;
this.id = telemetryDomainObjectDefinition.id;
this.telemetry = telemetryDomainObjectDefinition.telemetry;
this.operation = telemetryDomainObjectDefinition.operation;
this.input = telemetryDomainObjectDefinition.input;
this.metadata = telemetryDomainObjectDefinition.metadata;
this.subscription = null;
this.telemetryObjectIdAsString = null;
this.objectAPI.get(this.objectAPI.makeKeyString(this.telemetry)).then((obj) => this.initialize(obj));
}
this.result = undefined;
initialize(obj) {
this.telemetryObject = obj;
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(this.telemetryObject.identifier);
this.initialize();
this.emitEvent('criterionUpdated', this);
}
handleSubscription(data) {
initialize() {
this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
}
isValid() {
return this.telemetryObject && this.metadata && this.operation;
}
updateTelemetry(telemetryObjects) {
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
}
createNormalizedDatum(telemetryDatum, endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
return datum;
}, {});
normalizedDatum.id = id;
return normalizedDatum;
}
formatData(data) {
const datum = {
result: this.computeResult(data)
};
if (data) {
// TODO check back to see if we should format times here
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
datum[timeSystem.key] = data[timeSystem.key]
});
}
return datum;
}
this.emitEvent('criterionResultUpdated', datum);
getResult(data) {
const validatedData = this.isValid() ? data : {};
this.result = this.computeResult(validatedData);
}
requestLAD() {
const options = {
strategy: 'latest',
size: 1
};
if (!this.isValid()) {
return {
id: this.id,
data: this.formatData({})
};
}
return this.openmct.telemetry.request(
this.telemetryObject,
options
).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
return {
id: this.id,
data: this.formatData(normalizedDatum)
};
});
}
findOperation(operation) {
@@ -88,7 +140,7 @@ export default class TelemetryCriterion extends EventEmitter {
this.input.forEach(input => params.push(input));
}
if (typeof comparator === 'function') {
result = comparator(params);
result = !!comparator(params);
}
}
return result;
@@ -101,40 +153,9 @@ export default class TelemetryCriterion extends EventEmitter {
});
}
isValid() {
return this.telemetryObject && this.metadata && this.operation;
}
/**
* Subscribes to the telemetry object and returns an unsubscribe function
* If the telemetry is not valid, returns nothing
*/
subscribe() {
if (this.isValid()) {
this.unsubscribe();
this.subscription = this.telemetryAPI.subscribe(this.telemetryObject, (datum) => {
this.handleSubscription(datum);
});
} else {
this.handleSubscription();
}
}
/**
* Calls an unsubscribe function returned by subscribe() and deletes any initialized data
*/
unsubscribe() {
//unsubscribe from telemetry source
if (typeof this.subscription === 'function') {
this.subscription();
}
delete this.subscription;
}
destroy() {
this.unsubscribe();
this.emitEvent('criterionRemoved');
delete this.telemetryObjectIdAsString;
delete this.telemetryObject;
delete this.telemetryObjectIdAsString;
}
}

View File

@@ -24,7 +24,6 @@ import TelemetryCriterion from "./TelemetryCriterion";
let openmct = {},
mockListener,
mockListener2,
testCriterionDefinition,
testTelemetryObject,
telemetryCriterion;
@@ -37,30 +36,39 @@ describe("The telemetry criterion", function () {
type: "test-object",
name: "Test Object",
telemetry: {
values: [{
key: "some-key",
name: "Some attribute",
valueMetadatas: [{
key: "value",
name: "Value",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
}, {
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
key: "testSource",
source: "value",
name: "Test",
format: "string"
}]
}
};
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) {
resolve(testTelemetryObject);
}));
openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata"]);
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter"]);
openmct.telemetry.isTelemetryObject.and.returnValue(true);
openmct.telemetry.subscribe.and.returnValue(function () {});
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
openmct.telemetry.getValueFormatter.and.returnValue({
parse: function (value) {
return value;
}
});
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
openmct.time = jasmine.createSpyObj('timeAPI',
['timeSystem', 'bounds', 'getAllTimeSystems']
@@ -71,13 +79,14 @@ describe("The telemetry criterion", function () {
testCriterionDefinition = {
id: 'test-criterion-id',
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier)
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
operation: 'textContains',
metadata: 'value',
input: ['Hell'],
telemetryObject: testTelemetryObject
};
mockListener = jasmine.createSpy('listener');
mockListener2 = jasmine.createSpy('updatedListener', (data) => {
console.log(data);
});
telemetryCriterion = new TelemetryCriterion(
testCriterionDefinition,
@@ -85,39 +94,28 @@ describe("The telemetry criterion", function () {
);
telemetryCriterion.on('criterionResultUpdated', mockListener);
telemetryCriterion.on('criterionUpdated', mockListener2);
});
it("initializes with a telemetry objectId as string", function () {
telemetryCriterion.initialize(testTelemetryObject);
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
expect(mockListener2).toHaveBeenCalled();
});
it("subscribes to telemetry providers", function () {
telemetryCriterion.subscribe();
expect(telemetryCriterion.subscription).toBeDefined();
});
it("emits update event on new data from telemetry providers", function () {
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
telemetryCriterion.handleSubscription({
key: 'some-key',
source: 'testSource',
testSource: 'Hello'
it("returns a result on new data from relevant telemetry providers", function () {
telemetryCriterion.getResult({
value: 'Hello',
utc: 'Hi',
id: testTelemetryObject.identifier.key
});
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
expect(mockListener).toHaveBeenCalled();
});
it("un-subscribes from telemetry providers", function () {
telemetryCriterion.subscribe();
expect(telemetryCriterion.subscription).toBeDefined();
telemetryCriterion.destroy();
expect(telemetryCriterion.subscription).toBeUndefined();
expect(telemetryCriterion.telemetryObjectIdAsString).toBeUndefined();
expect(telemetryCriterion.telemetryObject).toBeUndefined();
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();
// });
});

View File

@@ -23,35 +23,42 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
import uuid from "uuid";
export default function ConditionPlugin() {
return function install(openmct) {
openmct.types.addType('condition', {
name: 'Condition',
key: 'condition',
description: 'A list of criteria which will be evaluated based on a trigger',
creatable: false,
initialize: function (domainObject) {
domainObject.composition = [];
}
});
openmct.types.addType('conditionSet', {
name: 'Condition Set',
key: 'conditionSet',
description: 'A set of one or more conditions based on user-specified criteria.',
description: 'Monitor and evaluate telemetry values in real-time with a wide variety of criteria. Use to control the styling of many objects in Open MCT.',
creatable: true,
cssClass: 'icon-conditional', // TODO: replace with class for new icon
cssClass: 'icon-conditional',
initialize: function (domainObject) {
domainObject.configuration = {
conditionCollection: []
conditionTestData: [],
conditionCollection: [{
isDefault: true,
id: uuid(),
configuration: {
name: 'Default',
output: 'Default',
trigger: 'all',
criteria: []
},
summary: 'Default condition'
}]
};
domainObject.composition = [];
domainObject.telemetry = {};
}
});
openmct.legacyExtension('policies', {
category: 'view',
implementation: ConditionSetViewPolicy
});
openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));

View File

@@ -20,43 +20,27 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { createOpenMct } from "testTools";
import { createOpenMct } from "testUtils";
import ConditionPlugin from "./plugin";
let openmct = createOpenMct();
openmct.install(new ConditionPlugin());
let conditionDefinition;
let conditionSetDefinition;
let mockDomainObject;
let mockDomainObject2;
let element;
let child;
describe('the plugin', function () {
let conditionSetDefinition;
let mockConditionSetDomainObject;
let element;
let child;
let openmct;
beforeAll((done) => {
conditionDefinition = openmct.types.get('condition').definition;
mockDomainObject = {
identifier: {
key: 'testConditionKey',
namespace: ''
},
type: 'condition'
};
conditionDefinition.initialize(mockDomainObject);
openmct = createOpenMct();
openmct.install(new ConditionPlugin());
conditionSetDefinition = openmct.types.get('conditionSet').definition;
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
element = document.createElement('div');
child = document.createElement('div');
element.appendChild(child);
mockDomainObject2 = {
mockConditionSetDomainObject = {
identifier: {
key: 'testConditionSetKey',
namespace: ''
@@ -64,20 +48,10 @@ describe('the plugin', function () {
type: 'conditionSet'
};
conditionSetDefinition.initialize(mockDomainObject2);
conditionSetDefinition.initialize(mockConditionSetDomainObject);
openmct.on('start', done);
openmct.start(appHolder);
});
let mockConditionObject = {
name: 'Condition',
key: 'condition',
creatable: false
};
it('defines a condition object type with the correct key', () => {
expect(conditionDefinition.key).toEqual(mockConditionObject.key);
openmct.startHeadless();
});
let mockConditionSetObject = {
@@ -90,18 +64,6 @@ describe('the plugin', function () {
expect(conditionSetDefinition.key).toEqual(mockConditionSetObject.key);
});
describe('the condition object', () => {
it('is not creatable', () => {
expect(conditionDefinition.creatable).toEqual(mockConditionObject.creatable);
});
it('initializes with an empty composition list', () => {
expect(mockDomainObject.composition instanceof Array).toBeTrue();
expect(mockDomainObject.composition.length).toEqual(0);
});
});
describe('the conditionSet object', () => {
it('is creatable', () => {
@@ -109,14 +71,17 @@ describe('the plugin', function () {
});
it('initializes with an empty composition list', () => {
expect(mockDomainObject2.composition instanceof Array).toBeTrue();
expect(mockDomainObject2.composition.length).toEqual(0);
expect(mockConditionSetDomainObject.composition instanceof Array).toBeTrue();
expect(mockConditionSetDomainObject.composition.length).toEqual(0);
});
it('provides a view', () => {
const testViewObject = {
id:"test-object",
type: "conditionSet"
type: "conditionSet",
configuration: {
conditionCollection: []
}
};
const applicableViews = openmct.objectViews.get(testViewObject);

Some files were not shown because too many files have changed in this diff Show More