Compare commits
60 Commits
pause-play
...
tree-rever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a0a67070d | ||
|
|
d36db3b958 | ||
|
|
5f4aa88556 | ||
|
|
73451700f2 | ||
|
|
a40867d544 | ||
|
|
dbed9262c0 | ||
|
|
43ac66233e | ||
|
|
04e85c176a | ||
|
|
8274c23129 | ||
|
|
5fafde5f23 | ||
|
|
80a6e7f719 | ||
|
|
2c13aeecce | ||
|
|
ac015c3e45 | ||
|
|
ae1a4bcc6a | ||
|
|
e1e0eeac56 | ||
|
|
5610ef9921 | ||
|
|
c90dfb2a1f | ||
|
|
1b46b0d032 | ||
|
|
048805418f | ||
|
|
0e7704d628 | ||
|
|
76614f8507 | ||
|
|
77460961ce | ||
|
|
ec83426de2 | ||
|
|
3e23a0b044 | ||
|
|
b96b099551 | ||
|
|
1dfa5e5b8c | ||
|
|
31c2dbd273 | ||
|
|
8009944f1d | ||
|
|
4748bb1e21 | ||
|
|
43a1d60a3f | ||
|
|
06d7486d2a | ||
|
|
99896b72ea | ||
|
|
979ba77c8e | ||
|
|
a2a9f99538 | ||
|
|
261ea7b543 | ||
|
|
4330b54f0f | ||
|
|
212adfc3c1 | ||
|
|
aebb5df611 | ||
|
|
9e371f29d2 | ||
|
|
605eeff9d7 | ||
|
|
5d8bef6ef8 | ||
|
|
52f70a0489 | ||
|
|
d48951c340 | ||
|
|
bd2d6d9ea5 | ||
|
|
8e735379f9 | ||
|
|
b9c8404d9d | ||
|
|
0b71f97b3b | ||
|
|
dc14858253 | ||
|
|
938a127e46 | ||
|
|
a83ee1f90f | ||
|
|
21ec2e46fe | ||
|
|
170927748b | ||
|
|
fe899cbcc8 | ||
|
|
633bac2ed5 | ||
|
|
dacec48aec | ||
|
|
3ca133c782 | ||
|
|
648c8a13c4 | ||
|
|
bd09b1a2cb | ||
|
|
4eb5fe3978 | ||
|
|
776baa159a |
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<!--- This is for filing bugs. If you have a general question, please -->
|
||||
<!--- visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Bug Report
|
||||
about: File a Bug !
|
||||
|
||||
---
|
||||
<!--- Focus on user impact in the title. Use the Summary Field to -->
|
||||
<!--- describe the problem technically. -->
|
||||
|
||||
#### Summary
|
||||
<!--- A description of the issue encountered. When possible, a description -->
|
||||
<!--- of the impact of the issue. What use case does it impede?-->
|
||||
|
||||
#### Expected vs Current Behavior
|
||||
<!--- Tell us what should have happened -->
|
||||
|
||||
#### Impact Check List
|
||||
<!--- Please select from the following options -->
|
||||
|
||||
- [ ] Data loss or misrepresented data?
|
||||
- [ ] Regression? Did this used to work or has it always been broken?
|
||||
- [ ] Is there a workaround available?
|
||||
- [ ] Does this impact a critical component?
|
||||
- [ ] Is this just a visual bug?
|
||||
|
||||
#### Steps to Reproduce
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
#### Environment
|
||||
* Open MCT Version: <!--- date of build, version, or SHA -->
|
||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yams? -->
|
||||
* OS:
|
||||
* Browser:
|
||||
|
||||
#### Additional Information
|
||||
<!--- Include any screenshots, gifs, or logs which will expedite triage -->
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<!--- This is for filing enhancements or features. If you have a general -->
|
||||
<!--- question, please visit https://github.com/nasa/openmct/discussions -->
|
||||
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting an idea to make Open MCT better.
|
||||
|
||||
Please fill in as much of the template below as you're able.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- Please describe the problem you are trying to solve. -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!--- Please describe the desired behavior. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!--- Please describe alternative solutions or features you have considered. -->
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
### All Submissions:
|
||||
|
||||
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
|
||||
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/nasa/openmct/pulls) for the same update/change?
|
||||
|
||||
### Author Checklist
|
||||
|
||||
* [ ] Changes address original issue?
|
||||
* [ ] Unit tests included and/or updated with changes?
|
||||
* [ ] Command line build passes?
|
||||
* [ ] Has this been smoke tested?
|
||||
* [ ] Testing instructions included in associated issue?
|
||||
2
API.md
2
API.md
@@ -423,7 +423,7 @@ attribute | type | flags | notes
|
||||
|
||||
###### Value Hints
|
||||
|
||||
Each telemetry value description has an object defining hints. Keys in this this object represent the hint itself, and the value represents the weight of that hint. A lower weight means the hint has a higher priority. For example, multiple values could be hinted for use as the y-axis of a plot (raw, engineering), but the highest priority would be the default choice. Likewise, a table will use hints to determine the default order of columns.
|
||||
Each telemetry value description has an object defining hints. Keys in this object represent the hint itself, and the value represents the weight of that hint. A lower weight means the hint has a higher priority. For example, multiple values could be hinted for use as the y-axis of a plot (raw, engineering), but the highest priority would be the default choice. Likewise, a table will use hints to determine the default order of columns.
|
||||
|
||||
Known hints:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ accept changes from external contributors.
|
||||
|
||||
The short version:
|
||||
|
||||
1. Write your contribution.
|
||||
1. Write your contribution or describe your idea in the form of an [GitHub issue](https://github.com/nasa/openmct/issues/new/choose) or [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions)
|
||||
2. Make sure your contribution meets code, test, and commit message
|
||||
standards as described below.
|
||||
3. Submit a pull request from a topic branch back to `master`. Include a check
|
||||
@@ -18,6 +18,7 @@ The short version:
|
||||
for review.)
|
||||
4. Respond to any discussion. When the reviewer decides it's ready, they
|
||||
will merge back `master` and fill out their own check list.
|
||||
5. If you are a first-time contributor, please see [this discussion](https://github.com/nasa/openmct/discussions/3821) for further information.
|
||||
|
||||
## Contribution Process
|
||||
|
||||
@@ -115,7 +116,7 @@ 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 request’s __author__. If no issue exists, create one.
|
||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, [create one](https://github.com/nasa/openmct/issues/new/choose).
|
||||
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
|
||||
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull request’s __author__.
|
||||
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
|
||||
@@ -296,23 +297,12 @@ these standards.
|
||||
|
||||
Issues are tracked at https://github.com/nasa/openmct/issues.
|
||||
|
||||
Issues should include:
|
||||
|
||||
* A short description of the issue encountered.
|
||||
* A longer-form description of the issue encountered. When possible, steps to
|
||||
reproduce the issue.
|
||||
* When possible, a description of the impact of the issue. What use case does
|
||||
it impede?
|
||||
* An assessment of the severity of the issue.
|
||||
|
||||
Issue severity is categorized as follows (in ascending order):
|
||||
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
|
||||
software; a "nice-to-have."
|
||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
|
||||
* _Critical_: Large-scale loss of functionality or impairment of use,
|
||||
such that remaining utility becomes marginal.
|
||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
|
||||
* _Medium_: Some impairment of use, but simple workarounds exist
|
||||
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
|
||||
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
|
||||
|
||||
## Check Lists
|
||||
|
||||
@@ -322,16 +312,19 @@ checklist).
|
||||
|
||||
### Author Checklist
|
||||
|
||||
1. Changes address original issue?
|
||||
2. Unit tests included and/or updated with changes?
|
||||
3. Command line build passes?
|
||||
4. Changes have been smoke-tested?
|
||||
5. Testing instructions included?
|
||||
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
1. Changes appear to address issue?
|
||||
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)
|
||||
* [ ] Changes appear to address issue?
|
||||
* [ ] Appropriate unit tests included?
|
||||
* [ ] Code style and in-line documentation are appropriate?
|
||||
* [ ] Commit messages meet standards?
|
||||
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
|
||||
* [ ] List of Acceptance Tests Performed.
|
||||
|
||||
Write out a small list of tests performed with just enough detail for another developer on the team
|
||||
to execute.
|
||||
|
||||
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```
|
||||
@@ -44,7 +44,7 @@ The clearest examples for developing Open MCT plugins are in the
|
||||
our documentation.
|
||||
|
||||
We want Open MCT to be as easy to use, install, run, and develop for as
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues/new/choose), [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
|
||||
## Building Applications With Open MCT
|
||||
|
||||
|
||||
@@ -73,11 +73,11 @@ acceptance testing (e.g. by resolving any blockers found); any
|
||||
resources not needed for this effort should be used to begin work
|
||||
for the subsequent sprint.
|
||||
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship |
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze and sprint branch |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
|
||||
|
||||
* If necessary.
|
||||
|
||||
@@ -105,14 +105,20 @@ emphasis on testing.
|
||||
that team may begin work for that sprint during the
|
||||
third week, since testing and blocker resolution is unlikely
|
||||
to require all available resources.
|
||||
* Testing success criteria identified per issue (where necessary). This could be in the form of acceptance tests on the issue or detailing performance tests, for example.
|
||||
* __Tag-up.__ Check in and status update among development team.
|
||||
May amend plan for sprint as-needed.
|
||||
* __Code freeze.__ Any new work from this sprint
|
||||
(features, bug fixes, enhancements) must be integrated by the
|
||||
end of the second week of the sprint. After code freeze
|
||||
(and until the end of the sprint) the only changes that should be
|
||||
merged into the master branch should directly address issues
|
||||
needed to pass acceptance testing.
|
||||
end of the second week of the sprint. After code freeze, a sprint
|
||||
branch will be created (and until the end of the sprint) the only
|
||||
changes that should be merged into the sprint branch should
|
||||
directly address issues needed to pass acceptance testing.
|
||||
During this time, any other feature development will continue to
|
||||
be merged into the master branch for the next sprint.
|
||||
* __Sprint branch merge to master.__ After acceptance testing, the sprint branch
|
||||
will be merged back to the master branch. Any code conflicts that
|
||||
arise will be resolved by the team.
|
||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
||||
Structured testing with predefined
|
||||
success criteria. No release should ship without passing
|
||||
@@ -126,8 +132,8 @@ emphasis on testing.
|
||||
* [__Testathon.__](testing/plan.md#user-testing)
|
||||
Multi-user testing, involving as many users as
|
||||
is feasible, plus development team. Open-ended; should verify
|
||||
completed work from this sprint, test exploratorily for
|
||||
regressions, et cetera.
|
||||
completed work from this sprint using the sprint branch, test
|
||||
exploratorily for regressions, et cetera.
|
||||
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
||||
test to verify that the software remains
|
||||
stable after running for longer durations. May include some
|
||||
@@ -143,7 +149,7 @@ emphasis on testing.
|
||||
Subset of Pre-release Testing
|
||||
which should be performed before shipping at the end of any
|
||||
sprint. Time is allocated for a second round of
|
||||
Pre-release Testing if the first round is not passed.
|
||||
Pre-release Testing if the first round is not passed. Smoke tests collected from issues/PRs
|
||||
* __Triage.__ Team reviews issues from acceptance testing and uses
|
||||
success criteria to determine whether or not they should block
|
||||
release, then formulates a plan to address these issues before
|
||||
|
||||
@@ -19,7 +19,7 @@ Testing for Open MCT includes:
|
||||
|
||||
Manual, non-rigorous testing of the software and/or specific features
|
||||
of interest. Verifies that the software runs and that basic functionality
|
||||
is present.
|
||||
is present. The outcome of Smoke Testing should be a simplified list of Acceptance Tests which could be executed by another team member with sufficient context.
|
||||
|
||||
### Unit Testing
|
||||
|
||||
@@ -49,7 +49,7 @@ User testing will focus on the following activities:
|
||||
* General "trying to break things."
|
||||
|
||||
During user testing, users will
|
||||
[report issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
[report issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
as they are encountered.
|
||||
|
||||
Desired outcomes of user testing are:
|
||||
@@ -71,7 +71,7 @@ usage. After twenty-four hours, the software is evaluated for:
|
||||
at the start of the test? Is it as responsive?
|
||||
|
||||
Any defects or unexpected behavior identified during testing should be
|
||||
[reported as issues](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
and reviewed for severity.
|
||||
|
||||
## Test Performance
|
||||
|
||||
@@ -92,8 +92,8 @@ should update (or delegate the task of updating) Open MCT version
|
||||
numbers by the following process:
|
||||
|
||||
1. Update version number in `package.json`
|
||||
1. Create a new branch off the `master` branch.
|
||||
2. Remove `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
1. Checkout branch created for the last sprint that has been successfully tested.
|
||||
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
3. Verify that resulting version number meets semantic versioning
|
||||
requirements relative to previous stable version. Increment the
|
||||
version number if necessary.
|
||||
|
||||
@@ -93,5 +93,36 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
|
||||
return {
|
||||
limits: function () {
|
||||
return {
|
||||
WARNING: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--yellow",
|
||||
sin: -YELLOW.sin,
|
||||
cos: -YELLOW.cos
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--yellow",
|
||||
...YELLOW
|
||||
}
|
||||
},
|
||||
DISTRESS: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--red",
|
||||
sin: -RED.sin,
|
||||
cos: -RED.cos
|
||||
},
|
||||
high: {
|
||||
cssClass: "is-limit--upr is-limit--red",
|
||||
...RED
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return SinewaveLimitProvider;
|
||||
});
|
||||
|
||||
@@ -88,7 +88,6 @@
|
||||
openmct.install(openmct.plugins.ExampleImagery());
|
||||
openmct.install(openmct.plugins.PlanLayout());
|
||||
openmct.install(openmct.plugins.Timeline());
|
||||
openmct.install(openmct.plugins.PlotVue());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.AutoflowView({
|
||||
type: "telemetry.panel"
|
||||
|
||||
@@ -78,6 +78,7 @@ module.exports = (config) => {
|
||||
preserveDescribeNesting: true,
|
||||
foldAll: false
|
||||
},
|
||||
browserConsoleLogOptions: { level: "error", format: "%b %T: %m", terminal: true },
|
||||
coverageIstanbulReporter: {
|
||||
fixWebpackSourcePaths: true,
|
||||
dir: process.env.CIRCLE_ARTIFACTS ?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.7.1-SNAPSHOT",
|
||||
"version": "1.7.3-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
@@ -78,7 +78,8 @@
|
||||
"zepto": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist",
|
||||
"clean": "rm -rf ./dist /node_modules; rm package-lock.json",
|
||||
"clean-test-lint": "npm run clean; npm install ; npm run test; npm run lint",
|
||||
"start": "node app.js",
|
||||
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||
|
||||
@@ -86,7 +86,7 @@ define(
|
||||
})
|
||||
.join('/');
|
||||
|
||||
window.location.href = url;
|
||||
openmct.router.navigate(url);
|
||||
|
||||
if (isFirstViewEditable(object.useCapability('adapter'), objectPath)) {
|
||||
openmct.editor.edit();
|
||||
|
||||
@@ -141,11 +141,17 @@ define(
|
||||
if (mutationResult !== false) {
|
||||
// Copy values if result was a different object
|
||||
// (either our clone or some other new thing)
|
||||
if (model !== result) {
|
||||
let modelHasChanged = _.isEqual(model, result) === false;
|
||||
if (modelHasChanged) {
|
||||
copyValues(model, result);
|
||||
}
|
||||
|
||||
model.modified = useTimestamp ? timestamp : now();
|
||||
if (modelHasChanged
|
||||
|| (useTimestamp !== undefined)
|
||||
|| (model.modified === undefined)) {
|
||||
model.modified = useTimestamp ? timestamp : now();
|
||||
}
|
||||
|
||||
notifyListeners(model);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,11 @@
|
||||
define([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/indicators/FollowIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/FollowTimerAction",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
@@ -39,13 +37,11 @@ define([
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
FollowIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
FollowTimerAction,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
@@ -144,15 +140,6 @@ define([
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.follow",
|
||||
"implementation": FollowTimerAction,
|
||||
"depends": ["timerService"],
|
||||
"category": "contextual",
|
||||
"name": "Follow Timer",
|
||||
"cssClass": "icon-clock",
|
||||
"priority": "optional"
|
||||
},
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
@@ -299,10 +286,7 @@ define([
|
||||
}
|
||||
}
|
||||
],
|
||||
"runs": [{
|
||||
"implementation": FollowIndicator,
|
||||
"depends": ["openmct", "timerService"]
|
||||
}],
|
||||
"runs": [],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "moment-duration-format",
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, 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 () {
|
||||
|
||||
/**
|
||||
* Designates a specific timer for following. Timelines, for example,
|
||||
* use the actively followed timer to display a time-of-interest line
|
||||
* and interpret time conductor bounds in the Timeline's relative
|
||||
* time frame.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function FollowTimerAction(timerService, context) {
|
||||
var domainObject =
|
||||
context.domainObject
|
||||
&& context.domainObject.useCapability('adapter');
|
||||
this.perform =
|
||||
timerService.setTimer.bind(timerService, domainObject);
|
||||
}
|
||||
|
||||
FollowTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel())
|
||||
|| {};
|
||||
|
||||
return model.type === 'timer';
|
||||
};
|
||||
|
||||
return FollowTimerAction;
|
||||
}
|
||||
);
|
||||
@@ -1,51 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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 () {
|
||||
|
||||
/**
|
||||
* Indicator that displays the active timer, as well as its
|
||||
* current state.
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
return function installFollowIndicator(openmct, timerService) {
|
||||
var indicator = openmct.indicators.simpleIndicator();
|
||||
var timer = timerService.getTimer();
|
||||
setIndicatorStatus(timer);
|
||||
|
||||
function setIndicatorStatus(newTimer) {
|
||||
if (newTimer !== undefined) {
|
||||
indicator.iconClass('icon-timer');
|
||||
indicator.statusClass('s-status-on');
|
||||
indicator.text('Following timer ' + newTimer.name);
|
||||
} else {
|
||||
indicator.iconClass('icon-timer');
|
||||
indicator.statusClass('s-status-disabled');
|
||||
indicator.text('No timer being followed');
|
||||
}
|
||||
}
|
||||
|
||||
timerService.on('change', setIndicatorStatus);
|
||||
|
||||
openmct.indicators.add(indicator);
|
||||
};
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, 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/actions/FollowTimerAction"
|
||||
], function (FollowTimerAction) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The Follow Timer action", function () {
|
||||
var testContext;
|
||||
var testModel;
|
||||
var testAdaptedObject;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = {};
|
||||
testContext = {
|
||||
domainObject: jasmine.createSpyObj('domainObject', [
|
||||
'getModel',
|
||||
'useCapability'
|
||||
])
|
||||
};
|
||||
testAdaptedObject = { foo: 'bar' };
|
||||
testContext.domainObject.getModel.and.returnValue(testModel);
|
||||
testContext.domainObject.useCapability.and.callFake(function (c) {
|
||||
return c === 'adapter' && testAdaptedObject;
|
||||
});
|
||||
});
|
||||
|
||||
it("is applicable to timers", function () {
|
||||
testModel.type = "timer";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
|
||||
});
|
||||
|
||||
it("is inapplicable to non-timers", function () {
|
||||
testModel.type = "folder";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when instantiated", function () {
|
||||
var mockTimerService;
|
||||
var action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService = jasmine.createSpyObj(
|
||||
'timerService',
|
||||
TIMER_SERVICE_METHODS
|
||||
);
|
||||
action = new FollowTimerAction(mockTimerService, testContext);
|
||||
});
|
||||
|
||||
it("does not interact with the timer service", function () {
|
||||
TIMER_SERVICE_METHODS.forEach(function (method) {
|
||||
expect(mockTimerService[method]).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and performed", function () {
|
||||
beforeEach(function () {
|
||||
action.perform();
|
||||
});
|
||||
|
||||
it("sets the active timer", function () {
|
||||
expect(mockTimerService.setTimer)
|
||||
.toHaveBeenCalledWith(testAdaptedObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-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/indicators/FollowIndicator",
|
||||
"../../src/services/TimerService",
|
||||
"../../../../../src/MCT",
|
||||
'zepto'
|
||||
], function (
|
||||
FollowIndicator,
|
||||
TimerService,
|
||||
MCT,
|
||||
$
|
||||
) {
|
||||
xdescribe("The timer-following indicator", function () {
|
||||
var timerService;
|
||||
var openmct;
|
||||
|
||||
beforeEach(function () {
|
||||
openmct = new MCT();
|
||||
timerService = new TimerService(openmct);
|
||||
spyOn(openmct.indicators, "add");
|
||||
});
|
||||
|
||||
it("adds an indicator when installed", function () {
|
||||
FollowIndicator(openmct, timerService);
|
||||
expect(openmct.indicators.add).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("indicates that no timer is being followed", function () {
|
||||
FollowIndicator(openmct, timerService);
|
||||
var simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
|
||||
var element = simpleIndicator.element;
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('No timer being followed');
|
||||
});
|
||||
|
||||
describe("when a timer is set", function () {
|
||||
var testObject;
|
||||
var simpleIndicator;
|
||||
|
||||
beforeEach(function () {
|
||||
testObject = {
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'key'
|
||||
},
|
||||
name: "some timer!"
|
||||
};
|
||||
timerService.setTimer(testObject);
|
||||
FollowIndicator(openmct, timerService);
|
||||
simpleIndicator = openmct.indicators.add.calls.mostRecent().args[0];
|
||||
});
|
||||
|
||||
it("displays the timer's name", function () {
|
||||
var element = simpleIndicator.element;
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('Following timer ' + testObject.name);
|
||||
});
|
||||
|
||||
it("displays the timer's name when it changes", function () {
|
||||
var secondTimer = {
|
||||
identifier: {
|
||||
namespace: 'namespace',
|
||||
key: 'key2'
|
||||
},
|
||||
name: "Some other timer"
|
||||
};
|
||||
var element = simpleIndicator.element;
|
||||
timerService.setTimer(secondTimer);
|
||||
var text = $('.indicator-text', element).text().trim();
|
||||
expect(text).toEqual('Following timer ' + secondTimer.name);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -252,7 +252,7 @@ define([
|
||||
|
||||
this.status = new api.StatusAPI(this);
|
||||
|
||||
this.router = new ApplicationRouter();
|
||||
this.router = new ApplicationRouter(this);
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ define([
|
||||
'./views/installLegacyViews',
|
||||
'./policies/LegacyCompositionPolicyAdapter',
|
||||
'./actions/LegacyActionAdapter',
|
||||
'./services/LegacyPersistenceAdapter'
|
||||
'./services/LegacyPersistenceAdapter',
|
||||
'./services/ExportImageService'
|
||||
], function (
|
||||
ActionDialogDecorator,
|
||||
AdapterCapability,
|
||||
@@ -53,7 +54,8 @@ define([
|
||||
installLegacyViews,
|
||||
legacyCompositionPolicyAdapter,
|
||||
LegacyActionAdapter,
|
||||
LegacyPersistenceAdapter
|
||||
LegacyPersistenceAdapter,
|
||||
ExportImageService
|
||||
) {
|
||||
return {
|
||||
name: 'src/adapter',
|
||||
@@ -82,6 +84,13 @@ define([
|
||||
"identifierService",
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": ExportImageService,
|
||||
"depends": [
|
||||
"dialogService"
|
||||
]
|
||||
}
|
||||
],
|
||||
components: [
|
||||
|
||||
@@ -161,6 +161,22 @@ define([
|
||||
evaluate: function (datum, property) {
|
||||
return limitEvaluator.evaluate(datum, property && property.key);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
|
||||
const oldObject = this.instantiate(
|
||||
utils.toOldFormat(domainObject),
|
||||
utils.makeKeyString(domainObject.identifier)
|
||||
);
|
||||
const limitEvaluator = oldObject.getCapability("limit");
|
||||
|
||||
return {
|
||||
limits: function () {
|
||||
return limitEvaluator.limits();
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,13 +24,6 @@ define([
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: Remove this when plots Angular implementation is deprecated
|
||||
let parent = selection[0].length > 1 && selection[0][1].context.item;
|
||||
if (parent && parent.type === 'time-strip') {
|
||||
return (selectionContext.item.type === typeDefinition.key)
|
||||
&& (typeDefinition.key !== 'telemetry.plot.overlay');
|
||||
}
|
||||
|
||||
return selectionContext.item.type === typeDefinition.key;
|
||||
},
|
||||
view: function (selection) {
|
||||
|
||||
@@ -119,7 +119,8 @@ describe('The ActionCollection', () => {
|
||||
|
||||
afterEach(() => {
|
||||
actionCollection.destroy();
|
||||
resetApplicationState(openmct);
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("disable method invoked with action keys", () => {
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('The Actions API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("register method", () => {
|
||||
|
||||
@@ -215,12 +215,12 @@ define([
|
||||
* @memberof {module:openmct.CompositionCollection#}
|
||||
* @name load
|
||||
*/
|
||||
CompositionCollection.prototype.load = function () {
|
||||
CompositionCollection.prototype.load = function (abortSignal) {
|
||||
this.cleanUpMutables();
|
||||
|
||||
return this.provider.load(this.domainObject)
|
||||
.then(function (children) {
|
||||
return Promise.all(children.map((c) => this.publicAPI.objects.get(c)));
|
||||
return Promise.all(children.map((c) => this.publicAPI.objects.get(c, abortSignal)));
|
||||
}.bind(this))
|
||||
.then(function (childObjects) {
|
||||
childObjects.forEach(c => this.add(c, true));
|
||||
|
||||
@@ -76,7 +76,7 @@ describe ('The Menu API', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("showMenu method", () => {
|
||||
|
||||
@@ -486,6 +486,12 @@ ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
|
||||
});
|
||||
};
|
||||
|
||||
ObjectAPI.prototype.isObjectPathToALink = function (domainObject, objectPath) {
|
||||
return objectPath !== undefined
|
||||
&& objectPath.length > 1
|
||||
&& domainObject.location !== this.makeKeyString(objectPath[1].identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Uniquely identifies a domain object.
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("The Status API", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe("set function", () => {
|
||||
|
||||
@@ -504,6 +504,26 @@ define([
|
||||
return this.getLimitEvaluator(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limits for this domain object.
|
||||
* Limits help you display limits and alarms of
|
||||
* telemetry for display purposes without having to interact directly
|
||||
* with the Limit API.
|
||||
*
|
||||
* This method is optional.
|
||||
* If a provider does not implement this method, it is presumed
|
||||
* that no limits are defined for this domain object's telemetry.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} domainObject the domain
|
||||
* object for which to get limits
|
||||
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
|
||||
* @method limits
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.limitDefinition = function (domainObject) {
|
||||
return this.getLimits(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limit evaluator for this domain object.
|
||||
* Limit Evaluators help you evaluate limit and alarm status of individual
|
||||
@@ -531,5 +551,42 @@ define([
|
||||
return provider.getLimitEvaluator(domainObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a limit definitions for this domain object.
|
||||
* Limit Definitions help you indicate limits and alarms of
|
||||
* telemetry for display purposes without having to interact directly
|
||||
* with the Limit API.
|
||||
*
|
||||
* This method is optional.
|
||||
* If a provider does not implement this method, it is presumed
|
||||
* that no limits are defined for this domain object's telemetry.
|
||||
*
|
||||
* @param {module:openmct.DomainObject} domainObject the domain
|
||||
* object for which to display limits
|
||||
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
|
||||
* @method limits returns a limits object of
|
||||
* type {
|
||||
* level1: {
|
||||
* low: { key1: value1, key2: value2 },
|
||||
* high: { key1: value1, key2: value2 }
|
||||
* },
|
||||
* level2: {
|
||||
* low: { key1: value1, key2: value2 },
|
||||
* high: { key1: value1, key2: value2 }
|
||||
* }
|
||||
* }
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.getLimits = function (domainObject) {
|
||||
const provider = this.findLimitEvaluator(domainObject);
|
||||
if (!provider) {
|
||||
return {
|
||||
limits: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
return provider.getLimits(domainObject);
|
||||
};
|
||||
|
||||
return TelemetryAPI;
|
||||
});
|
||||
|
||||
@@ -45,10 +45,14 @@ export default function LADTableSetViewProvider(openmct) {
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject,
|
||||
objectPath
|
||||
},
|
||||
template: '<lad-table-set></lad-table-set>'
|
||||
data() {
|
||||
return {
|
||||
domainObject
|
||||
};
|
||||
},
|
||||
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
|
||||
});
|
||||
},
|
||||
destroy: function (element) {
|
||||
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
objectPath: {
|
||||
pathToTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
@@ -66,20 +66,19 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let currentObjectPath = this.objectPath.slice();
|
||||
currentObjectPath.unshift(this.domainObject);
|
||||
|
||||
return {
|
||||
timestamp: undefined,
|
||||
value: '---',
|
||||
valueClass: '',
|
||||
currentObjectPath,
|
||||
unit: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedTimestamp() {
|
||||
return this.timestamp !== undefined ? this.getFormattedTimestamp(this.timestamp) : '---';
|
||||
},
|
||||
objectPath() {
|
||||
return [this.domainObject, ...this.pathToTable];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -182,7 +181,7 @@ export default {
|
||||
};
|
||||
},
|
||||
showContextMenu(event) {
|
||||
let actionCollection = this.openmct.actions.get(this.currentObjectPath, this.getView());
|
||||
let actionCollection = this.openmct.actions.get(this.objectPath, this.getView());
|
||||
let allActions = actionCollection.getActionsObject();
|
||||
let applicableActions = CONTEXT_MENU_ACTIONS.map(key => allActions[key]);
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<lad-row
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
:domain-object="item.domainObject"
|
||||
:object-path="objectPath"
|
||||
v-for="ladRow in items"
|
||||
:key="ladRow.key"
|
||||
:domain-object="ladRow.domainObject"
|
||||
:path-to-table="objectPath"
|
||||
:has-units="hasUnits"
|
||||
/>
|
||||
</tbody>
|
||||
|
||||
@@ -43,9 +43,10 @@
|
||||
</td>
|
||||
</tr>
|
||||
<lad-row
|
||||
v-for="telemetryObject in ladTelemetryObjects[ladTable.key]"
|
||||
:key="telemetryObject.key"
|
||||
:domain-object="telemetryObject.domainObject"
|
||||
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
||||
:key="ladRow.key"
|
||||
:domain-object="ladRow.domainObject"
|
||||
:path-to-table="ladTable.objectPath"
|
||||
:has-units="hasUnits"
|
||||
/>
|
||||
</template>
|
||||
@@ -60,7 +61,13 @@ export default {
|
||||
components: {
|
||||
LadRow
|
||||
},
|
||||
inject: ['openmct', 'domainObject'],
|
||||
inject: ['openmct', 'objectPath'],
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ladTableObjects: [],
|
||||
@@ -106,6 +113,7 @@ export default {
|
||||
let ladTable = {};
|
||||
ladTable.domainObject = domainObject;
|
||||
ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
ladTable.objectPath = [domainObject, ...this.objectPath];
|
||||
|
||||
this.$set(this.ladTelemetryObjects, ladTable.key, []);
|
||||
this.ladTableObjects.push(ladTable);
|
||||
|
||||
@@ -292,6 +292,11 @@ describe("The LAD Table Set", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
* 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 = ['timeSystem', 'clock', 'clockOffsets'];
|
||||
const SEARCH_MODE = 'tc.mode';
|
||||
@@ -49,9 +45,8 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.updateTimeSettings();
|
||||
this.openmct.router.on('change:params', this.updateTimeSettings);
|
||||
|
||||
window.addEventListener('hashchange', this.updateTimeSettings);
|
||||
TIME_EVENTS.forEach(event => {
|
||||
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
||||
});
|
||||
@@ -59,7 +54,8 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
window.removeEventListener('hashchange', this.updateTimeSettings);
|
||||
this.openmct.router.off('change:params', this.updateTimeSettings);
|
||||
|
||||
this.openmct.off('start', this.initialize);
|
||||
this.openmct.off('destroy', this.destroy);
|
||||
|
||||
@@ -70,22 +66,18 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
updateTimeSettings() {
|
||||
// Prevent from triggering self
|
||||
if (!this.isUrlUpdateInProgress) {
|
||||
let timeParameters = this.parseParametersFromUrl();
|
||||
let timeParameters = this.parseParametersFromUrl();
|
||||
|
||||
if (this.areTimeParametersValid(timeParameters)) {
|
||||
this.setTimeApiFromUrl(timeParameters);
|
||||
} else {
|
||||
this.setUrlFromTimeApi();
|
||||
}
|
||||
if (this.areTimeParametersValid(timeParameters)) {
|
||||
this.setTimeApiFromUrl(timeParameters);
|
||||
this.openmct.router.setLocationFromUrl();
|
||||
} else {
|
||||
this.isUrlUpdateInProgress = false;
|
||||
this.setUrlFromTimeApi();
|
||||
}
|
||||
}
|
||||
|
||||
parseParametersFromUrl() {
|
||||
let searchParams = getAllSearchParams();
|
||||
let searchParams = this.openmct.router.getAllSearchParams();
|
||||
|
||||
let mode = searchParams.get(SEARCH_MODE);
|
||||
let timeSystem = searchParams.get(SEARCH_TIME_SYSTEM);
|
||||
@@ -148,7 +140,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
setUrlFromTimeApi() {
|
||||
let searchParams = getAllSearchParams();
|
||||
let searchParams = this.openmct.router.getAllSearchParams();
|
||||
let clock = this.openmct.time.clock();
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let clockOffsets = this.openmct.time.clockOffsets();
|
||||
@@ -176,8 +168,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
}
|
||||
|
||||
searchParams.set(SEARCH_TIME_SYSTEM, this.openmct.time.timeSystem().key);
|
||||
this.isUrlUpdateInProgress = true;
|
||||
setAllSearchParams(searchParams);
|
||||
this.openmct.router.setAllSearchParams(searchParams);
|
||||
}
|
||||
|
||||
areTimeParametersValid(timeParameters) {
|
||||
|
||||
@@ -25,306 +25,118 @@ import {
|
||||
} from 'utils/testing';
|
||||
|
||||
describe("The URLTimeSettingsSynchronizer", () => {
|
||||
let appHolder;
|
||||
let openmct;
|
||||
let testClock;
|
||||
let resolveFunction;
|
||||
let oldHash;
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
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.install(openmct.plugins.UTCTimeSystem());
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
|
||||
appHolder = document.createElement("div");
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
|
||||
afterEach(() => resetApplicationState(openmct));
|
||||
afterEach(() => {
|
||||
openmct.time.stopClock();
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
|
||||
describe("realtime mode", () => {
|
||||
it("when the clock is set via the time API, it is immediately reflected in the URL", () => {
|
||||
//Test expected initial conditions
|
||||
appHolder = undefined;
|
||||
openmct = undefined;
|
||||
resolveFunction = undefined;
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("initial clock is set to fixed is reflected in URL", (done) => {
|
||||
resolveFunction = () => {
|
||||
oldHash = window.location.hash;
|
||||
expect(window.location.hash.includes('tc.mode=fixed')).toBe(true);
|
||||
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
it("when the clock is set via the time API, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
|
||||
resolveFunction = () => {
|
||||
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();
|
||||
const hasStartDelta = window.location.hash.includes('tc.startDelta=2000');
|
||||
const hasEndDelta = window.location.hash.includes('tc.endDelta=200');
|
||||
const hasLocalClock = window.location.hash.includes('tc.mode=local');
|
||||
success = hasStartDelta && hasEndDelta && hasLocalClock;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
|
||||
return switchToRealtimeMode().then(() => {
|
||||
let clock = openmct.time.clock();
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
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');
|
||||
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
|
||||
window.location.hash = hash;
|
||||
}
|
||||
resolveFunction = () => {
|
||||
let hash = window.location.hash;
|
||||
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||
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');
|
||||
success = window.location.hash.includes('tc.mode=local');
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
window.location.hash = hash;
|
||||
}
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
|
||||
function switchToRealtimeMode() {
|
||||
let resolveFunction;
|
||||
it("when the clock mode is set to local, it is reflected in the URL", (done) => {
|
||||
let success;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolveFunction = resolve;
|
||||
openmct.time.on('clock', resolveFunction);
|
||||
setRealtimeLocationParameters();
|
||||
}).then(() => {
|
||||
openmct.time.off('clock', resolveFunction);
|
||||
});
|
||||
}
|
||||
resolveFunction = () => {
|
||||
let hash = window.location.hash;
|
||||
|
||||
function switchToFixedMode() {
|
||||
let resolveFunction;
|
||||
hash = hash.replace('tc.mode=fixed', 'tc.mode=local');
|
||||
window.location.hash = hash;
|
||||
success = window.location.hash.includes('tc.mode=local');
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
openmct.router.on('change:hash', 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);
|
||||
}
|
||||
it("reset hash", (done) => {
|
||||
let success;
|
||||
|
||||
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);
|
||||
}
|
||||
window.location.hash = oldHash;
|
||||
resolveFunction = () => {
|
||||
success = window.location.hash === oldHash;
|
||||
if (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.on('change:hash', resolveFunction);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -272,11 +272,11 @@ export default class Condition extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
requestLADConditionResult() {
|
||||
requestLADConditionResult(options) {
|
||||
let latestTimestamp;
|
||||
let criteriaResults = {};
|
||||
const criteriaRequests = this.criteria
|
||||
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
|
||||
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects, options));
|
||||
|
||||
return Promise.all(criteriaRequests)
|
||||
.then(results => {
|
||||
|
||||
@@ -282,7 +282,7 @@ export default class ConditionManager extends EventEmitter {
|
||||
return currentCondition;
|
||||
}
|
||||
|
||||
requestLADConditionSetOutput() {
|
||||
requestLADConditionSetOutput(options) {
|
||||
if (!this.conditions.length) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export default class ConditionManager extends EventEmitter {
|
||||
let latestTimestamp;
|
||||
let conditionResults = {};
|
||||
const conditionRequests = this.conditions
|
||||
.map(condition => condition.requestLADConditionResult());
|
||||
.map(condition => condition.requestLADConditionResult(options));
|
||||
|
||||
return Promise.all(conditionRequests)
|
||||
.then((results) => {
|
||||
|
||||
@@ -40,10 +40,10 @@ export default class ConditionSetTelemetryProvider {
|
||||
return domainObject.type === 'conditionSet';
|
||||
}
|
||||
|
||||
request(domainObject) {
|
||||
request(domainObject, options) {
|
||||
let conditionManager = this.getConditionManager(domainObject);
|
||||
|
||||
return conditionManager.requestLADConditionSetOutput()
|
||||
return conditionManager.requestLADConditionSetOutput(options)
|
||||
.then(latestOutput => {
|
||||
return latestOutput;
|
||||
});
|
||||
@@ -52,7 +52,9 @@ export default class ConditionSetTelemetryProvider {
|
||||
subscribe(domainObject, callback) {
|
||||
let conditionManager = this.getConditionManager(domainObject);
|
||||
|
||||
conditionManager.on('conditionSetResultUpdated', callback);
|
||||
conditionManager.on('conditionSetResultUpdated', (data) => {
|
||||
callback(data);
|
||||
});
|
||||
|
||||
return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
if (styleConfiguration) {
|
||||
this.initialize(styleConfiguration);
|
||||
if (styleConfiguration.conditionSetIdentifier) {
|
||||
this.openmct.time.on("bounds", this.refreshData.bind(this));
|
||||
this.subscribeToConditionSet();
|
||||
} else {
|
||||
this.applyStaticStyle();
|
||||
@@ -83,6 +84,25 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
refreshData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
let options = {
|
||||
start: bounds.start,
|
||||
end: bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
};
|
||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||
this.openmct.telemetry.request(conditionSetDomainObject, options)
|
||||
.then(output => {
|
||||
if (output && output.length) {
|
||||
this.handleConditionSetResultUpdated(output[0]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateObjectStyleConfig(styleConfiguration) {
|
||||
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
||||
this.initialize(styleConfiguration || {});
|
||||
@@ -160,10 +180,14 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
|
||||
destroy() {
|
||||
if (this.stopProvidingTelemetry) {
|
||||
|
||||
this.stopProvidingTelemetry();
|
||||
delete this.stopProvidingTelemetry;
|
||||
}
|
||||
|
||||
this.openmct.time.off("bounds", this.refreshData);
|
||||
this.openmct.editor.off('isEditing', this.toggleSubscription);
|
||||
|
||||
this.conditionSetIdentifier = undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
<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>
|
||||
@@ -286,6 +285,8 @@ export default {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.previewAction.invoke(this.objectPath);
|
||||
} else {
|
||||
this.openmct.router.navigate(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
removeConditionSet() {
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||
<a v-if="conditionSetDomainObject"
|
||||
class="c-object-label"
|
||||
:href="navigateToPath"
|
||||
@click="navigateOrPreview"
|
||||
>
|
||||
<span class="c-object-label__type-icon icon-conditional"></span>
|
||||
@@ -309,6 +308,8 @@ export default {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.previewAction.invoke(this.objectPath);
|
||||
} else {
|
||||
this.openmct.router.navigate(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
isItemType(type, item) {
|
||||
|
||||
@@ -147,12 +147,16 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||
}
|
||||
|
||||
requestLAD(telemetryObjects) {
|
||||
const options = {
|
||||
requestLAD(telemetryObjects, requestOptions) {
|
||||
let options = {
|
||||
strategy: 'latest',
|
||||
size: 1
|
||||
};
|
||||
|
||||
if (requestOptions !== undefined) {
|
||||
options = Object.assign(options, requestOptions);
|
||||
}
|
||||
|
||||
if (!this.isValid()) {
|
||||
return this.formatData({}, telemetryObjects);
|
||||
}
|
||||
|
||||
@@ -137,12 +137,16 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
requestLAD() {
|
||||
const options = {
|
||||
requestLAD(telemetryObjects, requestOptions) {
|
||||
let options = {
|
||||
strategy: 'latest',
|
||||
size: 1
|
||||
};
|
||||
|
||||
if (requestOptions !== undefined) {
|
||||
options = Object.assign(options, requestOptions);
|
||||
}
|
||||
|
||||
if (!this.isValid()) {
|
||||
return {
|
||||
id: this.id,
|
||||
|
||||
@@ -46,7 +46,7 @@ xdescribe("the plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('installs the new folder action', () => {
|
||||
|
||||
@@ -235,7 +235,7 @@ define(['lodash'], function (_) {
|
||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
label: 'OK',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
removeItem(getAllTypes(selection));
|
||||
|
||||
@@ -269,7 +269,12 @@ export default {
|
||||
},
|
||||
subscribeToObject() {
|
||||
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
||||
if (this.openmct.time.clock() !== undefined) {
|
||||
const key = this.openmct.time.timeSystem().key;
|
||||
const datumTimeStamp = datum[key];
|
||||
if (this.openmct.time.clock() !== undefined
|
||||
|| (datumTimeStamp
|
||||
&& (this.openmct.time.bounds().end >= datumTimeStamp))
|
||||
) {
|
||||
this.updateView(datum);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("The Duplicate Action plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
|
||||
@@ -97,7 +97,7 @@ function ToolbarProvider(openmct) {
|
||||
message: `This action will remove this frame from this Flexible Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
label: 'OK',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
deleteFrameAction(primary.context.frameId);
|
||||
@@ -162,7 +162,7 @@ function ToolbarProvider(openmct) {
|
||||
message: 'This action will permanently delete this container from this Flexible Layout',
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
label: 'OK',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
removeContainer(containerId);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'is-alias': item.isAlias === true,
|
||||
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
|
||||
}, statusClass]"
|
||||
:href="objectLink"
|
||||
@click="navigate"
|
||||
>
|
||||
<div
|
||||
class="c-grid-item__type-icon"
|
||||
@@ -49,11 +49,17 @@ import statusListener from './status-listener';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigate() {
|
||||
this.openmct.router.navigate(this.objectLink);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
ref="objectLink"
|
||||
class="c-object-label"
|
||||
:class="[statusClass]"
|
||||
:href="objectLink"
|
||||
@click="navigate"
|
||||
>
|
||||
<div
|
||||
class="c-object-label__type-icon c-list-item__name__type-icon"
|
||||
@@ -45,6 +45,7 @@ import statusListener from './status-listener';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink, statusListener],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
@@ -56,7 +57,7 @@ export default {
|
||||
return moment(timestamp).format(format);
|
||||
},
|
||||
navigate() {
|
||||
this.$refs.objectLink.click();
|
||||
this.openmct.router.navigate(this.objectLink);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
margin: 0 $interiorMargin $interiorMargin 0;
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile & {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************* GRID ITEMS */
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class GoToOriginalAction {
|
||||
.slice(1)
|
||||
.join('/');
|
||||
|
||||
window.location.href = url;
|
||||
this._openmct.router.navigate(url);
|
||||
});
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
|
||||
@@ -47,7 +47,6 @@ describe("the plugin", () => {
|
||||
});
|
||||
|
||||
describe('when invoked', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockObjectPath = [{
|
||||
name: 'mock folder',
|
||||
@@ -63,11 +62,15 @@ describe("the plugin", () => {
|
||||
key: 'test'
|
||||
}
|
||||
}));
|
||||
|
||||
goToFolderAction.invoke(mockObjectPath);
|
||||
});
|
||||
|
||||
it('goes to the original location', () => {
|
||||
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
|
||||
it('goes to the original location', (done) => {
|
||||
setTimeout(() => {
|
||||
expect(window.location.href).toContain('context.html#/browse/?tc.mode=fixed&tc.startBound=0&tc.endBound=1&tc.timeSystem=utc');
|
||||
done();
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ $elemBg: rgba(black, 0.7);
|
||||
}
|
||||
|
||||
.c-compass {
|
||||
pointer-events: none; // This allows the image element to receive a browser-level context click
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
@@ -242,6 +243,7 @@ $elemBg: rgba(black, 0.7);
|
||||
left: 0;
|
||||
clip-path: circle(50% at 50% 50%);
|
||||
border-radius: 100%;
|
||||
pointer-events: all;
|
||||
|
||||
svg, div {
|
||||
position: absolute;
|
||||
|
||||
@@ -390,7 +390,9 @@ export default {
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
|
||||
this.imageContainerResizeObserver.disconnect();
|
||||
if (this.imageContainerResizeObserver) {
|
||||
this.imageContainerResizeObserver.disconnect();
|
||||
}
|
||||
|
||||
if (this.relatedTelemetry.hasRelatedTelemetry) {
|
||||
this.relatedTelemetry.destroy();
|
||||
@@ -702,7 +704,7 @@ export default {
|
||||
window.clearInterval(this.durationTracker);
|
||||
},
|
||||
updateDuration() {
|
||||
let currentTime = this.openmct.time.clock().currentValue();
|
||||
let currentTime = this.openmct.time.clock() && this.openmct.time.clock().currentValue();
|
||||
this.numericDuration = currentTime - this.parsedSelectedTime;
|
||||
},
|
||||
resetAgeCSS() {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import ImageryPlugin from './plugin.js';
|
||||
|
||||
import Vue from 'vue';
|
||||
import {
|
||||
createOpenMct,
|
||||
@@ -89,15 +89,11 @@ describe("The Imagery View Layout", () => {
|
||||
const START = Date.now();
|
||||
const COUNT = 10;
|
||||
|
||||
let resolveFunction;
|
||||
|
||||
let openmct;
|
||||
let imageryPlugin;
|
||||
let parent;
|
||||
let child;
|
||||
let timeFormat = 'utc';
|
||||
let bounds = {
|
||||
start: START - TEN_MINUTES,
|
||||
end: START
|
||||
};
|
||||
let imageTelemetry = generateTelemetry(START - TEN_MINUTES, COUNT);
|
||||
let imageryObject = {
|
||||
identifier: {
|
||||
@@ -205,6 +201,10 @@ describe("The Imagery View Layout", () => {
|
||||
|
||||
openmct = createOpenMct();
|
||||
|
||||
openmct.install(openmct.plugins.MyItems());
|
||||
openmct.install(openmct.plugins.LocalTimeSystem());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
|
||||
parent = document.createElement('div');
|
||||
child = document.createElement('div');
|
||||
parent.appendChild(child);
|
||||
@@ -215,22 +215,18 @@ describe("The Imagery View Layout", () => {
|
||||
});
|
||||
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
|
||||
|
||||
imageryPlugin = new ImageryPlugin();
|
||||
openmct.install(imageryPlugin);
|
||||
|
||||
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve({}));
|
||||
|
||||
openmct.time.timeSystem(timeFormat, {
|
||||
start: 0,
|
||||
end: 4
|
||||
});
|
||||
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless(appHolder);
|
||||
openmct.start(appHolder);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 1
|
||||
});
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
@@ -248,7 +244,7 @@ describe("The Imagery View Layout", () => {
|
||||
let imageryViewProvider;
|
||||
let imageryView;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
beforeEach(async () => {
|
||||
let telemetryRequestResolve;
|
||||
let telemetryRequestPromise = new Promise((resolve) => {
|
||||
telemetryRequestResolve = resolve;
|
||||
@@ -260,23 +256,18 @@ describe("The Imagery View Layout", () => {
|
||||
return telemetryRequestPromise;
|
||||
});
|
||||
|
||||
openmct.time.clock('local', {
|
||||
start: bounds.start,
|
||||
end: bounds.end + 100
|
||||
});
|
||||
|
||||
applicableViews = openmct.objectViews.get(imageryObject, []);
|
||||
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
|
||||
imageryView = imageryViewProvider.view(imageryObject);
|
||||
imageryView.show(child);
|
||||
|
||||
await telemetryRequestPromise;
|
||||
await Vue.nextTick();
|
||||
|
||||
return done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.time.stopClock();
|
||||
openmct.router.removeListener('change:hash', resolveFunction);
|
||||
|
||||
imageryView.destroy();
|
||||
});
|
||||
|
||||
@@ -286,43 +277,44 @@ describe("The Imagery View Layout", () => {
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should show the clicked thumbnail as the main image", async () => {
|
||||
it("should show the clicked thumbnail as the main image", (done) => {
|
||||
const target = imageTelemetry[5].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should show that an image is new", async (done) => {
|
||||
await Vue.nextTick();
|
||||
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
expect(imageIsNew).toBeTrue();
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should show that an image is not new", async (done) => {
|
||||
xit("should show that an image is new", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
expect(imageIsNew).toBeTrue();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
});
|
||||
|
||||
xit("should show that an image is not new", (done) => {
|
||||
const target = imageTelemetry[2].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
|
||||
await Vue.nextTick();
|
||||
Vue.nextTick(() => {
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
// used in code, need to wait to the 500ms here too
|
||||
setTimeout(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
expect(imageIsNew).toBeFalse();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
expect(imageIsNew).toBeFalse();
|
||||
done();
|
||||
}, REFRESH_CSS_MS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should navigate via arrow keys", async () => {
|
||||
it("should navigate via arrow keys", (done) => {
|
||||
let keyOpts = {
|
||||
element: parent.querySelector('.c-imagery'),
|
||||
key: 'ArrowLeft',
|
||||
@@ -332,14 +324,15 @@ describe("The Imagery View Layout", () => {
|
||||
|
||||
simulateKeyEvent(keyOpts);
|
||||
|
||||
await Vue.nextTick();
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should navigate via numerous arrow keys", async () => {
|
||||
it("should navigate via numerous arrow keys", (done) => {
|
||||
let element = parent.querySelector('.c-imagery');
|
||||
let type = 'keyup';
|
||||
let leftKeyOpts = {
|
||||
@@ -362,12 +355,12 @@ describe("The Imagery View Layout", () => {
|
||||
// right once
|
||||
simulateKeyEvent(rightKeyOpts);
|
||||
|
||||
await Vue.nextTick();
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe("The local time", () => {
|
||||
beforeEach(() => {
|
||||
localTimeSystem = openmct.time.timeSystem(LOCAL_SYSTEM_KEY, {
|
||||
start: 0,
|
||||
end: 4
|
||||
end: 1
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("The Move Action plugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
|
||||
@@ -135,6 +135,7 @@ import SearchResults from './SearchResults.vue';
|
||||
import Sidebar from './Sidebar.vue';
|
||||
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
|
||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
@@ -189,14 +190,14 @@ export default {
|
||||
selectedPage() {
|
||||
const pages = this.getPages();
|
||||
if (!pages) {
|
||||
return null;
|
||||
return {};
|
||||
}
|
||||
|
||||
return pages.find(page => page.isSelected);
|
||||
},
|
||||
selectedSection() {
|
||||
if (!this.sections.length) {
|
||||
return null;
|
||||
return {};
|
||||
}
|
||||
|
||||
return this.sections.find(section => section.isSelected);
|
||||
@@ -216,6 +217,7 @@ export default {
|
||||
|
||||
window.addEventListener('orientationchange', this.formatSidebar);
|
||||
window.addEventListener("hashchange", this.navigateToSectionPage, false);
|
||||
this.openmct.router.on('change:params', this.changeSectionPage);
|
||||
|
||||
this.navigateToSectionPage();
|
||||
},
|
||||
@@ -226,6 +228,7 @@ export default {
|
||||
|
||||
window.removeEventListener('orientationchange', this.formatSidebar);
|
||||
window.removeEventListener("hashchange", this.navigateToSectionPage);
|
||||
this.openmct.router.off('change:params', this.changeSectionPage);
|
||||
},
|
||||
updated: function () {
|
||||
this.$nextTick(() => {
|
||||
@@ -233,6 +236,28 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
changeSectionPage(newParams, oldParams, changedParams) {
|
||||
if (newParams.view !== NOTEBOOK_VIEW_TYPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pageId = newParams.pageId;
|
||||
let sectionId = newParams.sectionId;
|
||||
|
||||
if (!pageId && !sectionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sections.forEach(section => {
|
||||
section.isSelected = Boolean(section.id === sectionId);
|
||||
|
||||
if (section.isSelected) {
|
||||
section.pages.forEach(page => {
|
||||
page.isSelected = Boolean(page.id === pageId);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
changeSelectedSection({ sectionId, pageId }) {
|
||||
const sections = this.sections.map(s => {
|
||||
s.isSelected = false;
|
||||
@@ -518,9 +543,11 @@ export default {
|
||||
return this.sections.find(section => section.isSelected);
|
||||
},
|
||||
navigateToSectionPage() {
|
||||
const { pageId, sectionId } = this.openmct.router.getParams();
|
||||
let { pageId, sectionId } = this.openmct.router.getParams();
|
||||
|
||||
if (!pageId || !sectionId) {
|
||||
return;
|
||||
sectionId = this.selectedSection.id;
|
||||
pageId = this.selectedPage.id;
|
||||
}
|
||||
|
||||
const sections = this.sections.map(s => {
|
||||
|
||||
@@ -145,7 +145,7 @@ export default {
|
||||
|
||||
const relativeHash = hash.slice(hash.indexOf('#'));
|
||||
const url = new URL(relativeHash, `${location.protocol}//${location.host}${location.pathname}`);
|
||||
window.location.hash = url.hash;
|
||||
this.openmct.router.navigate(url.hash);
|
||||
},
|
||||
formatTime(unixTime, timeFormat) {
|
||||
return Moment.utc(unixTime).format(timeFormat);
|
||||
|
||||
@@ -111,10 +111,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pages() {
|
||||
const selectedSection = this.sections.find(section => section.isSelected);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export const EVENT_SNAPSHOTS_UPDATED = 'SNAPSHOTS_UPDATED';
|
||||
export const NOTEBOOK_DEFAULT = 'DEFAULT';
|
||||
export const NOTEBOOK_SNAPSHOT = 'SNAPSHOT';
|
||||
export const NOTEBOOK_VIEW_TYPE = 'notebook-vue';
|
||||
|
||||
@@ -65,7 +65,8 @@ describe("Notebook plugin:", () => {
|
||||
|
||||
afterAll(() => {
|
||||
appHolder.remove();
|
||||
resetApplicationState(openmct);
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("has type as Notebook", () => {
|
||||
|
||||
@@ -140,7 +140,8 @@ describe('Notebook Entries:', () => {
|
||||
|
||||
afterEach(() => {
|
||||
notebookDomainObject.configuration.entries[selectedSection.id][selectedPage.id] = [];
|
||||
resetApplicationState(openmct);
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('getNotebookEntries has no entries', () => {
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('Notebook Storage:', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetApplicationState(openmct);
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('has empty local Storage', () => {
|
||||
|
||||
@@ -91,7 +91,7 @@ export default class CouchObjectProvider {
|
||||
* persist any queued objects
|
||||
* @private
|
||||
*/
|
||||
checkResponse(response, intermediateResponse) {
|
||||
checkResponse(response, intermediateResponse, key) {
|
||||
let requestSuccess = false;
|
||||
const id = response ? response.id : undefined;
|
||||
let rev;
|
||||
@@ -113,6 +113,8 @@ export default class CouchObjectProvider {
|
||||
if (this.objectQueue[id].hasNext()) {
|
||||
this.updateQueued(id);
|
||||
}
|
||||
} else {
|
||||
this.objectQueue[key].pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +134,7 @@ export default class CouchObjectProvider {
|
||||
}
|
||||
|
||||
//Sometimes CouchDB returns the old rev which fetching the object if there is a document update in progress
|
||||
//Only update the rev if it's the first time we're getting the object from CouchDB. Subsequent revs should only be updated by updates.
|
||||
if (!this.objectQueue[key].pending && !this.objectQueue[key].rev) {
|
||||
if (!this.objectQueue[key].pending) {
|
||||
this.objectQueue[key].updateRevision(response[REV]);
|
||||
}
|
||||
|
||||
@@ -458,7 +459,7 @@ export default class CouchObjectProvider {
|
||||
const queued = this.objectQueue[key].dequeue();
|
||||
let document = new CouchDocument(key, queued.model);
|
||||
this.request(key, "PUT", document).then((response) => {
|
||||
this.checkResponse(response, queued.intermediateResponse);
|
||||
this.checkResponse(response, queued.intermediateResponse, key);
|
||||
});
|
||||
|
||||
return intermediateResponse.promise;
|
||||
@@ -473,7 +474,7 @@ export default class CouchObjectProvider {
|
||||
const queued = this.objectQueue[key].dequeue();
|
||||
let document = new CouchDocument(key, queued.model, this.objectQueue[key].rev);
|
||||
this.request(key, "PUT", document).then((response) => {
|
||||
this.checkResponse(response, queued.intermediateResponse);
|
||||
this.checkResponse(response, queued.intermediateResponse, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('the plugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updates an object', () => {
|
||||
it('updates an object', (done) => {
|
||||
return openmct.objects.save(mockDomainObject).then((result) => {
|
||||
expect(result).toBeTrue();
|
||||
expect(provider.create).toHaveBeenCalled();
|
||||
@@ -128,6 +128,7 @@ describe('the plugin', () => {
|
||||
return openmct.objects.save(mockDomainObject).then((updatedResult) => {
|
||||
expect(updatedResult).toBeTrue();
|
||||
expect(provider.update).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
:series="config.series.models"
|
||||
:highlights="highlights"
|
||||
:legend="config.legend"
|
||||
@legendHoverChanged="legendHoverChanged"
|
||||
/>
|
||||
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
||||
<y-axis v-if="config.series.models.length > 0"
|
||||
@@ -45,14 +46,9 @@
|
||||
|
||||
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
|
||||
<div class="l-state-indicators">
|
||||
<span v-if="plotHistory.length"
|
||||
class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
|
||||
<span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
|
||||
title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."
|
||||
></span>
|
||||
<span v-else
|
||||
class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
|
||||
title="This plot is not currently displaying the latest data. Resume to view latest data."
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<mct-ticks v-show="gridLines && !options.compact"
|
||||
@@ -70,9 +66,9 @@
|
||||
<div ref="chartContainer"
|
||||
class="gl-plot-chart-wrapper"
|
||||
>
|
||||
<mct-chart :series-config="config"
|
||||
:rectangles="rectangles"
|
||||
<mct-chart :rectangles="rectangles"
|
||||
:highlights="highlights"
|
||||
:show-limit-line-labels="showLimitLineLabels"
|
||||
@plotReinitializeCanvas="initCanvas"
|
||||
/>
|
||||
</div>
|
||||
@@ -120,6 +116,15 @@
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="isTimeOutOfSync || isFrozen"
|
||||
class="c-button-set c-button-set--strip-h"
|
||||
>
|
||||
<button class="c-button icon-clock"
|
||||
title="Synchronize Time Conductor"
|
||||
@click="showSynchronizeDialog()"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Cursor guides-->
|
||||
@@ -156,6 +161,7 @@ import MctTicks from "./MctTicks.vue";
|
||||
import MctChart from "./chart/MctChart.vue";
|
||||
import XAxis from "./axis/XAxis.vue";
|
||||
import YAxis from "./axis/YAxis.vue";
|
||||
import _ from "lodash";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -208,7 +214,9 @@ export default {
|
||||
config: {},
|
||||
pending: 0,
|
||||
isRealTime: this.openmct.time.clock() !== undefined,
|
||||
loaded: false
|
||||
loaded: false,
|
||||
isTimeOutOfSync: false,
|
||||
showLimitLineLabels: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -458,6 +466,7 @@ export default {
|
||||
|
||||
if (typeof value !== 'undefined') {
|
||||
this._synchronized = value;
|
||||
this.isTimeOutOfSync = value !== true;
|
||||
|
||||
const isUnsynced = isLocalClock && !value;
|
||||
this.setStatus(isUnsynced);
|
||||
@@ -468,6 +477,7 @@ export default {
|
||||
|
||||
setStatus(isNotInSync) {
|
||||
const outOfSync = isNotInSync === true
|
||||
|| this.isTimeOutOfSync === true
|
||||
|| this.isFrozen === true;
|
||||
if (outOfSync === true) {
|
||||
this.openmct.status.set(this.domainObject.identifier, 'timeconductor-unsynced');
|
||||
@@ -490,6 +500,10 @@ export default {
|
||||
},
|
||||
|
||||
initialize() {
|
||||
_.debounce(this.handleWindowResize, 400);
|
||||
this.plotContainerResizeObserver = new ResizeObserver(this.handleWindowResize);
|
||||
this.plotContainerResizeObserver.observe(this.$parent.$refs.plotWrapper);
|
||||
|
||||
// Setup canvas etc.
|
||||
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
|
||||
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
|
||||
@@ -931,6 +945,51 @@ export default {
|
||||
this.clear();
|
||||
},
|
||||
|
||||
showSynchronizeDialog() {
|
||||
const isLocalClock = this.openmct.time.clock();
|
||||
if (isLocalClock !== undefined) {
|
||||
const message = `
|
||||
This action will change the Time Conductor to Fixed Timespan mode with this plot view's current time bounds.
|
||||
Do you want to continue?
|
||||
`;
|
||||
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
title: 'Synchronize Time Conductor',
|
||||
iconClass: 'alert',
|
||||
size: 'fit',
|
||||
message: message,
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
this.synchronizeTimeConductor();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
this.openmct.notifications.alert('Time conductor bounds have changed.');
|
||||
this.synchronizeTimeConductor();
|
||||
}
|
||||
},
|
||||
|
||||
synchronizeTimeConductor() {
|
||||
this.openmct.time.stopClock();
|
||||
const range = this.config.xAxis.get('displayRange');
|
||||
this.openmct.time.bounds({
|
||||
start: range.min,
|
||||
end: range.max
|
||||
});
|
||||
this.isTimeOutOfSync = false;
|
||||
},
|
||||
|
||||
destroy() {
|
||||
configStore.deleteStore(this.config.id);
|
||||
|
||||
@@ -948,12 +1007,23 @@ export default {
|
||||
this.removeStatusListener();
|
||||
}
|
||||
|
||||
this.plotContainerResizeObserver.disconnect();
|
||||
|
||||
this.openmct.time.off('clock', this.updateRealTime);
|
||||
this.openmct.time.off('bounds', this.updateDisplayBounds);
|
||||
this.openmct.objectViews.off('clearData', this.clearData);
|
||||
},
|
||||
updateStatus(status) {
|
||||
this.$emit('statusUpdated', status);
|
||||
},
|
||||
handleWindowResize() {
|
||||
if (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth) {
|
||||
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
|
||||
this.config.series.models.forEach(this.loadSeriesData, this);
|
||||
}
|
||||
},
|
||||
legendHoverChanged(data) {
|
||||
this.showLimitLineLabels = data;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -228,15 +228,16 @@ export default {
|
||||
|
||||
doTickUpdate() {
|
||||
if (this.shouldCheckWidth) {
|
||||
const tickElements = this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
|
||||
const tickElements = this.$refs.tickContainer && this.$refs.tickContainer.querySelectorAll('.gl-plot-tick > span');
|
||||
if (tickElements) {
|
||||
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
|
||||
return Math.max(memo, first.offsetWidth);
|
||||
}, 0));
|
||||
|
||||
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
|
||||
return Math.max(memo, first.offsetWidth);
|
||||
}, 0));
|
||||
|
||||
this.tickWidth = tickWidth;
|
||||
this.$emit('plotTickWidth', tickWidth);
|
||||
this.shouldCheckWidth = false;
|
||||
this.tickWidth = tickWidth;
|
||||
this.$emit('plotTickWidth', tickWidth);
|
||||
this.shouldCheckWidth = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.tickUpdate = false;
|
||||
@@ -48,7 +48,7 @@ export default function PlotViewProvider(openmct) {
|
||||
name: 'Plot',
|
||||
cssClass: 'icon-telemetry',
|
||||
canView(domainObject, objectPath) {
|
||||
return isCompactView(objectPath) && hasTelemetry(domainObject, openmct);
|
||||
return hasTelemetry(domainObject, openmct);
|
||||
},
|
||||
|
||||
view: function (domainObject, objectPath) {
|
||||
46
src/plugins/plot/chart/LimitLabel.vue
Normal file
46
src/plugins/plot/chart/LimitLabel.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="plot-series-limit-label"
|
||||
:style="styleObj"
|
||||
:class="limit.cssClass"
|
||||
>
|
||||
<span class="plot-series-limit-value">{{ limit.value }}</span>
|
||||
<span class="plot-series-color-swatch"
|
||||
:style="{ 'background-color': limit.color }"
|
||||
></span>
|
||||
<span class="plot-series-name">{{ limit.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
limit: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
point: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top - 10}px`;
|
||||
const left = `${this.point.left + 5}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'top': top,
|
||||
'left': left,
|
||||
'color': '#fff'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
44
src/plugins/plot/chart/LimitLine.vue
Normal file
44
src/plugins/plot/chart/LimitLine.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<hr :style="styleObj"
|
||||
:class="cssWithoutUprLwr"
|
||||
>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
point: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top}px`;
|
||||
const left = `${this.point.left}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'width': '100%',
|
||||
'top': top,
|
||||
'left': left
|
||||
};
|
||||
},
|
||||
cssWithoutUprLwr() {
|
||||
let cssClass = this.cssClass.replace(/is-limit--upr/gi, 'is-limit--line');
|
||||
cssClass = cssClass.replace(/is-limit--lwr/gi, 'is-limit--line');
|
||||
|
||||
return cssClass;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
105
src/plugins/plot/chart/MCTChartAlarmLineSet.js
Normal file
105
src/plugins/plot/chart/MCTChartAlarmLineSet.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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 eventHelpers from '../lib/eventHelpers';
|
||||
|
||||
export default class MCTChartAlarmLineSet {
|
||||
constructor(series, chart, offset, bounds) {
|
||||
this.series = series;
|
||||
this.chart = chart;
|
||||
this.offset = offset;
|
||||
this.bounds = bounds;
|
||||
this.limits = [];
|
||||
|
||||
eventHelpers.extend(this);
|
||||
this.listenTo(series, 'limitBounds', this.updateBounds, this);
|
||||
this.listenTo(series, 'change:xKey', this.getLimitPoints, this);
|
||||
|
||||
if (series.limits) {
|
||||
this.getLimitPoints(series);
|
||||
}
|
||||
}
|
||||
|
||||
updateBounds(bounds) {
|
||||
this.bounds = bounds;
|
||||
this.getLimitPoints(this.series);
|
||||
}
|
||||
|
||||
color() {
|
||||
return this.series.get('color');
|
||||
}
|
||||
|
||||
name() {
|
||||
return this.series.get('name');
|
||||
}
|
||||
|
||||
makePoint(point, series) {
|
||||
if (!this.offset.xVal) {
|
||||
this.chart.setOffset(point, undefined, series);
|
||||
}
|
||||
|
||||
return {
|
||||
x: this.offset.xVal(point, series),
|
||||
y: this.offset.yVal(point, series)
|
||||
};
|
||||
}
|
||||
|
||||
getLimitPoints(series) {
|
||||
this.limits = [];
|
||||
let xKey = series.get('xKey');
|
||||
Object.keys(series.limits).forEach((key) => {
|
||||
const limitForLevel = series.limits[key];
|
||||
if (limitForLevel.high) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.high), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.high),
|
||||
color: this.color().asHexString(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.high.cssClass
|
||||
});
|
||||
}
|
||||
|
||||
if (limitForLevel.low) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.low), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.low),
|
||||
color: this.color().asHexString(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.low.cssClass
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.limits = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.stopListening();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,9 @@
|
||||
<div class="gl-plot-chart-area">
|
||||
<span v-html="canvasTemplate"></span>
|
||||
<span v-html="canvasTemplate"></span>
|
||||
<div ref="limitArea"
|
||||
class="js-limit-area"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,8 +37,12 @@ import MCTChartLineLinear from './MCTChartLineLinear';
|
||||
import MCTChartLineStepAfter from './MCTChartLineStepAfter';
|
||||
import MCTChartPointSet from './MCTChartPointSet';
|
||||
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
|
||||
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
|
||||
import configStore from "../configuration/configStore";
|
||||
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
|
||||
import LimitLine from "./LimitLine.vue";
|
||||
import LimitLabel from "./LimitLabel.vue";
|
||||
import Vue from 'vue';
|
||||
|
||||
const MARKER_SIZE = 6.0;
|
||||
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
|
||||
@@ -54,6 +61,12 @@ export default {
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
showLimitLineLabels: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -67,18 +80,22 @@ export default {
|
||||
},
|
||||
rectangles() {
|
||||
this.scheduleDraw();
|
||||
},
|
||||
showLimitLineLabels() {
|
||||
this.drawLimitLines();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
this.config = this.getConfig();
|
||||
this.isDestroyed = false;
|
||||
this.lines = [];
|
||||
this.limitLines = [];
|
||||
this.pointSets = [];
|
||||
this.alarmSets = [];
|
||||
this.offset = {};
|
||||
this.seriesElements = new WeakMap();
|
||||
this.seriesLimits = new WeakMap();
|
||||
|
||||
let canvasEls = this.$parent.$refs.chartContainer.querySelectorAll("canvas");
|
||||
const mainCanvas = canvasEls[1];
|
||||
@@ -90,8 +107,8 @@ export default {
|
||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
||||
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
|
||||
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
|
||||
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
|
||||
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
|
||||
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
|
||||
this.config.series.forEach(this.onSeriesAdd, this);
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -116,15 +133,18 @@ export default {
|
||||
this.changeInterpolate(mode, o, series);
|
||||
this.changeMarkers(mode, o, series);
|
||||
this.changeAlarmMarkers(mode, o, series);
|
||||
this.changeLimitLines(mode, o, series);
|
||||
},
|
||||
onSeriesAdd(series) {
|
||||
this.listenTo(series, 'change:xKey', this.reDraw, this);
|
||||
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
|
||||
this.listenTo(series, 'change:markers', this.changeMarkers, this);
|
||||
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
|
||||
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
|
||||
this.listenTo(series, 'change', this.scheduleDraw);
|
||||
this.listenTo(series, 'add', this.scheduleDraw);
|
||||
this.makeChartElement(series);
|
||||
this.makeLimitLines(series);
|
||||
},
|
||||
changeInterpolate(mode, o, series) {
|
||||
if (mode === o) {
|
||||
@@ -178,6 +198,14 @@ export default {
|
||||
this.pointSets.push(pointSet);
|
||||
}
|
||||
},
|
||||
changeLimitLines(mode, o, series) {
|
||||
if (mode === o) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeLimitLines(series);
|
||||
this.updateLimitsAndDraw();
|
||||
},
|
||||
onSeriesRemove(series) {
|
||||
this.stopListening(series);
|
||||
this.removeChartElement(series);
|
||||
@@ -187,6 +215,7 @@ export default {
|
||||
this.isDestroyed = true;
|
||||
this.stopListening();
|
||||
this.lines.forEach(line => line.destroy());
|
||||
this.limitLines.forEach(line => line.destroy());
|
||||
DrawLoader.releaseDrawAPI(this.drawAPI);
|
||||
},
|
||||
clearOffset() {
|
||||
@@ -199,6 +228,9 @@ export default {
|
||||
this.lines.forEach(function (line) {
|
||||
line.reset();
|
||||
});
|
||||
this.limitLines.forEach(function (line) {
|
||||
line.reset();
|
||||
});
|
||||
this.pointSets.forEach(function (pointSet) {
|
||||
pointSet.reset();
|
||||
});
|
||||
@@ -269,6 +301,8 @@ export default {
|
||||
}
|
||||
|
||||
this.seriesElements.delete(series);
|
||||
|
||||
this.clearLimitLines(series);
|
||||
},
|
||||
lineForSeries(series) {
|
||||
if (series.get('interpolate') === 'linear') {
|
||||
@@ -287,6 +321,14 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
limitLineForSeries(series) {
|
||||
return new MCTChartAlarmLineSet(
|
||||
series,
|
||||
this,
|
||||
this.offset,
|
||||
this.openmct.time.bounds()
|
||||
);
|
||||
},
|
||||
pointSetForSeries(series) {
|
||||
if (series.get('markers')) {
|
||||
return new MCTChartPointSet(
|
||||
@@ -308,7 +350,8 @@ export default {
|
||||
makeChartElement(series) {
|
||||
const elements = {
|
||||
lines: [],
|
||||
pointSets: []
|
||||
pointSets: [],
|
||||
limitLines: []
|
||||
};
|
||||
|
||||
const line = this.lineForSeries(series);
|
||||
@@ -330,6 +373,37 @@ export default {
|
||||
|
||||
this.seriesElements.set(series, elements);
|
||||
},
|
||||
makeLimitLines(series) {
|
||||
this.clearLimitLines(series);
|
||||
|
||||
if (!series.get('limitLines')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limitElements = {
|
||||
limitLines: []
|
||||
};
|
||||
|
||||
const limitLine = this.limitLineForSeries(series);
|
||||
if (limitLine) {
|
||||
limitElements.limitLines.push(limitLine);
|
||||
this.limitLines.push(limitLine);
|
||||
}
|
||||
|
||||
this.seriesLimits.set(series, limitElements);
|
||||
},
|
||||
clearLimitLines(series) {
|
||||
const seriesLimits = this.seriesLimits.get(series);
|
||||
|
||||
if (seriesLimits) {
|
||||
seriesLimits.limitLines.forEach(function (line) {
|
||||
this.limitLines.splice(this.limitLines.indexOf(line), 1);
|
||||
line.destroy();
|
||||
}, this);
|
||||
|
||||
this.seriesLimits.delete(series);
|
||||
}
|
||||
},
|
||||
canDraw() {
|
||||
if (!this.offset.x || !this.offset.y) {
|
||||
return false;
|
||||
@@ -337,6 +411,10 @@ export default {
|
||||
|
||||
return true;
|
||||
},
|
||||
updateLimitsAndDraw() {
|
||||
this.drawLimitLines();
|
||||
this.scheduleDraw();
|
||||
},
|
||||
scheduleDraw() {
|
||||
if (!this.drawScheduled) {
|
||||
requestAnimationFrame(this.draw);
|
||||
@@ -385,6 +463,68 @@ export default {
|
||||
this.pointSets.forEach(this.drawPoints, this);
|
||||
this.alarmSets.forEach(this.drawAlarmPoints, this);
|
||||
},
|
||||
drawLimitLines() {
|
||||
if (this.canDraw()) {
|
||||
this.updateViewport();
|
||||
|
||||
if (!this.drawAPI.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
|
||||
|
||||
this.limitLines.forEach((limitLine) => {
|
||||
let limitContainerEl = this.$refs.limitArea;
|
||||
limitLine.limits.forEach((limit) => {
|
||||
const showLabels = this.showLabels(limit.seriesKey);
|
||||
if (showLabels) {
|
||||
let limitLabelEl = this.getLimitLabel(limit);
|
||||
limitContainerEl.appendChild(limitLabelEl);
|
||||
}
|
||||
|
||||
let limitEl = this.getLimitElement(limit);
|
||||
limitContainerEl.appendChild(limitEl);
|
||||
|
||||
}, this);
|
||||
});
|
||||
}
|
||||
},
|
||||
showLabels(seriesKey) {
|
||||
return this.showLimitLineLabels.seriesKey
|
||||
&& (this.showLimitLineLabels.seriesKey === seriesKey);
|
||||
},
|
||||
getLimitElement(limit) {
|
||||
let point = {
|
||||
left: 0,
|
||||
top: this.drawAPI.y(limit.point.y)
|
||||
};
|
||||
let LimitLineClass = Vue.extend(LimitLine);
|
||||
const component = new LimitLineClass({
|
||||
propsData: {
|
||||
point,
|
||||
cssClass: limit.cssClass
|
||||
}
|
||||
});
|
||||
component.$mount();
|
||||
|
||||
return component.$el;
|
||||
},
|
||||
getLimitLabel(limit) {
|
||||
let point = {
|
||||
left: 0,
|
||||
top: this.drawAPI.y(limit.point.y)
|
||||
};
|
||||
let LimitLabelClass = Vue.extend(LimitLabel);
|
||||
const component = new LimitLabelClass({
|
||||
propsData: {
|
||||
limit,
|
||||
point
|
||||
}
|
||||
});
|
||||
component.$mount();
|
||||
|
||||
return component.$el;
|
||||
},
|
||||
drawAlarmPoints(alarmSet) {
|
||||
this.drawAPI.drawLimitPoints(
|
||||
alarmSet.points,
|
||||
@@ -401,11 +541,12 @@ export default {
|
||||
chartElement.series.get('markerShape')
|
||||
);
|
||||
},
|
||||
drawLine(chartElement) {
|
||||
drawLine(chartElement, disconnected) {
|
||||
this.drawAPI.drawLine(
|
||||
chartElement.getBuffer(),
|
||||
chartElement.color().asRGBAArray(),
|
||||
chartElement.count
|
||||
chartElement.count,
|
||||
disconnected
|
||||
);
|
||||
},
|
||||
drawHighlights() {
|
||||
@@ -97,7 +97,8 @@ export default class PlotSeries extends Model {
|
||||
markers: true,
|
||||
markerShape: 'point',
|
||||
markerSize: 2.0,
|
||||
alarmMarkers: true
|
||||
alarmMarkers: true,
|
||||
limitLines: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -115,9 +116,16 @@ export default class PlotSeries extends Model {
|
||||
this.domainObject = options.domainObject;
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
|
||||
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
|
||||
this.limits = this.limitDefinition.limits();
|
||||
this.openmct.time.on('bounds', this.updateLimits);
|
||||
this.on('destroy', this.onDestroy, this);
|
||||
}
|
||||
|
||||
updateLimits(bounds) {
|
||||
this.emit('limitBounds', bounds);
|
||||
}
|
||||
|
||||
locateOldObject(oldStyleParent) {
|
||||
return oldStyleParent.useCapability('composition')
|
||||
.then(function (children) {
|
||||
@@ -166,7 +166,6 @@ export default class YAxisModel extends Model {
|
||||
* Update yAxis format, values, and label from known series.
|
||||
*/
|
||||
updateFromSeries(series) {
|
||||
this.unset('displayRange');
|
||||
const plotModel = this.plot.get('domainObject');
|
||||
const label = _.get(plotModel, 'configuration.yAxis.label');
|
||||
const sampleSeries = series.first();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user