Compare commits

...

772 Commits

Author SHA1 Message Date
195aa0a95b Remove composition listeners on destroy 2020-06-30 09:30:49 -07:00
c252d435bf Clear data on bounds change. Clean up on destroy. 2020-06-29 18:21:57 -07:00
87aa5a4342 Cleaning up unused code 2020-06-29 17:16:57 -07:00
bd98b81339 some code reorg 2020-06-29 16:47:47 -07:00
f49556adad wip: functional with single trace, but debugging problems with changing bounds with multiple traces 2020-06-23 12:32:47 -07:00
f726bfa31a wip: refactoring 2020-06-22 14:56:22 -07:00
e9c6c5760e resolve conflicts 2020-06-19 13:42:23 -07:00
07c3dd6cfa wip: changes for bounds events 2020-06-19 13:40:20 -07:00
771fb9c044 [Display Layout] Allow multiple selection, duplication, and changing types (#3083)
* enable multiple selection

* enable object duplication

* enable copy styles

* enable converting plots and tables to alpha numerics

* enable merging multiple alpha numerics

* change icon for viewSwitcher

* allow users to merge overlay plots into a stacked plot

* New glyph for alphanumeric

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-06-19 11:44:17 -07:00
651a369391 refactored for multiple telemetry types 2020-06-18 15:36:38 -07:00
ae67a2f438 wip: refactoring for multiple telemetry types 2020-06-18 08:53:24 -07:00
055cf2b118 Lad testing (#3045)
* Added tests for LAD Tables and LAD Table Sets

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2020-06-17 17:25:34 -07:00
67ebcf4749 Update testing plan document with description of testathon process (#3022)
* Update testing plan document with description of testathon process
* Add instructions on unverified issues + link to instructions on pull requests.

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2020-06-17 15:05:02 -07:00
38dbf2ccab Addresses review comments for conditionals code (#2978)
Conditionals code refactoring

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-06-17 14:56:36 -07:00
e9968e3649 Replace Angular code that synchronizes URL parameters with Time API (#3089)
* Added new test to telemetry tables to check that telemetry data is correctly rendered in rows

* Added test tools for mocking builtins

* Changed order that promises are resolved to address race condition

* Remove duplicate installation of UTC Time System

* Added additional test telemetry

* Start Open MCT headless

* Added headless mode start option. Fixes #3064

* Added new non-angular URL handler

* Removed legacy Angular TimeSettingsURLHandler

* Added function to testTools to reset application state

* Use resetApplicationState function from telemetry table spec

* Added new TimeSettingsURLHandler to plugins

* Added missing semicolons

* #2826 Refactored code into separate class

* Handling of hash-relative URLs

* Refactoring URL sync code

* Refactored to external class

* Moved utils to new 'utils' directory. Refactored location util functions from class to exported functions

* Added test specs for openmctLocation

* Added new function to destroy instances of Open MCT between test runs

* Ensure test specs are cleaning up after themselves

* Added test spec for new URLTimeSettingsSynchronizer

* Removed use of shell script as it doesn't work in windows

* Pushed test coverage to 100%

* Added missing copyright statement

* Removed debugging output

* Fixed linting error

* Upgrade node version

* Clear cache

* Re-enabled tests

Co-authored-by: Melanie Lean <melanielean@Melanies-MacBook-Pro.local>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-06-17 13:58:25 -07:00
d9fafd2956 Tweaked code standards for ternaries and return statements (#3082)
* Tweaked code standards for ternaries and return statements

* Tweaked language slightly

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-06-17 10:52:30 -07:00
1acda469a9 fixed value formatting, removed listeners 2020-06-16 15:51:12 -07:00
d5b0ef735c Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-16 12:25:58 -07:00
a6cac13dfc removed duplicate function 2020-06-16 12:25:30 -07:00
3d058151f2 imported and used telemetry table components, removed console logs from telemetryTable.js 2020-06-16 12:16:17 -07:00
76817193eb minor color changes 2020-06-15 12:33:25 -07:00
35ef4407be changed way index is tracked mostly, other small changes 2020-06-15 11:16:01 -07:00
f3526f9185 refactored to follow telemetry tables pattern 2020-06-12 16:24:10 -07:00
b5aba7ce8f Merge pull request #3096 from nasa/revert-3095-patch-1
Revert "Update API.md"
2020-06-12 10:12:56 -07:00
0db5648e10 Revert "Update API.md" 2020-06-12 10:07:49 -07:00
83325da738 Merge pull request #3095 from willmendil/patch-1
Update API.md
2020-06-12 08:50:23 -07:00
4d1b2f3456 Update API.md
typo
2020-06-12 17:42:39 +02:00
6137700c82 Merge pull request #3092 from nasa/contributor-guide-grammar
Fixes a grammatical issue in the wording of the contributor's guide
2020-06-10 11:06:59 -07:00
91a1b3f31d Fixes a grammatical issue in the wording of the contributor's guide 2020-06-10 10:29:47 -07:00
70649b0657 added js-plotly-container class 2020-06-10 10:29:36 -07:00
357b25a76b LAD views should respond to conductor bounds changes (#2946)
Added bounds listener, moved history request to function, checking for race conditions
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-06-09 16:51:32 -07:00
3b89bf0b8c Merge branch 'plotly-test' of https://github.com/nasa/openmct into plotly-test 2020-06-09 11:08:03 -07:00
a314aa3c95 Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-09 11:07:27 -07:00
dc5723c227 tweaked margins 2020-06-09 11:07:14 -07:00
61fd7d4e4e mostly styling changes, removed isFixed arg from getLayout 2020-06-09 09:31:54 -07:00
bab53ad9bd Fix static styles bug - adding null checks. (#3084)
* Fix static styles bug - adding null checks. Fixes #3076

* Adds tests for updating conditional and static styles for layout drawing items

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-06-05 15:30:35 -07:00
d0d4579f13 Give users the option to hide table headers (#3085)
* working hide header config

* Simplified Inspector UI layout

* fix lint errors

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-06-05 15:09:59 -07:00
32fc871e67 correct usage of node_options 2020-06-04 09:28:43 -07:00
6b74a133d8 Upgraded karma 2020-06-03 17:41:42 -07:00
ceaf4c2ef0 Cleared circle cache 2020-06-03 16:29:55 -07:00
982b69e7e1 Don't rebuild sass every time 2020-06-03 16:25:08 -07:00
8e5182732d Used cross-platform instead of export 2020-06-03 16:24:13 -07:00
2d0b92cc38 Removed no-sandbox flag 2020-06-03 16:23:17 -07:00
75e846ea3f Added missing new line 2020-06-03 16:22:08 -07:00
1b5dee981d Reverted resource class change 2020-06-03 16:20:49 -07:00
4fee7f73e7 Fixed circle config 2020-06-03 16:19:49 -07:00
4308a4c9cf Removed report files 2020-06-03 16:17:23 -07:00
abbffcfbf1 Merge branch 'plotly-test' of https://github.com/nasa/openmct into plotly-test 2020-06-03 16:16:00 -07:00
01710876fb Increase circle resource class 2020-06-03 16:15:52 -07:00
9e3d97c85d added no-sandbox flag 2020-06-03 13:33:59 -07:00
519075001e bumped docker config to node 13 2020-06-03 13:12:45 -07:00
96a48dd9ee moved rebuild node-sass later 2020-06-03 13:08:00 -07:00
550daae76f add rebuild node-sass to config 2020-06-03 13:03:36 -07:00
b8fcb8ff14 change circleci config to node12 2020-06-03 12:45:46 -07:00
373ddd0bf5 updated circleci to use node13 2020-06-03 11:47:00 -07:00
d62cc6b3ee bumped karma-jasmine to ^2.0.0 2020-06-03 11:11:07 -07:00
5116d38437 added test memory increase 2020-06-03 11:03:36 -07:00
29771f2722 Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-02 16:52:34 -07:00
7bfe4bb25c restored karma-jasmine to original 2020-06-02 16:31:04 -07:00
510c637081 locked karma-jasmine dependency 2020-06-02 16:01:02 -07:00
02b537580c Merge pull request #3057 from nasa/code-standards-update
Updated code standard
2020-06-02 13:31:21 -07:00
d7c65fec4c changed jasmine-core to ^2.0.0 2020-06-02 13:11:18 -07:00
7073b0717f Merge branch 'master' into code-standards-update 2020-06-02 13:05:32 -07:00
d6bb1b2a12 Added note about requiring a single return statement. 2020-06-02 13:05:15 -07:00
626e2d8e80 changed jasmine-core to 2.0.0 to test circle-ci specfilter issue 2020-06-02 12:56:51 -07:00
1fe673c1f5 Merge branch 'plotly-test' of https://github.com/nasa/openmct into plotly-test 2020-06-02 12:44:35 -07:00
d9b00574e7 Merge branch 'master' of https://github.com/nasa/openmct into plotly-test 2020-06-02 12:38:39 -07:00
7c48b3ba9a Merge branch 'master' into plotly-test 2020-06-02 12:29:46 -07:00
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
b6e589eed4 removed telemetry from initialization config 2020-06-01 14:46:54 -07:00
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
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
fb1813c14b change in legend position, trace names 2020-06-01 07:40:24 -07:00
cce834f873 changed colors, added legend, fixed removeTelemetry 2020-05-29 15:14:05 -07:00
ca60d02614 added name to plugin function, changed colors 2020-05-29 13:38:52 -07:00
9520c09b49 fixed updateData so plot is extended in fixed timespan 2020-05-29 12:02:31 -07:00
0d70717b35 addressed review comments 2020-05-29 09:55:37 -07:00
f18da542d6 added updateRange function, removed length arg from updateData, fixed unsubscribe 2020-05-29 09:37:56 -07:00
e7c38d473b added unsubscribe, subscribeTo methods 2020-05-28 15:26:30 -07:00
2f8db31a33 added dynamic layout for fixed, local clock 2020-05-28 15:25:30 -07:00
1c58d8c85f implemented Plotly.relayout to set x-axis correctly for local clock 2020-05-28 15:25:05 -07:00
baf426055c added back if in updateData 2020-05-28 15:23:04 -07:00
aa3af520ea refactor of refreshData 2020-05-28 15:21:15 -07:00
4a43893ccc debugging local clock issues 2020-05-28 15:21:01 -07:00
77f50a41c9 added some comments 2020-05-28 15:20:38 -07:00
e4cd5f441f added requestHistory 2020-05-28 15:19:44 -07:00
a9cfb002fb added listeners and bounds methods 2020-05-28 15:15:39 -07:00
b603a5b722 WIP... 2020-05-28 15:13:35 -07:00
be165761c7 WIP: adding bounds functions 2020-05-28 15:13:10 -07:00
da88cf58cc add, remove multiple traces, remove hover effects 2020-05-28 15:12:48 -07:00
bc2bd53f9b WIP 2020-05-28 15:12:11 -07:00
6b7fd5f22a import RemoveAction 2020-05-28 15:09:01 -07:00
4ee214a142 add code to set Y-axis and plot multiple telemetry types 2020-05-28 15:07:35 -07:00
4af24db38a changed mode and type, dropped old data as data extends 2020-05-28 15:06:24 -07:00
8db27809ef rendering a plotly plot from SWG 2020-05-28 15:04:36 -07:00
3116b1addd wip: added telemetry provider 2020-05-28 14:58:24 -07:00
5f67c45b50 working on viewProvider 2020-05-28 14:57:52 -07:00
8158afc29a added hardcoded test plot 2020-05-28 14:57:13 -07:00
13b3acb7e0 basic plotly plugin structure 2020-05-28 14:55:06 -07:00
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
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
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
67bea86bc8 Added headless mode start option (#3065)
* Added headless mode start option. Fixes #3064
2020-05-26 11:39:55 -07:00
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
eda01abcbc Merge branch 'master' into updated-checklists 2020-05-18 12:12:26 -07:00
2fa29124bf Merge branch 'master' into code-standards-update 2020-05-18 11:45:27 -07:00
694b8f4666 provide format for name metadata (#3054) 2020-05-18 11:40:05 -07:00
33faeafa98 New proposed ESLint rules that require no code changes 2020-05-16 16:19:36 -07:00
a40ff07353 Updated guidance on anonymous functions 2020-05-16 16:11:42 -07:00
41dc9c794d Fixed capitalization on CONST 2020-05-16 16:08:43 -07:00
f1faf3965d Changed reference to constructors to classes 2020-05-16 16:07:49 -07:00
da2ecbbcad Fixed formatting issues, removed outdated example code. 2020-05-16 16:06:59 -07:00
32c892fe98 Updated code standards 2020-05-16 15:56:30 -07:00
bbb271a678 clarify value hints (#2673)
remove confusing comments regarding domain - input and range - output
2020-05-15 14:48:25 -07:00
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
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
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
eb49ffae02 Update CONTRIBUTING.md 2020-05-13 09:21:00 -07:00
5751012872 Update CONTRIBUTING.md 2020-05-13 09:20:03 -07:00
aa041e04cf Merge remote-tracking branch 'origin/update-contributing-guidelines' into updated-checklists 2020-05-11 17:13:53 -07:00
24e7ea143a Added more details on the process around pull requests 2020-05-11 17:12:25 -07:00
79d5d9c4d0 Update CONTRIBUTING.md 2020-05-11 16:15:17 -07:00
b5bfdc4418 Update CONTRIBUTING.md 2020-05-11 15:56:49 -07:00
59730c60ec Update CONTRIBUTING.md 2020-05-11 15:54:25 -07:00
4a87a5d847 Show object styles in preview modal (#3018)
* Adds conditional styles to Preview window
2020-05-11 14:25:39 -07:00
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
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
83f9c6c528 improve plot gestures - and clean up (#3013) 2020-05-06 10:33:59 -07:00
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
a70facf0c8 Merge pull request #3000 from nasa/upgrade-moment
Upgrade moment to 2.25.3
2020-05-05 11:01:39 -07:00
447fe94325 Merge branch 'upgrade-moment' of https://github.com/nasa/openmct into upgrade-moment 2020-05-05 10:36:43 -07:00
8e2b666766 Upgraded moment version to 2.25.3 2020-05-05 10:36:33 -07:00
dcbfbdbb89 Merge branch 'master' into upgrade-moment 2020-05-05 10:12:50 -07:00
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
81b7a9d3e0 Merge branch 'master' into upgrade-moment 2020-05-01 16:47:26 -07:00
dc573c479c Upgrade moment to 2.24.0 2020-05-01 16:25:49 -07:00
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
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
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
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
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
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
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
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
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
396817b2d1 handle non-valid requests (#2984)
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-04-29 15:24:12 -07:00
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
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
ea90d02d66 Show the Styles tab for non creatable layout objects including condition sets (#2975) 2020-04-28 13:10:29 -07:00
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
a37c686993 Merge pull request #2968 from nasa/revert-2885-lodash-upgrade-test
Revert "Lodash upgrade"
2020-04-24 11:55:44 -07:00
f12166097c Revert "Lodash upgrade (#2885)"
This reverts commit d103a22fa0.
2020-04-24 11:53:31 -07:00
d103a22fa0 Lodash upgrade (#2885)
* upgraded lodash, changed method names
* native implementations as requested
2020-04-23 10:38:44 -07:00
04a60cfcbb fixes #2713 (#2928)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-22 15:22:11 -07:00
8d723960f4 removed acorn from package.json (#2906)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-04-21 15:50:45 -07:00
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
87bf94fe0a Updated year in index.html (#2930) 2020-04-21 15:28:10 -07:00
af93823b6f Elasticsearch support for change to typeless API (#2941)
* added elasticsearch to bundlemap
2020-04-21 15:23:43 -07:00
4a39ddf425 Check for any and all criteria (#2948) 2020-04-16 15:01:14 -07:00
83c273b976 Remove telemetry from criteria when not editing a condition set (#2933) 2020-04-16 12:32:32 -07:00
7dd81beb03 Remove telemetry data cache if a telemetry endpoint is removed (#2916) 2020-04-10 16:49:29 -07:00
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
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
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
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
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
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
f885e83505 Merge pull request #2911 from nasa/revert-2818-condition-improve-reorder
Revert "Condition improve reorder"
2020-04-10 14:35:41 -07:00
928bc4c68a Revert "Condition improve reorder (#2818)"
This reverts commit 46fedc1a30.
2020-04-10 14:32:48 -07:00
d5539c7ae4 Merge pull request #2898 from nasa/fix-enum-comparison
Fixes enum comparisons
2020-04-10 14:03:05 -07:00
c86a104fb6 Merge branch 'master' into fix-enum-comparison 2020-04-10 13:54:04 -07:00
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
3b6ef9b44b Refactor duplicate code into functions 2020-04-09 15:33:52 -07:00
c68edd9b7d Fixes enum comparisons
Adds check for undefined
2020-04-09 09:14:31 -07:00
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
abc2cd2413 Merge branch 'master' into fix-telemetryview-styles 2020-04-08 14:40:54 -07:00
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
9fe7f230e6 Merge branch 'master' into dave/conditionals-own-results 2020-04-08 14:16:50 -07:00
de4c5b3729 Disables conditional and static styles for hyperlinks and summary widgets. (#2887) 2020-04-08 12:30:59 -07:00
2a7901914a Merge branch 'master' into fix-telemetryview-styles 2020-04-08 11:57:33 -07:00
73b0fc6f79 Removes preventNone as per conversation with UX designer. 2020-04-08 11:46:12 -07:00
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
d188b9a056 do not mutate function args for criteria results either 2020-04-07 13:22:03 -07:00
f510f3edd0 Removes missed code 2020-04-07 11:59:24 -07:00
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
713c5e9fb7 Merge branch 'master' into dave/conditionals-own-results 2020-04-06 18:56:08 -07:00
17bca04560 do not mutate function args 2020-04-06 18:53:48 -07:00
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
e0c5bca47d fix unit tests 2020-04-06 14:25:55 -07:00
cdc7c1af64 Removes unused data 2020-04-06 14:04:11 -07:00
3158baa998 Merge branch 'fix-telemetryview-styles' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-06 14:02:45 -07:00
698508fde4 Use the subobject view type to determine where styles should be saved 2020-04-06 14:02:06 -07:00
68a96989e1 Merge branch 'master' into fix-telemetryview-styles 2020-04-06 13:57:41 -07:00
46a6a43234 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-06 13:56:30 -07:00
d41fc27b55 subscriptions should use latest timestamp 2020-04-06 11:17:59 -07:00
24bb96cc90 WIP detach request data from subscription data
extract getLatestTimestamp function
2020-04-03 18:39:26 -07:00
483ee173d6 pass correct args into off listener 2020-04-02 16:34:40 -07:00
469e93d916 Merge pull request #2865 from nasa/criterion-delete
Criterion delete
2020-04-02 10:51:12 -07:00
f96dfcc942 Reassign ids for criteria when we clone conditions 2020-04-02 10:45:37 -07:00
063a6c0e51 Merge branch 'criterion-delete' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:40:57 -07:00
7c289d76b6 Revert object.assign change 2020-04-02 10:40:39 -07:00
c617a440eb Merge branch 'master' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:40:32 -07:00
8f81a45b9b Removes coverage branch 2020-04-02 10:30:23 -07:00
666459be87 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-02 10:30:07 -07:00
53df89aa5d Merge branch 'master' into criterion-delete 2020-04-02 10:26:57 -07:00
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
f91a64483b Adds missing id during create condition 2020-04-02 10:18:41 -07:00
de8d63c09d Merge branch 'master' of https://github.com/nasa/openmct into criterion-delete 2020-04-02 10:11:42 -07:00
58b4a6ebf5 Merge branch 'master' into fix-any-telemetry 2020-04-02 10:06:37 -07:00
9d45080526 Merge pull request #2858 from nasa/dave/conditionals-request-default
request LAD for default condition
2020-04-02 10:05:42 -07:00
d3fe2a6811 Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-01 15:52:55 -07:00
97b37edce4 Store telemetry styles on their container domain objects. 2020-04-01 15:51:40 -07:00
d3443518d6 reverted changes unrelated to id 2020-04-01 15:08:31 -07:00
c33314a4bf added uuid to addCriteria, cloneCriteria, changed key of v-for to criteria.id 2020-04-01 14:56:32 -07:00
59eb034ab4 Merge branch 'master' into dave/conditionals-request-default 2020-04-01 14:42:21 -07:00
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
0fd637c0e9 [Notebook]: Error in nextTick: "TypeError: Cannot read property 'focus' of null" #2845 (#2857) 2020-04-01 14:22:11 -07:00
169cc6617a added uuid() in conditionManager 2020-04-01 13:58:58 -07:00
a946325e95 request LAD for default condition 2020-04-01 13:58:27 -07:00
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
2edfeaa606 added checkTelemetry call in inputCount computed prop 2020-04-01 12:15:02 -07:00
2a1f9fd063 removed recursive call 2020-04-01 11:10:10 -07:00
63fe92f8ea Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into criterion-delete 2020-04-01 10:37:01 -07:00
35f303ffa4 changed inputCount to computed property 2020-04-01 10:35:49 -07:00
dd70bb470f Merge branch 'master' of https://github.com/nasa/openmct into fix-telemetryview-styles 2020-04-01 10:18:30 -07:00
250fee125a Merge pull request #2854 from nasa/notebook-preview-action-fix
[Notebook] Embed preview action fix
2020-04-01 10:13:01 -07:00
956029c786 Merge branch 'fix-any-telemetry' of https://github.com/nasa/openmct into fix-any-telemetry 2020-04-01 10:11:30 -07:00
b9ab599c35 Fix description and errors for all/any telemetry 2020-04-01 10:10:31 -07:00
5a690932e9 Merge branch 'master' of https://github.com/nasa/openmct into notebook-preview-action-fix 2020-04-01 09:59:57 -07:00
b00757150e use objectPathJSON 2020-04-01 09:58:30 -07:00
8515a411bd Merge branch 'master' of https://github.com/nasa/openmct into fix-any-telemetry 2020-04-01 09:55:55 -07:00
3034ec016a Merge branch 'master' into fix-any-telemetry 2020-04-01 09:55:03 -07:00
a81af1ce34 check format first (#2851)
default to number
2020-03-31 21:04:52 -07:00
64c5725687 pass correct object path, and remove notebook snapshot button from preview 2020-03-31 20:59:20 -07:00
72a3248123 Fix LAD requests for any/all telemetry 2020-03-31 16:48:59 -07:00
ee4a81bdfd Conditionals feature (#2830)
Introduces conditional styling feature.
2020-03-31 15:56:06 -07:00
b90eb80584 Addresses review comments: Rename file, change copyright year, remove commented out code 2020-03-31 15:50:30 -07:00
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
3956cd1c06 Merge pull request #2829 from nasa/topic-conditionals-master-merge
Topic conditionals master merge
2020-03-31 14:25:16 -07:00
064cf6747e Adds copyright text 2020-03-31 14:21:30 -07:00
8512b634c7 Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-31 12:54:12 -07:00
4220a8a68a [Conditionals] default condition handling (#2827)
* Sisplay default condition output by... default
2020-03-31 12:32:06 -07:00
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
079201273e Merge branch 'master' into topic-conditionals 2020-03-31 12:02:37 -07:00
e4c9f156a7 Merge pull request #2800 from nasa/any-all-telemetry
Any all telemetry option for conditions.
2020-03-31 11:49:23 -07:00
42eeeea374 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into any-all-telemetry 2020-03-31 11:00:00 -07:00
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
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
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
87d838c690 [Tables] Use parsed data to sort (#2785)
* Use parsed values when sorting
2020-03-30 16:57:20 -07:00
072bf361de Store styles for telemetry on domain objects 2020-03-30 16:20:22 -07:00
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
14eaf4e899 Addresses comments: Fixes dropdown logic 2020-03-30 12:17:49 -07:00
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
f9e88321a3 Condition Widget refinements (#2814)
* New Condition Widget
* Add new glyphs and symbols font files
2020-03-30 11:59:27 -07:00
9e12203b86 Merge branch 'master' into topic-conditionals 2020-03-30 11:19:45 -07:00
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
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
d00e8b68a5 Merge pull request #2802 from nasa/clone-condition-issues
Clone condition, criteria issues
2020-03-30 09:40:00 -07:00
2907d6d79c moved opy to cloneCondition 2020-03-30 07:13:15 -07:00
a5a4bb87c5 Removed extra if statement, copied only config 2020-03-30 07:06:03 -07:00
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
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
90b475e17b addressed review comments 2020-03-29 14:03:49 -07:00
89a298f5b3 Removes name string fix 2020-03-27 15:25:24 -07:00
f990a14a3c Remove duplicate metadata options 2020-03-27 15:22:50 -07:00
6b00af6ece fix errors when navigated away from a layout that uses toolbar 2020-03-27 10:15:09 -07:00
5ec8ac95c1 Fixes linting issues 2020-03-27 09:42:18 -07:00
5f061001d6 fixed updateOperations, clearDependentFields, removed unnecessary ref 2020-03-27 08:52:23 -07:00
ef3c4ccf47 clear dependent fields when field selector is changed 2020-03-27 08:09:04 -07:00
1a86204637 used lodash cloneDeep for criteria, conditions 2020-03-27 07:31:41 -07:00
0a1959df38 Fixes linting issues 2020-03-26 23:00:59 -07:00
03a690a158 Fixes import 2020-03-26 22:56:39 -07:00
459a055455 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into any-all-telemetry 2020-03-26 22:55:44 -07:00
502d29dd25 Add all and any options for telemetry 2020-03-26 16:44:44 -07:00
bf947a8835 reverted to espresso (#2797) 2020-03-26 15:12:43 -07:00
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
7282792da1 fixed refs and ev bugs 2020-03-26 14:57:55 -07:00
ec0291c54d Implement any telemetry and all telemetry options 2020-03-26 14:01:02 -07:00
4413c29abb Merge pull request #2793 from nasa/new-widget
Add Condition Widget
2020-03-26 13:26:47 -07:00
91e1a144ed Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-26 13:22:59 -07:00
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
5128af2531 New Condition Widget, WIP
- Remove temp test styling;
2020-03-26 13:08:44 -07:00
eedc0f13bc adds formatting for strings (#2792) 2020-03-26 13:08:28 -07:00
4172fdf1d5 Merge pull request #2788 from nasa/criterion-enum-persist
Criterion enum persist
2020-03-26 12:58:25 -07:00
7c200df4c4 resolved merge conflict 2020-03-26 12:39:50 -07:00
d1b28e079a Merge branch 'topic-conditionals' into new-widget 2020-03-26 12:34:56 -07:00
23ec838643 Merge pull request #2764 from nasa/condition-persist
Telemetry stickiness issue
2020-03-26 12:34:37 -07:00
271c619c63 Merge pull request #2789 from nasa/fix-description
Fixes description for enums
2020-03-26 12:23:40 -07:00
0552769670 change back to refs 2020-03-26 11:30:00 -07:00
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
4b535ade31 metadataManager should check for a values array
before calling map on it
2020-03-26 10:25:04 -07:00
de8f8088e2 Merge pull request #2786 from nasa/issue-2773
[Conditions] cleanup subscriptions in style rule manager
2020-03-26 09:53:16 -07:00
32c16416d3 Fixes description for enums 2020-03-26 09:45:44 -07:00
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
015aa8c637 Checks for input before processing it. 2020-03-26 09:37:52 -07:00
4a07ddbefc added persist call to change enum input 2020-03-26 09:03:32 -07:00
a81009541c Merge branch 'condition-persist' of https://github.com/nasa/openmct into criterion-enum-persist 2020-03-26 08:22:59 -07:00
1b680cfaca moved persist call into change event method and removed check on $ref 2020-03-26 08:10:35 -07:00
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
0cae61444d New Condition Widget, WIP
- Refine base styling;
- Fix so that edited properties are reactive;
2020-03-25 19:47:21 -07:00
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
d2b7407674 add request for LAD data 2020-03-25 17:41:33 -07:00
fff89a6384 delete unsubscribe method on destroy 2020-03-25 17:36:27 -07:00
953b95f79c Merge branch 'topic-conditionals' into new-widget 2020-03-25 17:22:43 -07:00
6ff5ce78e1 Merge remote-tracking branch 'origin' into new-widget 2020-03-25 17:16:23 -07:00
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
a1f2608e7c Merge pull request #2782 from nasa/conditionals-ch-fixes-032420
Misc Conditionals fixes
2020-03-25 17:14:08 -07:00
b7cea7b955 New Condition Widget, WIP
- Normalize usage of `c-object-label` across components;
2020-03-25 16:34:11 -07:00
32da19a486 New Condition Widget, WIP
- Add new Vue components, mod configs;
2020-03-25 16:19:46 -07:00
39d7dc8372 Adds unit tests for isOneOf and isNotOneOf 2020-03-25 15:50:16 -07:00
f76f537be7 removed extraneous emits and handlers 2020-03-25 15:27:48 -07:00
a681d67e05 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionset-multi-value-operations 2020-03-25 14:14:23 -07:00
0a634eb490 Fixes unit tests 2020-03-25 14:08:33 -07:00
fcca8fa8d9 Fixes error while getting metadata when telemetry is not yet available for a criterion 2020-03-25 13:44:26 -07:00
829eecf1ae resolved merge conflicts 2020-03-25 13:24:43 -07:00
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
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
6dbdebbe05 Merge branch 'master' into global-filters-no-request-bug-fix 2020-03-25 11:54:45 -07:00
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
761ca7ad56 remove console log 2020-03-25 11:16:25 -07:00
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
ca022b8a28 make a copy of filters before checking isEqual
- to prevent using same pointer
2020-03-25 11:11:17 -07:00
6f500d0d0b Resolves ##2741
Uses parsed telemetry data values for comparisons
2020-03-25 10:45:28 -07:00
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
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
c27c347d29 Conditionals fixes and tweaks
- Fixes #2778;
2020-03-24 18:17:13 -07:00
dc54eef2c9 Resolves #2776
Adds null checks for telemetry and domain object configuration
2020-03-24 15:40:33 -07:00
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
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
437e8a0263 always load composition 2020-03-24 11:15:58 -07:00
0a2e912091 removed refs from dom elements and added index to call to isLastCriterion 2020-03-24 09:43:54 -07:00
87513a14b7 removed log 2020-03-23 17:47:35 -07:00
168c040f3c fix sticky telemetry selection issue 2020-03-23 16:16:58 -07:00
78487a48f6 restrict conditionManager instances to one per domainObject 2020-03-23 16:04:37 -07:00
a5a197680d remove debug 2020-03-23 15:58:01 -07:00
d42bd44485 remove logging statement 2020-03-23 15:24:24 -07:00
77b705ecc8 add telemetry object back for criterion 2020-03-23 15:21:51 -07:00
48af39a584 Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-23 14:58:32 -07:00
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
f13714e0c4 rename telemetry subscription calls 2020-03-23 13:53:19 -07:00
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
26ffe8efde remove comment 2020-03-23 13:05:48 -07:00
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
f790c9bd39 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into topic-conditionals 2020-03-23 11:55:33 -07:00
096c9688d5 Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-23 11:55:12 -07:00
1f19f480ce fix unit tests 2020-03-20 17:38:29 -07:00
44f48a3e2a moved persist calls to events, used emit to save name and fieldname props 2020-03-20 17:37:48 -07:00
60fce4a003 remove completed TODO comments 2020-03-20 17:08:55 -07:00
f04b5b689e suppress plot view on view condition sets 2020-03-20 17:08:23 -07:00
05d981768e change string output to enum for plot views 2020-03-20 17:07:51 -07:00
e4a6c21101 load composition using less code 2020-03-20 15:08:45 -07:00
d51dd8b7d0 fix broken tests 2020-03-20 15:00:24 -07:00
aed5377ad2 use existing var instead of recalculating
on listener should be off in destroy()
2020-03-20 11:44:05 -07:00
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
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
b38a9ad4ce exclude default condition when requesting telemetry 2020-03-20 10:12:02 -07:00
ecf3e19f16 remove listener on destroy 2020-03-19 20:30:27 -07:00
3b82fd5d8b Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-19 20:28:40 -07:00
e08b4ff0ab Merge pull request #2751 from nasa/fix-enum-metadata
Handles enum meta data for criteria
2020-03-19 20:18:35 -07:00
1f3ec77bf1 Merge branch 'topic-conditionals' into fix-enum-metadata 2020-03-19 20:15:05 -07:00
3f61db2067 pass telemetry down to criteria. criteria listens to corresponding endpoints. 2020-03-19 17:30:02 -07:00
ce1fdbddda Merge pull request #2757 from nasa/conditions-set-cleanup
[Conditions] Condition Set cleanup
2020-03-19 15:19:13 -07:00
5332d136b7 Fix small bug with inputCount 2020-03-19 15:16:12 -07:00
983ed7f0e7 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into fix-enum-metadata 2020-03-19 14:06:25 -07:00
11978cd869 remove straggler call
linting fix
2020-03-19 10:22:48 -07:00
00d1b5e69f avoid multiple instantiations of condtionManager if possible 2020-03-19 10:18:57 -07:00
6731283cf8 Merge pull request #2734 from nasa/condition-description
Condition dynamic summary description based on criteria
2020-03-18 15:25:19 -07:00
6b4cd25417 Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-18 10:58:17 -07:00
0cd2799d00 added isLastCriterion comp method 2020-03-18 10:24:43 -07:00
243b9cac24 subscribe to telemetry 2020-03-18 10:21:53 -07:00
316e0f24cf added fieldName prop to telemetry object, computed props for canEvaluateCriteria, getConjunction, simplified getRule method 2020-03-18 10:18:42 -07:00
05f94edb49 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into topic-conditionals 2020-03-18 09:29:10 -07:00
e22458f09e Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-18 09:28:53 -07:00
cc2df8401b vue style render of description 2020-03-17 17:36:55 -07:00
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
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
60aecfe27e Merge branch 'topic-conditionals' into dave/conditions-telemetry 2020-03-17 12:00:57 -07:00
5d21a8b6fe Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into remove-ConditionSetView-telemetry 2020-03-17 11:06:51 -07:00
500ab52476 Merge pull request #2754 from nasa/conditions-observables
[Conditions] Remove uneccessary update function
2020-03-17 11:05:46 -07:00
b0bb723357 resolve merge, address review comments 2020-03-17 10:31:19 -07:00
b7fffeab1c conditionManager controls subscriptions 2020-03-16 21:57:42 -07:00
e339d743ed remove uneccessary update call 2020-03-16 16:01:12 -07:00
84f0d49d6f code is documentation 2020-03-16 15:35:13 -07:00
090e89d524 remove testing aid 2020-03-16 15:34:12 -07:00
90dd53e954 Merge branch 'topic-conditionals' into dave/condition-telemetry-request 2020-03-16 15:31:19 -07:00
6ab60ab52e ConditionSet view listens to a listener from conditionCollection to display current output. 2020-03-16 15:00:16 -07:00
8975bc8c55 Handles enum meta data for criteria
Ensures metadata with sources are also handled correctly
2020-03-16 13:36:33 -07:00
55e5c49f6e Merge pull request #2747 from nasa/conditions-do-refactor
Conditions refactor - not domain objects anymore
2020-03-16 10:59:31 -07:00
f090f7ffe7 Removes comment. 2020-03-16 10:51:33 -07:00
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
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
83c648cc26 Addresses comments 2020-03-13 14:24:47 -07:00
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
09bfd80f69 Fix bug with telemetry disappearing 2020-03-13 12:50:06 -07:00
15a7d03e74 rename function for clarity 2020-03-13 09:36:17 -07:00
1dc9743484 remove unecessary get call 2020-03-13 09:26:46 -07:00
8f05c57d1a Fixes clone condition bug 2020-03-13 09:09:50 -07:00
81caa27cba Fixes failing tests 2020-03-12 14:38:39 -07:00
74a7ef2565 Fix bug with removeCondition 2020-03-12 14:06:43 -07:00
649575fd2d Moves domain object observe logic to the condition set telemetry provider 2020-03-12 13:29:50 -07:00
b75b7a958a added input types and fixed bug for isDefined, isUndefined comparisons 2020-03-12 13:14:54 -07:00
625b39d722 Fix typo 2020-03-12 13:06:56 -07:00
65f80f4c45 Resubscribe to the conditionSet telemetry provider when the condition set domain object changes. 2020-03-12 13:05:03 -07:00
02cd3048c8 Removed conditions as domain objects 2020-03-12 12:33:04 -07:00
63feaef988 reinitialize rule string in forEach 2020-03-11 15:22:17 -07:00
6095872682 remove logging 2020-03-10 10:08:53 -07:00
dba55867f4 fix broken unit tests 2020-03-10 09:59:12 -07:00
0da80c2a67 move var declaration around for clarity 2020-03-10 09:58:57 -07:00
084df5329a Merge branch 'topic-conditionals' into dave/condition-telemetry-request 2020-03-09 16:33:55 -07:00
49ff0c79db tidy up condition manager use in telemetry provider 2020-03-09 16:15:53 -07:00
7a4b967a01 condition sets with no telemetry points should not provide telemetry 2020-03-09 13:28:37 -07:00
ddfa611c44 Merge branch 'master' into swg-randomness 2020-03-09 13:11:31 -07:00
efca7c8e58 added descriptions to conditions 2020-03-09 12:42:44 -07:00
8900072239 added computed property and method to get description 2020-03-09 11:50:11 -07:00
a7e57c62f4 linting fixes (#2733) 2020-03-09 11:15:15 -07:00
24bade2284 provide correct data to getLatestTimestamp call 2020-03-09 10:59:05 -07:00
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
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
8d86c914a1 Merge pull request #2732 from nasa/minmax-fix-382020
Revert minMax to minmax
2020-03-09 10:05:42 -07:00
fab04519c6 fix minmax typo 2020-03-08 20:11:04 -07:00
4a5e106709 request LAD for conditions provides telemetry 2020-03-06 17:33:21 -08:00
4675fc8ae6 Conditional styles (#2718)
* 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.
2020-03-06 14:09:52 -08:00
cf9336dae9 WIP receive criterion results and compute condition results 2020-03-06 11:01:15 -08:00
7f32d196e4 WIP request gets to telemetry criterion before erroring 2020-03-05 23:11:20 -08:00
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
3e6509ce6f WIP add support for LAD request 2020-03-05 12:14:27 -08:00
576b843bd5 Merge branch 'master' of https://github.com/nasa/openmct into topic-conditionals 2020-03-05 10:00:54 -08:00
9b8b63a0d8 Revert record size limit to 5000 2020-03-05 08:56:47 -08:00
a1b1fa464e Adds customizable randomness factor to sine wave generator. Fixes #2726 2020-03-05 08:55:05 -08:00
95f855f905 Merge pull request #2717 from nasa/dave/conditions-telemetry
[Conditions] Use latest incoming telemetry timestamp for providing telemetry
2020-03-04 12:05:33 -08:00
5b00246cc0 fix failing unit test 2020-03-04 12:00:42 -08:00
47c388450f Merge pull request #2707 from nasa/alternate-control-bar-fixes
[Telemetry Tables] - Alternate control bar fixes
2020-03-04 11:54:38 -08:00
34a149661c keep track of latest timestamp 2020-03-03 17:34:27 -08:00
4c4b587d9c pass timestamp datum through to ConditionManager 2020-03-03 14:37:11 -08:00
b8b838f490 get all timesystems 2020-03-03 12:50:40 -08:00
8cb29ba4a9 add handleConditionUpdated back 2020-03-03 11:34:30 -08:00
ece6223b23 Merge pull request #2704 from nasa/dave/provide-conditions-telemetry
[Conditions] Conditions to provide telemetry
2020-03-02 14:56:10 -08:00
ecabd00b0c fix failing unit tests 2020-03-02 14:48:24 -08:00
768df84f10 Merge pull request #2711 from nasa/condition-output-persistance2
Added persistance for changes to output selector
2020-03-02 13:56:31 -08:00
f8b3899bb9 addressed review comments and lint errors 2020-03-02 12:30:28 -08:00
3b046db4f8 Merge pull request #2695 from nasa/firefox-drag-fix
Add setData on dragStart for FF support
2020-03-02 11:57:45 -08:00
97f829da9f added dragstop listener and dragStop method 2020-03-02 11:46:16 -08:00
fb1eed1982 Merge pull request #2706 from nasa/current-output-refactor
Refactored CurrentOutput component into ConditionSet
2020-03-02 11:28:51 -08:00
dd9b567025 revert alternate control bar for telemetry table 2020-03-02 11:09:50 -08:00
fa83b4867c removed extra div 2020-03-02 06:17:27 -08:00
47d4fc9103 removed CurrentOutput component 2020-02-28 15:32:40 -08:00
dabd0bff29 only listen for condition updates in telemetry provider 2020-02-28 15:18:14 -08:00
51c70d02d7 fix naming bug 2020-02-28 15:17:27 -08:00
b74733bf3f Add back id and conditionId required for styling 2020-02-28 15:05:27 -08:00
84ae65536b remove unused var 2020-02-28 14:40:12 -08:00
71424dcf8d Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into firefox-drag-fix 2020-02-28 14:39:02 -08:00
2c40396139 refactor for clarity 2020-02-28 14:35:57 -08:00
16a0bf9d6c Merge pull request #2703 from nasa/criterion-multi-input
Criterion multiple input fields
2020-02-28 14:07:40 -08:00
5498ba8e1e changed computeResults 2020-02-28 13:51:46 -08:00
0f9d7d2832 fix default condition edge case 2020-02-28 13:10:06 -08:00
9bd1c51a6e always use timestamp from latest datum 2020-02-28 13:06:57 -08:00
fd74fb0ec4 remove unused var 2020-02-28 12:50:17 -08:00
3626ff9947 fix linting issues 2020-02-28 12:47:34 -08:00
fd568409d3 Merge branch 'topic-conditionals' into dave/provide-conditions-telemetry 2020-02-28 12:28:59 -08:00
14e3500a88 add timestamps for default condition 2020-02-28 12:27:51 -08:00
83d08ae369 resolve merge conflicts 2020-02-28 12:17:12 -08:00
39bf601ee1 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into firefox-drag-fix 2020-02-28 11:43:17 -08:00
cfafecdd64 Merge pull request #2685 from nasa/conditionals-refactor
Conditionals refactor
2020-02-28 11:35:09 -08:00
629ca089cf Fixes for table's alternateControlBar
- Fixed hide/show of controls for better UX;
- Unit tested click select/deselect toggling;
2020-02-28 11:31:23 -08:00
89ae6ef8c7 provide telemetry with timestamps 2020-02-28 11:15:19 -08:00
300acd6ec8 remove row if user unmarks in showMarkedRowsOnly mode 2020-02-28 11:12:49 -08:00
ba780981a5 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionals-refactor 2020-02-28 11:09:41 -08:00
62774678a7 enable alternatte control bar in telemetry tables for charles 2020-02-28 11:04:34 -08:00
ac13bc5850 Merge pull request #2688 from nasa/condition-trigger
Condition Any/All criteria match enabled
2020-02-28 10:53:02 -08:00
e526626e09 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-trigger 2020-02-28 10:49:56 -08:00
564be6f8ba Merge branch 'condition-trigger' of https://github.com/nasa/openmct into condition-trigger 2020-02-28 10:05:11 -08:00
371a7d3a3e removed disabled inputs with no telemetry 2020-02-28 10:04:10 -08:00
8539d60562 enable trigger 2020-02-28 10:01:37 -08:00
d333fd5822 disabled add or edit condition set without telemetry 2020-02-28 10:01:37 -08:00
364191eddc Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionals-refactor 2020-02-28 09:57:00 -08:00
583f4dac85 Merge branch 'conditionals-refactor' into dave/provide-conditions-telemetry 2020-02-28 09:56:25 -08:00
28255dce01 Merge pull request #2687 from nasa/remove-criteria
Added controls for remove and duplicate criteria
2020-02-28 09:53:53 -08:00
c9419d3e2d Fix for when input is not an array. This will be handled differently in an upcoming code change. 2020-02-28 09:47:58 -08:00
b386275330 removed disabled inputs with no telemetry 2020-02-28 08:55:59 -08:00
d2a45e46f1 modified getOperationFormat 2020-02-28 08:37:20 -08:00
35d1727dbf Merge pull request #2696 from nasa/marking-improvement-tables
An improvement for components using telemetry tables
2020-02-27 17:20:08 -08:00
8125a4f321 emit event when rows are marked - useful for other components using telemetry tables 2020-02-27 17:05:02 -08:00
1a409afb03 wip: addressing review comments 2020-02-27 16:32:38 -08:00
e57c18fd69 removed unused isDefault prop 2020-02-27 14:55:52 -08:00
3aec9ec6ff add setData on dragStart for FF support 2020-02-27 14:36:21 -08:00
0e9bf74332 Merge branch 'conditionals-refactor' into dave/provide-conditions-telemetry 2020-02-27 13:36:46 -08:00
2609a41ee8 Addresses comments
Removes console log statements
Fixes import paths
2020-02-27 11:21:40 -08:00
b8dc5acf00 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionals-refactor 2020-02-27 11:19:35 -08:00
fbbdf8cff7 Merge pull request #2607 from krynju/f#2594
Time x-axis tick labels reversed - fix #2596
2020-02-27 11:11:05 -08:00
b0edb19239 Merge branch 'master' into f#2594 2020-02-27 11:06:59 -08:00
85902b878e Update telemetry table for multisession (#2686)
* update telemetry table to ingest marked row data, add a new alterntate bar with includes row name, selected rows and show selected rows toggle

* Enhancements for alternate toolbar in telem tables

- .c-control-bar adds style enhancements and `__label` element;
- Added `label` prop, markup and styling to ToggleSwitch;
- ToggleSwitch layout enhanced;
- Unit tested in main view and placed in Display Layout;

* made improvements to row marking

* bug fixes for marking

* fix linting issues

* -Make reviewer requested changes
-Clarify prop for marking
-Include alternateControlBar in the marking prop
- - since it only makes sense for making

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
2020-02-27 10:27:57 -08:00
9d5c7a4015 destroy conditionManager instance on stop providing telemetry 2020-02-26 15:26:39 -08:00
fc53e855c4 Merge branch 'conditionals-refactor' into dave/provide-conditions-telemetry 2020-02-26 15:15:22 -08:00
467c57b7c6 Merge branch 'criterion-multi-input' of https://github.com/nasa/openmct into criterion-multi-input 2020-02-26 12:32:21 -08:00
a51c0d5139 addressed review comments 2020-02-26 12:31:37 -08:00
d46310ca7d handle multiple inputs in telemetryCriterion 2020-02-26 12:31:37 -08:00
8f87cc78e8 rendering correct number of input fields depending on operation and persisting values 2020-02-26 12:31:37 -08:00
ee6e0f310e changed default input to array and clear downstream selection lists on change 2020-02-26 12:31:37 -08:00
f328a1078e filter comparison list based on field selected 2020-02-26 12:31:37 -08:00
b4cf81a0ef wip: filtering select comparison 2020-02-26 12:31:37 -08:00
1b9b7e2345 added hid/show selects 2020-02-26 12:31:37 -08:00
4456633010 output telemetry to views 2020-02-26 12:27:28 -08:00
cda97d142a Merge branch 'condition-trigger' of https://github.com/nasa/openmct into condition-trigger 2020-02-26 12:23:03 -08:00
858199e396 enable trigger 2020-02-26 12:22:29 -08:00
f504a335af disabled add or edit condition set without telemetry 2020-02-26 12:22:29 -08:00
463ec47af6 changed classname of controls div 2020-02-26 12:21:53 -08:00
ec4d121a98 changed classname of controls div 2020-02-26 11:59:04 -08:00
fea6d2df96 Merge branch 'remove-criteria' of https://github.com/nasa/openmct into remove-criteria 2020-02-26 11:53:09 -08:00
598d2b31e9 remove criterion working 2020-02-26 11:51:41 -08:00
25e28ab97c added controls for remove and duplicate criteria 2020-02-26 11:49:17 -08:00
43056c4068 implimented clone criterion 2020-02-26 11:42:06 -08:00
614206b10c remove criterion working 2020-02-26 11:42:06 -08:00
30a493d038 resolved merge conflict 2020-02-26 11:40:52 -08:00
96e433beaa Merge pull request #2677 from nasa/condition-clone-name
Condition clone name persistance
2020-02-26 11:32:48 -08:00
0915aaea3b allow conditions to be dropped into telemetry views 2020-02-26 11:31:26 -08:00
80656c1be0 Merge branch 'topic-conditionals' into dave/provide-conditions-telemetry 2020-02-26 11:11:48 -08:00
acd75f86f4 add name to conditions view 2020-02-26 11:07:25 -08:00
486dae54bd add metadata provider 2020-02-25 17:27:08 -08:00
92ecf3af1d provide telemetry
* listen to realtime data
2020-02-25 16:37:43 -08:00
fd0c19026d addressed review comments 2020-02-25 15:36:37 -08:00
3109c8d825 Destroy condition manager when the ConditionSet vue is destroyed 2020-02-25 15:06:45 -08:00
78cf75323f Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionals-refactor 2020-02-25 12:31:44 -08:00
b744467f21 Bug fixes:
Ensures that the default condition emits a condition result update
Fixes remove condition
2020-02-25 12:29:47 -08:00
a0b7999ea2 Imagery fixes (#2668)
* Fix imagery-related styles and markup

- VERY WIP!!!
- Style modernizing;
- Also, padding fixes for pane.scss - unit test for regressions!

* Fix imagery-related styles and markup

- VERY WIP!!!
- Style modernizing WIP;
- Fixes to pane classes for better padding in vertical panes;

* Fix imagery-related styles and markup

- Migrated all imagery CSS into imagery-view-layout.scss from _legacy
.scss;
- CSS cleanups;
- Refactoring/simplification of thumb layout;
- Color fixed for $colorPausedFg in theme constants;

* Scroll to right instead of bottom, on autoscroll.

* Fix imagery-related styles and markup

- Make the most recent thumb visually distinct;
- Clicking a selected thumb now deselects it and unpauses the view;

* Imagery fixes

- Fixed thumb click logic to properly toggle paused when clicking a selected thumb;
- Improved CSS so that `selected` updates more quickly when selecting the latest thumb;
- Clicking the main image pause button now selects the proper thumb;

* Fix linting errors

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2020-02-25 11:47:27 -08:00
2bb2bb6a1b Adds conditional style inspector provider (#2655)
* resolved conflicts

* Revert "Condition reorder"

* Adds conditional style inspector provider
Adds condition style tabbed view (this needs to be extended to allow more than one pane per tab in a separate issue)

* Fixes linting issues

* Merge from topic-conditionals

* In order to accomodate two tab layout for the Conditionals feature, rename the following:
- openmct.inspectorViews registry to openmct.propertiesInspector
- InspectorViewRegistry.prototype.addProvider to InspectorViewRegistry.prototype.addViewProvider.
Replace occurances of the same to the new names.

In a subsequent commit expect a new view registry for the styles inspector view registry.

* Use 'styles' property on domain objects to indicate that they should have a styles inspector tab - Note that this will not show up on existing objects but only ones that are created after this feature is added.
Use 'styles' property on domain objects to determine if a styles view can be viewed
Removed the TabbedInspectorView and repurposed the InspectorViews to show both a properties or a styles registry.
Simplified markup in Inspector.vue

* Addresses review comments:
1. Go back to using inspectorViews
2. Remove stylesInspector registry
3. Hardcode Styles Inspector component view
4. Styles tab can be viewed for all creatable objects except for the folders, webPages and conditionSets
5. ConditionalStylesInspectorViewProvider is no longer needed because we are hardcoding the styles view component.

Co-authored-by: Joel McKinnon <JoelMcKinnon@users.noreply.github.com>
2020-02-25 10:41:58 -08:00
11ed7027e7 enable trigger 2020-02-25 09:31:29 -08:00
36bcfd5a41 disabled add or edit condition set without telemetry 2020-02-25 08:25:02 -08:00
70b5c627ca handle multiple inputs in telemetryCriterion 2020-02-24 16:48:27 -08:00
f4f1d0387b rendering correct number of input fields depending on operation and persisting values 2020-02-24 16:31:59 -08:00
a1bf4a92e5 changed default input to array and clear downstream selection lists on change 2020-02-24 15:50:01 -08:00
7d2256d70f Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionals-refactor 2020-02-24 15:44:07 -08:00
5814d2a35e Updates conditionManager.js to emit the output of the winning condition instead of the identifier of that condition 2020-02-24 15:43:03 -08:00
6ab84c0bc3 Refactoring conditionals
Move logic for conditions into conditionManager out of conditionCollection.vue
2020-02-24 15:05:33 -08:00
a7fc9b3caa filter comparison list based on field selected 2020-02-23 15:19:03 -08:00
67f493f012 rebased with topic-conditionals and resolved conflict 2020-02-23 11:30:01 -08:00
0686e6d38f implimented clone criterion 2020-02-23 11:26:58 -08:00
7fc825949c remove criterion working 2020-02-23 11:26:58 -08:00
2a9ccdcffd added controls for remove and duplicate criteria 2020-02-23 11:26:58 -08:00
6db78af69f fixed condition name persistance 2020-02-23 09:58:32 -08:00
038489256c updated Condition.vue with modifications to ConditionEdit.vue and deleted ConditionEdit as it is no longer used 2020-02-23 09:06:37 -08:00
53b785269b wip: filtering select comparison 2020-02-21 06:43:16 -08:00
007b14b5c9 added hid/show selects 2020-02-20 14:59:04 -08:00
50b331c451 fixed condition clone 2020-02-20 14:45:30 -08:00
44fc62e0ba fix reordering 2020-02-20 14:17:15 -08:00
2635f085f0 Merge pull request #2665 from nasa/add-criteria
Add multiple criteria and persist inputs
2020-02-20 13:51:31 -08:00
22161fce7f implimented clone criterion 2020-02-20 12:44:50 -08:00
386fc75047 remove criterion working 2020-02-20 12:23:18 -08:00
fa6dd84945 added controls for remove and duplicate criteria 2020-02-20 07:23:38 -08:00
d425bd564c fixed merge conflicts 2020-02-19 13:29:14 -08:00
93e3065b3e Fixes subscription of telelemtry when new criteria is added to a condition 2020-02-19 12:51:24 -08:00
0ad2d59924 Fixes tests 2020-02-19 12:51:15 -08:00
f4468a8233 Fixing condition output 2020-02-18 16:04:14 -08:00
dc08877bbb Persists the condition domain object so that we can retrieve it with it's identifier in child components like condition.vue 2020-02-18 15:35:03 -08:00
f08caa6135 addressed review comments 2020-02-18 12:40:33 -08:00
ad7d029ce8 addressed review comments 2020-02-18 11:14:50 -08:00
387912b4d3 WIP fixing telemetry subscribe 2020-02-18 11:05:54 -08:00
53e0ed4d4a refactored to address review comments 2020-02-14 15:23:15 -08:00
11c205b5c4 addressed review comments 2020-02-14 14:46:14 -08:00
4ede6351ec addressed some review comments 2020-02-14 12:45:32 -08:00
24bbcb466f wip: fixing add criteria 2020-02-13 16:13:29 -08:00
682601477c Add glyphs (#2667)
- icon-flag for use with VISTA Frame Accountability;
- icon-conditional, and bg-icon-conditional for Conditionals;
2020-02-13 13:11:24 -08:00
b6b5cfe403 fixed default state of output selector 2020-02-13 10:52:37 -08:00
b6ce9c6ed7 trigger label and add divider 2020-02-12 15:58:17 -08:00
6e5e8f0ce8 refactored setOutput 2020-02-12 13:31:55 -08:00
2415d785cc fixed set output field 2020-02-12 12:45:38 -08:00
2b5d6beb84 resolved conflicts 2020-02-12 11:14:17 -08:00
86316d8940 Add multiple criteria, set and persist inputs 2020-02-12 10:41:43 -08:00
1f2b5ec5c8 WIP: separating criterion level logic to its own component 2020-02-11 10:28:59 -08:00
0a9d23d86f Merge pull request #2633 from nasa/pane-persistence
[Navigation Tree] save current state of navigation tree
2020-02-06 16:23:19 -08:00
379e37c881 Merge branch 'master' into pane-persistence 2020-02-06 16:17:59 -08:00
37ef269dce Merge pull request #2611 from nasa/filters-fetch-bug-fix
Check if filters are not equal before refetching
2020-02-06 16:16:12 -08:00
8db6f8f633 criteria component WIP 2020-02-06 15:22:56 -08:00
79557165a3 refactored condition and conditionEdit components into single component named condition 2020-02-06 11:53:31 -08:00
ec1d4abde9 before Deep comments 2020-02-06 07:23:32 -08:00
07c5e2800a Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-clone 2020-02-04 15:19:11 -08:00
79811d6662 Merge condition-ui branch into this. 2020-02-04 15:12:33 -08:00
67919ece16 Merge pull request #2656 from nasa/condition-ui-redo
Condition ui redo
2020-02-04 15:11:02 -08:00
7029dcf09e Merge commit 'e580734c9593ab863f397b52ec9662f045fc7b61' of https://github.com/nasa/openmct into condition-ui-redo 2020-02-04 14:37:39 -08:00
38deef6e72 Merge branch 'master' into filters-fetch-bug-fix 2020-02-03 13:45:26 -08:00
b6220288ac Merge branch 'master' into f#2594 2020-02-03 13:36:29 -08:00
fc03b3a79d updateTemetry failing to return object on line 328 2020-02-02 08:22:23 -08:00
096d6371f1 add condition no errors 2020-02-01 10:07:40 -08:00
e580734c95 Set criteria options (#2630)
* Set criteria options on condition edit

* Persists telemetry options correctly and loads them on mount

* Fixes saving the input value for criteria

* Display active condition's output in read only view

* Destroy classes and unsubscribe when condition set view is destroyed

* Fixes saving the input value for a criteria

* Handle telemetry removal

* Fixes tests

* Addresses comments - change function names, consolidate compute function

* Addresses review comments
- Use camelCase for events (did not change properties as eslint complains)
- Reduce repeated property access by assigning to a variable
- Use descriptive variable name

* Addressing comments - improves input value field visibility logic

* Change variable name to reflect intent
2020-01-31 14:44:28 -08:00
2690156a9d changed kebab-case event names to camelCase 2020-01-31 13:44:07 -08:00
7ac7a40b1b resolve merge conflicts 2020-01-31 13:21:31 -08:00
dc9e572052 Change variable name to reflect intent 2020-01-31 13:10:36 -08:00
b15ebfd492 Addressing comments - improves input value field visibility logic 2020-01-31 13:08:41 -08:00
8baee7a0c9 WIP: debugging 2020-01-31 09:54:37 -08:00
dc85063467 added isDragging prop to prevent other drag actions 2020-01-31 08:08:39 -08:00
be428b326e Merge branch 'condition-ui' of https://github.com/nasa/openmct into condition-ui 2020-01-30 15:12:34 -08:00
dd0e360709 addressed review comments 2020-01-30 15:11:47 -08:00
04da88e3b4 addressed review comments 2020-01-30 14:53:19 -08:00
9bcab02e35 WIP add criteria 2020-01-30 12:02:08 -08:00
1ff4d41b7c Addresses review comments
- Use camelCase for events (did not change properties as eslint complains)
- Reduce repeated property access by assigning to a variable
- Use descriptive variable name
2020-01-30 11:06:25 -08:00
04a5c8f69f resolve merge 2020-01-29 16:23:13 -08:00
8886a94a01 fixed merge conflict 2020-01-29 14:48:22 -08:00
f25eebdf3f WIP - Adding criteria using this.currentCriteria 2020-01-29 14:43:12 -08:00
f9ba46fe85 added button and set up iteratable render of criteria 2020-01-29 12:45:34 -08:00
6f6fb859d6 Merge branch 'condition-ui' of https://github.com/nasa/openmct into set-criteria-options 2020-01-28 16:33:38 -08:00
1e3389b427 Merge branch 'condition-reorder' of https://github.com/nasa/openmct into condition-reorder 2020-01-28 16:32:07 -08:00
c977c64139 added drag image and grab cursor 2020-01-28 16:31:01 -08:00
e419149378 Addresses comments - change function names, consolidate compute function 2020-01-28 16:27:30 -08:00
a5a3e41d21 Merge branch 'condition-ui' of https://github.com/nasa/openmct into condition-clone 2020-01-28 13:03:43 -08:00
ecef8eaf86 Merge branch 'condition-ui' of https://github.com/nasa/openmct into condition-reorder 2020-01-28 12:40:33 -08:00
de03cfbe64 Merge branch 'condition-ui' of https://github.com/nasa/openmct into condition-ui 2020-01-28 12:36:57 -08:00
03a6de55d6 typo in comment 2020-01-28 12:16:18 -08:00
3c5047df5e passed definition prop instead of just name 2020-01-28 12:10:21 -08:00
3cc630d4c2 added testdata toggle control 2020-01-28 11:44:29 -08:00
b3488c54cd Fixes tests 2020-01-28 10:36:13 -08:00
01b1d66bea duplicate condition complete 2020-01-28 10:05:58 -08:00
2e82edb306 remove unused var 2020-01-27 13:44:56 -08:00
8f0e773ac1 remove storing navigated to local storage 2020-01-27 13:43:13 -08:00
223a0feada comments to describe localStorage implementation 2020-01-27 10:17:27 -08:00
bc9cadaa77 fixed reorder bugs 2020-01-27 09:34:50 -08:00
0fd0da8331 rework local storage mechanisms
* use one navigated local storage item instead one per node

* use one expanded local storage item instead of one per node

* fix navigated

* collapse children when node collapsed
2020-01-27 01:06:40 -08:00
f42ec7e2c5 Handle telemetry removal 2020-01-26 23:30:19 -08:00
d6a422fbdb Fixes saving the input value for a criteria 2020-01-26 22:56:21 -08:00
d98b54bea7 Destroy classes and unsubscribe when condition set view is destroyed 2020-01-26 22:21:06 -08:00
0beda1d053 Display active condition's output in read only view 2020-01-26 22:19:27 -08:00
e912ab8f4e Fixes saving the input value for criteria 2020-01-26 22:14:26 -08:00
5055a18ca1 reorder wip 2020-01-25 12:46:15 -08:00
fa21911287 persist expanded and navigated state of node to local storage 2020-01-25 00:52:36 -08:00
96746f4042 drag to reorder conditions 2020-01-24 13:23:02 -08:00
b22ad3ded9 Persists telemetry options correctly and loads them on mount 2020-01-24 11:26:31 -08:00
7e0f475c63 style changes for dnd 2020-01-23 16:13:28 -08:00
efb3c2b71e Merge branch 'condition-ui' of https://github.com/nasa/openmct into set-criteria-options 2020-01-23 15:54:24 -08:00
862ea6986f Set criteria options on condition edit 2020-01-23 15:51:08 -08:00
cfa5dcb02e drag wip 2020-01-23 12:16:16 -08:00
23aaada79d Evaluate telemetry criterion results and display the output of conditions accordingly 2020-01-23 12:08:28 -08:00
9e4458db10 fix merge conflicts 2020-01-23 10:28:59 -08:00
a8da06033c condition view with highlighting of current condition, add/remove condition, change name, output and persist 2020-01-23 10:24:53 -08:00
0bf3597147 Display the current output based on the first condition that has evaluated to true.
Fix remove condition
2020-01-22 21:35:22 -08:00
cfd9730055 Adds missing updated method
Adds definition property to CurrentOutput
2020-01-22 13:45:32 -08:00
e88ead30dc Merge branch 'condition-from-identifier' of https://github.com/nasa/openmct into conditionSet-ui-merge 2020-01-22 13:39:08 -08:00
67b24ce846 Persist conditionCollection to the conditionSet
Populate persisted operation
2020-01-22 13:29:01 -08:00
709c3fff65 no current output rendered 2020-01-22 13:15:44 -08:00
ab6e87ae6b Updates to operation show up in conditionclass 2020-01-22 13:01:23 -08:00
cdb7066bed Moves condition class initialization to conditionEdit component
sets correct condition output in condition edit component
2020-01-22 12:07:14 -08:00
73d0507f1f persist name change 2020-01-22 11:15:49 -08:00
4d263bcf32 added persist for remove 2020-01-22 08:53:25 -08:00
2d8f61172d fixed remove 2020-01-22 08:17:03 -08:00
621c1dc11e renders current output section 2020-01-21 16:00:21 -08:00
dd136a5ff4 WIP: set current condition 2020-01-21 13:37:36 -08:00
8fc785bbd6 Merge branch 'condition-class' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-21 10:18:51 -08:00
82be503f4f Fix broken rendering 2020-01-17 13:00:41 -08:00
7feb933519 Fix errors after converting conditionCollection to persist identifiers only 2020-01-17 11:26:46 -08:00
e806e5a293 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-17 11:04:41 -08:00
770951c6da Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-class 2020-01-17 11:02:27 -08:00
7b7c7b528a 1. Persist the condition domain object — done
2. Persist the condition identifier only in configuration.conditionCollection array, not the domain object — done
3. WIP - Retrieve the condition domain object and instantiate the condition classes on load
2020-01-16 16:11:23 -08:00
a554aa20f8 Addresses code review comments (#2624)
* Addresses code review comments
- Change copyright to 2020
- Fix class parameters, tests

* Fixes this for initilize function
2020-01-16 15:56:58 -08:00
ff1ef1f184 fixes typo 2020-01-16 15:48:40 -08:00
bf1efaf912 Merge branch 'condition-class' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:47:34 -08:00
ff2bc41317 Merge branch 'telemetry-criterion' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:47:04 -08:00
bdaf8aff15 Merge branch 'condition-view' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:46:28 -08:00
1dc4f9f6bb Fixes this for the handleConditionUpdated function 2020-01-16 15:44:59 -08:00
d6320f5da1 Fixes this for initilize function 2020-01-16 15:43:33 -08:00
276be5e857 Merge branch 'condition-class' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:36:29 -08:00
3101e77ecc Merge branch 'telemetry-criterion' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:34:53 -08:00
ab6dae16f1 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-16 15:32:40 -08:00
36222d79c6 Updates copyright, jsdocs, small refactor 2020-01-16 15:30:49 -08:00
08656a6674 persist output string selection 2020-01-16 15:29:07 -08:00
8034317796 Merge branch 'telemetry-criterion' of https://github.com/nasa/openmct into condition-class 2020-01-16 15:08:15 -08:00
a59f3a550e Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-class 2020-01-16 15:07:57 -08:00
415b967c0b Addresses code review comments
- Change copyright to 2020
- Fix class parameters, tests
2020-01-16 15:03:14 -08:00
642499d519 added string output field 2020-01-16 09:56:17 -08:00
fa0a54eee7 css fixes and add any/all control 2020-01-16 09:13:43 -08:00
82f175f6c7 fixed style issues and added default select labels 2020-01-15 17:59:51 -08:00
8df549e8d9 Addresses comments from PR: https://github.com/nasa/openmct/pull/2621 (#2623)
* Addresses comments from PR: https://github.com/nasa/openmct/pull/2621

* Fixes import path.
2020-01-15 15:39:31 -08:00
9fd720777b Merge branch 'conditionSet-view' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-15 11:28:18 -08:00
4c68c725b1 Adding telemetry options to ConditionEdit 2020-01-15 10:51:45 -08:00
06a5207c6d deriving current output from current (blue) condition output and persisting 2020-01-14 17:07:32 -08:00
78b885c508 using current condition output for current output label 2020-01-14 13:44:19 -08:00
a18a3b6099 Adds telemetry objects on composition add 2020-01-14 13:32:13 -08:00
68949e070c Merge pull request #2621 from nasa/conditionSet-telemetry
Updates conditionSet composition policy to allow only telemetry since…
2020-01-14 11:04:46 -08:00
654333dabe Removes fdescribe 2020-01-14 10:59:11 -08:00
81b8a76f1b (WIP) Fixes cyclic error while saving conditionCollection 2020-01-14 10:57:00 -08:00
31736fa194 Merge branch 'conditionSet-view' of https://github.com/nasa/openmct into conditionSet-with-classes 2020-01-14 10:27:10 -08:00
33632ef1dc (WIP) conditionSets using ConditionClasses 2020-01-14 09:52:12 -08:00
94305ed82c persist data changes on update 2020-01-14 09:29:00 -08:00
c8abc45e25 add default condition only when none present 2020-01-14 08:38:12 -08:00
cd25459ac9 Merge branch 'conditionSet-telemetry' of https://github.com/nasa/openmct into conditionSet-view 2020-01-13 22:53:45 -08:00
aaf1eb8059 Merge branch 'condition-class' of https://github.com/nasa/openmct into conditionSet-view 2020-01-13 22:52:51 -08:00
1589e4236a Merge branch 'conditionSet-telemetry' of https://github.com/nasa/openmct into condition-class 2020-01-13 22:51:13 -08:00
8ca202d0a9 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-class 2020-01-13 22:48:08 -08:00
d2f7904118 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionSet-telemetry 2020-01-13 22:47:52 -08:00
2d059fb856 Merge pull request #2610 from nasa/telemetry-criterion
Adds telemetry criterion class and related tests
2020-01-13 16:07:32 -08:00
8bbd7898bb Updates conditionSet composition policy to allow only telemetry since we're no longer using composition for condition domain objects 2020-01-13 14:59:45 -08:00
ea6f8c9a50 small fixes 2020-01-13 14:45:00 -08:00
d152440436 Fixes careless mistake - undefined object. 2020-01-13 14:12:05 -08:00
1ffe76a525 Fixes linting issue. 2020-01-13 14:09:59 -08:00
a6825f530c Fixes failing test. 2020-01-13 14:05:43 -08:00
7cf6dc386f Removes use of createOpenMCT and uses mock object for openmct instead. 2020-01-13 13:58:25 -08:00
5d8252bb07 Adds tests for condition class 2020-01-13 13:55:54 -08:00
e1e1e0fb2f temp 2020-01-13 13:52:26 -08:00
4f7345563f temp 2020-01-13 13:40:43 -08:00
68a2b9f3a8 added Default name 2020-01-10 16:45:20 -08:00
d79402c568 name property WIP 2020-01-10 15:58:16 -08:00
d0e8f650be remove condition with persistance 2020-01-10 12:59:43 -08:00
d819c6efe2 add condition button working with persistance 2020-01-10 12:22:05 -08:00
91c877f234 Adds tests for Condition class 2020-01-10 10:43:55 -08:00
55a674ba7b Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into condition-class 2020-01-09 06:46:27 -08:00
36055b7c04 Changes name of emitted event 2020-01-09 06:44:19 -08:00
fe3cc661d3 Fixes tests. 2020-01-08 12:20:17 -08:00
eb7efae1cc Persist condition to conditionSet. Remove extra condition unshift in addCondition method. 2020-01-07 11:56:24 -08:00
63f8fb54d4 conditions added to conditioncollection 2020-01-07 11:42:31 -08:00
097fa2e655 condition collection add to composition 2020-01-07 08:44:30 -08:00
3d0b4d51c2 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into telemetry-criterion 2020-01-06 13:10:58 -08:00
37650487f7 Merge pull request #2608 from nasa/conditionSet-view-provider
Condition set view provider
2020-01-06 13:10:00 -08:00
6ccc0b4fbf added TODO comments 2020-01-06 12:04:19 -08:00
79fe95372d fix lint errors 2020-01-06 10:44:09 -08:00
6adb190d0e Merge branch 'conditionSet-view' of https://github.com/nasa/openmct into conditionSet-view-provider 2020-01-06 10:36:25 -08:00
c094e6c6f4 change icon 2020-01-06 10:35:10 -08:00
8c796b4e57 Allows adding new conditions and associated criteria.
Stub for evaluating conditions.
2020-01-06 10:09:47 -08:00
c08e9a89ff add condition 2020-01-06 09:48:11 -08:00
03829af2ad check if filters are not equal before refetching 2020-01-05 20:56:53 -08:00
cc8ba18ccc Fixes condition plugin tests 2020-01-03 11:27:06 -08:00
57c671a42e combined tests in one pluginSpec file 2020-01-03 09:13:17 -08:00
1ee6ecf3ae removed conditionSet folder and fixed lint errors 2020-01-03 08:39:13 -08:00
5f80b3773b restructured folders 2020-01-02 15:19:57 -08:00
8452455050 WIP: all condition set UI elements in place - started condition form elements 2020-01-02 12:59:32 -08:00
e5d8f60cdb WIP: all condition set UI elements in place except condition form elements 2019-12-31 11:33:33 -08:00
de466000a0 WIP: styling for conditionSet and condition 2019-12-30 18:26:59 -08:00
49664c011c component styling and expand funcitonality 2019-12-27 18:17:56 -08:00
cf34d6b127 added domainObject bakc into provider 2019-12-27 14:23:04 -08:00
e52f6ce099 WIP: styling components 2019-12-27 14:19:39 -08:00
1ecdc4c487 addressed review comment 2019-12-27 13:22:05 -08:00
d38e2c49cb WIP: current output styling 2019-12-27 13:20:21 -08:00
f8464fa76f Adds telemetry criterion class and related tests 2019-12-27 12:57:30 -08:00
308ae2cb2e added CurrentOutput and TestData components 2019-12-26 16:07:55 -08:00
88219659fb renamed Conditions component to ConditionSet 2019-12-26 13:40:58 -08:00
c34c2df061 remove fdescribe 2019-12-26 12:36:58 -08:00
99c7bd4c10 added conditions and condition components 2019-12-26 12:25:30 -08:00
f93d5a6fbf skeletal html mockup 2019-12-26 08:25:58 -08:00
cd116667be change to inject domainObject 2019-12-23 14:32:57 -08:00
2f2de3952d addressed review comments 2019-12-23 12:14:35 -08:00
45e56798c5 removed fit 2019-12-23 11:24:25 -08:00
0664d480e6 conditionSet provider with tests 2019-12-23 11:19:56 -08:00
5d31806fb7 fix #2596 2019-12-22 01:31:23 +01:00
283599ddf5 Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into conditionSet-view-provider 2019-12-20 09:56:26 -08:00
09e3ceefa0 Merge pull request #2600 from nasa/condition-set-policy
Adds ConditionSet composition policy. Allows conditions to be added to conditionSets
2019-12-20 08:51:37 -08:00
87f76ebfe4 Addresses comments. 2019-12-19 21:20:38 -08:00
55a195b841 Adds ConditionSet composition policy. Allows conditions to be added to conditionSets 2019-12-19 12:33:11 -08:00
c7946fd7b3 refactor conditionSet pluginSpec 2019-12-19 11:14:55 -08:00
5d3ba3199c Merge remote-tracking branch 'origin/topic-conditionals' into conditionSet-view-provider 2019-12-19 09:07:44 -08:00
f0d10306fc completing merge 2019-12-19 09:06:19 -08:00
161943b5b8 Merge pull request #2598 from nasa/condition-type
Adds new condition type and plugin. Also adds tests
2019-12-18 15:56:20 -08:00
e545043a26 Merge branch 'topic-conditionals' into condition-type 2019-12-18 15:52:46 -08:00
40fb58b5b7 Merge pull request #2597 from nasa/conditionSet-object-type
Condition set object type
2019-12-18 15:50:42 -08:00
1f9d4708b3 Adds copyright 2019-12-18 13:19:21 -08:00
162809e081 [#2570] adds new condition type and plugin. Also adds tests 2019-12-18 13:13:54 -08:00
482c871ac2 removed fdescribe 2019-12-18 13:10:29 -08:00
f0b3311630 fixed merge conflicts 2019-12-18 13:01:28 -08:00
656d6d6c3f addressed review comments 2019-12-18 12:48:05 -08:00
ea45f0f4aa WIP 2019-12-18 12:16:25 -08:00
6a25cb0a58 renaming 2019-12-18 11:50:10 -08:00
4a1901420d removed fdescribe 2019-12-18 11:44:40 -08:00
ad64f00608 WIP renaming 2019-12-18 11:43:20 -08:00
65aea29cb9 renamed conditionCollection to conditionSet and made all filenames and references consistent 2019-12-18 11:23:37 -08:00
7981424e9a WIP: - preparing to rename branch 2019-12-18 11:08:21 -08:00
10c4340475 completed tests for condition collection object 2019-12-17 13:24:34 -08:00
0a95db1a51 fixed missing commas 2019-12-17 08:15:15 -08:00
ace77dce65 WIP 2019-12-17 08:10:35 -08:00
c1d58bb25f fixed another minor merge conflict 2019-12-16 16:55:26 -08:00
fbcafe0f62 mfixed merge conflicts 2019-12-16 16:50:57 -08:00
9a9d9222a9 Merge branch 'master' into condition-object-type 2019-12-16 14:52:48 -08:00
221e5b4f6c Added tests for ConditionPlugin 2019-12-16 14:42:19 -08:00
5df74aee68 [test-framework] Adds basic test for condition plugin 2019-12-16 11:06:19 -08:00
17838d8040 WIP: setting up test framework for conditionals 2019-12-12 14:36:24 -08:00
f82ca91a61 changed node mules path 2019-12-06 15:13:03 -08:00
b06c234b59 import path for vue component 2019-12-06 13:02:23 -08:00
31a7ebd4f1 basic skeleton for conditions code 2019-12-06 12:05:36 -08:00
295 changed files with 16996 additions and 3987 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,17 +11,17 @@ jobs:
name: Update npm
command: 'sudo npm install -g npm@latest'
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
key: dependency-cache-13-{{ checksum "package.json" }}
- run:
name: Installing dependencies (npm install)
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
key: dependency-cache-13-{{ checksum "package.json" }}
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.
The following guidelines are provided for anyone contributing source code to the Open MCT project:
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. A single return statement at the end of the function should be used, except where an early return would improve code clarity.
1. Avoid the use of "magic" values.
eg.
```JavaScript
Const UNAUTHORIZED = 401
if (responseCode === UNAUTHORIZED)
```
is preferable to
```JavaScript
if (responseCode === 401)
```
1. Use the ternary operator only for simple cases such as variable assignment. Nested ternaries should be avoided in all cases.
1. Test specs should reside alongside the source code they test, not in a separate directory.
1. Organize code by feature, not by type.
eg.
```
- telemetryTable
- row
TableRow.js
TableRowCollection.js
TableRow.vue
- column
TableColumn.js
TableColumn.vue
plugin.js
pluginSpec.js
```
is preferable to
```
- telemetryTable
- components
TableRow.vue
TableColumn.vue
- collections
TableRowCollection.js
TableColumn.js
TableRow.js
plugin.js
pluginSpec.js
```
Deviations from Open MCT code style guidelines require two-party agreement,
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

@ -125,3 +125,22 @@ A release is not closed until both categories have been performed on
the latest snapshot of the software, _and_ no issues labelled as
["blocker" or "critical"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
remain open.
### Testathons
Testathons can be used as a means of performing per-sprint and per-release testing.
#### Timing
For per-sprint testing, a testathon is typically performed at the beginning of the third week of a sprint, and again later that week to verify any fixes. For per-release testing, a testathon is typically performed prior to any formal testing processes that are applicable to that release.
#### Process
1. Prior to the scheduled testathon, a list will be compiled of all issues that are closed and unverified.
2. For each issue, testers should review the associated PR for testing instructions. See the contributing guide for instructions on [pull requests](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md#merging).
3. As each issue is verified via testing, any team members testing it should leave a comment on that issue indicating that it has been verified fixed.
4. If a bug is found that relates to an issue being tested, notes should be included on the associated issue, and the issue should be reopened. Bug notes should include reproduction steps.
5. For any bugs that are not obviously related to any of the issues under test, a new issue should be created with details about the bug, including reproduction steps. If unsure about whether a bug relates to an issue being tested, just create a new issue.
6. At the end of the testathon, triage will take place, where all tested issues will be reviewed.
7. If verified fixed, an issue will remain closed, and will have the “unverified” label removed.
8. For any bugs found, a severity will be assigned.
9. A second testathon will be scheduled for later in the week that will aim to address all issues identified as blockers, as well as any other issues scoped by the team during triage.
10. Any issues that were not tested will remain "unverified" and will be picked up in the next testathon.

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.
@ -34,6 +34,7 @@
<body>
</body>
<script>
const ONE_MINUTE = 60 * 1000;
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_MINUTES = 30 * 60 * 1000;
@ -43,9 +44,9 @@
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
@ -70,8 +71,8 @@
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: - THIRTY_MINUTES,
end: FIVE_MINUTES
start: - FIVE_MINUTES,
end: ONE_MINUTE
}
}
]

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,31 +38,34 @@
"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-dist": "^1.54.1",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
"split": "^1.0.0",
"style-loader": "^1.0.1",
"uuid": "^3.3.3",
"v8-compile-cache": "^1.1.0",
"vue": "2.5.6",
"vue-loader": "^15.2.6",
@ -75,15 +77,17 @@
"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:watch": "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_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",

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-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.
*
@ -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,9 +249,10 @@ 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.PlotlyPlot());
this.install(this.plugins.TelemetryTable());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
@ -264,6 +265,10 @@ define([
this.install(this.plugins.GoToOriginalAction());
this.install(this.plugins.ImportExport());
this.install(this.plugins.WebPage());
this.install(this.plugins.Condition());
this.install(this.plugins.ConditionWidget());
this.install(this.plugins.URLTimeSettingsSynchronizer());
this.install(this.plugins.NotificationIndicator());
}
MCT.prototype = Object.create(EventEmitter.prototype);
@ -348,17 +353,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', {
@ -398,24 +399,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.
*
@ -427,6 +435,10 @@ define([
plugin(this);
};
MCT.prototype.destroy = function () {
this.emit('destroy');
};
MCT.prototype.plugins = plugins;
return MCT;

View File

@ -21,24 +21,28 @@
*****************************************************************************/
define([
'./MCT',
'./plugins/plugins',
'legacyRegistry'
], function (MCT, plugins, legacyRegistry) {
xdescribe("MCT", function () {
'legacyRegistry',
'utils/testing'
], function (plugins, legacyRegistry, testUtils) {
describe("MCT", function () {
var openmct;
var mockPlugin;
var mockPlugin2;
var mockListener;
var oldBundles;
beforeAll(() => {
testUtils.resetApplicationState();
});
beforeEach(function () {
mockPlugin = jasmine.createSpy('plugin');
mockPlugin2 = jasmine.createSpy('plugin2');
mockListener = jasmine.createSpy('listener');
oldBundles = legacyRegistry.list();
openmct = new MCT();
openmct = testUtils.createOpenMct();
openmct.install(mockPlugin);
openmct.install(mockPlugin2);
@ -52,6 +56,7 @@ define([
legacyRegistry.delete(bundle);
}
});
testUtils.resetApplicationState(openmct);
});
it("exposes plugins", function () {
@ -63,8 +68,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 +83,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

@ -29,7 +29,6 @@ define([
'./capabilities/APICapabilityDecorator',
'./policies/AdaptedViewPolicy',
'./runs/AlternateCompositionInitializer',
'./runs/TimeSettingsURLHandler',
'./runs/TypeDeprecationChecker',
'./runs/LegacyTelemetryProvider',
'./runs/RegisterLegacyTypes',
@ -46,7 +45,6 @@ define([
APICapabilityDecorator,
AdaptedViewPolicy,
AlternateCompositionInitializer,
TimeSettingsURLHandler,
TypeDeprecationChecker,
LegacyTelemetryProvider,
RegisterLegacyTypes,
@ -134,16 +132,6 @@ define([
implementation: AlternateCompositionInitializer,
depends: ["openmct"]
},
{
implementation: function (openmct, $location, $rootScope) {
return new TimeSettingsURLHandler(
openmct.time,
$location,
$rootScope
);
},
depends: ["openmct", "$location", "$rootScope"]
},
{
implementation: LegacyTelemetryProvider,
depends: [

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

@ -1,150 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'lodash'
], function (
_
) {
// Parameter names in query string
var SEARCH = {
MODE: 'tc.mode',
TIME_SYSTEM: 'tc.timeSystem',
START_BOUND: 'tc.startBound',
END_BOUND: 'tc.endBound',
START_DELTA: 'tc.startDelta',
END_DELTA: 'tc.endDelta'
};
var TIME_EVENTS = ['bounds', 'timeSystem', 'clock', 'clockOffsets'];
// Used to shorthand calls to $location, which clears null parameters
var NULL_PARAMETERS = { key: null, start: null, end: null };
/**
* Communicates settings from the URL to the time API,
* and vice versa.
*/
function TimeSettingsURLHandler(time, $location, $rootScope) {
this.time = time;
this.$location = $location;
$rootScope.$on('$locationChangeSuccess', this.updateTime.bind(this));
TIME_EVENTS.forEach(function (event) {
this.time.on(event, this.updateQueryParams.bind(this));
}, this);
this.updateTime(); // Initialize
}
TimeSettingsURLHandler.prototype.updateQueryParams = function () {
var clock = this.time.clock();
var fixed = !clock;
var mode = fixed ? 'fixed' : clock.key;
var timeSystem = this.time.timeSystem() || NULL_PARAMETERS;
var bounds = fixed ? this.time.bounds() : NULL_PARAMETERS;
var deltas = fixed ? NULL_PARAMETERS : this.time.clockOffsets();
bounds = bounds || NULL_PARAMETERS;
deltas = deltas || NULL_PARAMETERS;
if (deltas.start) {
deltas = { start: -deltas.start, end: deltas.end };
}
this.$location.search(SEARCH.MODE, mode);
this.$location.search(SEARCH.TIME_SYSTEM, timeSystem.key);
this.$location.search(SEARCH.START_BOUND, bounds.start);
this.$location.search(SEARCH.END_BOUND, bounds.end);
this.$location.search(SEARCH.START_DELTA, deltas.start);
this.$location.search(SEARCH.END_DELTA, deltas.end);
};
TimeSettingsURLHandler.prototype.parseQueryParams = function () {
var searchParams = _.pick(this.$location.search(), _.values(SEARCH));
var parsedParams = {
clock: searchParams[SEARCH.MODE],
timeSystem: searchParams[SEARCH.TIME_SYSTEM]
};
if (!isNaN(parseInt(searchParams[SEARCH.START_DELTA], 0xA)) &&
!isNaN(parseInt(searchParams[SEARCH.END_DELTA], 0xA))) {
parsedParams.clockOffsets = {
start: -searchParams[SEARCH.START_DELTA],
end: +searchParams[SEARCH.END_DELTA]
};
}
if (!isNaN(parseInt(searchParams[SEARCH.START_BOUND], 0xA)) &&
!isNaN(parseInt(searchParams[SEARCH.END_BOUND], 0xA))) {
parsedParams.bounds = {
start: +searchParams[SEARCH.START_BOUND],
end: +searchParams[SEARCH.END_BOUND]
};
}
return parsedParams;
};
TimeSettingsURLHandler.prototype.updateTime = function () {
var params = this.parseQueryParams();
if (_.isEqual(params, this.last)) {
return; // Do nothing;
}
this.last = params;
if (!params.timeSystem) {
this.updateQueryParams();
} else if (params.clock === 'fixed' && params.bounds) {
if (!this.time.timeSystem() ||
this.time.timeSystem().key !== params.timeSystem) {
this.time.timeSystem(
params.timeSystem,
params.bounds
);
} else if (!_.isEqual(this.time.bounds(), params.bounds)) {
this.time.bounds(params.bounds);
}
if (this.time.clock()) {
this.time.stopClock();
}
} else if (params.clockOffsets) {
if (params.clock === 'fixed') {
this.time.stopClock();
return;
}
if (!this.time.clock() ||
this.time.clock().key !== params.clock) {
this.time.clock(params.clock, params.clockOffsets);
} else if (!_.isEqual(this.time.clockOffsets(), params.clockOffsets)) {
this.time.clockOffsets(params.clockOffsets);
}
if (!this.time.timeSystem() ||
this.time.timeSystem().key !== params.timeSystem) {
this.time.timeSystem(params.timeSystem);
}
} else {
// Neither found, update from timeSystem.
this.updateQueryParams();
}
};
return TimeSettingsURLHandler;
});

View File

@ -1,576 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./TimeSettingsURLHandler',
'../../api/time/TimeAPI'
], function (
TimeSettingsURLHandler,
TimeAPI
) {
describe("TimeSettingsURLHandler", function () {
var time;
var $location;
var $rootScope;
var search;
var handler; // eslint-disable-line
var clockA;
var clockB;
var timeSystemA;
var timeSystemB;
var boundsA;
var boundsB;
var offsetsA;
var offsetsB;
var initialize;
var triggerLocationChange;
beforeEach(function () {
clockA = jasmine.createSpyObj('clockA', ['on', 'off']);
clockA.key = 'clockA';
clockA.currentValue = function () {
return 1000;
};
clockB = jasmine.createSpyObj('clockB', ['on', 'off']);
clockB.key = 'clockB';
clockB.currentValue = function () {
return 2000;
};
timeSystemA = {key: 'timeSystemA'};
timeSystemB = {key: 'timeSystemB'};
boundsA = {
start: 10,
end: 20
};
boundsB = {
start: 120,
end: 360
};
offsetsA = {
start: -100,
end: 0
};
offsetsB = {
start: -50,
end: 50
};
time = new TimeAPI();
[
'on',
'bounds',
'clockOffsets',
'timeSystem',
'clock',
'stopClock'
].forEach(function (method) {
spyOn(time, method).and.callThrough();
});
time.addTimeSystem(timeSystemA);
time.addTimeSystem(timeSystemB);
time.addClock(clockA);
time.addClock(clockB);
$location = jasmine.createSpyObj('$location', [
'search'
]);
$rootScope = jasmine.createSpyObj('$rootScope', [
'$on'
]);
search = {};
$location.search.and.callFake(function (key, value) {
if (arguments.length === 0) {
return search;
}
if (value === null) {
delete search[key];
} else {
search[key] = String(value);
}
return this;
});
expect(time.timeSystem()).toBeUndefined();
expect(time.bounds()).toEqual({});
expect(time.clockOffsets()).toBeUndefined();
expect(time.clock()).toBeUndefined();
initialize = function () {
handler = new TimeSettingsURLHandler(
time,
$location,
$rootScope
);
expect($rootScope.$on).toHaveBeenCalledWith(
'$locationChangeSuccess',
jasmine.any(Function)
);
triggerLocationChange = $rootScope.$on.calls.mostRecent().args[1];
};
});
it("initializes with missing time system", function () {
// This handles an odd transitory case where a url does not include
// a timeSystem. It's generally only experienced by those who
// based their code on the tutorial before it specified a time
// system.
search['tc.mode'] = 'clockA';
search['tc.timeSystem'] = undefined;
search['tc.startDelta'] = '123';
search['tc.endDelta'] = '456';
// We don't specify behavior right now other than "don't break."
expect(initialize).not.toThrow();
});
it("can initalize fixed mode from location", function () {
search['tc.mode'] = 'fixed';
search['tc.timeSystem'] = 'timeSystemA';
search['tc.startBound'] = '123';
search['tc.endBound'] = '456';
initialize();
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemA',
{
start: 123,
end: 456
}
);
});
it("can initialize clock mode from location", function () {
search['tc.mode'] = 'clockA';
search['tc.timeSystem'] = 'timeSystemA';
search['tc.startDelta'] = '123';
search['tc.endDelta'] = '456';
initialize();
expect(time.clock).toHaveBeenCalledWith(
'clockA',
{
start: -123,
end: 456
}
);
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemA'
);
});
it("can initialize fixed mode from time API", function () {
time.timeSystem(timeSystemA.key, boundsA);
initialize();
expect($location.search)
.toHaveBeenCalledWith('tc.mode', 'fixed');
expect($location.search)
.toHaveBeenCalledWith('tc.timeSystem', 'timeSystemA');
expect($location.search)
.toHaveBeenCalledWith('tc.startBound', 10);
expect($location.search)
.toHaveBeenCalledWith('tc.endBound', 20);
expect($location.search)
.toHaveBeenCalledWith('tc.startDelta', null);
expect($location.search)
.toHaveBeenCalledWith('tc.endDelta', null);
});
it("can initialize clock mode from time API", function () {
time.clock(clockA.key, offsetsA);
time.timeSystem(timeSystemA.key);
initialize();
expect($location.search)
.toHaveBeenCalledWith('tc.mode', 'clockA');
expect($location.search)
.toHaveBeenCalledWith('tc.timeSystem', 'timeSystemA');
expect($location.search)
.toHaveBeenCalledWith('tc.startBound', null);
expect($location.search)
.toHaveBeenCalledWith('tc.endBound', null);
expect($location.search)
.toHaveBeenCalledWith('tc.startDelta', 100);
expect($location.search)
.toHaveBeenCalledWith('tc.endDelta', 0);
});
describe('location changes in fixed mode', function () {
beforeEach(function () {
time.timeSystem(timeSystemA.key, boundsA);
initialize();
time.timeSystem.calls.reset();
time.bounds.calls.reset();
time.clock.calls.reset();
time.stopClock.calls.reset();
});
it("does not change on spurious location change", function () {
triggerLocationChange();
expect(time.timeSystem).not.toHaveBeenCalledWith(
'timeSystemA',
jasmine.any(Object)
);
expect(time.bounds).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
expect(time.stopClock).not.toHaveBeenCalled();
});
it("updates timeSystem changes", function () {
search['tc.timeSystem'] = 'timeSystemB';
triggerLocationChange();
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemB',
{
start: 10,
end: 20
}
);
});
it("updates bounds changes", function () {
search['tc.startBound'] = '100';
search['tc.endBound'] = '200';
triggerLocationChange();
expect(time.timeSystem).not.toHaveBeenCalledWith(
jasmine.anything(), jasmine.anything()
);
expect(time.bounds).toHaveBeenCalledWith({
start: 100,
end: 200
});
search['tc.endBound'] = '300';
triggerLocationChange();
expect(time.timeSystem).not.toHaveBeenCalledWith(
jasmine.anything(), jasmine.anything()
);
expect(time.bounds).toHaveBeenCalledWith({
start: 100,
end: 300
});
});
it("updates clock mode w/o timeSystem change", function () {
search['tc.mode'] = 'clockA';
search['tc.startDelta'] = '50';
search['tc.endDelta'] = '50';
delete search['tc.endBound'];
delete search['tc.startBound'];
triggerLocationChange();
expect(time.clock).toHaveBeenCalledWith(
'clockA',
{
start: -50,
end: 50
}
);
expect(time.timeSystem).not.toHaveBeenCalledWith(
jasmine.anything(), jasmine.anything()
);
});
it("updates clock mode and timeSystem", function () {
search['tc.mode'] = 'clockA';
search['tc.startDelta'] = '50';
search['tc.endDelta'] = '50';
search['tc.timeSystem'] = 'timeSystemB';
delete search['tc.endBound'];
delete search['tc.startBound'];
triggerLocationChange();
expect(time.clock).toHaveBeenCalledWith(
'clockA',
{
start: -50,
end: 50
}
);
expect(time.timeSystem).toHaveBeenCalledWith('timeSystemB');
});
});
describe('location changes in clock mode', function () {
beforeEach(function () {
time.clock(clockA.key, offsetsA);
time.timeSystem(timeSystemA.key);
initialize();
time.timeSystem.calls.reset();
time.bounds.calls.reset();
time.clock.calls.reset();
time.clockOffsets.calls.reset();
time.stopClock.calls.reset();
});
it("does not change on spurious location change", function () {
triggerLocationChange();
expect(time.timeSystem).not.toHaveBeenCalledWith(
'timeSystemA',
jasmine.any(Object)
);
expect(time.clockOffsets).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
expect(time.clock).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
expect(time.bounds).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
});
it("changes time system", function () {
search['tc.timeSystem'] = 'timeSystemB';
triggerLocationChange();
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemB'
);
expect(time.clockOffsets).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
expect(time.clock).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
expect(time.stopClock).not.toHaveBeenCalled();
expect(time.bounds).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
});
it("changes offsets", function () {
search['tc.startDelta'] = '50';
search['tc.endDelta'] = '50';
triggerLocationChange();
expect(time.timeSystem).not.toHaveBeenCalledWith(
'timeSystemA',
jasmine.any(Object)
);
expect(time.clockOffsets).toHaveBeenCalledWith(
{
start: -50,
end: 50
}
);
expect(time.clock).not.toHaveBeenCalledWith(
jasmine.any(Object)
);
});
it("updates to fixed w/o timeSystem change", function () {
search['tc.mode'] = 'fixed';
search['tc.startBound'] = '234';
search['tc.endBound'] = '567';
delete search['tc.endDelta'];
delete search['tc.startDelta'];
triggerLocationChange();
expect(time.stopClock).toHaveBeenCalled();
expect(time.bounds).toHaveBeenCalledWith({
start: 234,
end: 567
});
expect(time.timeSystem).not.toHaveBeenCalledWith(
jasmine.anything(), jasmine.anything()
);
});
it("updates fixed and timeSystem", function () {
search['tc.mode'] = 'fixed';
search['tc.startBound'] = '234';
search['tc.endBound'] = '567';
search['tc.timeSystem'] = 'timeSystemB';
delete search['tc.endDelta'];
delete search['tc.startDelta'];
triggerLocationChange();
expect(time.stopClock).toHaveBeenCalled();
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemB',
{
start: 234,
end: 567
}
);
});
it("updates clock", function () {
search['tc.mode'] = 'clockB';
triggerLocationChange();
expect(time.clock).toHaveBeenCalledWith(
'clockB',
{
start: -100,
end: 0
}
);
expect(time.timeSystem).not.toHaveBeenCalledWith(jasmine.anything());
});
it("updates clock and timeSystem", function () {
search['tc.mode'] = 'clockB';
search['tc.timeSystem'] = 'timeSystemB';
triggerLocationChange();
expect(time.clock).toHaveBeenCalledWith(
'clockB',
{
start: -100,
end: 0
}
);
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemB'
);
});
it("updates clock and timeSystem and offsets", function () {
search['tc.mode'] = 'clockB';
search['tc.timeSystem'] = 'timeSystemB';
search['tc.startDelta'] = '50';
search['tc.endDelta'] = '50';
triggerLocationChange();
expect(time.clock).toHaveBeenCalledWith(
'clockB',
{
start: -50,
end: 50
}
);
expect(time.timeSystem).toHaveBeenCalledWith(
'timeSystemB'
);
});
it("stops the clock", function () {
// this is a robustness test, unsure if desired, requires
// user to be manually editing location strings.
search['tc.mode'] = 'fixed';
triggerLocationChange();
expect(time.stopClock).toHaveBeenCalled();
});
});
describe("location updates from time API in fixed", function () {
beforeEach(function () {
time.timeSystem(timeSystemA.key, boundsA);
initialize();
});
it("updates on bounds change", function () {
time.bounds(boundsB);
expect(search).toEqual({
'tc.mode': 'fixed',
'tc.startBound': '120',
'tc.endBound': '360',
'tc.timeSystem': 'timeSystemA'
});
});
it("updates on timeSystem change", function () {
time.timeSystem(timeSystemB, boundsA);
expect(search).toEqual({
'tc.mode': 'fixed',
'tc.startBound': '10',
'tc.endBound': '20',
'tc.timeSystem': 'timeSystemB'
});
time.timeSystem(timeSystemA, boundsB);
expect(search).toEqual({
'tc.mode': 'fixed',
'tc.startBound': '120',
'tc.endBound': '360',
'tc.timeSystem': 'timeSystemA'
});
});
it("Updates to clock", function () {
time.clock(clockA, offsetsA);
expect(search).toEqual({
'tc.mode': 'clockA',
'tc.startDelta': '100',
'tc.endDelta': '0',
'tc.timeSystem': 'timeSystemA'
});
});
});
describe("location updates from time API in fixed", function () {
beforeEach(function () {
time.clock(clockA.key, offsetsA);
time.timeSystem(timeSystemA.key);
initialize();
});
it("updates offsets", function () {
time.clockOffsets(offsetsB);
expect(search).toEqual({
'tc.mode': 'clockA',
'tc.startDelta': '50',
'tc.endDelta': '50',
'tc.timeSystem': 'timeSystemA'
});
});
it("updates clocks", function () {
time.clock(clockB, offsetsA);
expect(search).toEqual({
'tc.mode': 'clockB',
'tc.startDelta': '100',
'tc.endDelta': '0',
'tc.timeSystem': 'timeSystemA'
});
time.clock(clockA, offsetsB);
expect(search).toEqual({
'tc.mode': 'clockA',
'tc.startDelta': '50',
'tc.endDelta': '50',
'tc.timeSystem': 'timeSystemA'
});
});
it("updates timesystems", function () {
time.timeSystem(timeSystemB);
expect(search).toEqual({
'tc.mode': 'clockA',
'tc.startDelta': '100',
'tc.endDelta': '0',
'tc.timeSystem': 'timeSystemB'
});
});
it("stops the clock", function () {
time.stopClock();
expect(search).toEqual({
'tc.mode': 'fixed',
'tc.startBound': '900',
'tc.endBound': '1000',
'tc.timeSystem': 'timeSystemA'
});
});
});
});
});

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

@ -0,0 +1,39 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
export default class LADTableCompositionPolicy {
constructor(openmct) {
this.openmct = openmct;
return this.allow.bind(this);
}
allow(parent, child) {
if(parent.type === 'LadTable') {
return this.openmct.telemetry.isTelemetryObject(child);
} else if(parent.type === 'LadTableSet') {
return child.type === 'LadTable';
}
return true;
}
}

View File

@ -19,53 +19,46 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTableSet from './components/LadTableSet.vue';
import Vue from 'vue';
define([
'./components/LadTableSet.vue',
'vue'
], function (
LadTableSet,
Vue
) {
function LADTableSetViewProvider(openmct) {
return {
key: 'LadTableSet',
name: 'LAD Table Set',
cssClass: 'icon-tabular-lad-set',
canView: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject, objectPath) {
let component;
export default function LADTableSetViewProvider(openmct) {
return {
key: 'LadTableSet',
name: 'LAD Table Set',
cssClass: 'icon-tabular-lad-set',
canView: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableSet: LadTableSet.default
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-set></lad-table-set>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return LADTableSetViewProvider;
});
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableSet: LadTableSet
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-set></lad-table-set>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}

View File

@ -19,53 +19,46 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTable from './components/LADTable.vue';
import Vue from 'vue';
define([
'./components/LADTable.vue',
'vue'
], function (
LadTableComponent,
Vue
) {
function LADTableViewProvider(openmct) {
return {
key: 'LadTable',
name: 'LAD Table',
cssClass: 'icon-tabular-lad',
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject, objectPath) {
let component;
export default function LADTableViewProvider(openmct) {
return {
key: 'LadTable',
name: 'LAD Table',
cssClass: 'icon-tabular-lad',
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableComponent: LadTableComponent.default
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-component></lad-table-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return LADTableViewProvider;
});
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableComponent: LadTable
},
provide: {
openmct,
domainObject,
objectPath
},
template: '<lad-table-component></lad-table-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -22,12 +22,16 @@
*****************************************************************************/
<template>
<tr @contextmenu.prevent="showContextMenu">
<td>{{ name }}</td>
<td>{{ timestamp }}</td>
<td :class="valueClass">
{{ value }}
</td>
<tr
class="js-lad-table__body__row"
@contextmenu.prevent="showContextMenu"
>
<td class="js-first-data">{{ name }}</td>
<td class="js-second-data">{{ formattedTimestamp }}</td>
<td
class="js-third-data"
:class="valueClass"
>{{ value }}</td>
</tr>
</template>
@ -52,16 +56,22 @@ export default {
return {
name: this.domainObject.name,
timestamp: '---',
timestamp: undefined,
value: '---',
valueClass: '',
currentObjectPath
}
},
computed: {
formattedTimestamp() {
return this.timestamp !== undefined ? this.formats[this.timestampKey].format(this.timestamp) : '---';
}
},
mounted() {
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.bounds = this.openmct.time.bounds();
this.limitEvaluator = this.openmct
.telemetry
@ -76,6 +86,7 @@ export default {
);
this.openmct.time.on('timeSystem', this.updateTimeSystem);
this.openmct.time.on('bounds', this.updateBounds);
this.timestampKey = this.openmct.time.timeSystem().key;
@ -89,43 +100,63 @@ export default {
.telemetry
.subscribe(this.domainObject, this.updateValues);
this.openmct
.telemetry
.request(this.domainObject, {strategy: 'latest'})
.then((array) => this.updateValues(array[array.length - 1]));
this.requestHistory();
},
destroyed() {
this.stopWatchingMutation();
this.unsubscribe();
this.openmct.off('timeSystem', this.updateTimeSystem);
this.openmct.time.off('timeSystem', this.updateTimeSystem);
this.openmct.time.off('bounds', this.updateBounds);
},
methods: {
updateValues(datum) {
this.timestamp = this.formats[this.timestampKey].format(datum);
this.value = this.formats[this.valueKey].format(datum);
let newTimestamp = this.formats[this.timestampKey].parse(datum),
limit;
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
if (limit) {
this.valueClass = limit.cssClass;
} else {
this.valueClass = '';
if(this.shouldUpdate(newTimestamp)) {
this.timestamp = this.formats[this.timestampKey].parse(datum);
this.value = this.formats[this.valueKey].format(datum);
limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
if (limit) {
this.valueClass = limit.cssClass;
} else {
this.valueClass = '';
}
}
},
shouldUpdate(newTimestamp) {
return (this.timestamp === undefined) ||
(this.inBounds(newTimestamp) &&
newTimestamp > this.timestamp);
},
requestHistory() {
this.openmct
.telemetry
.request(this.domainObject, {
start: this.bounds.start,
end: this.bounds.end,
size: 1,
strategy: 'latest'
})
.then((array) => this.updateValues(array[array.length - 1]));
},
updateName(name) {
this.name = name;
},
updateBounds(bounds, isTick) {
this.bounds = bounds;
if(!isTick) {
this.requestHistory();
}
},
inBounds(timestamp) {
return timestamp >= this.bounds.start && timestamp <= this.bounds.end;
},
updateTimeSystem(timeSystem) {
this.value = '---';
this.timestamp = '---';
this.valueClass = '';
this.timestampKey = timeSystem.key;
this.openmct
.telemetry
.request(this.domainObject, {strategy: 'latest'})
.then((array) => this.updateValues(array[array.length - 1]));
},
showContextMenu(event) {
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);

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);
},
@ -86,4 +88,3 @@ export default {
}
}
</script>

View File

@ -35,7 +35,7 @@
>
<tr
:key="primary.key"
class="c-table__group-header"
class="c-table__group-header js-lad-table-set__table-headers"
>
<td colspan="10">
{{ primary.domainObject.name }}
@ -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

@ -19,38 +19,36 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LADTableViewProvider from './LADTableViewProvider';
import LADTableSetViewProvider from './LADTableSetViewProvider';
import LADTableCompositionPolicy from './LADTableCompositionPolicy';
define([
'./LADTableViewProvider',
'./LADTableSetViewProvider'
], function (
LADTableViewProvider,
LADTableSetViewProvider
) {
return function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
export default function plugin() {
return function install(openmct) {
openmct.types.addType('LadTable', {
name: "LAD Table",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
openmct.types.addType('LadTableSet', {
name: "LAD Table Set",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad-set',
initialize(domainObject) {
domainObject.composition = [];
}
});
};
openmct.types.addType('LadTable', {
name: "LAD Table",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.types.addType('LadTableSet', {
name: "LAD Table Set",
creatable: true,
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
cssClass: 'icon-tabular-lad-set',
initialize(domainObject) {
domainObject.composition = [];
}
});
openmct.composition.addPolicy(new LADTableCompositionPolicy(openmct));
};
});
}

View File

@ -0,0 +1,365 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadPlugin from './plugin.js';
import Vue from 'vue';
import {
createOpenMct,
getMockObjects,
getMockTelemetry,
getLatestTelemetry,
resetApplicationState
} from 'utils/testing';
const TABLE_BODY_ROWS = '.js-lad-table__body__row';
const TABLE_BODY_FIRST_ROW = TABLE_BODY_ROWS + ':first-child';
const TABLE_BODY_FIRST_ROW_FIRST_DATA = TABLE_BODY_FIRST_ROW + ' .js-first-data';
const TABLE_BODY_FIRST_ROW_SECOND_DATA = TABLE_BODY_FIRST_ROW + ' .js-second-data';
const TABLE_BODY_FIRST_ROW_THIRD_DATA = TABLE_BODY_FIRST_ROW + ' .js-third-data';
const LAD_SET_TABLE_HEADERS = '.js-lad-table-set__table-headers';
function utcTimeFormat(value) {
return new Date(value).toISOString().replace('T', ' ')
}
describe("The LAD Table", () => {
const ladTableKey = 'LadTable';
let openmct,
ladPlugin,
parent,
child,
telemetryCount = 3,
timeFormat = 'utc',
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
mockObj = getMockObjects({
objectKeyStrings: ['ladTable', 'telemetry'],
format: timeFormat
}),
bounds = {
start: 0,
end: 4
};
// add telemetry object as composition in lad table
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
// this setups up the app
beforeEach((done) => {
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
parent = document.createElement('div');
child = document.createElement('div');
parent.appendChild(child);
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
ladPlugin = new LadPlugin();
openmct.install(ladPlugin);
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
openmct.time.bounds({ start: bounds.start, end: bounds.end });
openmct.on('start', done);
openmct.startHeadless(appHolder);
});
afterEach(() => {
resetApplicationState(openmct);
});
it("should provide a table view only for lad table objects", () => {
let applicableViews = openmct.objectViews.get(mockObj.ladTable),
ladTableView = applicableViews.find(
(viewProvider) => viewProvider.key === ladTableKey
);
expect(applicableViews.length).toEqual(1);
expect(ladTableView).toBeDefined();
});
describe('composition', () => {
let ladTableCompositionCollection;
beforeEach(() => {
ladTableCompositionCollection = openmct.composition.get(mockObj.ladTable);
ladTableCompositionCollection.load();
});
it("should accept telemetry producing objects", () => {
expect(() => {
ladTableCompositionCollection.add(mockObj.telemetry);
}).not.toThrow();
});
it("should reject non-telemtry producing objects", () => {
expect(()=> {
ladTableCompositionCollection.add(mockObj.ladTable);
}).toThrow();
});
});
describe("table view", () => {
let applicableViews,
ladTableViewProvider,
ladTableView,
anotherTelemetryObj = getMockObjects({
objectKeyStrings: ['telemetry'],
overwrite: {
telemetry: {
name: "New Telemetry Object",
identifier: { namespace: "", key: "another-telemetry-object" }
}
}
}).telemetry;
// add another telemetry object as composition in lad table to test multi rows
mockObj.ladTable.composition.push(anotherTelemetryObj.identifier);
beforeEach(async () => {
let telemetryRequestResolve,
telemetryObjectResolve,
anotherTelemetryObjectResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
}),
telemetryObjectPromise = new Promise((resolve) => {
telemetryObjectResolve = resolve;
}),
anotherTelemetryObjectPromise = new Promise((resolve) => {
anotherTelemetryObjectResolve = resolve;
})
openmct.telemetry.request.and.callFake(() => {
telemetryRequestResolve(mockTelemetry);
return telemetryRequestPromise;
});
openmct.objects.get.and.callFake((obj) => {
if(obj.key === 'telemetry-object') {
telemetryObjectResolve(mockObj.telemetry);
return telemetryObjectPromise;
} else {
anotherTelemetryObjectResolve(anotherTelemetryObj);
return anotherTelemetryObjectPromise;
}
});
openmct.time.bounds({ start: bounds.start, end: bounds.end });
applicableViews = openmct.objectViews.get(mockObj.ladTable);
ladTableViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableKey);
ladTableView = ladTableViewProvider.view(mockObj.ladTable, [mockObj.ladTable]);
ladTableView.show(child, true);
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise]);
await Vue.nextTick();
return;
});
it("should show one row per object in the composition", () => {
const rowCount = parent.querySelectorAll(TABLE_BODY_ROWS).length;
expect(rowCount).toBe(mockObj.ladTable.composition.length);
});
it("should show the most recent datum from the telemetry producing object", async () => {
const latestDatum = getLatestTelemetry(mockTelemetry, { timeFormat });
const expectedDate = utcTimeFormat(latestDatum[timeFormat]);
await Vue.nextTick();
const latestDate = parent.querySelector(TABLE_BODY_FIRST_ROW_SECOND_DATA).innerText;
expect(latestDate).toBe(expectedDate);
});
it("should show the name provided for the the telemetry producing object", () => {
const rowName = parent.querySelector(TABLE_BODY_FIRST_ROW_FIRST_DATA).innerText,
expectedName = mockObj.telemetry.name;
expect(rowName).toBe(expectedName);
});
it("should show the correct values for the datum based on domain and range hints", async () => {
const range = mockObj.telemetry.telemetry.values.find((val) => {
return val.hints && val.hints.range !== undefined;
}).key;
const domain = mockObj.telemetry.telemetry.values.find((val) => {
return val.hints && val.hints.domain !== undefined;
}).key;
const mostRecentTelemetry = getLatestTelemetry(mockTelemetry, { timeFormat });
const rangeValue = mostRecentTelemetry[range];
const domainValue = utcTimeFormat(mostRecentTelemetry[domain]);
await Vue.nextTick();
const actualDomainValue = parent.querySelector(TABLE_BODY_FIRST_ROW_SECOND_DATA).innerText;
const actualRangeValue = parent.querySelector(TABLE_BODY_FIRST_ROW_THIRD_DATA).innerText;
expect(actualRangeValue).toBe(rangeValue);
expect(actualDomainValue).toBe(domainValue);
});
});
});
describe("The LAD Table Set", () => {
const ladTableSetKey = 'LadTableSet';
let openmct,
ladPlugin,
parent,
child,
telemetryCount = 3,
timeFormat = 'utc',
mockTelemetry = getMockTelemetry({ count: telemetryCount, format: timeFormat }),
mockObj = getMockObjects({
objectKeyStrings: ['ladTable', 'ladTableSet', 'telemetry']
}),
bounds = {
start: 0,
end: 4
};
// add mock telemetry to lad table and lad table to lad table set (composition)
mockObj.ladTable.composition.push(mockObj.telemetry.identifier);
mockObj.ladTableSet.composition.push(mockObj.ladTable.identifier);
beforeEach((done) => {
const appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
parent = document.createElement('div');
child = document.createElement('div');
parent.appendChild(child);
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
ladPlugin = new LadPlugin();
openmct.install(ladPlugin);
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
openmct.time.bounds({ start: bounds.start, end: bounds.end });
openmct.on('start', done);
openmct.start(appHolder);
});
afterEach(() => {
resetApplicationState(openmct);
});
it("should provide a lad table set view only for lad table set objects", () => {
let applicableViews = openmct.objectViews.get(mockObj.ladTableSet),
ladTableSetView = applicableViews.find(
(viewProvider) => viewProvider.key === ladTableSetKey
);
expect(applicableViews.length).toEqual(1);
expect(ladTableSetView).toBeDefined();
});
describe('composition', () => {
let ladTableSetCompositionCollection;
beforeEach(() => {
ladTableSetCompositionCollection = openmct.composition.get(mockObj.ladTableSet);
ladTableSetCompositionCollection.load();
});
it("should accept lad table objects", () => {
expect(() => {
ladTableSetCompositionCollection.add(mockObj.ladTable);
}).not.toThrow();
});
it("should reject non lad table objects", () => {
expect(()=> {
ladTableSetCompositionCollection.add(mockObj.telemetry);
}).toThrow();
});
});
describe("table view", () => {
let applicableViews,
ladTableSetViewProvider,
ladTableSetView,
otherObj = getMockObjects({
objectKeyStrings: ['ladTable'],
overwrite: {
ladTable: {
name: "New LAD Table Object",
identifier: { namespace: "", key: "another-lad-object" }
}
}
});
// add another lad table (with telemetry object) object to the lad table set for multi row test
otherObj.ladTable.composition.push(mockObj.telemetry.identifier);
mockObj.ladTableSet.composition.push(otherObj.ladTable.identifier);
beforeEach(async () => {
let telemetryRequestResolve,
ladObjectResolve,
anotherLadObjectResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
}),
ladObjectPromise = new Promise((resolve) => {
ladObjectResolve = resolve;
}),
anotherLadObjectPromise = new Promise((resolve) => {
anotherLadObjectResolve = resolve;
})
openmct.telemetry.request.and.callFake(() => {
telemetryRequestResolve(mockTelemetry);
return telemetryRequestPromise;
});
openmct.objects.get.and.callFake((obj) => {
if(obj.key === 'lad-object') {
ladObjectResolve(mockObj.ladObject);
return ladObjectPromise;
} else if(obj.key === 'another-lad-object') {
anotherLadObjectResolve(otherObj.ladObject);
return anotherLadObjectPromise;
}
return Promise.resolve({});
});
openmct.time.bounds({ start: bounds.start, end: bounds.end });
applicableViews = openmct.objectViews.get(mockObj.ladTableSet);
ladTableSetViewProvider = applicableViews.find((viewProvider) => viewProvider.key === ladTableSetKey);
ladTableSetView = ladTableSetViewProvider.view(mockObj.ladTableSet, [mockObj.ladTableSet]);
ladTableSetView.show(child, true);
await Promise.all([telemetryRequestPromise, ladObjectPromise, anotherLadObjectPromise]);
await Vue.nextTick();
return;
});
it("should show one row per lad table object in the composition", () => {
const rowCount = parent.querySelectorAll(LAD_SET_TABLE_HEADERS).length;
expect(rowCount).toBe(mockObj.ladTableSet.composition.length);
pending();
});
});
});

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

@ -0,0 +1,230 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
getAllSearchParams,
setAllSearchParams
} from 'utils/openmctLocation';
const TIME_EVENTS = ['bounds', 'timeSystem', 'clock', 'clockOffsets'];
const SEARCH_MODE = 'tc.mode';
const SEARCH_TIME_SYSTEM = 'tc.timeSystem';
const SEARCH_START_BOUND = 'tc.startBound';
const SEARCH_END_BOUND = 'tc.endBound';
const SEARCH_START_DELTA = 'tc.startDelta';
const SEARCH_END_DELTA = 'tc.endDelta';
const MODE_FIXED = 'fixed';
export default class URLTimeSettingsSynchronizer {
constructor(openmct) {
this.openmct = openmct;
this.isUrlUpdateInProgress = false;
this.initialize = this.initialize.bind(this);
this.destroy = this.destroy.bind(this);
this.updateTimeSettings = this.updateTimeSettings.bind(this);
this.setUrlFromTimeApi = this.setUrlFromTimeApi.bind(this);
openmct.on('start', this.initialize);
openmct.on('destroy', this.destroy);
}
initialize() {
this.updateTimeSettings();
window.addEventListener('hashchange', this.updateTimeSettings);
TIME_EVENTS.forEach(event => {
this.openmct.time.on(event, this.setUrlFromTimeApi);
});
}
destroy() {
window.removeEventListener('hashchange', this.updateTimeSettings);
this.openmct.off('start', this.initialize);
this.openmct.off('destroy', this.destroy);
TIME_EVENTS.forEach(event => {
this.openmct.time.off(event, this.setUrlFromTimeApi);
});
}
updateTimeSettings() {
// Prevent from triggering self
if (!this.isUrlUpdateInProgress) {
let timeParameters = this.parseParametersFromUrl();
if (this.areTimeParametersValid(timeParameters)) {
this.setTimeApiFromUrl(timeParameters);
} else {
this.setUrlFromTimeApi();
}
} else {
this.isUrlUpdateInProgress = false;
}
}
parseParametersFromUrl() {
let searchParams = getAllSearchParams();
let mode = searchParams.get(SEARCH_MODE);
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
let startBound = parseInt(searchParams.get(SEARCH_START_BOUND), 10);
let endBound = parseInt(searchParams.get(SEARCH_END_BOUND), 10);
let bounds = {
start: startBound,
end: endBound
};
let startOffset = parseInt(searchParams.get(SEARCH_START_DELTA), 10);
let endOffset = parseInt(searchParams.get(SEARCH_END_DELTA), 10);
let clockOffsets = {
start: 0 - startOffset,
end: endOffset
};
return {
mode,
timeSystem,
bounds,
clockOffsets
};
}
setTimeApiFromUrl(timeParameters) {
if (timeParameters.mode === 'fixed') {
if (this.openmct.time.timeSystem().key !== timeParameters.timeSystem) {
this.openmct.time.timeSystem(
timeParameters.timeSystem,
timeParameters.bounds
);
} else if (!this.areStartAndEndEqual(this.openmct.time.bounds(), timeParameters.bounds)) {
this.openmct.time.bounds(timeParameters.bounds);
}
if (this.openmct.time.clock()) {
this.openmct.time.stopClock();
}
} else {
if (!this.openmct.time.clock() ||
this.openmct.time.clock().key !== timeParameters.mode) {
this.openmct.time.clock(timeParameters.mode, timeParameters.clockOffsets);
} else if (!this.areStartAndEndEqual(this.openmct.time.clockOffsets(), timeParameters.clockOffsets)) {
this.openmct.time.clockOffsets(timeParameters.clockOffsets);
}
if (!this.openmct.time.timeSystem() ||
this.openmct.time.timeSystem().key !== timeParameters.timeSystem) {
this.openmct.time.timeSystem(timeParameters.timeSystem);
}
}
}
setUrlFromTimeApi() {
let searchParams = getAllSearchParams();
let clock = this.openmct.time.clock();
let bounds = this.openmct.time.bounds();
let clockOffsets = this.openmct.time.clockOffsets();
if (clock === undefined) {
searchParams.set(SEARCH_MODE, MODE_FIXED);
searchParams.set(SEARCH_START_BOUND, bounds.start);
searchParams.set(SEARCH_END_BOUND, bounds.end);
searchParams.delete(SEARCH_START_DELTA);
searchParams.delete(SEARCH_END_DELTA);
} else {
searchParams.set(SEARCH_MODE, clock.key);
if (clockOffsets !== undefined) {
searchParams.set(SEARCH_START_DELTA, 0 - clockOffsets.start);
searchParams.set(SEARCH_END_DELTA, clockOffsets.end);
} else {
searchParams.delete(SEARCH_START_DELTA);
searchParams.delete(SEARCH_END_DELTA);
}
searchParams.delete(SEARCH_START_BOUND);
searchParams.delete(SEARCH_END_BOUND);
}
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
this.isUrlUpdateInProgress = true;
setAllSearchParams(searchParams);
}
areTimeParametersValid(timeParameters) {
let isValid = false;
if (this.isModeValid(timeParameters.mode) &&
this.isTimeSystemValid(timeParameters.timeSystem)) {
if (timeParameters.mode === 'fixed') {
isValid = this.areStartAndEndValid(timeParameters.bounds);
} else {
isValid = this.areStartAndEndValid(timeParameters.clockOffsets);
}
}
return isValid;
}
areStartAndEndValid(bounds) {
return bounds !== undefined &&
bounds.start !== undefined &&
bounds.start !== null &&
bounds.end !== undefined &&
bounds.start !== null &&
!isNaN(bounds.start) &&
!isNaN(bounds.end);
}
isTimeSystemValid(timeSystem) {
let isValid = timeSystem !== undefined;
if (isValid) {
let timeSystemObject = this.openmct.time.timeSystems.get(timeSystem);
isValid = timeSystemObject !== undefined;
}
return isValid;
}
isModeValid(mode) {
let isValid = false;
if (mode !== undefined &&
mode !== null) {
isValid = true;
}
if (isValid) {
if (mode.toLowerCase() === MODE_FIXED) {
isValid = true;
} else {
isValid = this.openmct.time.clocks.get(mode) !== undefined;
}
}
return isValid;
}
areStartAndEndEqual(firstBounds, secondBounds) {
return firstBounds.start === secondBounds.start &&
firstBounds.end === secondBounds.end;
}
}

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.
*
@ -19,15 +19,10 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import URLTimeSettingsSynchronizer from "./URLTimeSettingsSynchronizer.js";
define(
[],
function () {
function NotificationIndicator() {}
NotificationIndicator.template = 'notificationIndicatorTemplate';
return NotificationIndicator;
export default function () {
return function install(openmct) {
return new URLTimeSettingsSynchronizer(openmct);
}
);
}

View File

@ -0,0 +1,307 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
createOpenMct,
resetApplicationState
} from 'utils/testing';
describe("The URLTimeSettingsSynchronizer", () => {
let openmct;
let testClock;
beforeAll(() => resetApplicationState());
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(openmct.plugins.LocalTimeSystem());
testClock = jasmine.createSpyObj("testClock", ["start", "stop", "tick", "currentValue", "on", "off"]);
testClock.key = "test-clock";
testClock.currentValue.and.returnValue(0);
openmct.time.addClock(testClock);
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => resetApplicationState(openmct));
describe("realtime mode", () => {
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
openmct.time.clock('local', {start: -1000, end: 100});
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
});
it("when offsets are set via the time API, they are immediately reflected in the URL", () => {
//Test expected initial conditions
expect(window.location.hash.includes('tc.startDelta')).toBe(false);
expect(window.location.hash.includes('tc.endDelta')).toBe(false);
openmct.time.clock('local', {start: -1000, end: 100});
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
openmct.time.clockOffsets({start: -2000, end: 200});
expect(window.location.hash.includes('tc.startDelta=2000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=200')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
});
describe("when set in the url", () => {
it("will change from fixed to realtime mode when the mode changes", () => {
expectLocationToBeInFixedMode();
return switchToRealtimeMode().then(() => {
let clock = openmct.time.clock();
expect(clock).toBeDefined();
expect(clock.key).toBe('local');
});
});
it("the clock is correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clock', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.mode=local', 'tc.mode=test-clock');
window.location.hash = hash;
}).then(() => {
let clock = openmct.time.clock();
expect(clock).toBeDefined();
expect(clock.key).toBe('test-clock');
openmct.time.off('clock', resolveFunction);
});
});
});
it("the clock offsets are correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clockOffsets', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.startDelta=1000', 'tc.startDelta=2000');
hash = hash.replace('tc.endDelta=100', 'tc.endDelta=200');
window.location.hash = hash;
}).then(() => {
let clockOffsets = openmct.time.clockOffsets();
expect(clockOffsets).toBeDefined();
expect(clockOffsets.start).toBe(-2000);
expect(clockOffsets.end).toBe(200);
openmct.time.off('clockOffsets', resolveFunction);
});
});
});
it("the time system is correctly set in the API from the URL parameters", () => {
return switchToRealtimeMode().then(() => {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('timeSystem', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
window.location.hash = hash;
}).then(() => {
let timeSystem = openmct.time.timeSystem();
expect(timeSystem).toBeDefined();
expect(timeSystem.key).toBe('local');
openmct.time.off('timeSystem', resolveFunction);
});
});
});
});
});
describe("fixed timespan mode", () => {
beforeEach(() => {
openmct.time.stopClock();
openmct.time.timeSystem('utc', {start: 0, end: 1});
});
it("when bounds are set via the time API, they are immediately reflected in the URL", ()=>{
//Test expected initial conditions
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
openmct.time.bounds({start: 10, end: 20});
expect(window.location.hash.includes('tc.startBound=10')).toBe(true);
expect(window.location.hash.includes('tc.endBound=20')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.startBound=0')).toBe(false);
expect(window.location.hash.includes('tc.endBound=1')).toBe(false);
});
it("when time system is set via the time API, it is immediately reflected in the URL", ()=>{
//Test expected initial conditions
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(true);
openmct.time.timeSystem('local', {start: 20, end: 30});
expect(window.location.hash.includes('tc.timeSystem=local')).toBe(true);
//Test that expected initial conditions are no longer true
expect(window.location.hash.includes('tc.timeSystem=utc')).toBe(false);
});
describe("when set in the url", () => {
it("time system changes are reflected in the API", () => {
let resolveFunction;
return new Promise((resolve) => {
let timeSystem = openmct.time.timeSystem();
resolveFunction = resolve;
expect(timeSystem.key).toBe('utc');
window.location.hash = window.location.hash.replace('tc.timeSystem=utc', 'tc.timeSystem=local');
openmct.time.on('timeSystem', resolveFunction);
}).then(() => {
let timeSystem = openmct.time.timeSystem();
expect(timeSystem.key).toBe('local');
openmct.time.off('timeSystem', resolveFunction);
});
});
it("mode can be changed from realtime to fixed", () => {
return switchToRealtimeMode().then(() => {
expectLocationToBeInRealtimeMode();
expect(openmct.time.clock()).toBeDefined();
}).then(switchToFixedMode).then(() => {
let clock = openmct.time.clock();
expect(clock).not.toBeDefined();
});
});
it("bounds are correctly set in the API from the URL parameters", () => {
let resolveFunction;
expectLocationToBeInFixedMode();
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('bounds', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.startBound=0', 'tc.startBound=222')
.replace('tc.endBound=1', 'tc.endBound=333');
window.location.hash = hash;
}).then(() => {
let bounds = openmct.time.bounds();
expect(bounds).toBeDefined();
expect(bounds.start).toBe(222);
expect(bounds.end).toBe(333);
});
});
it("bounds are correctly set in the API from the URL parameters where only the end bound changes", () => {
let resolveFunction;
expectLocationToBeInFixedMode();
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('bounds', resolveFunction);
let hash = window.location.hash;
hash = hash.replace('tc.endBound=1', 'tc.endBound=333');
window.location.hash = hash;
}).then(() => {
let bounds = openmct.time.bounds();
expect(bounds).toBeDefined();
expect(bounds.start).toBe(0);
expect(bounds.end).toBe(333);
});
});
});
});
function setRealtimeLocationParameters() {
let hash = window.location.hash.toString()
.replace('tc.mode=fixed', 'tc.mode=local')
.replace('tc.startBound=0', 'tc.startDelta=1000')
.replace('tc.endBound=1', 'tc.endDelta=100');
window.location.hash = hash;
}
function setFixedLocationParameters() {
let hash = window.location.hash.toString()
.replace('tc.mode=local', 'tc.mode=fixed')
.replace('tc.timeSystem=utc', 'tc.timeSystem=local')
.replace('tc.startDelta=1000', 'tc.startBound=50')
.replace('tc.endDelta=100', 'tc.endBound=60');
window.location.hash = hash;
}
function switchToRealtimeMode() {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
openmct.time.on('clock', resolveFunction);
setRealtimeLocationParameters();
}).then(() => {
openmct.time.off('clock', resolveFunction);
});
}
function switchToFixedMode() {
let resolveFunction;
return new Promise((resolve) => {
resolveFunction = resolve;
//The 'hashchange' event appears to be asynchronous, so we need to wait until a clock change has been
//detected in the API.
openmct.time.on('clock', resolveFunction);
setFixedLocationParameters();
}).then(() => {
openmct.time.off('clock', resolveFunction);
});
}
function expectLocationToBeInRealtimeMode() {
expect(window.location.hash.includes('tc.mode=local')).toBe(true);
expect(window.location.hash.includes('tc.startDelta=1000')).toBe(true);
expect(window.location.hash.includes('tc.endDelta=100')).toBe(true);
expect(window.location.hash.includes('tc.mode=fixed')).toBe(false);
}
function expectLocationToBeInFixedMode() {
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
expect(window.location.hash.includes('tc.startBound=0')).toBe(true);
expect(window.location.hash.includes('tc.endBound=1')).toBe(true);
expect(window.location.hash.includes('tc.mode=local')).toBe(false);
}
});

View File

@ -0,0 +1,281 @@
/*****************************************************************************
* 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 EventEmitter from 'EventEmitter';
import uuid from 'uuid';
import TelemetryCriterion from "./criterion/TelemetryCriterion";
import { evaluateResults } from './utils/evaluator';
import { getLatestTimestamp } from './utils/time';
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
import {TRIGGER_CONJUNCTION, TRIGGER_LABEL} from "./utils/constants";
/*
* conditionConfiguration = {
* id: uuid,
* trigger: 'any'/'all'/'not','xor',
* criteria: [
* {
* telemetry: '',
* operation: '',
* input: [],
* metadata: ''
* }
* ]
* }
*/
export default class Condition extends EventEmitter {
/**
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
* @constructor
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
* @param openmct
* @param conditionManager
*/
constructor(conditionConfiguration, openmct, conditionManager) {
super();
this.openmct = openmct;
this.conditionManager = conditionManager;
this.id = conditionConfiguration.id;
this.criteria = [];
this.result = undefined;
this.timeSystems = this.openmct.time.getAllTimeSystems();
if (conditionConfiguration.configuration.criteria) {
this.createCriteria(conditionConfiguration.configuration.criteria);
}
this.trigger = conditionConfiguration.configuration.trigger;
this.description = '';
}
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);
}
}
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;
}
}
generateCriterion(criterionConfiguration) {
return {
id: criterionConfiguration.id || uuid(),
telemetry: criterionConfiguration.telemetry || '',
telemetryObjects: this.conditionManager.telemetryObjects,
operation: criterionConfiguration.operation || '',
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
metadata: criterionConfiguration.metadata || ''
};
}
createCriteria(criterionConfigurations) {
criterionConfigurations.forEach((criterionConfiguration) => {
this.addCriterion(criterionConfiguration);
});
this.updateDescription();
}
updateCriteria(criterionConfigurations) {
this.destroyCriteria();
this.createCriteria(criterionConfigurations);
}
updateTelemetryObjects() {
this.criteria.forEach((criterion) => {
criterion.updateTelemetryObjects(this.conditionManager.telemetryObjects);
});
this.updateDescription();
}
/**
* adds criterion to the condition.
*/
addCriterion(criterionConfiguration) {
let criterion;
let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
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));
if (!this.criteria) {
this.criteria = [];
}
this.criteria.push(criterion);
return criterionConfigurationWithId.id;
}
findCriterion(id) {
let criterion;
for (let i=0, ii=this.criteria.length; i < ii; i ++) {
if (this.criteria[i].id === id) {
criterion = {
item: this.criteria[i],
index: i
}
}
}
return criterion;
}
updateCriterion(id, criterionConfiguration) {
let found = this.findCriterion(id);
if (found) {
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
let criterion = found.item;
criterion.unsubscribe();
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
this.criteria.splice(found.index, 1, newCriterion);
this.updateDescription();
}
}
destroyCriterion(id) {
let found = this.findCriterion(id);
if (found) {
let criterion = found.item;
criterion.off('criterionUpdated', (obj) => {
this.handleCriterionUpdated(obj);
});
criterion.destroy();
this.criteria.splice(found.index, 1);
this.updateDescription();
return true;
}
return false;
}
handleCriterionUpdated(criterion) {
let found = this.findCriterion(criterion.id);
if (found) {
this.criteria[found.index] = criterion.data;
this.updateDescription();
}
}
updateDescription() {
const triggerDescription = this.getTriggerDescription();
let description = '';
this.criteria.forEach((criterion, index) => {
if (!index) {
description = `Match if ${triggerDescription.prefix}`;
}
description = `${description} ${criterion.getDescription()} ${(index < this.criteria.length - 1) ? triggerDescription.conjunction : ''}`;
});
this.description = description;
this.conditionManager.updateConditionDescription(this);
}
getTriggerDescription() {
return {
conjunction: TRIGGER_CONJUNCTION[this.trigger],
prefix: `${TRIGGER_LABEL[this.trigger]}: `
};
}
requestLADConditionResult() {
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) }
)
};
});
}
getCriteria() {
return this.criteria;
}
destroyCriteria() {
let success = true;
//looping through the array backwards since destroyCriterion modifies the criteria array
for (let i=this.criteria.length-1; i >= 0; i--) {
success = success && this.destroyCriterion(this.criteria[i].id);
}
return success;
}
destroy() {
this.destroyCriteria();
}
}

View File

@ -0,0 +1,382 @@
/*****************************************************************************
* 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 Condition from "./Condition";
import { getLatestTimestamp } from './utils/time';
import uuid from "uuid";
import EventEmitter from 'EventEmitter';
export default class ConditionManager extends EventEmitter {
constructor(conditionSetDomainObject, openmct) {
super();
this.openmct = openmct;
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.updateConditionTelemetryObjects();
}
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.removeConditionTelemetryObjects();
}
initialize() {
this.conditions = [];
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.initCondition(conditionConfiguration, index);
});
}
}
updateConditionTelemetryObjects() {
this.conditions.forEach((condition) => condition.updateTelemetryObjects());
}
removeConditionTelemetryObjects() {
let conditionsChanged = false;
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, conditionIndex) => {
let conditionChanged = false;
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 = '';
conditionChanged = true;
}
}
});
if (conditionChanged) {
this.updateCondition(conditionConfiguration, conditionIndex);
conditionsChanged = true;
}
});
if (conditionsChanged) {
this.persistConditions();
}
}
updateCondition(conditionConfiguration, index) {
let condition = this.conditions[index];
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
condition.update(conditionConfiguration);
this.persistConditions();
}
updateConditionDescription(condition) {
const found = this.conditionSetDomainObject.configuration.conditionCollection.find(conditionConfiguration => (conditionConfiguration.id === condition.id));
found.summary = condition.description;
this.persistConditions();
}
initCondition(conditionConfiguration, index) {
let condition = new Condition(conditionConfiguration, this.openmct, this);
if (index !== undefined) {
this.conditions.splice(index + 1, 0, condition);
} else {
this.conditions.unshift(condition);
}
}
createCondition(conditionConfiguration) {
let conditionObj;
if (conditionConfiguration) {
conditionObj = {
...conditionConfiguration,
id: uuid(),
configuration: {
...conditionConfiguration.configuration,
name: `Copy of ${conditionConfiguration.configuration.name}`
}
};
} else {
conditionObj = {
id: uuid(),
configuration: {
name: 'Unnamed Condition',
output: 'false',
trigger: 'all',
criteria: [{
id: uuid(),
telemetry: '',
operation: '',
input: [],
metadata: ''
}]
},
summary: ''
};
}
return conditionObj;
}
addCondition() {
this.createAndSaveCondition();
}
cloneCondition(conditionConfiguration, index) {
let clonedConfig = JSON.parse(JSON.stringify(conditionConfiguration));
clonedConfig.configuration.criteria.forEach((criterion) => criterion.id = uuid());
this.createAndSaveCondition(index, clonedConfig);
}
createAndSaveCondition(index, conditionConfiguration) {
const newCondition = this.createCondition(conditionConfiguration);
if (index !== undefined) {
this.conditionSetDomainObject.configuration.conditionCollection.splice(index + 1, 0, newCondition);
} else {
this.conditionSetDomainObject.configuration.conditionCollection.unshift(newCondition);
}
this.initCondition(newCondition, index);
this.persistConditions();
}
removeCondition(index) {
let condition = this.conditions[index];
condition.destroy();
this.conditions.splice(index, 1);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
this.persistConditions();
}
findConditionById(id) {
return this.conditions.find(condition => condition.id === id);
}
reorderConditions(reorderPlan) {
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
let newCollection = [];
reorderPlan.forEach((reorderEvent) => {
let item = oldConditions[reorderEvent.oldIndex];
newCollection.push(item);
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
});
this.persistConditions();
}
getCurrentCondition() {
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length-1];
for (let i = 0; i < conditionCollection.length - 1; i++) {
const condition = this.findConditionById(conditionCollection[i].id)
if (condition.result) {
//first condition to be true wins
currentCondition = conditionCollection[i];
break;
}
}
return currentCondition;
}
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.conditions.length) {
return Promise.resolve([]);
}
return this.compositionLoad.then(() => {
let latestTimestamp;
let conditionResults = {};
const conditionRequests = this.conditions
.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];
});
});
}
isTelemetryUsed(endpoint) {
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
for(const condition of this.conditions) {
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.conditions.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() {
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.conditions.forEach((condition) => {
condition.destroy();
})
}
}

View File

@ -0,0 +1,133 @@
/*****************************************************************************
* 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';
describe('ConditionManager', () => {
let conditionMgr;
let mockListener;
let openmct = {};
let mockCondition = {
isDefault: true,
id: '1234-5678',
configuration: {
criteria: []
}
};
let conditionSetDomainObject = {
identifier: {
namespace: "",
key: "600a7372-8d48-4dc4-98b6-548611b1ff7e"
},
type: "conditionSet",
location: "mine",
configuration: {
conditionCollection: [
mockCondition
]
}
};
let mockComposition;
let loader;
let mockTimeSystems;
function mockAngularComponents() {
let mockInjector = jasmine.createSpyObj('$injector', ['get']);
let mockInstantiate = jasmine.createSpy('mockInstantiate');
mockInstantiate.and.returnValue(mockInstantiate);
let mockDomainObject = {
useCapability: function () {
return mockCondition;
}
};
mockInstantiate.and.callFake(function () {
return mockDomainObject;
});
mockInjector.get.and.callFake(function (service) {
return {
'instantiate': mockInstantiate
}[service];
});
openmct.$injector = mockInjector;
}
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(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);
conditionMgr.on('conditionSetResultUpdated', mockListener);
conditionMgr.on('telemetryReceived', mockListener);
});
it('creates a conditionCollection with a default condition', function () {
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionId = conditionMgr.conditions[0].id;
expect(defaultConditionId).toEqual(mockCondition.id);
});
});

View File

@ -0,0 +1,33 @@
/*****************************************************************************
* 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 function ConditionSetCompositionPolicy(openmct) {
return {
allow: function (parent, child) {
if (parent.type === 'conditionSet' && !openmct.telemetry.isTelemetryObject(child)) {
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,85 @@
/*****************************************************************************
* 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 ConditionSetCompositionPolicy from './ConditionSetCompositionPolicy';
describe('ConditionSetCompositionPolicy', () => {
let policy;
let testTelemetryObject;
let openmct = {};
let parentDomainObject;
beforeAll(function () {
testTelemetryObject = {
identifier:{ namespace: "", key: "test-object"},
type: "test-object",
name: "Test Object",
telemetry: {
values: [{
key: "some-key",
name: "Some attribute",
hints: {
domain: 1
}
}, {
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
}]
}
};
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
openmct.objects.get.and.returnValue(testTelemetryObject);
openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']);
policy = new ConditionSetCompositionPolicy(openmct);
parentDomainObject = {};
});
it('returns true for object types that are not conditionSets', function () {
parentDomainObject.type = 'random';
openmct.telemetry.isTelemetryObject.and.returnValue(false);
expect(policy.allow(parentDomainObject, {})).toBe(true);
});
it('returns false for object types that are not telemetry objects when parent is a conditionSet', function () {
parentDomainObject.type = 'conditionSet';
openmct.telemetry.isTelemetryObject.and.returnValue(false);
expect(policy.allow(parentDomainObject, {})).toBe(false);
});
it('returns true for object types that are telemetry objects when parent is a conditionSet', function () {
parentDomainObject.type = 'conditionSet';
openmct.telemetry.isTelemetryObject.and.returnValue(true);
expect(policy.allow(parentDomainObject, testTelemetryObject)).toBe(true);
});
it('returns true for object types that are telemetry objects when parent is not a conditionSet', function () {
parentDomainObject.type = 'random';
openmct.telemetry.isTelemetryObject.and.returnValue(true);
expect(policy.allow(parentDomainObject, testTelemetryObject)).toBe(true);
});
});

View File

@ -0,0 +1,77 @@
/*****************************************************************************
* 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;
}
supportsMetadata(domainObject) {
return domainObject.type === 'conditionSet';
}
getDomains(domainObject) {
return this.openmct.time.getAllTimeSystems().map(function (ts, i) {
return {
key: ts.key,
name: ts.name,
format: ts.timeFormat,
hints: {
domain: i
}
};
});
}
getMetadata(domainObject) {
const enumerations = domainObject.configuration.conditionCollection
.map((condition, index) => {
return {
string: condition.configuration.output,
value: index
};
});
return {
values: this.getDomains().concat([
{
key: "state",
source: "output",
name: "State",
format: "enum",
enumerations: enumerations,
hints: {
range: 1
}
},
{
key: "output",
name: "Value",
format: "string",
hints: {
range: 2
}
}
])
};
}
}

View File

@ -0,0 +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) {
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 = this.getConditionManager(domainObject);
conditionManager.on('conditionSetResultUpdated', callback);
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

@ -0,0 +1,33 @@
/*****************************************************************************
* 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.
*****************************************************************************/
function ConditionSetViewPolicy() {
}
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
if (domainObject.getModel().type === 'conditionSet') {
return view.key === 'conditionSet.view';
}
return true;
}
export default ConditionSetViewPolicy;

View File

@ -0,0 +1,84 @@
/*****************************************************************************
* 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 ConditionSet from './components/ConditionSet.vue';
import Vue from 'vue';
const DEFAULT_VIEW_PRIORITY = 100;
export default class ConditionSetViewProvider {
constructor(openmct) {
this.openmct = openmct;
this.name = 'Conditions View';
this.key = 'conditionSet.view';
this.cssClass = 'icon-conditional';
}
canView(domainObject) {
return domainObject.type === 'conditionSet';
}
canEdit(domainObject) {
return domainObject.type === 'conditionSet';
}
view(domainObject, objectPath) {
let component;
const openmct = this.openmct;
return {
show: (container, isEditing) => {
component = new Vue({
el: container,
components: {
ConditionSet
},
provide: {
openmct,
domainObject,
objectPath
},
data() {
return {
isEditing
}
},
template: '<condition-set :isEditing="isEditing"></condition-set>'
});
},
onEditModeChange: (isEditing) => {
component.isEditing = isEditing;
},
destroy: () => {
component.$destroy();
component = undefined;
}
};
}
priority(domainObject) {
if (domainObject.type === 'conditionSet') {
return Number.MAX_VALUE;
} else {
return DEFAULT_VIEW_PRIORITY;
}
}
}

View File

@ -0,0 +1,181 @@
/*****************************************************************************
* 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 Condition from "./Condition";
import {TRIGGER} from "./utils/constants";
import TelemetryCriterion from "./criterion/TelemetryCriterion";
let openmct = {},
testConditionDefinition,
testTelemetryObject,
conditionObj,
conditionManager,
mockTelemetryReceived,
mockTimeSystems;
describe("The condition", function () {
beforeEach (() => {
conditionManager = jasmine.createSpyObj('conditionManager',
['on', 'updateConditionDescription']
);
mockTelemetryReceived = jasmine.createSpy('listener');
conditionManager.on('telemetryReceived', mockTelemetryReceived);
conditionManager.updateConditionDescription.and.returnValue(function () {});
testTelemetryObject = {
identifier:{ namespace: "", key: "test-object"},
type: "test-object",
name: "Test Object",
telemetry: {
valueMetadatas: [{
key: "some-key",
name: "Some attribute",
hints: {
range: 2
}
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 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);
})); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']);
openmct.telemetry.isTelemetryObject.and.returnValue(true);
openmct.telemetry.subscribe.and.returnValue(function () {});
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
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: ['0'],
metadata: 'value',
telemetry: testTelemetryObject.identifier
}
]
}
};
conditionObj = new Condition(
testConditionDefinition,
openmct,
conditionManager
);
});
it("generates criteria with the correct properties", function () {
const testCriterion = testConditionDefinition.configuration.criteria[0];
let criterion = conditionObj.generateCriterion(testCriterion);
expect(criterion.id).toBeDefined();
expect(criterion.operation).toEqual(testCriterion.operation);
expect(criterion.input).toEqual(testCriterion.input);
expect(criterion.metadata).toEqual(testCriterion.metadata);
expect(criterion.telemetry).toEqual(testCriterion.telemetry);
});
it("initializes with an id", function () {
expect(conditionObj.id).toBeDefined();
});
it("initializes with criteria from the condition definition", function () {
expect(conditionObj.criteria.length).toEqual(1);
let criterion = conditionObj.criteria[0];
expect(criterion instanceof TelemetryCriterion).toBeTrue();
expect(criterion.operator).toEqual(testConditionDefinition.configuration.criteria[0].operator);
expect(criterion.input).toEqual(testConditionDefinition.configuration.criteria[0].input);
expect(criterion.metadata).toEqual(testConditionDefinition.configuration.criteria[0].metadata);
});
it("initializes with the trigger from the condition definition", function () {
expect(conditionObj.trigger).toEqual(testConditionDefinition.configuration.trigger);
});
it("destroys all criteria for a condition", function () {
const result = conditionObj.destroyCriteria();
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

@ -0,0 +1,162 @@
/*****************************************************************************
* 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 EventEmitter from 'EventEmitter';
export default class StyleRuleManager extends EventEmitter {
constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
super();
this.openmct = openmct;
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(styleConfiguration) {
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
this.staticStyle = styleConfiguration.staticStyle;
this.selectedConditionId = styleConfiguration.selectedConditionId;
this.defaultConditionId = styleConfiguration.defaultConditionId;
this.updateConditionStylesMap(styleConfiguration.styles || []);
}
subscribeToConditionSet() {
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
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));
});
}
updateObjectStyleConfig(styleConfiguration) {
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
this.initialize(styleConfiguration || {});
this.destroy();
} else {
let isNewConditionSet = !this.conditionSetIdentifier ||
!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();
}
}
}
}
updateConditionStylesMap(conditionStyles) {
let conditionStyleMap = {};
conditionStyles.forEach((conditionStyle) => {
if (conditionStyle.conditionId) {
conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
} else {
conditionStyleMap.static = conditionStyle.style;
}
});
this.conditionalStyleMap = conditionStyleMap;
}
handleConditionSetResultUpdated(resultData) {
let foundStyle = this.conditionalStyleMap[resultData.conditionId];
if (foundStyle) {
if (foundStyle !== this.currentStyle) {
this.currentStyle = foundStyle;
}
this.updateDomainObjectStyle();
} else {
this.applyStaticStyle();
}
}
updateDomainObjectStyle() {
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.applyStaticStyle();
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
delete this.stopProvidingTelemetry;
this.conditionSetIdentifier = undefined;
}
}

View File

@ -0,0 +1,377 @@
/*****************************************************************************
* 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-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"
>
<!-- 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"
@dragend="dragEnd"
></span>
<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>
<span class="c-condition__name">{{ condition.configuration.name }}</span>
<span class="c-condition__summary">
<template v-if="!condition.isDefault && !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>
</div>
<div v-if="expanded"
class="c-condition__definition c-cdef"
>
<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"
>
<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 v-else
class="c-condition c-condition--browse"
:class="{'is-current': condition.id === currentConditionId}"
>
<!-- Browse view -->
<div class="c-condition__header">
<span class="c-condition__name">
{{ condition.configuration.name }}
</span>
<span class="c-condition__output">
Output: {{ condition.configuration.output }}
</span>
</div>
<div class="c-condition__summary">
<condition-description :show-label="false"
:condition="condition"
/>
</div>
</div>
</div>
</template>
<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,
ConditionDescription
},
props: {
currentConditionId: {
type: String,
default: ''
},
condition: {
type: Object,
required: true
},
conditionIndex: {
type: Number,
required: true
},
isEditing: {
type: Boolean,
required: true
},
telemetry: {
type: Array,
required: true,
default: () => []
},
isDragging: {
type: Boolean,
default: false
},
moveIndex: {
type: Number,
default: 0
}
},
data() {
return {
currentCriteria: this.currentCriteria,
expanded: true,
trigger: 'all',
selectedOutputSelection: '',
outputOptions: ['false', 'true', 'string'],
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: `when ${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.setOutputSelection();
},
methods: {
setOutputSelection() {
let conditionOutput = this.condition.configuration.output;
if (conditionOutput) {
if (conditionOutput !== 'false' && conditionOutput !== 'true') {
this.selectedOutputSelection = 'string';
} else {
this.selectedOutputSelection = conditionOutput;
}
}
},
setOutputValue() {
if (this.selectedOutputSelection === 'string') {
this.condition.configuration.output = '';
} else {
this.condition.configuration.output = this.selectedOutputSelection;
}
this.persist();
},
addCriteria() {
const criteriaObject = {
id: uuid(),
telemetry: '',
operation: '',
input: '',
metadata: ''
};
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-condition-h'), 0, 0);
this.$emit('setMoveIndex', this.conditionIndex);
},
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.conditionIndex);
},
cloneCondition(ev) {
this.$emit('cloneCondition', {
condition: this.condition,
index: this.conditionIndex
});
},
removeCriterion(index) {
this.condition.configuration.criteria.splice(index, 1);
this.persist();
},
cloneCriterion(index) {
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.$emit('updateCondition', {
condition: this.condition,
index: this.conditionIndex
});
},
initCap(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}
</script>

View File

@ -0,0 +1,233 @@
/*****************************************************************************
* 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>
<section id="conditionCollection"
:class="{ 'is-expanded': expanded }"
>
<div class="c-cs__header c-section__header">
<span
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__content"
>
<div v-show="isEditing"
class="hint"
:class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
>
<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>
<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>
</template>
<script>
import Condition from './Condition.vue';
import ConditionManager from '../ConditionManager';
export default {
inject: ['openmct', 'domainObject'],
components: {
Condition
},
props: {
isEditing: Boolean,
testData: {
type: Object,
required: true,
default: () => {
return {
applied: false,
conditionTestInputs: []
}
}
}
},
data() {
return {
expanded: true,
conditionCollection: [],
conditionResults: {},
conditions: [],
telemetryObjs: [],
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 (this.stopObservingForChanges) {
this.stopObservingForChanges();
}
},
mounted() {
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addTelemetryObject);
this.composition.on('remove', this.removeTelemetryObject);
this.composition.load();
this.conditionCollection = this.domainObject.configuration.conditionCollection;
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, 'configuration.conditionCollection', (newConditionCollection) => {
//this forces children to re-render
this.conditionCollection = newConditionCollection.map(condition => condition);
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(targetIndex) {
const oldIndexArr = Object.keys(this.conditionCollection);
const move = function (arr, old_index, new_index) {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length;
while ((k--) + 1) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr;
}
const newIndexArr = move(oldIndexArr, this.moveIndex, targetIndex);
const reorderPlan = [];
for (let i = 0; i < oldIndexArr.length; i++) {
reorderPlan.push({oldIndex: Number(newIndexArr[i]), newIndex: i});
}
this.reorder(reorderPlan);
},
dragComplete() {
this.isDragging = false;
},
addTelemetryObject(domainObject) {
this.telemetryObjs.push(domainObject);
this.$emit('telemetryUpdated', this.telemetryObjs);
},
removeTelemetryObject(identifier) {
let index = this.telemetryObjs.findIndex(obj => {
let objId = this.openmct.objects.makeKeyString(obj.identifier);
let id = this.openmct.objects.makeKeyString(identifier);
return objId === id;
});
if (index > -1) {
this.telemetryObjs.splice(index, 1);
}
},
addCondition() {
this.conditionManager.addCondition();
},
updateCondition(data) {
this.conditionManager.updateCondition(data.condition, data.index);
},
removeCondition(index) {
this.conditionManager.removeCondition(index);
},
reorder(reorderPlan) {
this.conditionManager.reorderConditions(reorderPlan);
},
cloneCondition(data) {
this.conditionManager.cloneCondition(data.condition, data.index);
},
updateTestData() {
this.conditionManager.updateTestData(this.testData);
}
}
}
</script>

View File

@ -0,0 +1,68 @@
/*****************************************************************************
* 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 class="c-style__condition-desc__text"
v-if="!condition.isDefault"
>
{{ description }}
</span>
<span class="c-style__condition-desc__text"
v-else
>
Match if no other condition is matched
</span>
</div>
</template>
<script>
export default {
name: 'ConditionDescription',
inject: [
'openmct'
],
props: {
showLabel: {
type: Boolean,
default: false
},
condition: {
type: Object,
default() {
return undefined;
}
}
},
computed: {
description() {
return this.condition ? this.condition.summary : '';
}
}
}
</script>

View File

@ -0,0 +1,80 @@
/*****************************************************************************
* 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) {
//It is sufficient to check for absence of telemetry here since the condition manager ensures that telemetry for a criterion is set if it exists
const isInvalidTelemetry = !criterion.telemetry && (criterion.telemetry !== 'all' && criterion.telemetry !== 'any');
if (isInvalidTelemetry) {
this.conditionErrors.push({
message: ERROR.TELEMETRY_NOT_FOUND,
additionalInfo: ''
});
}
}
}
}
</script>

View File

@ -0,0 +1,100 @@
/*****************************************************************************
* 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-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 }}
</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>
<script>
import TestData from './TestData.vue';
import ConditionCollection from './ConditionCollection.vue';
export default {
inject: ["openmct", "domainObject"],
components: {
TestData,
ConditionCollection
},
props: {
isEditing: Boolean
},
data() {
return {
currentConditionOutput: '',
defaultConditionOutput: '',
telemetryObjs: [],
testData: {}
}
},
mounted() {
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.testData = {
applied: false,
conditionTestInputs: this.domainObject.configuration.conditionTestData || []
};
},
methods: {
updateCurrentOutput(currentConditionResult) {
this.currentConditionOutput = currentConditionResult.output;
},
updateDefaultOutput(output) {
this.currentConditionOutput = output;
},
updateTelemetry(telemetryObjs) {
this.telemetryObjs = telemetryObjs;
},
updateTestData(testData) {
this.testData = testData;
}
}
};
</script>

View File

@ -0,0 +1,295 @@
/*****************************************************************************
* 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-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"
>
{{ telemetryOption.name }}
</option>
</select>
</span>
<span v-if="criterion.telemetry"
class="c-cdef__control"
>
<select ref="metadataSelect"
v-model="criterion.metadata"
@change="updateOperations"
>
<option value="">- Select Field -</option>
<option v-for="option in telemetryMetadataOptions"
:key="option.key"
:value="option.key"
>
{{ option.name }}
</option>
</select>
</span>
<span v-if="criterion.telemetry && criterion.metadata"
class="c-cdef__control"
>
<select v-model="criterion.operation"
@change="updateInputVisibilityAndValues"
>
<option value="">- Select Comparison -</option>
<option v-for="option in filteredOps"
:key="option.name"
:value="option.name"
>
{{ option.text }}
</option>
</select>
<template v-if="!enumerations.length">
<span v-for="(item, inputIndex) in inputCount"
:key="inputIndex"
class="c-cdef__control__inputs"
>
<input v-model="criterion.input[inputIndex]"
class="c-cdef__control__input"
:type="setInputType"
@change="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>
</div>
</template>
<script>
import { OPERATIONS } from '../utils/operations';
import { INPUT_TYPES } from '../utils/operations';
import {TRIGGER_CONJUNCTION} from "../utils/constants";
export default {
inject: ['openmct'],
props: {
criterion: {
type: Object,
required: true
},
telemetry: {
type: Array,
required: true,
default: () => []
},
index: {
type: Number,
required: true
},
trigger: {
type: String,
required: true
}
},
data() {
return {
telemetryMetadataOptions: [],
operations: OPERATIONS,
inputCount: 0,
rowLabel: '',
operationFormat: '',
enumerations: [],
inputTypes: INPUT_TYPES
}
},
computed: {
setRowLabel: function () {
let operator = TRIGGER_CONJUNCTION[this.trigger];
return (this.index !== 0 ? operator : '') + ' when';
},
filteredOps: function () {
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: {
checkTelemetry() {
if(this.criterion.telemetry) {
const isAnyAllTelemetry = this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all';
const telemetryForCriterionExists = this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier));
if (!isAnyAllTelemetry &&
!telemetryForCriterionExists) {
//telemetry being used was removed. So reset this criterion.
this.criterion.telemetry = '';
this.criterion.metadata = '';
this.criterion.input = [];
this.criterion.operation = '';
this.persist();
} else {
this.updateMetadataOptions();
}
}
},
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) {
let telemetryObjects = this.telemetry;
if (this.criterion.telemetry !== 'all' && this.criterion.telemetry !== 'any') {
const found = this.telemetry.find(telemetryObj => (this.openmct.objects.areIdsEqual(telemetryObj.identifier, this.criterion.telemetry)));
telemetryObjects = found ? [found] : [];
}
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);
}
});
},
updateOperations(ev) {
this.updateOperationFormat();
if (ev) {
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 = [];
}
},
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);
}
}
};
</script>

View File

@ -0,0 +1,236 @@
/*****************************************************************************
* 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>
<section v-show="isEditing"
id="test-data"
:class="{ 'is-expanded': expanded }"
>
<div class="c-cs__header c-section__header">
<span
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__content"
>
<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>
<script>
export default {
inject: ['openmct'],
props: {
isEditing: Boolean,
telemetry: {
type: Array,
required: true,
default: () => []
},
testData: {
type: Object,
required: true,
default: () => {
return {
applied: false,
conditionTestInputs: []
}
}
}
},
data() {
return {
expanded: 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() {
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
});
}
}
}
</script>

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

@ -0,0 +1,458 @@
/*****************************************************************************
* 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">
<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
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 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: {
ConditionDescription,
ConditionError,
StyleEditor
},
inject: [
'openmct',
'selection'
],
data() {
return {
conditionalStyles: [],
staticStyle: undefined,
conditionSetDomainObject: undefined,
isEditing: this.openmct.editor.isEditing(),
conditions: undefined,
conditionsLoaded: false,
navigateToPath: '',
selectedConditionId: ''
}
},
destroyed() {
this.removeListeners();
},
mounted() {
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: {
isItemType(type, item) {
return item && (item.type === type);
},
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);
}
});
},
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 {
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;
},
getCondition(id) {
return this.conditions ? this.conditions[id] : {};
},
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.persist(this.getDomainObjectConditionalStyle());
},
persist(style) {
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
}
}
}
</script>

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 (objectStyles && Object.keys(objectStyles).length <= 0) {
objectStyles = undefined;
}
this.persist(domainObject, objectStyles);
});
}
if (this.items.length) {
this.items.forEach((item) => {
const itemId = item.id;
this.removeConditionalStyles(domainObjectStyles, itemId);
if (domainObjectStyles[itemId] && Object.keys(domainObjectStyles[itemId]).length <= 0) {
delete domainObjectStyles[itemId];
}
});
} else {
this.removeConditionalStyles(domainObjectStyles);
}
if (domainObjectStyles && Object.keys(domainObjectStyles).length <= 0) {
domainObjectStyles = undefined;
}
this.persist(this.domainObject, domainObjectStyles);
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();
}
},
removeConditionalStyles(domainObjectStyles, itemId) {
if (itemId && domainObjectStyles[itemId]) {
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
delete domainObjectStyles[itemId].conditionSetIdentifier;
domainObjectStyles[itemId].selectedConditionId = undefined;
domainObjectStyles[itemId].defaultConditionId = undefined;
domainObjectStyles[itemId].styles = undefined;
delete domainObjectStyles[itemId].styles;
} else {
domainObjectStyles.conditionSetIdentifier = undefined;
delete domainObjectStyles.conditionSetIdentifier;
domainObjectStyles.selectedConditionId = undefined;
domainObjectStyles.defaultConditionId = undefined;
domainObjectStyles.styles = undefined;
delete domainObjectStyles.styles;
}
},
updateStaticStyle(staticStyle, property) {
//update the static style for each of the layoutItems as well as each sub object item
this.staticStyle = staticStyle;
this.removeConditionSet();
this.getAndPersistStyles(property);
},
updateConditionalStyle(conditionStyle, property) {
let foundStyle = this.findStyleByConditionId(conditionStyle.conditionId);
if (foundStyle) {
foundStyle.style = conditionStyle.style;
this.selectedConditionId = foundStyle.conditionId;
this.getAndPersistStyles(property);
}
},
getAndPersistStyles(property, defaultConditionId) {
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items, defaultConditionId));
if (this.domainObjectsById) {
const domainObjects = Object.values(this.domainObjectsById);
domainObjects.forEach(domainObject => {
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property, null, defaultConditionId));
});
}
if (!this.items.length && !this.domainObjectsById) {
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, null, defaultConditionId));
}
this.isStaticAndConditionalStyles = false;
if (property) {
let foundIndex = this.mixedStyles.indexOf(property);
if (foundIndex > -1) {
this.mixedStyles.splice(foundIndex, 1);
}
}
},
getDomainObjectStyle(domainObject, property, items, defaultConditionId) {
let objectStyle = {
styles: this.conditionalStyles,
staticStyle: this.staticStyle,
selectedConditionId: this.selectedConditionId
};
if (defaultConditionId) {
objectStyle.defaultConditionId = defaultConditionId;
}
if (this.conditionSetDomainObject) {
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
}
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
if (items) {
items.forEach(item => {
let itemStaticStyle = {};
let itemConditionalStyle = { styles: []};
if (!this.conditionSetDomainObject) {
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
itemStaticStyle = Object.assign({}, domainObjectStyles[item.id].staticStyle.style);
}
if (item.applicableStyles[property] !== undefined) {
itemStaticStyle[property] = this.staticStyle.style[property];
}
if (Object.keys(itemStaticStyle).length <= 0) {
itemStaticStyle = undefined;
}
domainObjectStyles[item.id] = { staticStyle: { style: itemStaticStyle } };
} else {
objectStyle.styles.forEach((conditionalStyle, index) => {
let style = {};
Object.keys(item.applicableStyles).concat(['isStyleInvisible']).forEach(key => {
style[key] = conditionalStyle.style[key];
});
itemConditionalStyle.styles.push({
...conditionalStyle,
style
});
});
domainObjectStyles[item.id] = {
...domainObjectStyles[item.id],
...objectStyle,
...itemConditionalStyle
};
}
});
} else {
domainObjectStyles = {
...domainObjectStyles,
...objectStyle
};
}
return domainObjectStyles;
},
applySelectedConditionStyle(conditionId) {
this.selectedConditionId = conditionId;
this.getAndPersistStyles();
},
persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
}
}
}
</script>

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