Compare commits

..

101 Commits

Author SHA1 Message Date
Shefali Joshi
b46441ee77 Merge branch 'master' into telemetry-collection 2021-08-10 10:31:49 -07:00
Nikhil
2564e75fc9 [Notebook] Example Imagery doesn't capture images #2942 (#2943)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2021-08-10 10:26:51 -07:00
Andrew Henry
3ab120ee26 Merge branch 'master' into telemetry-collection 2021-08-10 09:50:30 -07:00
Shefali Joshi
f42fe78acf 1.7.6 master merge (#4097)
* remove can edit from hyperlink (#4076)

* Add check for stop observing before calling it (#4080)

* Set the yKey value on the series when it's changed (#4083)

* [Imagery] Click on image to get a large view #3582 (#4085)

fixed issue where large imagery view opens only once.

Co-authored-by: Henry Hsu <hhsu0219@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2021-08-10 07:00:13 -07:00
Nikhil
fe928a1386 [Imagery] Click on image to get a large view #3582 (#4077)
fixed issue where large imagery view opens only once.
2021-08-04 12:57:48 -07:00
Jamie Vigliotta
a7a9740610 fix lint error 2021-08-03 17:15:59 -07:00
Jamie Vigliotta
b0fb8bfb03 callout addRows reg vs filter more clearly 2021-08-03 17:07:37 -07:00
Jamie Vigliotta
3865d95a5b handling filtering correctly 2021-08-03 16:19:35 -07:00
Jamie Vigliotta
4d1fc642ea wiping rows on a filter reset 2021-08-03 09:45:28 -07:00
Jamie Vigliotta
5d5f13af79 removing console log 2021-08-02 14:35:35 -07:00
Jamie Vigliotta
b8e8a70211 fixed scope issue 2021-08-02 14:23:12 -07:00
Jamie Vigliotta
0ab4dbbdb2 removing commented code 2021-08-02 13:52:33 -07:00
Jamie Vigliotta
98b77f46a0 moving TableRows to TelemetryTable from TableRow collection, streamline what collections are doing 2021-08-02 13:27:50 -07:00
Jamie Vigliotta
25d83a2599 Merge branch 'master' into telemetry-collection
Merge'n master
2021-08-02 07:46:22 -07:00
Jamie V
560a2ce95a Merge branch 'master' into telemetry-collection 2021-07-30 14:56:56 -07:00
Jamie V
a735a5b680 Merge branch 'master' into telemetry-collection 2021-07-30 13:26:40 -07:00
Jamie Vigliotta
2feb1cac11 removing unused variable 2021-07-30 13:11:59 -07:00
Jamie Vigliotta
2d64f0e816 Merge branch 'telemetry-collection' of https://github.com/nasa/openmct into telemetry-collection
Merge'n master
2021-07-30 13:09:50 -07:00
Jamie Vigliotta
2c6c069e6c rename unclear function and array 2021-07-30 13:09:36 -07:00
Jamie V
3fca0fbf72 Merge branch 'master' into telemetry-collection 2021-07-27 15:51:34 -07:00
Jamie V
7fbe22ab10 Merge branch 'master' into telemetry-collection 2021-07-26 14:04:53 -07:00
Jamie V
08f924825b Merge branch 'master' into telemetry-collection 2021-07-22 12:05:18 -07:00
Jamie Vigliotta
073b7f28dd adding abort on destroy 2021-07-21 14:14:56 -07:00
Jamie V
20fe7a25eb Merge branch 'master' into telemetry-collection 2021-07-16 10:16:06 -07:00
Jamie V
1309d73a12 Merge branch 'master' into telemetry-collection 2021-07-15 09:05:23 -07:00
Andrew Henry
94a9dfc498 Merge branch 'master' into telemetry-collection 2021-07-14 09:27:18 -07:00
Jamie V
d8c0d4b656 Merge branch 'master' into telemetry-collection 2021-07-13 12:08:36 -07:00
Jamie Vigliotta
23988f5afe adding some missing items for rerequesting data 2021-07-06 16:19:41 -07:00
Jamie Vigliotta
53e0d8131e testing 2021-07-06 15:51:27 -07:00
Jamie Vigliotta
c153167045 fixing issue TESTING brought up! wooohooooo go tests! 2021-07-06 15:40:20 -07:00
Jamie Vigliotta
be6ab0ee15 Merge branch 'master' into telemetry-collection
Merge'n master
2021-07-06 13:06:14 -07:00
Jamie Vigliotta
c5fe38dd70 update tests 2021-07-06 13:05:45 -07:00
Jamie Vigliotta
7c4d70cfda optimizing setcolumnfilter method 2021-07-06 12:51:26 -07:00
Jamie Vigliotta
2ae3b1678e removing unneccessary code 2021-07-06 12:42:29 -07:00
Jamie Vigliotta
5208bdf891 removing unneccessary code 2021-07-06 12:29:14 -07:00
Jamie Vigliotta
4dc2069476 small changes 2021-07-06 12:27:28 -07:00
Jamie Vigliotta
2d4a3a3a5d removing perfomance testing 2021-07-06 12:19:45 -07:00
Jamie Vigliotta
bbbb176e96 fixing unpause 2021-07-06 12:19:24 -07:00
Jamie Vigliotta
30d9012af9 moved TCs to the TRcollection, modified the code to handle this 2021-07-06 12:06:36 -07:00
Jamie Vigliotta
1a02da38d2 WIP: testing performance, removed all row collections except one, moved telemetry collections into row collection for quick access to bounded telemetry arrays 2021-07-06 09:53:36 -07:00
Jamie Vigliotta
3a585f8773 wip 2021-07-01 09:53:23 -07:00
Jamie Vigliotta
b17d92d2ef Merge branch 'master' into telemetry-collection
Merge'n master
2021-06-28 10:47:06 -07:00
Andrew Henry
0f159f7ce1 Merge branch 'master' into telemetry-collection 2021-06-21 16:19:37 -07:00
Jamie Vigliotta
f2140c768c Merge branch 'telemetry-collection' of https://github.com/nasa/openmct into telemetry-collection
Merge'n master
2021-06-10 15:53:18 -07:00
Jamie Vigliotta
4bdb8cf0ea WIP PR updates 2021-06-10 15:53:02 -07:00
Jamie V
33fb70442a Merge branch 'master' into telemetry-collection 2021-06-08 13:25:46 -07:00
Jamie Vigliotta
993aa78562 Merge branch 'master' into telemetry-collection
Merge'n main branch
2021-06-07 09:47:41 -07:00
Jamie Vigliotta
b97a0616d6 WIP 2021-06-02 15:13:47 -07:00
Jamie V
d76cb9d344 Merge branch 'master' into telemetry-collection 2021-06-01 16:15:38 -07:00
Jamie Vigliotta
b4cfbd8d27 working through PR comments, removed erroneous timesystem and bounds emits, as well as _addPage and nextPage as those will be address in future enhancements 2021-06-01 13:43:46 -07:00
Shefali Joshi
defbfe6b62 Merge branch 'master' into telemetry-collection 2021-05-27 10:57:32 -07:00
Jamie Vigliotta
d4d39a4135 reverting unceccessary code change 2021-04-22 11:27:31 -07:00
Jamie Vigliotta
155cbb95fb more PR comment addressing, handling async errorsgit add . 2021-04-22 11:26:31 -07:00
Jamie Vigliotta
131ef579f9 removed unecessary method binding, using EventEmitter instead of rolling my own 2021-04-22 10:55:11 -07:00
Jamie Vigliotta
f87b87c248 Merge branch 'master' into telemetry-collection
Merge'n master
2021-04-22 10:25:44 -07:00
Jamie Vigliotta
bdc50564f2 updated telemetry table tests to handle telemetry collections 2021-04-15 13:19:39 -07:00
Jamie Vigliotta
d349315944 trying rule out tests as issues 2021-04-15 11:55:53 -07:00
Jamie Vigliotta
c5034beb9c changing circle ci config back 2021-04-15 11:27:13 -07:00
Jamie Vigliotta
5b97d96b2e testing browser versions for circle ci issue 2021-04-15 11:16:38 -07:00
Jamie Vigliotta
6a586635c3 testing if telemetry api tests are causing other issues 2021-04-15 10:55:52 -07:00
Jamie Vigliotta
fc8021c72c added very basic telemetry collection test to telemetry api tests, will need separate telemtery collection tests 2021-04-14 13:21:37 -07:00
Jamie Vigliotta
7fe19d424f fixed telemetryAPI tests and removed xdescribe 2021-04-14 11:41:49 -07:00
Jamie V
515c18278b Merge branch 'master' into telemetry-collection 2021-04-13 14:51:16 -07:00
Jamie Vigliotta
9b8518e6d2 last bit of tidying up before PR 2021-04-13 14:47:44 -07:00
Jamie Vigliotta
4639a1eabc removing debug code 2021-04-13 13:46:25 -07:00
Jamie Vigliotta
26f6407283 handling emitted arguments correctly 2021-04-13 13:37:04 -07:00
Jamie Vigliotta
b9fc3d0499 more updates for options handling 2021-04-13 12:35:18 -07:00
Jamie Vigliotta
5db71db974 cleaning up some code and handling arguments more clearly in regard to subscription vs request 2021-04-13 12:31:44 -07:00
Jamie Vigliotta
0b9bdb0c74 removing old commented code and files 2021-04-13 11:43:37 -07:00
Jamie Vigliotta
cdedea4bed another one more commit with old code commented out 2021-04-13 11:40:00 -07:00
Jamie Vigliotta
82096ea887 removed bounded row collection, moved to sorted collection, added limt on end value for state generator telemetry to prevent future telemetry 2021-04-12 17:30:34 -07:00
Jamie Vigliotta
43488646c0 removed bounded rows, moved functionality to sortedrows WIP 2021-04-09 12:32:17 -07:00
Jamie Vigliotta
71bbb67d78 WIP 2021-04-07 09:56:52 -07:00
Jamie Vigliotta
ad96e5ce66 WIP 2021-04-06 10:48:49 -07:00
Jamie Vigliotta
1dc245c9ee WIP 2021-04-05 17:30:34 -07:00
Jamie Vigliotta
c6c432005f WIP 2021-04-05 12:51:22 -07:00
Jamie Vigliotta
8a1a2bbf84 Merge branch 'master' into telemetry-collection
Merge'n master
2021-03-31 10:25:38 -07:00
Jamie Vigliotta
0cbb0e7ae2 adding some additional functionality to telemetry collectinos 2021-03-29 13:47:26 -07:00
Jamie Vigliotta
993576a471 Merge branch 'master' into telemetry-collection
Mege'n master
2021-03-29 10:30:11 -07:00
Jamie Vigliotta
d69056361f WIP 2021-03-29 10:29:30 -07:00
Jamie Vigliotta
30da83eb0a WIP 2021-03-25 15:08:57 -07:00
Jamie Vigliotta
e2b4f0a3fa updated comments and reorganized telemetry collection class a bit 2021-03-24 13:37:02 -07:00
Jamie Vigliotta
6f952789ed WIP 2021-03-22 12:24:27 -07:00
Jamie Vigliotta
2bf06dbf23 mergeing master 2021-03-19 10:28:54 -07:00
Jamie V
b02bd08a12 Merge branch 'master' into telemetry-collection 2021-02-19 12:00:01 -08:00
Jamie Vigliotta
a9b70c6f9a reworking telemetry collections to utilize collectionStates for state tracking 2021-02-16 15:56:06 -08:00
Jamie Vigliotta
c09fc6110b Merge branch 'master' into telemetry-collection
Merge'n master
2021-02-16 12:25:04 -08:00
Jamie Vigliotta
84e45e2543 Merge branch 'master' into telemetry-collection
Merge'n master
2021-02-12 11:42:22 -08:00
Jamie Vigliotta
8f50425043 Minor updates after meeting 2021-01-13 09:06:16 -08:00
Jamie Vigliotta
f7f1b0d97e removed unnecessary subscription service, just reusing from telemetry api 2021-01-07 09:46:24 -08:00
Jamie Vigliotta
cb08a82a5d WIP: reusing subscribe from telemetry api 2021-01-07 09:40:36 -08:00
Jamie Vigliotta
d9caa734d3 Merge branch 'master' into telemetry-collection
Merg'n master
2021-01-05 11:24:06 -08:00
Jamie Vigliotta
72848849dd WIP working with imagery for testing 2020-12-14 14:14:47 -08:00
Jamie Vigliotta
66130ba542 Merge branch 'master' into telemetry-collection
Merg'n master
2020-12-11 10:49:12 -08:00
Jamie Vigliotta
895bdc164f WIP 2020-12-09 11:57:11 -08:00
Jamie Vigliotta
c9728144a5 Merge branch 'master' into telemetry-collection
Merg'n master
2020-12-07 12:12:21 -08:00
Jamie Vigliotta
76fec7f3bc WIP 2020-12-01 12:04:14 -08:00
Jamie Vigliotta
1b4717065a WIP 2020-11-30 14:35:02 -08:00
Jamie Vigliotta
e24542c1a6 Merge branch 'master' into telemetry-collection
Merg'n master
2020-11-24 11:22:04 -08:00
Jamie Vigliotta
b08f3106ed WIP 2020-11-24 10:34:08 -08:00
Jamie Vigliotta
700bc7616d WIP 2020-11-20 11:13:41 -08:00
35 changed files with 1790 additions and 1477 deletions

View File

@@ -63,7 +63,7 @@ define([
StateGeneratorProvider.prototype.request = function (domainObject, options) {
var start = options.start;
var end = options.end;
var end = Math.min(Date.now(), options.end); // no future values
var duration = domainObject.telemetry.duration * 1000;
if (options.strategy === 'latest' || options.size === 1) {
start = end;

View File

@@ -195,7 +195,6 @@
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
{indicator: true}
));
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
openmct.start();
</script>
</html>

View File

@@ -21,24 +21,32 @@
*****************************************************************************/
define([
"moment-timezone",
"./src/indicators/ClockIndicator",
"./src/services/TickerService",
"./src/services/TimerService",
"./src/controllers/ClockController",
"./src/controllers/TimerController",
"./src/controllers/RefreshingController",
"./src/actions/StartTimerAction",
"./src/actions/RestartTimerAction",
"./src/actions/StopTimerAction",
"./src/actions/PauseTimerAction",
"./res/templates/clock.html",
"./res/templates/timer.html"
], function (
MomentTimezone,
ClockIndicator,
TickerService,
TimerService,
ClockController,
TimerController,
RefreshingController,
StartTimerAction,
RestartTimerAction,
StopTimerAction,
PauseTimerAction,
clockTemplate,
timerTemplate
) {
return {
@@ -65,6 +73,16 @@ define([
"value": "YYYY/MM/DD HH:mm:ss"
}
],
"indicators": [
{
"implementation": ClockIndicator,
"depends": [
"tickerService",
"CLOCK_INDICATOR_FORMAT"
],
"priority": "preferred"
}
],
"services": [
{
"key": "tickerService",
@@ -81,6 +99,14 @@ define([
}
],
"controllers": [
{
"key": "ClockController",
"implementation": ClockController,
"depends": [
"$scope",
"tickerService"
]
},
{
"key": "TimerController",
"implementation": TimerController,
@@ -100,6 +126,12 @@ define([
}
],
"views": [
{
"key": "clock",
"type": "clock",
"editable": false,
"template": clockTemplate
},
{
"key": "timer",
"type": "timer",
@@ -154,6 +186,70 @@ define([
}
],
"types": [
{
"key": "clock",
"name": "Clock",
"cssClass": "icon-clock",
"description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
"priority": 101,
"features": [
"creation"
],
"properties": [
{
"key": "clockFormat",
"name": "Display Format",
"control": "composite",
"items": [
{
"control": "select",
"options": [
{
"value": "YYYY/MM/DD hh:mm:ss",
"name": "YYYY/MM/DD hh:mm:ss"
},
{
"value": "YYYY/DDD hh:mm:ss",
"name": "YYYY/DDD hh:mm:ss"
},
{
"value": "hh:mm:ss",
"name": "hh:mm:ss"
}
],
"cssClass": "l-inline"
},
{
"control": "select",
"options": [
{
"value": "clock12",
"name": "12hr"
},
{
"value": "clock24",
"name": "24hr"
}
],
"cssClass": "l-inline"
}
]
},
{
"key": "timezone",
"name": "Timezone",
"control": "autocomplete",
"options": MomentTimezone.tz.names()
}
],
"model": {
"clockFormat": [
"YYYY/MM/DD hh:mm:ss",
"clock12"
],
"timezone": "UTC"
}
},
{
"key": "timer",
"name": "Timer",

View File

@@ -0,0 +1,32 @@
<!--
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.
-->
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
<div class="c-clock__timezone">
{{clock.zone()}}
</div>
<div class="c-clock__value">
{{clock.text()}}
</div>
<div class="c-clock__ampm">
{{clock.ampm()}}
</div>
</div>

View File

@@ -0,0 +1,110 @@
/*****************************************************************************
* 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([
'moment',
'moment-timezone'
],
function (
moment,
momentTimezone
) {
/**
* Controller for views of a Clock domain object.
*
* @constructor
* @memberof platform/features/clock
* @param {angular.Scope} $scope the Angular scope
* @param {platform/features/clock.TickerService} tickerService
* a service used to align behavior with clock ticks
*/
function ClockController($scope, tickerService) {
var lastTimestamp,
unlisten,
timeFormat,
zoneName,
self = this;
function update() {
var m = zoneName
? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp);
self.zoneAbbr = m.zoneAbbr();
self.textValue = timeFormat && m.format(timeFormat);
self.ampmValue = m.format("A"); // Just the AM or PM part
}
function tick(timestamp) {
lastTimestamp = timestamp;
update();
}
function updateModel(model) {
var baseFormat;
if (model !== undefined) {
baseFormat = model.clockFormat[0];
self.use24 = model.clockFormat[1] === 'clock24';
timeFormat = self.use24
? baseFormat.replace('hh', "HH") : baseFormat;
// If wrong timezone is provided, the UTC will be used
zoneName = momentTimezone.tz.names().includes(model.timezone)
? model.timezone : "UTC";
update();
}
}
// Pull in the model (clockFormat and timezone) from the domain object model
$scope.$watch('model', updateModel);
// Listen for clock ticks ... and stop listening on destroy
unlisten = tickerService.listen(tick);
$scope.$on('$destroy', unlisten);
}
/**
* Get the clock's time zone, as displayable text.
* @returns {string}
*/
ClockController.prototype.zone = function () {
return this.zoneAbbr;
};
/**
* Get the current time, as displayable text.
* @returns {string}
*/
ClockController.prototype.text = function () {
return this.textValue;
};
/**
* Get the text to display to qualify a time as AM or PM.
* @returns {string}
*/
ClockController.prototype.ampm = function () {
return this.use24 ? '' : this.ampmValue;
};
return ClockController;
}
);

View File

@@ -0,0 +1,65 @@
/*****************************************************************************
* 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(
['moment'],
function (moment) {
/**
* Indicator that displays the current UTC time in the status area.
* @implements {Indicator}
* @memberof platform/features/clock
* @param {platform/features/clock.TickerService} tickerService
* a service used to align behavior with clock ticks
* @param {string} indicatorFormat format string for timestamps
* shown in this indicator
*/
function ClockIndicator(tickerService, indicatorFormat) {
var self = this;
this.text = "";
tickerService.listen(function (timestamp) {
self.text = moment.utc(timestamp)
.format(indicatorFormat) + " UTC";
});
}
ClockIndicator.prototype.getGlyphClass = function () {
return "";
};
ClockIndicator.prototype.getCssClass = function () {
return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
};
ClockIndicator.prototype.getText = function () {
return this.text;
};
ClockIndicator.prototype.getDescription = function () {
return "";
};
return ClockIndicator;
}
);

View File

@@ -0,0 +1,107 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2017, 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/controllers/ClockController"],
function (ClockController) {
// Wed, 03 Jun 2015 17:56:14 GMT
var TEST_TIMESTAMP = 1433354174000;
describe("A clock view's controller", function () {
var mockScope,
mockTicker,
mockUnticker,
controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
mockUnticker = jasmine.createSpy('unticker');
mockTicker.listen.and.returnValue(mockUnticker);
controller = new ClockController(mockScope, mockTicker);
});
it("watches for model (clockFormat and timezone) from the domain object model", function () {
expect(mockScope.$watch).toHaveBeenCalledWith(
"model",
jasmine.any(Function)
);
});
it("subscribes to clock ticks", function () {
expect(mockTicker.listen)
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("unsubscribes to ticks when destroyed", function () {
// Make sure $destroy is being listened for...
expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy');
expect(mockUnticker).not.toHaveBeenCalled();
// ...and makes sure that its listener unsubscribes from ticker
mockScope.$on.calls.mostRecent().args[1]();
expect(mockUnticker).toHaveBeenCalled();
});
it("formats using the format string from the model", function () {
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
mockScope.$watch.calls.mostRecent().args[1]({
"clockFormat": [
"YYYY-DDD hh:mm:ss",
"clock24"
],
"timezone": "Canada/Eastern"
});
expect(controller.zone()).toEqual("EDT");
expect(controller.text()).toEqual("2015-154 13:56:14");
expect(controller.ampm()).toEqual("");
});
it("formats 12-hour time", function () {
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
mockScope.$watch.calls.mostRecent().args[1]({
"clockFormat": [
"YYYY-DDD hh:mm:ss",
"clock12"
],
"timezone": ""
});
expect(controller.zone()).toEqual("UTC");
expect(controller.text()).toEqual("2015-154 05:56:14");
expect(controller.ampm()).toEqual("PM");
});
it("does not throw exceptions when model is undefined", function () {
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
expect(function () {
mockScope.$watch.calls.mostRecent().args[1](undefined);
}).not.toThrow();
});
});
}
);

View File

@@ -0,0 +1,58 @@
/*****************************************************************************
* 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/indicators/ClockIndicator"],
function (ClockIndicator) {
// Wed, 03 Jun 2015 17:56:14 GMT
var TEST_TIMESTAMP = 1433354174000,
TEST_FORMAT = "YYYY-DDD HH:mm:ss";
describe("The clock indicator", function () {
var mockTicker,
mockUnticker,
indicator;
beforeEach(function () {
mockTicker = jasmine.createSpyObj('ticker', ['listen']);
mockUnticker = jasmine.createSpy('unticker');
mockTicker.listen.and.returnValue(mockUnticker);
indicator = new ClockIndicator(mockTicker, TEST_FORMAT);
});
it("displays the current time", function () {
mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC");
});
it("implements the Indicator interface", function () {
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
expect(indicator.getText()).toEqual(jasmine.any(String));
expect(indicator.getDescription()).toEqual(jasmine.any(String));
});
});
}
);

View File

@@ -20,6 +20,8 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
const { TelemetryCollection } = require("./TelemetryCollection");
define([
'../../plugins/displayLayout/CustomStringFormatter',
'./TelemetryMetadataManager',
@@ -273,6 +275,28 @@ define([
}
};
/**
* Request telemetry collection for a domain object.
* The `options` argument allows you to specify filters
* (start, end, etc.), sort order, and strategies for retrieving
* telemetry (aggregation, latest available, etc.).
*
* @method requestTelemetryCollection
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
* @param {module:openmct.DomainObject} domainObject the object
* which has associated telemetry
* @param {module:openmct.TelemetryAPI~TelemetryRequest} options
* options for this telemetry collection request
* @returns {TelemetryCollection} a TelemetryCollection instance
*/
TelemetryAPI.prototype.requestTelemetryCollection = function (domainObject, options = {}) {
return new TelemetryCollection(
this.openmct,
domainObject,
options
);
};
/**
* Request historical telemetry for a domain object.
* The `options` argument allows you to specify filters

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,366 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import _ from 'lodash';
import EventEmitter from 'EventEmitter';
/** Class representing a Telemetry Collection. */
export class TelemetryCollection extends EventEmitter {
/**
* Creates a Telemetry Collection
*
* @param {object} openmct - Openm MCT
* @param {object} domainObject - Domain Object to user for telemetry collection
* @param {object} options - Any options passed in for request/subscribe
*/
constructor(openmct, domainObject, options) {
super();
this.loaded = false;
this.openmct = openmct;
this.domainObject = domainObject;
this.boundedTelemetry = [];
this.futureBuffer = [];
this.parseTime = undefined;
this.metadata = this.openmct.telemetry.getMetadata(domainObject);
this.unsubscribe = undefined;
this.historicalProvider = undefined;
this.options = options;
this.pageState = undefined;
this.lastBounds = undefined;
this.requestAbort = undefined;
}
/**
* This will start the requests for historical and realtime data,
* as well as setting up initial values and watchers
*/
load() {
if (this.loaded) {
throw new Error('Telemetry Collection has already been loaded.');
}
this._timeSystem(this.openmct.time.timeSystem());
this.lastBounds = this.openmct.time.bounds();
this._watchBounds();
this._watchTimeSystem();
this._initiateHistoricalRequests();
this._initiateSubscriptionTelemetry();
this.loaded = true;
}
/**
* can/should be called by the requester of the telemetry collection
* to remove any listeners
*/
destroy() {
if (this.requestAbort) {
this.requestAbort.abort();
}
this._unwatchBounds();
this._unwatchTimeSystem();
if (this.unsubscribe) {
this.unsubscribe();
}
this.removeAllListeners();
}
/**
* This will start the requests for historical and realtime data,
* as well as setting up initial values and watchers
*/
getAll() {
return this.boundedTelemetry;
}
/**
* Sets up the telemetry collection for historical requests,
* this uses the "standardizeRequestOptions" from Telemetry API
* @private
*/
_initiateHistoricalRequests() {
this.openmct.telemetry.standardizeRequestOptions(this.options);
this.historicalProvider = this.openmct.telemetry.
findRequestProvider(this.domainObject, this.options);
this._requestHistoricalTelemetry();
}
/**
* If a historical provider exists, then historical requests will be made
* @private
*/
async _requestHistoricalTelemetry() {
if (!this.historicalProvider) {
return;
}
let historicalData;
try {
this.requestAbort = new AbortController();
this.options.abortSignal = this.requestAbort.signal;
historicalData = await this.historicalProvider.request(this.domainObject, this.options);
this.requestAbort = undefined;
} catch (error) {
console.error('Error requesting telemetry data...');
this.requestAbort = undefined;
throw new Error(error);
}
this._processNewTelemetry(historicalData);
}
/**
* This uses the built in subscription function from Telemetry API
* @private
*/
_initiateSubscriptionTelemetry() {
if (this.unsubscribe) {
this.unsubscribe();
}
this.unsubscribe = this.openmct.telemetry
.subscribe(
this.domainObject,
datum => this._processNewTelemetry(datum),
this.options
);
}
/**
* Filter any new telemetry (add/page, historical, subscription) based on
* time bounds and dupes
*
* @param {(Object|Object[])} telemetryData - telemetry data object or
* array of telemetry data objects
* @private
*/
_processNewTelemetry(telemetryData) {
let data = Array.isArray(telemetryData) ? telemetryData : [telemetryData];
let parsedValue;
let beforeStartOfBounds;
let afterEndOfBounds;
let added = [];
for (let datum of data) {
parsedValue = this.parseTime(datum);
beforeStartOfBounds = parsedValue < this.lastBounds.start;
afterEndOfBounds = parsedValue > this.lastBounds.end;
if (!afterEndOfBounds && !beforeStartOfBounds) {
let isDuplicate = false;
let startIndex = this._sortedIndex(datum);
let endIndex = undefined;
// dupe check
if (startIndex !== this.boundedTelemetry.length) {
endIndex = _.sortedLastIndexBy(
this.boundedTelemetry,
datum,
boundedDatum => this.parseTime(boundedDatum)
);
if (endIndex > startIndex) {
let potentialDupes = this.boundedTelemetry.slice(startIndex, endIndex);
isDuplicate = potentialDupes.some(_.isEqual(undefined, datum));
}
}
if (!isDuplicate) {
let index = endIndex || startIndex;
this.boundedTelemetry.splice(index, 0, datum);
added.push(datum);
}
} else if (afterEndOfBounds) {
this.futureBuffer.push(datum);
}
}
if (added.length) {
this.emit('add', added);
}
}
/**
* Finds the correct insertion point for the given telemetry datum.
* Leverages lodash's `sortedIndexBy` function which implements a binary search.
* @private
*/
_sortedIndex(datum) {
if (this.boundedTelemetry.length === 0) {
return 0;
}
let parsedValue = this.parseTime(datum);
let lastValue = this.parseTime(this.boundedTelemetry[this.boundedTelemetry.length - 1]);
if (parsedValue > lastValue || parsedValue === lastValue) {
return this.boundedTelemetry.length;
} else {
return _.sortedIndexBy(
this.boundedTelemetry,
datum,
boundedDatum => this.parseTime(boundedDatum)
);
}
}
/**
* when the start time, end time, or both have been updated.
* data could be added OR removed here we update the current
* bounded telemetry
*
* @param {TimeConductorBounds} bounds The newly updated bounds
* @param {boolean} [tick] `true` if the bounds update was due to
* a "tick" event (ie. was an automatic update), false otherwise.
* @private
*/
_bounds(bounds, isTick) {
let startChanged = this.lastBounds.start !== bounds.start;
let endChanged = this.lastBounds.end !== bounds.end;
this.lastBounds = bounds;
if (isTick) {
// need to check futureBuffer and need to check
// if anything has fallen out of bounds
let startIndex = 0;
let endIndex = 0;
let discarded = [];
let added = [];
let testDatum = {};
if (startChanged) {
testDatum[this.timeKey] = bounds.start;
// Calculate the new index of the first item within the bounds
startIndex = _.sortedIndexBy(
this.boundedTelemetry,
testDatum,
datum => this.parseTime(datum)
);
discarded = this.boundedTelemetry.splice(0, startIndex);
}
if (endChanged) {
testDatum[this.timeKey] = bounds.end;
// Calculate the new index of the last item in bounds
endIndex = _.sortedLastIndexBy(
this.futureBuffer,
testDatum,
datum => this.parseTime(datum)
);
added = this.futureBuffer.splice(0, endIndex);
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
}
if (discarded.length > 0) {
this.emit('remove', discarded);
}
if (added.length > 0) {
this.emit('add', added);
}
} else {
// user bounds change, reset
this._reset();
}
}
/**
* whenever the time system is updated need to update related values in
* the Telemetry Collection and reset the telemetry collection
*
* @param {TimeSystem} timeSystem - the value of the currently applied
* Time System
* @private
*/
_timeSystem(timeSystem) {
this.timeKey = timeSystem.key;
let metadataValue = this.metadata.value(this.timeKey) || { format: this.timeKey };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
this.parseTime = (datum) => {
return valueFormatter.parse(datum);
};
this._reset();
}
/**
* Reset the telemetry data of the collection, and re-request
* historical telemetry
* @private
*
* @todo handle subscriptions more granually
*/
_reset() {
this.boundedTelemetry = [];
this.futureBuffer = [];
this._requestHistoricalTelemetry();
}
/**
* adds the _bounds callback to the 'bounds' timeAPI listener
* @private
*/
_watchBounds() {
this.openmct.time.on('bounds', this._bounds, this);
}
/**
* removes the _bounds callback from the 'bounds' timeAPI listener
* @private
*/
_unwatchBounds() {
this.openmct.time.off('bounds', this._bounds, this);
}
/**
* adds the _timeSystem callback to the 'timeSystem' timeAPI listener
* @private
*/
_watchTimeSystem() {
this.openmct.time.on('timeSystem', this._timeSystem, this);
}
/**
* removes the _timeSystem callback from the 'timeSystem' timeAPI listener
* @private
*/
_unwatchTimeSystem() {
this.openmct.time.off('timeSystem', this._timeSystem, this);
}
}

View File

@@ -78,6 +78,9 @@ class ImageExporter {
}
return html2canvas(element, {
useCORS: true,
allowTaint: true,
logging: false,
onclone: function (document) {
if (className) {
const clonedElement = document.getElementById(exportId);

View File

@@ -1,59 +0,0 @@
/*****************************************************************************
* 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 Clock from './components/Clock.vue';
import Vue from 'vue';
export default function ClockViewProvider(openmct) {
return {
key: 'clock.view',
name: 'Clock',
cssClass: 'icon-clock',
canView(domainObject) {
return domainObject.type === 'clock';
},
view: function (domainObject) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
Clock
},
provide: {
openmct,
domainObject
},
template: '<clock></clock>'
});
},
destroy: function () {
component.$destroy();
component = undefined;
}
};
}
};
}

View File

@@ -1,99 +0,0 @@
<!--
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.
-->
<template>
<div class="l-angular-ov-wrapper">
<div class="u-contents">
<div class="c-clock l-time-display u-style-receiver js-style-receiver">
<div class="c-clock__timezone">
{{ timeZoneAbbr }}
</div>
<div class="c-clock__value">
{{ timeTextValue }}
</div>
<div class="c-clock__ampm">
{{ timeAmPm }}
</div>
</div>
</div>
</div>
</template>
<script>
import moment from 'moment';
import momentTimezone from 'moment-timezone';
export default {
inject: ['openmct', 'domainObject'],
data() {
return {
lastTimestamp: null
};
},
computed: {
configuration() {
return this.domainObject.configuration;
},
baseFormat() {
return this.configuration.baseFormat;
},
use24() {
return this.configuration.use24 === 'clock24';
},
timezone() {
return this.configuration.timezone;
},
timeFormat() {
return this.use24 ? this.baseFormat.replace('hh', "HH") : this.baseFormat;
},
zoneName() {
return momentTimezone.tz.names().includes(this.timezone) ? this.timezone : "UTC";
},
momentTime() {
return this.zoneName ? moment.utc(this.lastTimestamp).tz(this.zoneName) : moment.utc(this.lastTimestamp);
},
timeZoneAbbr() {
return this.momentTime.zoneAbbr();
},
timeTextValue() {
return this.timeFormat && this.momentTime.format(this.timeFormat);
},
timeAmPm() {
return this.use24 ? '' : this.momentTime.format("A");
}
},
mounted() {
const TickerService = this.openmct.$injector.get('tickerService');
this.unlisten = TickerService.listen(this.tick);
},
beforeDestroy() {
if (this.unlisten) {
this.unlisten();
}
},
methods: {
tick(timestamp) {
this.lastTimestamp = timestamp;
}
}
};
</script>

View File

@@ -1,64 +0,0 @@
<!--
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.
-->
<template>
<div class="c-indicator t-indicator-clock icon-clock no-minify c-indicator--not-clickable">
<span class="label c-indicator__label">
{{ timeTextValue }}
</span>
</div>
</template>
<script>
import moment from 'moment';
export default {
inject: ['openmct'],
props: {
indicatorFormat: {
type: String,
required: true
}
},
data() {
return {
timeTextValue: null
};
},
mounted() {
this.openmct.on('start', () => {
const TickerService = this.openmct.$injector.get('tickerService');
this.unlisten = TickerService.listen(this.tick);
});
},
beforeDestroy() {
if (this.unlisten) {
this.unlisten();
}
},
methods: {
tick(timestamp) {
this.timeTextValue = `${moment.utc(timestamp).format(this.indicatorFormat)} UTC`;
}
}
};
</script>

View File

@@ -1,154 +0,0 @@
/*****************************************************************************
* 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 ClockViewProvider from './ClockViewProvider';
import ClockIndicator from './components/ClockIndicator.vue';
import momentTimezone from 'moment-timezone';
import Vue from 'vue';
export default function ClockPlugin(options) {
return function install(openmct) {
const CLOCK_INDICATOR_FORMAT = 'YYYY/MM/DD HH:mm:ss';
openmct.types.addType('clock', {
name: 'Clock',
description: 'A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.',
creatable: true,
cssClass: 'icon-clock',
initialize: function (domainObject) {
domainObject.configuration = {
baseFormat: 'YYYY/MM/DD hh:mm:ss',
use24: 'clock12',
timezone: 'UTC'
};
},
"form": [
{
"key": "displayFormat",
"name": "Display Format",
control: 'select',
options: [
{
value: 'YYYY/MM/DD hh:mm:ss',
name: 'YYYY/MM/DD hh:mm:ss'
},
{
value: 'YYYY/DDD hh:mm:ss',
name: 'YYYY/DDD hh:mm:ss'
},
{
value: 'hh:mm:ss',
name: 'hh:mm:ss'
}
],
cssClass: 'l-inline',
property: [
'configuration',
'baseFormat'
]
},
{
control: 'select',
options: [
{
value: 'clock12',
name: '12hr'
},
{
value: 'clock24',
name: '24hr'
}
],
cssClass: 'l-inline',
property: [
'configuration',
'use24'
]
},
{
"key": "timezone",
"name": "Timezone",
"control": "autocomplete",
"options": momentTimezone.tz.names(),
property: [
'configuration',
'timezone'
]
}
]
});
openmct.objectViews.addProvider(new ClockViewProvider(openmct));
if (options && options.enableClockIndicator) {
const clockIndicator = new Vue ({
components: {
ClockIndicator
},
provide: {
openmct
},
data() {
return {
indicatorFormat: CLOCK_INDICATOR_FORMAT
};
},
template: '<ClockIndicator :indicator-format="indicatorFormat"></ClockIndicator>'
});
const indicator = {
element: clockIndicator.$mount().$el,
key: 'clock-indicator'
};
openmct.indicators.add(indicator);
}
openmct.objects.addGetInterceptor({
appliesTo: (identifier, domainObject) => {
return domainObject && domainObject.type === 'clock';
},
invoke: (identifier, domainObject) => {
if (domainObject.configuration) {
return domainObject;
}
if (domainObject.clockFormat
&& domainObject.timezone) {
const baseFormat = domainObject.clockFormat[0];
const use24 = domainObject.clockFormat[1];
const timezone = domainObject.timezone;
domainObject.configuration = {
baseFormat,
use24,
timezone
};
openmct.objects.mutate(domainObject, 'configuration', domainObject.configuration);
}
return domainObject;
}
});
};
}

View File

@@ -25,16 +25,16 @@
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
>
<plot-legend :cursor-locked="!!lockHighlightPoint"
:series="seriesModels"
:series="config.series.models"
:highlights="highlights"
:legend="legend"
:legend="config.legend"
@legendHoverChanged="legendHoverChanged"
/>
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
<y-axis v-if="seriesModels.length > 0"
<y-axis v-if="config.series.models.length > 0"
:tick-width="tickWidth"
:single-series="seriesModels.length === 1"
:series-model="seriesModels[0]"
:single-series="config.series.models.length === 1"
:series-model="config.series.models[0]"
@yKeyChanged="setYAxisKey"
@tickWidthChanged="onTickWidthChange"
/>
@@ -141,8 +141,8 @@
>
</div>
</div>
<x-axis v-if="seriesModels.length > 0 && !options.compact"
:series-model="seriesModels[0]"
<x-axis v-if="config.series.models.length > 0 && !options.compact"
:series-model="config.series.models[0]"
/>
</div>
@@ -213,8 +213,7 @@ export default {
plotHistory: [],
selectedXKeyOption: {},
xKeyOptions: [],
seriesModels: [],
legend: {},
config: {},
pending: 0,
isRealTime: this.openmct.time.clock() !== undefined,
loaded: false,
@@ -240,13 +239,18 @@ export default {
watch: {
plotTickWidth(newTickWidth) {
this.onTickWidthChange(newTickWidth, true);
},
gridLines(newGridLines) {
this.setGridLinesVisibility(newGridLines);
},
cursorGuide(newCursorGuide) {
this.setCursorGuideVisibility(newCursorGuide);
}
},
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.legend = this.config.legend;
this.listenTo(this.config.series, 'add', this.addSeries, this);
this.listenTo(this.config.series, 'remove', this.removeSeries, this);
@@ -286,18 +290,14 @@ export default {
config = new PlotConfigurationModel({
id: configId,
domainObject: this.domainObject,
openmct: this.openmct,
callback: (data) => {
this.data = data;
}
openmct: this.openmct
});
configStore.add(configId, config);
}
return config;
},
addSeries(series, index) {
this.$set(this.seriesModels, index, series);
addSeries(series) {
this.listenTo(series, 'change:xKey', (xKey) => {
this.setDisplayRange(series, xKey);
}, this);
@@ -377,8 +377,11 @@ export default {
},
stopLoading() {
this.pending -= 1;
this.updateLoading();
//TODO: Is Vue.$nextTick ok to replace $scope.$evalAsync?
this.$nextTick().then(() => {
this.pending -= 1;
this.updateLoading();
});
},
updateLoading() {
@@ -504,7 +507,7 @@ export default {
},
initialize() {
this.handleWindowResize = _.debounce(this.handleWindowResize, 500);
_.debounce(this.handleWindowResize, 400);
this.plotContainerResizeObserver = new ResizeObserver(this.handleWindowResize);
this.plotContainerResizeObserver.observe(this.$parent.$refs.plotWrapper);
@@ -620,7 +623,7 @@ export default {
this.config.series.models.forEach(series => delete series.closest);
} else {
this.highlights = this.config.series.models
.filter(series => series.getSeriesData().length > 0)
.filter(series => series.data.length > 0)
.map(series => {
series.closest = series.nearestPoint(point);
@@ -924,6 +927,14 @@ export default {
this.userViewportChangeEnd();
},
setCursorGuideVisibility(cursorGuide) {
this.cursorGuide = cursorGuide === true;
},
setGridLinesVisibility(gridLines) {
this.gridLines = gridLines === true;
},
setYAxisKey(yKey) {
this.config.series.models[0].set('yKey', yKey);
},

View File

@@ -106,9 +106,8 @@ export default {
},
toggleXKeyOption() {
const selectedXKey = this.selectedXKeyOptionKey;
const seriesData = this.seriesModel.getSeriesData();
const dataForSelectedXKey = seriesData
? seriesData[0][selectedXKey]
const dataForSelectedXKey = this.seriesModel.data
? this.seriesModel.data[0][selectedXKey]
: undefined;
if (dataForSelectedXKey !== undefined) {

View File

@@ -36,7 +36,7 @@ export default class MCTChartAlarmPointSet {
this.listenTo(series, 'reset', this.reset, this);
this.listenTo(series, 'destroy', this.destroy, this);
this.series.getSeriesData().forEach(function (point, index) {
series.data.forEach(function (point, index) {
this.append(point, index, series);
}, this);
}

View File

@@ -36,7 +36,7 @@ export default class MCTChartSeriesElement {
this.listenTo(series, 'remove', this.remove, this);
this.listenTo(series, 'reset', this.reset, this);
this.listenTo(series, 'destroy', this.destroy, this);
this.series.getSeriesData().forEach(function (point, index) {
series.data.forEach(function (point, index) {
this.append(point, index, series);
}, this);
}
@@ -133,7 +133,7 @@ export default class MCTChartSeriesElement {
this.buffer = new Float32Array(20000);
this.count = 0;
if (this.offset.x) {
this.series.getSeriesData().forEach(function (point, index) {
this.series.data.forEach(function (point, index) {
this.append(point, index, this.series);
}, this);
}

View File

@@ -107,7 +107,6 @@ export default class PlotConfigurationModel extends Model {
updateDomainObject(domainObject) {
this.set('domainObject', domainObject);
}
/**
* Clean up all objects and remove all listeners.
*/

View File

@@ -22,7 +22,6 @@
import _ from 'lodash';
import Model from "./Model";
import { MARKER_SHAPES } from '../draw/MarkerShapes';
import configStore from "../configuration/configStore";
/**
* Plot series handle interpreting telemetry metadata for a single telemetry
@@ -63,6 +62,7 @@ import configStore from "../configuration/configStore";
export default class PlotSeries extends Model {
constructor(options) {
super(options);
this.data = [];
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
this.listenTo(this, 'change:yKey', this.onYKeyChange, this);
@@ -115,8 +115,6 @@ export default class PlotSeries extends Model {
this.openmct = options.openmct;
this.domainObject = options.domainObject;
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.dataStoreId = `data-${options.collection.plot.id}-${this.keyString}`;
this.updateSeriesData([]);
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
this.limits = [];
@@ -184,8 +182,7 @@ export default class PlotSeries extends Model {
.telemetry
.request(this.domainObject, options)
.then(function (points) {
const data = this.getSeriesData();
const newPoints = _(data)
const newPoints = _(this.data)
.concat(points)
.sortBy(this.getXVal)
.uniq(true, point => [this.getXVal(point), this.getYVal(point)].join())
@@ -239,7 +236,7 @@ export default class PlotSeries extends Model {
*/
resetStats() {
this.unset('stats');
this.getSeriesData().forEach(this.updateStats, this);
this.data.forEach(this.updateStats, this);
}
/**
@@ -247,7 +244,7 @@ export default class PlotSeries extends Model {
* data to series after reset.
*/
reset(newData) {
this.updateSeriesData([]);
this.data = [];
this.resetStats();
this.emit('reset');
if (newData) {
@@ -261,9 +258,8 @@ export default class PlotSeries extends Model {
*/
nearestPoint(xValue) {
const insertIndex = this.sortedIndex(xValue);
const data = this.getSeriesData();
const lowPoint = data[insertIndex - 1];
const highPoint = data[insertIndex];
const lowPoint = this.data[insertIndex - 1];
const highPoint = this.data[insertIndex];
const indexVal = this.getXVal(xValue);
const lowDistance = lowPoint
? indexVal - this.getXVal(lowPoint)
@@ -296,7 +292,7 @@ export default class PlotSeries extends Model {
* @private
*/
sortedIndex(point) {
return _.sortedIndexBy(this.getSeriesData(), point, this.getXVal);
return _.sortedIndexBy(this.data, point, this.getXVal);
}
/**
* Update min/max stats for the series.
@@ -350,10 +346,9 @@ export default class PlotSeries extends Model {
* a point to the end without dupe checking.
*/
add(point, appendOnly) {
let data = this.getSeriesData();
let insertIndex = data.length;
let insertIndex = this.data.length;
const currentYVal = this.getYVal(point);
const lastYVal = this.getYVal(data[insertIndex - 1]);
const lastYVal = this.getYVal(this.data[insertIndex - 1]);
if (this.isValueInvalid(currentYVal) && this.isValueInvalid(lastYVal)) {
console.warn('[Plot] Invalid Y Values detected');
@@ -363,19 +358,18 @@ export default class PlotSeries extends Model {
if (!appendOnly) {
insertIndex = this.sortedIndex(point);
if (this.getXVal(data[insertIndex]) === this.getXVal(point)) {
if (this.getXVal(this.data[insertIndex]) === this.getXVal(point)) {
return;
}
if (this.getXVal(data[insertIndex - 1]) === this.getXVal(point)) {
if (this.getXVal(this.data[insertIndex - 1]) === this.getXVal(point)) {
return;
}
}
this.updateStats(point);
point.mctLimitState = this.evaluate(point);
data.splice(insertIndex, 0, point);
this.updateSeriesData(data);
this.data.splice(insertIndex, 0, point);
this.emit('add', point, insertIndex, this);
}
@@ -392,10 +386,8 @@ export default class PlotSeries extends Model {
* @private
*/
remove(point) {
let data = this.getSeriesData();
const index = data.indexOf(point);
data.splice(index, 1);
this.updateSeriesData(data);
const index = this.data.indexOf(point);
this.data.splice(index, 1);
this.emit('remove', point, index, this);
}
/**
@@ -411,16 +403,14 @@ export default class PlotSeries extends Model {
purgeRecordsOutsideRange(range) {
const startIndex = this.sortedIndex(range.min);
const endIndex = this.sortedIndex(range.max) + 1;
let data = this.getSeriesData();
const pointsToRemove = startIndex + (data.length - endIndex + 1);
const pointsToRemove = startIndex + (this.data.length - endIndex + 1);
if (pointsToRemove > 0) {
if (pointsToRemove < 1000) {
data.slice(0, startIndex).forEach(this.remove, this);
data.slice(endIndex, data.length).forEach(this.remove, this);
this.updateSeriesData(data);
this.data.slice(0, startIndex).forEach(this.remove, this);
this.data.slice(endIndex, this.data.length).forEach(this.remove, this);
this.resetStats();
} else {
const newData = this.getSeriesData().slice(startIndex, endIndex);
const newData = this.data.slice(startIndex, endIndex);
this.reset(newData);
}
}
@@ -451,13 +441,12 @@ export default class PlotSeries extends Model {
}
}
getDisplayRange(xKey) {
const unsortedData = this.getSeriesData();
this.updateSeriesData([]);
const unsortedData = this.data;
this.data = [];
unsortedData.forEach(point => this.add(point, false));
let data = this.getSeriesData();
const minValue = this.getXVal(data[0]);
const maxValue = this.getXVal(data[data.length - 1]);
const minValue = this.getXVal(this.data[0]);
const maxValue = this.getXVal(this.data[this.data.length - 1]);
return {
min: minValue,
@@ -481,18 +470,4 @@ export default class PlotSeries extends Model {
return this.get('name') + (unit ? ' ' + unit : '');
}
/**
* Update the series data with the given value.
*/
updateSeriesData(data) {
configStore.add(this.dataStoreId, data);
}
/**
* Update the series data with the given value.
*/
getSeriesData() {
return configStore.get(this.dataStoreId) || [];
}
}

View File

@@ -25,10 +25,7 @@ function ConfigStore() {
ConfigStore.prototype.deleteStore = function (id) {
if (this.store[id]) {
if (this.store[id].destroy) {
this.store[id].destroy();
}
this.store[id].destroy();
delete this.store[id];
}
};

View File

@@ -176,9 +176,7 @@ DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
this.gl.uniform4fv(this.uColor, color);
this.gl.uniform1i(this.uMarkerShape, shapeCode);
if (points !== 0) {
this.gl.drawArrays(drawType, 0, points);
}
this.gl.drawArrays(drawType, 0, points);
};
DrawWebGL.prototype.clear = function () {

View File

@@ -715,15 +715,14 @@ describe("the plugin", function () {
});
it("Adds a new point to the plot", (done) => {
let originalLength = config.series.models[0].getSeriesData().length;
let originalLength = config.series.models[0].data.length;
config.series.models[0].add({
utc: 2,
'some-key': 1,
'some-other-key': 2
});
Vue.nextTick(() => {
const seriesData = config.series.models[0].getSeriesData();
expect(seriesData.length).toEqual(originalLength + 1);
expect(config.series.models[0].data.length).toEqual(originalLength + 1);
done();
});
});

View File

@@ -68,8 +68,7 @@ define([
'./performanceIndicator/plugin',
'./CouchDBSearchFolder/plugin',
'./timeline/plugin',
'./hyperlink/plugin',
'./clock/plugin'
'./hyperlink/plugin'
], function (
_,
UTCTimeSystem,
@@ -118,8 +117,7 @@ define([
PerformanceIndicator,
CouchDBSearchFolder,
Timeline,
Hyperlink,
Clock
Hyperlink
) {
const bundleMap = {
LocalStorage: 'platform/persistence/local',
@@ -223,7 +221,6 @@ define([
plugins.CouchDBSearchFolder = CouchDBSearchFolder.default;
plugins.Timeline = Timeline.default;
plugins.Hyperlink = Hyperlink.default;
plugins.Clock = Clock.default;
return plugins;
});

View File

@@ -137,10 +137,7 @@ describe("the RemoteClock plugin", () => {
it('will request the latest datum for the object it received and process the datum returned', () => {
expect(openmct.telemetry.request).toHaveBeenCalledWith(remoteClock.timeTelemetryObject, REQ_OPTIONS);
expect(boundsCallback).toHaveBeenCalledWith({
start: TIME_VALUE + OFFSET_START,
end: TIME_VALUE + OFFSET_END
}, true);
expect(boundsCallback).toHaveBeenCalledWith({ start: TIME_VALUE + OFFSET_START, end: TIME_VALUE + OFFSET_END }, true);
});
it('will set up subscriptions correctly', () => {

View File

@@ -22,7 +22,7 @@
import RemoteClock from "./RemoteClock";
/**
* Install a clock that uses a configurable telemetry endpoint.
* Install a clock that uses a configurable telemetry endpoint.
*/
export default function (identifier) {

View File

@@ -23,20 +23,18 @@
define([
'EventEmitter',
'lodash',
'./collections/BoundedTableRowCollection',
'./collections/FilteredTableRowCollection',
'./TelemetryTableNameColumn',
'./collections/TableRowCollection',
'./TelemetryTableRow',
'./TelemetryTableNameColumn',
'./TelemetryTableColumn',
'./TelemetryTableUnitColumn',
'./TelemetryTableConfiguration'
], function (
EventEmitter,
_,
BoundedTableRowCollection,
FilteredTableRowCollection,
TelemetryTableNameColumn,
TableRowCollection,
TelemetryTableRow,
TelemetryTableNameColumn,
TelemetryTableColumn,
TelemetryTableUnitColumn,
TelemetryTableConfiguration
@@ -48,20 +46,23 @@ define([
this.domainObject = domainObject;
this.openmct = openmct;
this.rowCount = 100;
this.subscriptions = {};
this.tableComposition = undefined;
this.telemetryObjects = [];
this.datumCache = [];
this.outstandingRequests = 0;
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
this.paused = false;
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.telemetryObjects = {};
this.telemetryCollections = {};
this.delayedActions = [];
this.outstandingRequests = 0;
this.addTelemetryObject = this.addTelemetryObject.bind(this);
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
this.removeTelemetryCollection = this.removeTelemetryCollection.bind(this);
this.resetRowsFromAllData = this.resetRowsFromAllData.bind(this);
this.isTelemetryObject = this.isTelemetryObject.bind(this);
this.refreshData = this.refreshData.bind(this);
this.requestDataFor = this.requestDataFor.bind(this);
this.updateFilters = this.updateFilters.bind(this);
this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
@@ -102,8 +103,7 @@ define([
}
createTableRowCollections() {
this.boundedRows = new BoundedTableRowCollection(this.openmct);
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
this.tableRows = new TableRowCollection();
//Fetch any persisted default sort
let sortOptions = this.configuration.getConfiguration().sortOptions;
@@ -113,11 +113,14 @@ define([
key: this.openmct.time.timeSystem().key,
direction: 'asc'
};
this.filteredRows.sortBy(sortOptions);
this.tableRows.sortBy(sortOptions);
this.tableRows.on('resetRowsFromAllData', this.resetRowsFromAllData);
}
loadComposition() {
this.tableComposition = this.openmct.composition.get(this.domainObject);
if (this.tableComposition !== undefined) {
this.tableComposition.load().then((composition) => {
@@ -132,66 +135,64 @@ define([
addTelemetryObject(telemetryObject) {
this.addColumnsForObject(telemetryObject, true);
this.requestDataFor(telemetryObject);
this.subscribeTo(telemetryObject);
this.telemetryObjects.push(telemetryObject);
const keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
let columnMap = this.getColumnMapForObject(keyString);
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
this.incrementOutstandingRequests();
const telemetryProcessor = this.getTelemetryProcessor(keyString, columnMap, limitEvaluator);
const telemetryRemover = this.getTelemetryRemover();
this.removeTelemetryCollection(keyString);
this.telemetryCollections[keyString] = this.openmct.telemetry
.requestTelemetryCollection(telemetryObject, requestOptions);
this.telemetryCollections[keyString].on('remove', telemetryRemover);
this.telemetryCollections[keyString].on('add', telemetryProcessor);
this.telemetryCollections[keyString].load();
this.decrementOutstandingRequests();
this.telemetryObjects[keyString] = {
telemetryObject,
keyString,
requestOptions,
columnMap,
limitEvaluator
};
this.emit('object-added', telemetryObject);
}
updateFilters(updatedFilters) {
let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters));
getTelemetryProcessor(keyString, columnMap, limitEvaluator) {
return (telemetry) => {
//Check that telemetry object has not been removed since telemetry was requested.
if (!this.telemetryObjects[keyString]) {
return;
}
if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) {
this.filters = deepCopiedFilters;
this.clearAndResubscribe();
} else {
this.filters = deepCopiedFilters;
}
let telemetryRows = telemetry.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
if (this.paused) {
this.delayedActions.push(this.tableRows.addRows.bind(this, telemetryRows, 'add'));
} else {
this.tableRows.addRows(telemetryRows, 'add');
}
};
}
clearAndResubscribe() {
this.filteredRows.clear();
this.boundedRows.clear();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.telemetryObjects.forEach(this.requestDataFor.bind(this));
this.telemetryObjects.forEach(this.subscribeTo.bind(this));
}
removeTelemetryObject(objectIdentifier) {
this.configuration.removeColumnsForObject(objectIdentifier, true);
let keyString = this.openmct.objects.makeKeyString(objectIdentifier);
this.boundedRows.removeAllRowsForObject(keyString);
this.unsubscribe(keyString);
this.telemetryObjects = this.telemetryObjects.filter((object) => !_.eq(objectIdentifier, object.identifier));
this.emit('object-removed', objectIdentifier);
}
requestDataFor(telemetryObject) {
this.incrementOutstandingRequests();
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
return this.openmct.telemetry.request(telemetryObject, requestOptions)
.then(telemetryData => {
//Check that telemetry object has not been removed since telemetry was requested.
if (!this.telemetryObjects.includes(telemetryObject)) {
return;
}
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let columnMap = this.getColumnMapForObject(keyString);
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
this.processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator);
}).finally(() => {
this.decrementOutstandingRequests();
});
}
processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator) {
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
this.boundedRows.add(telemetryRows);
getTelemetryRemover() {
return (telemetry) => {
if (this.paused) {
this.delayedActions.push(this.tableRows.removeRowsByData.bind(this, telemetry));
} else {
this.tableRows.removeRowsByData(telemetry);
}
};
}
/**
@@ -216,35 +217,72 @@ define([
}
}
// will pull all necessary information for all existing bounded telemetry
// and pass to table row collection to reset without making any new requests
// triggered by filtering
resetRowsFromAllData() {
let allRows = [];
Object.keys(this.telemetryCollections).forEach(keyString => {
let { columnMap, limitEvaluator } = this.telemetryObjects[keyString];
this.telemetryCollections[keyString].getAll().forEach(datum => {
allRows.push(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
});
});
this.tableRows.addRows(allRows, 'filter');
}
updateFilters(updatedFilters) {
let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters));
if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) {
this.filters = deepCopiedFilters;
this.tableRows.clear();
this.clearAndResubscribe();
} else {
this.filters = deepCopiedFilters;
}
}
clearAndResubscribe() {
let objectKeys = Object.keys(this.telemetryObjects);
this.tableRows.clear();
objectKeys.forEach((keyString) => {
this.addTelemetryObject(this.telemetryObjects[keyString].telemetryObject);
});
}
removeTelemetryObject(objectIdentifier) {
const keyString = this.openmct.objects.makeKeyString(objectIdentifier);
this.configuration.removeColumnsForObject(objectIdentifier, true);
this.tableRows.removeRowsByObject(keyString);
this.removeTelemetryCollection(keyString);
delete this.telemetryObjects[keyString];
this.emit('object-removed', objectIdentifier);
}
refreshData(bounds, isTick) {
if (!isTick && this.outstandingRequests === 0) {
this.filteredRows.clear();
this.boundedRows.clear();
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
this.telemetryObjects.forEach(this.requestDataFor);
if (!isTick && this.tableRows.outstandingRequests === 0) {
this.tableRows.clear();
this.tableRows.sortBy({
key: this.openmct.time.timeSystem().key,
direction: 'asc'
});
this.tableRows.resubscribe();
}
}
clearData() {
this.filteredRows.clear();
this.boundedRows.clear();
this.tableRows.clear();
this.emit('refresh');
}
getColumnMapForObject(objectKeyString) {
let columns = this.configuration.getColumns();
if (columns[objectKeyString]) {
return columns[objectKeyString].reduce((map, column) => {
map[column.getKey()] = column;
return map;
}, {});
}
return {};
}
addColumnsForObject(telemetryObject) {
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
@@ -264,54 +302,18 @@ define([
});
}
createColumn(metadatum) {
return new TelemetryTableColumn(this.openmct, metadatum);
}
getColumnMapForObject(objectKeyString) {
let columns = this.configuration.getColumns();
createUnitColumn(metadatum) {
return new TelemetryTableUnitColumn(this.openmct, metadatum);
}
if (columns[objectKeyString]) {
return columns[objectKeyString].reduce((map, column) => {
map[column.getKey()] = column;
subscribeTo(telemetryObject) {
let subscribeOptions = this.buildOptionsFromConfiguration(telemetryObject);
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let columnMap = this.getColumnMapForObject(keyString);
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
return map;
}, {});
}
this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
//Check that telemetry object has not been removed since telemetry was requested.
if (!this.telemetryObjects.includes(telemetryObject)) {
return;
}
if (this.paused) {
let realtimeDatum = {
datum,
columnMap,
keyString,
limitEvaluator
};
this.datumCache.push(realtimeDatum);
} else {
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
}
}, subscribeOptions);
}
processDatumCache() {
this.datumCache.forEach(cachedDatum => {
this.processRealtimeDatum(cachedDatum.datum, cachedDatum.columnMap, cachedDatum.keyString, cachedDatum.limitEvaluator);
});
this.datumCache = [];
}
processRealtimeDatum(datum, columnMap, keyString, limitEvaluator) {
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
}
isTelemetryObject(domainObject) {
return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry');
return {};
}
buildOptionsFromConfiguration(telemetryObject) {
@@ -323,13 +325,20 @@ define([
return {filters} || {};
}
unsubscribe(keyString) {
this.subscriptions[keyString]();
delete this.subscriptions[keyString];
createColumn(metadatum) {
return new TelemetryTableColumn(this.openmct, metadatum);
}
createUnitColumn(metadatum) {
return new TelemetryTableUnitColumn(this.openmct, metadatum);
}
isTelemetryObject(domainObject) {
return Object.prototype.hasOwnProperty.call(domainObject, 'telemetry');
}
sortBy(sortOptions) {
this.filteredRows.sortBy(sortOptions);
this.tableRows.sortBy(sortOptions);
if (this.openmct.editor.isEditing()) {
let configuration = this.configuration.getConfiguration();
@@ -338,21 +347,36 @@ define([
}
}
runDelayedActions() {
this.delayedActions.forEach(action => action());
this.delayedActions = [];
}
removeTelemetryCollection(keyString) {
if (this.telemetryCollections[keyString]) {
this.telemetryCollections[keyString].destroy();
this.telemetryCollections[keyString] = undefined;
delete this.telemetryCollections[keyString];
}
}
pause() {
this.paused = true;
this.boundedRows.unsubscribeFromBounds();
}
unpause() {
this.paused = false;
this.processDatumCache();
this.boundedRows.subscribeToBounds();
this.runDelayedActions();
}
destroy() {
this.boundedRows.destroy();
this.filteredRows.destroy();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.tableRows.destroy();
this.tableRows.off('resetRowsFromAllData', this.resetRowsFromAllData);
let keystrings = Object.keys(this.telemetryCollections);
keystrings.forEach(this.removeTelemetryCollection);
this.openmct.time.off('bounds', this.refreshData);
this.openmct.time.off('timeSystem', this.refreshData);

View File

@@ -1,166 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[
'lodash',
'./SortedTableRowCollection'
],
function (
_,
SortedTableRowCollection
) {
class BoundedTableRowCollection extends SortedTableRowCollection {
constructor(openmct) {
super();
this.futureBuffer = new SortedTableRowCollection();
this.openmct = openmct;
this.sortByTimeSystem = this.sortByTimeSystem.bind(this);
this.bounds = this.bounds.bind(this);
this.sortByTimeSystem(openmct.time.timeSystem());
this.lastBounds = openmct.time.bounds();
this.subscribeToBounds();
}
addOne(item) {
let parsedValue = this.getValueForSortColumn(item);
// Insert into either in-bounds array, or the future buffer.
// Data in the future buffer will be re-evaluated for possible
// insertion on next bounds change
let beforeStartOfBounds = parsedValue < this.lastBounds.start;
let afterEndOfBounds = parsedValue > this.lastBounds.end;
if (!afterEndOfBounds && !beforeStartOfBounds) {
return super.addOne(item);
} else if (afterEndOfBounds) {
this.futureBuffer.addOne(item);
}
return false;
}
sortByTimeSystem(timeSystem) {
this.sortBy({
key: timeSystem.key,
direction: 'asc'
});
let formatter = this.openmct.telemetry.getValueFormatter({
key: timeSystem.key,
source: timeSystem.key,
format: timeSystem.timeFormat
});
this.parseTime = formatter.parse.bind(formatter);
this.futureBuffer.sortBy({
key: timeSystem.key,
direction: 'asc'
});
}
/**
* This function is optimized for ticking - it assumes that start and end
* bounds will only increase and as such this cannot be used for decreasing
* bounds changes.
*
* An implication of this is that data will not be discarded that exceeds
* the given end bounds. For arbitrary bounds changes, it's assumed that
* a telemetry requery is performed anyway, and the collection is cleared
* and repopulated.
*
* @fires TelemetryCollection#added
* @fires TelemetryCollection#discarded
* @param bounds
*/
bounds(bounds) {
let startChanged = this.lastBounds.start !== bounds.start;
let endChanged = this.lastBounds.end !== bounds.end;
let startIndex = 0;
let endIndex = 0;
let discarded = [];
let added = [];
let testValue = {
datum: {}
};
this.lastBounds = bounds;
if (startChanged) {
testValue.datum[this.sortOptions.key] = bounds.start;
// Calculate the new index of the first item within the bounds
startIndex = this.sortedIndex(this.rows, testValue);
discarded = this.rows.splice(0, startIndex);
}
if (endChanged) {
testValue.datum[this.sortOptions.key] = bounds.end;
// Calculate the new index of the last item in bounds
endIndex = this.sortedLastIndex(this.futureBuffer.rows, testValue);
added = this.futureBuffer.rows.splice(0, endIndex);
added.forEach((datum) => this.rows.push(datum));
}
if (discarded && discarded.length > 0) {
/**
* A `discarded` event is emitted when telemetry data fall out of
* bounds due to a bounds change event
* @type {object[]} discarded the telemetry data
* discarded as a result of the bounds change
*/
this.emit('remove', discarded);
}
if (added && added.length > 0) {
/**
* An `added` event is emitted when a bounds change results in
* received telemetry falling within the new bounds.
* @type {object[]} added the telemetry data that is now within bounds
*/
this.emit('add', added);
}
}
getValueForSortColumn(row) {
return this.parseTime(row.datum[this.sortOptions.key]);
}
unsubscribeFromBounds() {
this.openmct.time.off('bounds', this.bounds);
}
subscribeToBounds() {
this.openmct.time.on('bounds', this.bounds);
}
destroy() {
this.unsubscribeFromBounds();
}
}
return BoundedTableRowCollection;
});

View File

@@ -1,136 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[
'./SortedTableRowCollection'
],
function (
SortedTableRowCollection
) {
class FilteredTableRowCollection extends SortedTableRowCollection {
constructor(masterCollection) {
super();
this.masterCollection = masterCollection;
this.columnFilters = {};
//Synchronize with master collection
this.masterCollection.on('add', this.add);
this.masterCollection.on('remove', this.remove);
//Default to master collection's sort options
this.sortOptions = masterCollection.sortBy();
}
setColumnFilter(columnKey, filter) {
filter = filter.trim().toLowerCase();
let rowsToFilter = this.getRowsToFilter(columnKey, filter);
if (filter.length === 0) {
delete this.columnFilters[columnKey];
} else {
this.columnFilters[columnKey] = filter;
}
this.rows = rowsToFilter.filter(this.matchesFilters, this);
this.emit('filter');
}
setColumnRegexFilter(columnKey, filter) {
filter = filter.trim();
let rowsToFilter = this.masterCollection.getRows();
this.columnFilters[columnKey] = new RegExp(filter);
this.rows = rowsToFilter.filter(this.matchesFilters, this);
this.emit('filter');
}
/**
* @private
*/
getRowsToFilter(columnKey, filter) {
if (this.isSubsetOfCurrentFilter(columnKey, filter)) {
return this.getRows();
} else {
return this.masterCollection.getRows();
}
}
/**
* @private
*/
isSubsetOfCurrentFilter(columnKey, filter) {
if (this.columnFilters[columnKey] instanceof RegExp) {
return false;
}
return this.columnFilters[columnKey]
&& filter.startsWith(this.columnFilters[columnKey])
// startsWith check will otherwise fail when filter cleared
// because anyString.startsWith('') === true
&& filter !== '';
}
addOne(row) {
return this.matchesFilters(row) && super.addOne(row);
}
/**
* @private
*/
matchesFilters(row) {
let doesMatchFilters = true;
Object.keys(this.columnFilters).forEach((key) => {
if (!doesMatchFilters || !this.rowHasColumn(row, key)) {
return false;
}
let formattedValue = row.getFormattedValue(key);
if (formattedValue === undefined) {
return false;
}
if (this.columnFilters[key] instanceof RegExp) {
doesMatchFilters = this.columnFilters[key].test(formattedValue);
} else {
doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
}
});
return doesMatchFilters;
}
rowHasColumn(row, key) {
return Object.prototype.hasOwnProperty.call(row.columns, key);
}
destroy() {
this.masterCollection.off('add', this.add);
this.masterCollection.off('remove', this.remove);
}
}
return FilteredTableRowCollection;
});

View File

@@ -36,85 +36,72 @@ define(
/**
* @constructor
*/
class SortedTableRowCollection extends EventEmitter {
class TableRowCollection extends EventEmitter {
constructor() {
super();
this.dupeCheck = false;
this.rows = [];
this.columnFilters = {};
this.addRows = this.addRows.bind(this);
this.removeRowsByObject = this.removeRowsByObject.bind(this);
this.removeRowsByData = this.removeRowsByData.bind(this);
this.add = this.add.bind(this);
this.remove = this.remove.bind(this);
this.clear = this.clear.bind(this);
}
/**
* Add a datum or array of data to this telemetry collection
* @fires TelemetryCollection#added
* @param {object | object[]} rows
*/
add(rows) {
if (Array.isArray(rows)) {
this.dupeCheck = false;
removeRowsByObject(keyString) {
let removed = [];
let rowsAdded = rows.filter(this.addOne, this);
if (rowsAdded.length > 0) {
this.emit('add', rowsAdded);
}
this.rows = this.rows.filter((row) => {
if (row.objectKeyString === keyString) {
removed.push(row);
this.dupeCheck = true;
} else {
let wasAdded = this.addOne(rows);
if (wasAdded) {
this.emit('add', rows);
return false;
} else {
return true;
}
}
});
this.emit('remove', removed);
}
/**
* @private
*/
addOne(row) {
addRows(rows, type = 'add') {
if (this.sortOptions === undefined) {
throw 'Please specify sort options';
}
let isDuplicate = false;
let isFilterTriggeredReset = type === 'filter';
let anyActiveFilters = Object.keys(this.columnFilters).length > 0;
let rowsToAdd = !anyActiveFilters ? rows : rows.filter(this.matchesFilters, this);
// Going to check for duplicates. Bound the search problem to
// items around the given time. Use sortedIndex because it
// employs a binary search which is O(log n). Can use binary search
// because the array is guaranteed ordered due to sorted insertion.
let startIx = this.sortedIndex(this.rows, row);
let endIx = undefined;
if (this.dupeCheck && startIx !== this.rows.length) {
endIx = this.sortedLastIndex(this.rows, row);
// Create an array of potential dupes, based on having the
// same time stamp
let potentialDupes = this.rows.slice(startIx, endIx + 1);
// Search potential dupes for exact dupe
isDuplicate = potentialDupes.some(_.isEqual.bind(undefined, row));
// if type is filter, then it's a reset of all rows,
// need to wipe current rows
if (isFilterTriggeredReset) {
this.rows = [];
}
if (!isDuplicate) {
this.rows.splice(endIx || startIx, 0, row);
return true;
for (let row of rowsToAdd) {
let index = this.sortedIndex(this.rows, row);
this.rows.splice(index, 0, row);
}
return false;
// we emit filter no matter what to trigger
// an update of visible rows
if (rowsToAdd.length > 0 || isFilterTriggeredReset) {
this.emit(type, rowsToAdd);
}
}
sortedLastIndex(rows, testRow) {
return this.sortedIndex(rows, testRow, _.sortedLastIndex);
}
/**
* Finds the correct insertion point for the given row.
* Leverages lodash's `sortedIndex` function which implements a binary search.
* @private
*/
sortedIndex(rows, testRow, lodashFunction) {
sortedIndex(rows, testRow, lodashFunction = _.sortedIndexBy) {
if (this.rows.length === 0) {
return 0;
}
@@ -123,8 +110,6 @@ define(
const firstValue = this.getValueForSortColumn(this.rows[0]);
const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
lodashFunction = lodashFunction || _.sortedIndexBy;
if (this.sortOptions.direction === 'asc') {
if (testRowValue > lastValue) {
return this.rows.length;
@@ -162,6 +147,22 @@ define(
}
}
removeRowsByData(data) {
let removed = [];
this.rows = this.rows.filter((row) => {
if (data.includes(row.fullDatum)) {
removed.push(row);
return false;
} else {
return true;
}
});
this.emit('remove', removed);
}
/**
* Sorts the telemetry collection based on the provided sort field
* specifier. Subsequent inserts are sorted to maintain specified sport
@@ -205,6 +206,7 @@ define(
if (arguments.length > 0) {
this.sortOptions = sortOptions;
this.rows = _.orderBy(this.rows, (row) => row.getParsedValue(sortOptions.key), sortOptions.direction);
this.emit('sort');
}
@@ -212,44 +214,114 @@ define(
return Object.assign({}, this.sortOptions);
}
removeAllRowsForObject(objectKeyString) {
let removed = [];
this.rows = this.rows.filter(row => {
if (row.objectKeyString === objectKeyString) {
removed.push(row);
setColumnFilter(columnKey, filter) {
filter = filter.trim().toLowerCase();
let wasBlank = this.columnFilters[columnKey] === undefined;
let isSubset = this.isSubsetOfCurrentFilter(columnKey, filter);
if (filter.length === 0) {
delete this.columnFilters[columnKey];
} else {
this.columnFilters[columnKey] = filter;
}
if (isSubset || wasBlank) {
this.rows = this.rows.filter(this.matchesFilters, this);
this.emit('filter');
} else {
this.emit('resetRowsFromAllData');
}
}
setColumnRegexFilter(columnKey, filter) {
filter = filter.trim();
this.columnFilters[columnKey] = new RegExp(filter);
this.emit('resetRowsFromAllData');
}
getColumnMapForObject(objectKeyString) {
let columns = this.configuration.getColumns();
if (columns[objectKeyString]) {
return columns[objectKeyString].reduce((map, column) => {
map[column.getKey()] = column;
return map;
}, {});
}
return {};
}
// /**
// * @private
// */
isSubsetOfCurrentFilter(columnKey, filter) {
if (this.columnFilters[columnKey] instanceof RegExp) {
return false;
}
return this.columnFilters[columnKey]
&& filter.startsWith(this.columnFilters[columnKey])
// startsWith check will otherwise fail when filter cleared
// because anyString.startsWith('') === true
&& filter !== '';
}
/**
* @private
*/
matchesFilters(row) {
let doesMatchFilters = true;
Object.keys(this.columnFilters).forEach((key) => {
if (!doesMatchFilters || !this.rowHasColumn(row, key)) {
return false;
}
return true;
let formattedValue = row.getFormattedValue(key);
if (formattedValue === undefined) {
return false;
}
if (this.columnFilters[key] instanceof RegExp) {
doesMatchFilters = this.columnFilters[key].test(formattedValue);
} else {
doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
}
});
this.emit('remove', removed);
return doesMatchFilters;
}
getValueForSortColumn(row) {
return row.getParsedValue(this.sortOptions.key);
}
remove(removedRows) {
this.rows = this.rows.filter(row => {
return removedRows.indexOf(row) === -1;
});
this.emit('remove', removedRows);
rowHasColumn(row, key) {
return Object.prototype.hasOwnProperty.call(row.columns, key);
}
getRows() {
return this.rows;
}
getRowsLength() {
return this.rows.length;
}
getValueForSortColumn(row) {
return row.getParsedValue(this.sortOptions.key);
}
clear() {
let removedRows = this.rows;
this.rows = [];
this.emit('remove', removedRows);
}
destroy() {
this.removeAllListeners();
}
}
return SortedTableRowCollection;
return TableRowCollection;
});

View File

@@ -466,22 +466,21 @@ export default {
this.table.on('object-added', this.addObject);
this.table.on('object-removed', this.removeObject);
this.table.on('outstanding-requests', this.outstandingRequests);
this.table.on('refresh', this.clearRowsAndRerender);
this.table.on('historical-rows-processed', this.checkForMarkedRows);
this.table.on('outstanding-requests', this.outstandingRequests);
this.table.filteredRows.on('add', this.rowsAdded);
this.table.filteredRows.on('remove', this.rowsRemoved);
this.table.filteredRows.on('sort', this.updateVisibleRows);
this.table.filteredRows.on('filter', this.updateVisibleRows);
this.table.tableRows.on('add', this.rowsAdded);
this.table.tableRows.on('remove', this.rowsRemoved);
this.table.tableRows.on('sort', this.updateVisibleRows);
this.table.tableRows.on('filter', this.updateVisibleRows);
//Default sort
this.sortOptions = this.table.filteredRows.sortBy();
this.sortOptions = this.table.tableRows.sortBy();
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
this.table.configuration.on('change', this.updateConfiguration);
this.calculateTableSize();
@@ -493,13 +492,14 @@ export default {
destroyed() {
this.table.off('object-added', this.addObject);
this.table.off('object-removed', this.removeObject);
this.table.off('outstanding-requests', this.outstandingRequests);
this.table.off('historical-rows-processed', this.checkForMarkedRows);
this.table.off('refresh', this.clearRowsAndRerender);
this.table.off('outstanding-requests', this.outstandingRequests);
this.table.filteredRows.off('add', this.rowsAdded);
this.table.filteredRows.off('remove', this.rowsRemoved);
this.table.filteredRows.off('sort', this.updateVisibleRows);
this.table.filteredRows.off('filter', this.updateVisibleRows);
this.table.tableRows.off('add', this.rowsAdded);
this.table.tableRows.off('remove', this.rowsRemoved);
this.table.tableRows.off('sort', this.updateVisibleRows);
this.table.tableRows.off('filter', this.updateVisibleRows);
this.table.configuration.off('change', this.updateConfiguration);
@@ -517,13 +517,13 @@ export default {
let start = 0;
let end = VISIBLE_ROW_COUNT;
let filteredRows = this.table.filteredRows.getRows();
let filteredRowsLength = filteredRows.length;
let tableRows = this.table.tableRows.getRows();
let tableRowsLength = tableRows.length;
this.totalNumberOfRows = filteredRowsLength;
this.totalNumberOfRows = tableRowsLength;
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
end = filteredRowsLength;
if (tableRowsLength < VISIBLE_ROW_COUNT) {
end = tableRowsLength;
} else {
let firstVisible = this.calculateFirstVisibleRow();
let lastVisible = this.calculateLastVisibleRow();
@@ -535,15 +535,15 @@ export default {
if (start < 0) {
start = 0;
end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength);
} else if (end >= filteredRowsLength) {
end = filteredRowsLength;
end = Math.min(VISIBLE_ROW_COUNT, tableRowsLength);
} else if (end >= tableRowsLength) {
end = tableRowsLength;
start = end - VISIBLE_ROW_COUNT + 1;
}
}
this.rowOffset = start;
this.visibleRows = filteredRows.slice(start, end);
this.visibleRows = tableRows.slice(start, end);
this.updatingView = false;
});
@@ -630,19 +630,19 @@ export default {
filterChanged(columnKey) {
if (this.enableRegexSearch[columnKey]) {
if (this.isCompleteRegex(this.filters[columnKey])) {
this.table.filteredRows.setColumnRegexFilter(columnKey, this.filters[columnKey].slice(1, -1));
this.table.tableRows.setColumnRegexFilter(columnKey, this.filters[columnKey].slice(1, -1));
} else {
return;
}
} else {
this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
this.table.tableRows.setColumnFilter(columnKey, this.filters[columnKey]);
}
this.setHeight();
},
clearFilter(columnKey) {
this.filters[columnKey] = '';
this.table.filteredRows.setColumnFilter(columnKey, '');
this.table.tableRows.setColumnFilter(columnKey, '');
this.setHeight();
},
rowsAdded(rows) {
@@ -674,8 +674,8 @@ export default {
* Calculates height based on total number of rows, and sets table height.
*/
setHeight() {
let filteredRowsLength = this.table.filteredRows.getRows().length;
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
let tableRowsLength = this.table.tableRows.getRowsLength();
this.totalHeight = this.rowHeight * tableRowsLength - 1;
// Set element height directly to avoid having to wait for Vue to update DOM
// which causes subsequent scroll to use an out of date height.
this.contentTable.style.height = this.totalHeight + 'px';
@@ -689,13 +689,13 @@ export default {
});
},
exportAllDataAsCSV() {
const justTheData = this.table.filteredRows.getRows()
const justTheData = this.table.tableRows.getRows()
.map(row => row.getFormattedDatum(this.headers));
this.exportAsCSV(justTheData);
},
exportMarkedDataAsCSV() {
const data = this.table.filteredRows.getRows()
const data = this.table.tableRows.getRows()
.filter(row => row.marked === true)
.map(row => row.getFormattedDatum(this.headers));
@@ -900,7 +900,7 @@ export default {
let lastRowToBeMarked = this.visibleRows[rowIndex];
let allRows = this.table.filteredRows.getRows();
let allRows = this.table.tableRows.getRows();
let firstRowIndex = allRows.indexOf(this.markedRows[0]);
let lastRowIndex = allRows.indexOf(lastRowToBeMarked);
@@ -923,17 +923,17 @@ export default {
},
checkForMarkedRows() {
this.isShowingMarkedRowsOnly = false;
this.markedRows = this.table.filteredRows.getRows().filter(row => row.marked);
this.markedRows = this.table.tableRows.getRows().filter(row => row.marked);
},
showRows(rows) {
this.table.filteredRows.rows = rows;
this.table.filteredRows.emit('filter');
this.table.tableRows.rows = rows;
this.table.emit('filter');
},
toggleMarkedRows(flag) {
if (flag) {
this.isShowingMarkedRowsOnly = true;
this.userScroll = this.scrollable.scrollTop;
this.allRows = this.table.filteredRows.getRows();
this.allRows = this.table.tableRows.getRows();
this.showRows(this.markedRows);
this.setHeight();

View File

@@ -48,6 +48,8 @@ describe("the plugin", () => {
let tablePlugin;
let element;
let child;
let historicalProvider;
let originalRouterPath;
let unlistenConfigMutation;
beforeEach((done) => {
@@ -58,7 +60,12 @@ describe("the plugin", () => {
tablePlugin = new TablePlugin();
openmct.install(tablePlugin);
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([]));
historicalProvider = {
request: () => {
return Promise.resolve([]);
}
};
spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider);
element = document.createElement('div');
child = document.createElement('div');
@@ -78,6 +85,8 @@ describe("the plugin", () => {
callBack();
});
originalRouterPath = openmct.router.path;
openmct.on('start', done);
openmct.startHeadless();
});
@@ -190,11 +199,12 @@ describe("the plugin", () => {
let telemetryPromise = new Promise((resolve) => {
telemetryPromiseResolve = resolve;
});
openmct.telemetry.request.and.callFake(() => {
historicalProvider.request = () => {
telemetryPromiseResolve(testTelemetry);
return telemetryPromise;
});
};
openmct.router.path = [testTelemetryObject];
@@ -208,6 +218,10 @@ describe("the plugin", () => {
return telemetryPromise.then(() => Vue.nextTick());
});
afterEach(() => {
openmct.router.path = originalRouterPath;
});
it("Renders a row for every telemetry datum returned", () => {
let rows = element.querySelectorAll('table.c-telemetry-table__body tr');
expect(rows.length).toBe(3);
@@ -256,14 +270,14 @@ describe("the plugin", () => {
});
it("Supports filtering telemetry by regular text search", () => {
tableInstance.filteredRows.setColumnFilter("some-key", "1");
tableInstance.tableRows.setColumnFilter("some-key", "1");
return Vue.nextTick().then(() => {
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
expect(filteredRowElements.length).toEqual(1);
tableInstance.filteredRows.setColumnFilter("some-key", "");
tableInstance.tableRows.setColumnFilter("some-key", "");
return Vue.nextTick().then(() => {
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
@@ -274,14 +288,14 @@ describe("the plugin", () => {
});
it("Supports filtering using Regex", () => {
tableInstance.filteredRows.setColumnRegexFilter("some-key", "^some-value$");
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value$");
return Vue.nextTick().then(() => {
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
expect(filteredRowElements.length).toEqual(0);
tableInstance.filteredRows.setColumnRegexFilter("some-key", "^some-value");
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value");
return Vue.nextTick().then(() => {
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');

View File

@@ -233,7 +233,6 @@ export default {
},
mounted: function () {
document.addEventListener('click', this.closeViewAndSaveMenu);
this.promptUserbeforeNavigatingAway = this.promptUserbeforeNavigatingAway.bind(this);
window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
this.openmct.editor.on('isEditing', (isEditing) => {
@@ -254,7 +253,7 @@ export default {
}
document.removeEventListener('click', this.closeViewAndSaveMenu);
window.removeEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
window.removeEventListener('click', this.promptUserbeforeNavigatingAway);
},
methods: {
toggleSaveMenu() {