Compare commits
	
		
			30 Commits
		
	
	
		
			esm-is-wei
			...
			vue-table-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | dbed9b8712 | ||
|   | e9e67e12be | ||
|   | 255774cee0 | ||
|   | cb7151cea9 | ||
|   | ed90aa04fc | ||
|   | 61ce16d3b0 | ||
|   | edaebe005f | ||
|   | 575264d29d | ||
|   | 038377410a | ||
|   | 73418dee77 | ||
|   | 1fbbab29ff | ||
|   | 4166811ec6 | ||
|   | df332e6edf | ||
|   | f07dfecf23 | ||
|   | c86fe54ee5 | ||
|   | e2dcb6a7d4 | ||
|   | 5113ea9464 | ||
|   | 575583d9b4 | ||
|   | 7a2c1ea10e | ||
|   | f2443a5c20 | ||
|   | d5f6116226 | ||
|   | c45f857108 | ||
|   | a94610ced2 | ||
|   | 4c04aaf32a | ||
|   | 60f5700dbf | ||
|   | 4685328fc7 | ||
|   | bcac3164a0 | ||
|   | 000037229d | ||
|   | 9e4b3d8052 | ||
|   | a4629633ef | 
| @@ -60,8 +60,8 @@ define([ | ||||
|                         "source": "eventGenerator", | ||||
|                         "domains": [ | ||||
|                             { | ||||
|                                 "key": "time", | ||||
|                                 "name": "Time", | ||||
|                                 "key": "utc", | ||||
|                                 "name": "Timestamp", | ||||
|                                 "format": "utc" | ||||
|                             } | ||||
|                         ], | ||||
|   | ||||
| @@ -27,8 +27,14 @@ define([ | ||||
|  | ||||
| ) { | ||||
|  | ||||
|     var RED = 0.9, | ||||
|         YELLOW = 0.5, | ||||
|     var RED = { | ||||
|             sin: 0.9,  | ||||
|             cos: 0.9 | ||||
|         }, | ||||
|         YELLOW = { | ||||
|             sin: 0.5, | ||||
|             cos: 0.5 | ||||
|         }, | ||||
|         LIMITS = { | ||||
|             rh: { | ||||
|                 cssClass: "s-limit-upr s-limit-red", | ||||
| @@ -67,17 +73,18 @@ define([ | ||||
|     SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) { | ||||
|         return { | ||||
|             evaluate: function (datum, valueMetadata) { | ||||
|                 var range = valueMetadata ? valueMetadata.key : 'sin' | ||||
|                 if (datum[range] > RED) { | ||||
|                 var range = valueMetadata && valueMetadata.key; | ||||
|                  | ||||
|                 if (datum[range] > RED[range]) { | ||||
|                     return LIMITS.rh; | ||||
|                 } | ||||
|                 if (datum[range] < -RED) { | ||||
|                 if (datum[range] < -RED[range]) { | ||||
|                     return LIMITS.rl; | ||||
|                 } | ||||
|                 if (datum[range] > YELLOW) { | ||||
|                 if (datum[range] > YELLOW[range]) { | ||||
|                     return LIMITS.yh; | ||||
|                 } | ||||
|                 if (datum[range] < -YELLOW) { | ||||
|                 if (datum[range] < -YELLOW[range]) { | ||||
|                     return LIMITS.yl; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -72,7 +72,7 @@ define([ | ||||
|         var data = []; | ||||
|         while (start <= end && data.length < 5000) { | ||||
|             data.push(pointForTimestamp(start, duration, domainObject.name)); | ||||
|             start += 5000; | ||||
|             start += duration; | ||||
|         } | ||||
|         return Promise.resolve(data); | ||||
|     }; | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
|         <script> | ||||
|             var THIRTY_MINUTES = 30 * 60 * 1000; | ||||
|  | ||||
|             require(['openmct'], function (openmct) { | ||||
|             require(['openmct', './src/plugins/telemetryTable/plugin'], function (openmct, TelemetryTablePlugin) { | ||||
|                 [ | ||||
|                     'example/eventGenerator', | ||||
|                     'example/styleguide' | ||||
| @@ -70,6 +70,7 @@ | ||||
|                 })); | ||||
|                 openmct.install(openmct.plugins.SummaryWidget()); | ||||
|                 openmct.install(openmct.plugins.Notebook()); | ||||
|                 openmct.install(TelemetryTablePlugin()); | ||||
|                 openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0}); | ||||
|                 openmct.time.timeSystem('utc'); | ||||
|                 openmct.start(); | ||||
|   | ||||
							
								
								
									
										68
									
								
								notes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								notes.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| * Delete old table | ||||
| * Update new table type and test backward compatibility. | ||||
| * re-evaluate TableConfiguration object. Name doesn't make sense right now, and some duplicated code for configuration handling in components. | ||||
| * Rebase over refactor branch | ||||
| * Move css to new table location | ||||
| * Test (see list of issues below) | ||||
| * Push WIP PR | ||||
|  | ||||
| * [X] Remove column sizing rows on object removal (should be trivial since tracking by object ID already). | ||||
| * [X] Look at optimizing styles in telemetry-table-row | ||||
|    - Right now profiling does not highlight this as a bottleneck? | ||||
| * [X] Add title to table cells | ||||
| * [X] Add elipses for overflow on table cells | ||||
| * [X] On entry, filter boxes need to remove magnifying glass. | ||||
| * [X] auto-scroll | ||||
| * [X] Show / hide columns (ie. table configuration) | ||||
| * [X] Why aren't limits being applied until I scroll or do something? | ||||
| * [X] Handle window resizing | ||||
| * [X] Fix memory leaks | ||||
| * [X] Remove isFromObject and hasColumn from TelemetryTableRow | ||||
| * [X] Remove format caching | ||||
| * [X] Add filtering | ||||
|     * If the new filter string starts with the old filter string, filter based on the list of previously filtered results, not the base list. | ||||
|     * Add the clear filter button | ||||
| * [X] Cache formatted values for "just in time" formatting. I think cache on row. Opportunity to cache on column to benefit from multiple rows with the same value, but memory management becomes a problem then as cache could grow infinitely if the table is left to run. | ||||
| * [X] Do some more testing with multiple objects. Not working properly right now. | ||||
| * [X] Rows not being removed when object removed from composition | ||||
| * [X] Subscribe to realtime data | ||||
| * [X] Column widths should be done on receipt of FIRST DATA, not on receipt of historical data. | ||||
| * [X] Filter subscription data | ||||
| * [X] Export | ||||
| * [X] Add loading spinner | ||||
| * [X] in 'mounted', should not be necessary to bind to 'this'. | ||||
| * [X] Stop Vue from decorating EVERYTHING (but especially the telemetry collection) | ||||
| * [X] Need minimum width on tables. Provided by calcTableWidthPx in MCTTableController | ||||
| * [X] Limits | ||||
|  | ||||
| * Benchmark - loading 1 million rows | ||||
|     - Old tables: ~90s | ||||
|     - New tables: ~11s | ||||
| * 1 million rows in 11 secs vs 90s | ||||
|  | ||||
| To Test | ||||
| * Multiple instances of tables | ||||
| * Make sure time columns are being correctly merged | ||||
| * Test with MSL data sources | ||||
| * Test with tutorial data sources | ||||
| * Behavior at different widths. | ||||
| * Short tables | ||||
| * Test with bounds / clock / time system changes. | ||||
| * Memory leaks | ||||
|  | ||||
| Post WIP PR | ||||
| * Fix jitter on auto-scroll | ||||
| * Look at scroll-x again. Sounded like there might be some subtlety missing there (something to do with small columns?). | ||||
| * Split TelemetryTableComponent into more components. It's too large now. | ||||
| * Performance | ||||
|     * Don't wrap row on load, do it on scroll. | ||||
|     * On batch insert, check bounds once, rather than on each insert. | ||||
| * See if sticky headers can be simplified (eg. can we combine headers table with content table?) | ||||
| * Default sort behavior, and sticking to the bottom for realtime numerical | ||||
| * Look at setting top on tbody, instead of each tr | ||||
| * Replace all "mct-table" classes | ||||
| * Consider making the sizing row a separate component. Encapsulate all sizing logic in there. | ||||
| * consider making the header table a separate component. | ||||
| * Test where no time column present (what will it sort by) | ||||
|  | ||||
| * [X] Optimization - don't both sorting filtered rows initially, just copy over values from bounded row collection which have already been sorted.  | ||||
| @@ -133,19 +133,11 @@ | ||||
| /******************************************************** LOCAL CONTROLS */ | ||||
| // Controls placed in proximity to or overlaid on components and views | ||||
|  | ||||
| .local-controls-persist { | ||||
|  | ||||
| } | ||||
|  | ||||
| .local-controls-hidden { | ||||
|     // Used within .has-local-controls, hidden by default | ||||
|  | ||||
| } | ||||
|  | ||||
| .local-controls-flyout { | ||||
|  | ||||
| } | ||||
|  | ||||
| body.desktop .has-local-controls { | ||||
|     // Helper class, provides hover ability to show local controls | ||||
|  | ||||
| @@ -156,7 +148,7 @@ body.desktop .has-local-controls { | ||||
|     } | ||||
|  | ||||
|     .local-controls-hidden { | ||||
|         @include trans-prop-nice($props: opacity, $dur: 1000ms); | ||||
|         @include trans-prop-nice($props: opacity, $dur: 500ms); | ||||
|         opacity: 0; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| @@ -211,6 +203,7 @@ body.desktop .has-local-controls { | ||||
|     cursor: pointer; | ||||
|     height: 1em; width: 1em; | ||||
|     line-height: inherit; | ||||
|     position: relative; | ||||
|     &:before { | ||||
|         position: absolute; | ||||
|         @include trans-prop-nice(transform, 100ms); | ||||
|   | ||||
| @@ -97,6 +97,7 @@ input.c-search__search-input { | ||||
|     box-shadow: none !important; // !important needed to override default for [input] | ||||
|     flex: 1 1 99%; | ||||
|     min-width: 10px; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| .c-search__search-menu-holder { | ||||
| @@ -109,6 +110,10 @@ input.c-search__search-input { | ||||
| .holder-search { | ||||
| 	$iconWidth: 20px; | ||||
|  | ||||
|     .c-search-btn-wrapper { | ||||
|         margin-right: $interiorMarginLg; // Fend off rights side from pane splitter control | ||||
|     } | ||||
|  | ||||
|     .results-msg { | ||||
|         font-size: 0.8rem; | ||||
|         opacity: 0.6; | ||||
|   | ||||
| @@ -144,6 +144,13 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| .s-status-taking-snapshot, | ||||
| .overlay.snapshot { | ||||
|     // Handle overflow-y issues with tables and html2canvas | ||||
|     .l-sticky-headers .l-tabular-body { overflow: auto; } | ||||
| } | ||||
|  | ||||
|  | ||||
| /********************************************* MOBILE */ | ||||
| body.mobile { | ||||
|     // Hide the start entry area, and disable ability to edit or delete an entry in mobile context | ||||
| @@ -285,24 +292,3 @@ body.phone.portrait { | ||||
| .overlay.l-dialog .abs.editor { | ||||
|     padding-right: 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
| .overlay.l-dialog .outer-holder.annotation-dialog{ | ||||
|     width: 90%; | ||||
|     height: 90%; | ||||
| } | ||||
| */ | ||||
|  | ||||
| /* | ||||
| .snap-annotation-wrapper{ | ||||
|     padding-top: 40px; | ||||
| } | ||||
|  | ||||
|  | ||||
| .t-console { | ||||
|     // Temp console-like reporting element | ||||
|     max-height: 200px; | ||||
|     box-sizing: border-box; | ||||
|     padding: 5px; | ||||
| } | ||||
| */ | ||||
|   | ||||
| @@ -34,10 +34,12 @@ | ||||
| } | ||||
|  | ||||
| .s-notebook-entry { | ||||
|     transition: background-color 500ms ease-out; | ||||
|     background-color: rgba($colorBodyFg, 0.1); | ||||
|     border-radius: $basicCr; | ||||
|  | ||||
|     &:hover { | ||||
|         transition: background-color 50ms ease-in; | ||||
|         background-color: rgba($colorBodyFg, 0.2); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -109,7 +109,7 @@ | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <!-- delete entry --> | ||||
|                 <div class="holder flex-elem local-control notebook-entry-delete"> | ||||
|                 <div class="holder flex-elem local-control local-controls-hidden notebook-entry-delete"> | ||||
|                     <a class="s-icon-button icon-trash" id={{entry.id}} title="Delete Entry" ng-click="deleteEntry($event)"></a> | ||||
|                 </div> | ||||
|             </li> | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| mct-table { | ||||
|  | ||||
|     .mct-sizing-table { | ||||
|         z-index: -1; | ||||
|         visibility: hidden; | ||||
| @@ -57,10 +57,12 @@ mct-table { | ||||
|                 overflow: hidden; | ||||
|                 box-sizing: border-box; | ||||
|                 display: inline-block; | ||||
|                 text-overflow: ellipsis; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| mct-table { | ||||
|     .l-control-bar { | ||||
|         margin-bottom: 3px; | ||||
|     } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     <mct-table | ||||
|         headers="headers" | ||||
|         rows="rows" | ||||
|         time-columns="tableController.timeColumns" | ||||
|         time-columns="[tableController.table.timeSystemColumnTitle]" | ||||
|         format-cell="formatCell" | ||||
|         enableFilter="true" | ||||
|         enableSort="true" | ||||
|   | ||||
							
								
								
									
										69
									
								
								platform/features/table/src/TableColumn.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								platform/features/table/src/TableColumn.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| define(function () { | ||||
|     function TableColumn(openmct, telemetryObject, metadatum) { | ||||
|         this.openmct = openmct; | ||||
|         this.telemetryObject = telemetryObject; | ||||
|         this.metadatum = metadatum; | ||||
|         this.formatter = openmct.telemetry.getValueFormatter(metadatum); | ||||
|  | ||||
|         this.titleValue = this.metadatum.name; | ||||
|     } | ||||
|  | ||||
|     TableColumn.prototype.title = function (title) { | ||||
|         if (arguments.length > 0) { | ||||
|             this.titleValue = title; | ||||
|         } | ||||
|         return this.titleValue; | ||||
|     }; | ||||
|  | ||||
|     TableColumn.prototype.isCurrentTimeSystem = function () { | ||||
|         var isCurrentTimeSystem = this.metadatum.hints.hasOwnProperty('domain') && | ||||
|         this.metadatum.key === this.openmct.time.timeSystem().key; | ||||
|  | ||||
|         return isCurrentTimeSystem; | ||||
|     }; | ||||
|  | ||||
|     TableColumn.prototype.hasValue = function (telemetryObject, telemetryDatum) { | ||||
|         var keyStringForDatum = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|         var keyStringForColumn = this.openmct.objects.makeKeyString(this.telemetryObject.identifier); | ||||
|         return keyStringForDatum === keyStringForColumn && telemetryDatum.hasOwnProperty(this.metadatum.source); | ||||
|     }; | ||||
|  | ||||
|     TableColumn.prototype.getValue = function (telemetryDatum, limitEvaluator) { | ||||
|         var isValueColumn = !!(this.metadatum.hints.y || this.metadatum.hints.range); | ||||
|         var alarm = isValueColumn && | ||||
|                     limitEvaluator && | ||||
|                     limitEvaluator.evaluate(telemetryDatum, this.metadatum); | ||||
|         var value = { | ||||
|             text: this.formatter.format(telemetryDatum), | ||||
|             value: this.formatter.parse(telemetryDatum) | ||||
|         }; | ||||
|  | ||||
|         if (alarm) { | ||||
|             value.cssClass = alarm.cssClass; | ||||
|         } | ||||
|         return value; | ||||
|     }; | ||||
|  | ||||
|     return TableColumn; | ||||
| }); | ||||
| @@ -19,10 +19,10 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /* global Set */ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|     ['./TableColumn'], | ||||
|     function (TableColumn) { | ||||
|  | ||||
|         /** | ||||
|          * Class that manages table metadata, state, and contents. | ||||
| @@ -32,62 +32,31 @@ define( | ||||
|          */ | ||||
|         function TableConfiguration(domainObject, openmct) { | ||||
|             this.domainObject = domainObject; | ||||
|             this.columns = []; | ||||
|             this.openmct = openmct; | ||||
|             this.timeSystemColumn = undefined; | ||||
|             this.columns = []; | ||||
|             this.headers = new Set(); | ||||
|             this.timeSystemColumnTitle = undefined; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Build column definitions based on supplied telemetry metadata | ||||
|          * Build column definition based on supplied telemetry metadata | ||||
|          * @param telemetryObject the telemetry producing object associated with this column | ||||
|          * @param metadata Metadata describing the domains and ranges available | ||||
|          * @returns {TableConfiguration} This object | ||||
|          */ | ||||
|         TableConfiguration.prototype.populateColumns = function (metadata) { | ||||
|             var self = this; | ||||
|             var telemetryApi = this.openmct.telemetry; | ||||
|         TableConfiguration.prototype.addColumn = function (telemetryObject, metadatum) { | ||||
|             var column = new TableColumn(this.openmct, telemetryObject, metadatum); | ||||
|  | ||||
|             this.columns = []; | ||||
|  | ||||
|             if (metadata) { | ||||
|  | ||||
|                 metadata.forEach(function (metadatum) { | ||||
|                     var formatter = telemetryApi.getValueFormatter(metadatum); | ||||
|  | ||||
|                     self.columns.push({ | ||||
|                         getKey: function () { | ||||
|                             return metadatum.key; | ||||
|                         }, | ||||
|                         getTitle: function () { | ||||
|                             return metadatum.name; | ||||
|                         }, | ||||
|                         getValue: function (telemetryDatum, limitEvaluator) { | ||||
|                             var isValueColumn = !!(metadatum.hints.y || metadatum.hints.range); | ||||
|                             var alarm = isValueColumn && | ||||
|                                         limitEvaluator && | ||||
|                                         limitEvaluator.evaluate(telemetryDatum, metadatum); | ||||
|                             var value = { | ||||
|                                 text: formatter.format(telemetryDatum), | ||||
|                                 value: formatter.parse(telemetryDatum) | ||||
|                             }; | ||||
|  | ||||
|                             if (alarm) { | ||||
|                                 value.cssClass = alarm.cssClass; | ||||
|                             } | ||||
|                             return value; | ||||
|                         } | ||||
|                     }); | ||||
|                 }); | ||||
|             if (column.isCurrentTimeSystem()) { | ||||
|                 if (!this.timeSystemColumnTitle) { | ||||
|                     this.timeSystemColumnTitle = column.title(); | ||||
|                 } | ||||
|                 column.title(this.timeSystemColumnTitle); | ||||
|             } | ||||
|             return this; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Get a simple list of column titles | ||||
|          * @returns {Array} The titles of the columns | ||||
|          */ | ||||
|         TableConfiguration.prototype.getHeaders = function () { | ||||
|             return this.columns.map(function (column, i) { | ||||
|                 return column.getTitle() || 'Column ' + (i + 1); | ||||
|             }); | ||||
|             this.columns.push(column); | ||||
|             this.headers.add(column.title()); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -98,22 +67,32 @@ define( | ||||
|          * @returns {Object} Key value pairs where the key is the column | ||||
|          * title, and the value is the formatted value from the provided datum. | ||||
|          */ | ||||
|         TableConfiguration.prototype.getRowValues = function (limitEvaluator, datum) { | ||||
|             return this.columns.reduce(function (rowObject, column, i) { | ||||
|                 var columnTitle = column.getTitle() || 'Column ' + (i + 1), | ||||
|                     columnValue = column.getValue(datum, limitEvaluator); | ||||
|  | ||||
|                 if (columnValue !== undefined && columnValue.text === undefined) { | ||||
|                     columnValue.text = ''; | ||||
|                 } | ||||
|                 // Don't replace something with nothing. | ||||
|                 // This occurs when there are multiple columns with the same | ||||
|                 // column title | ||||
|                 if (rowObject[columnTitle] === undefined || | ||||
|                     rowObject[columnTitle].text === undefined || | ||||
|                     rowObject[columnTitle].text.length === 0) { | ||||
|         TableConfiguration.prototype.getRowValues = function (telemetryObject, limitEvaluator, datum) { | ||||
|             return this.columns.reduce(function (rowObject, column) { | ||||
|                 var columnTitle = column.title(); | ||||
|                 var columnValue = { | ||||
|                     text: '', | ||||
|                     value: undefined | ||||
|                 }; | ||||
|                 if (rowObject[columnTitle] === undefined) { | ||||
|                     rowObject[columnTitle] = columnValue; | ||||
|                 } | ||||
|  | ||||
|                 if (column.hasValue(telemetryObject, datum)) { | ||||
|                     columnValue = column.getValue(datum, limitEvaluator); | ||||
|  | ||||
|                     if (columnValue.text === undefined) { | ||||
|                         columnValue.text = ''; | ||||
|                     } | ||||
|                     // Don't replace something with nothing. | ||||
|                     // This occurs when there are multiple columns with the same | ||||
|                     // column title | ||||
|                     if (rowObject[columnTitle].text === undefined || | ||||
|                         rowObject[columnTitle].text.length === 0) { | ||||
|                         rowObject[columnTitle] = columnValue; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return rowObject; | ||||
|             }, {}); | ||||
|         }; | ||||
| @@ -164,7 +143,7 @@ define( | ||||
|              * specifying whether the column is visible or not. Default to | ||||
|              * existing (persisted) configuration if available | ||||
|              */ | ||||
|             this.getHeaders().forEach(function (columnTitle) { | ||||
|             this.headers.forEach(function (columnTitle) { | ||||
|                 configuration[columnTitle] = | ||||
|                     typeof defaultConfig[columnTitle] === 'undefined' ? true : | ||||
|                         defaultConfig[columnTitle]; | ||||
|   | ||||
| @@ -93,7 +93,9 @@ define( | ||||
|                 // Calculate the new index of the last item in bounds | ||||
|                 endIndex = _.sortedLastIndex(this.highBuffer, testValue, this.sortField); | ||||
|                 added = this.highBuffer.splice(0, endIndex); | ||||
|                 this.telemetry = this.telemetry.concat(added); | ||||
|                 added.forEach(function (datum) { | ||||
|                     this.telemetry.push(datum); | ||||
|                 }.bind(this)); | ||||
|             } | ||||
|  | ||||
|             if (discarded && discarded.length > 0) { | ||||
| @@ -132,6 +134,7 @@ define( | ||||
|             // bounds events, so no bounds checking necessary | ||||
|             if (this.sortField === undefined) { | ||||
|                 this.telemetry.push(item); | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
| @@ -153,7 +156,6 @@ define( | ||||
|  | ||||
|             // If out of bounds low, disregard data | ||||
|             if (!boundsLow) { | ||||
|  | ||||
|                 // 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 | ||||
|   | ||||
| @@ -118,23 +118,18 @@ define( | ||||
|          * to sort by. By default will just match on key. | ||||
|          * | ||||
|          * @private | ||||
|          * @param {TimeSystem} timeSystem | ||||
|          */ | ||||
|         TelemetryTableController.prototype.sortByTimeSystem = function (timeSystem) { | ||||
|         TelemetryTableController.prototype.sortByTimeSystem = function () { | ||||
|             var scope = this.$scope; | ||||
|             var sortColumn; | ||||
|             scope.defaultSort = undefined; | ||||
|  | ||||
|             if (timeSystem !== undefined) { | ||||
|                 this.table.columns.forEach(function (column) { | ||||
|                     if (column.getKey() === timeSystem.key) { | ||||
|                         sortColumn = column; | ||||
|                     } | ||||
|                 }); | ||||
|                 if (sortColumn) { | ||||
|                     scope.defaultSort = sortColumn.getTitle(); | ||||
|                     this.telemetry.sort(sortColumn.getTitle() + '.value'); | ||||
|                 } | ||||
|             sortColumn = this.table.columns.filter(function (column) { | ||||
|                 return column.isCurrentTimeSystem(); | ||||
|             })[0]; | ||||
|             if (sortColumn) { | ||||
|                 scope.defaultSort = sortColumn.title(); | ||||
|                 this.telemetry.sort(sortColumn.title() + '.value'); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -172,9 +167,6 @@ define( | ||||
|          * @param rows | ||||
|          */ | ||||
|         TelemetryTableController.prototype.addRowsToTable = function (rows) { | ||||
|             rows.forEach(function (row) { | ||||
|                 this.$scope.rows.push(row); | ||||
|             }, this); | ||||
|             this.$scope.$broadcast('add:rows', rows); | ||||
|         }; | ||||
|  | ||||
| @@ -237,35 +229,21 @@ define( | ||||
|         TelemetryTableController.prototype.loadColumns = function (objects) { | ||||
|             var telemetryApi = this.openmct.telemetry; | ||||
|  | ||||
|             this.table = new TableConfiguration(this.$scope.domainObject, | ||||
|                 this.openmct); | ||||
|  | ||||
|             this.$scope.headers = []; | ||||
|  | ||||
|             if (objects.length > 0) { | ||||
|                 var allMetadata = objects.map(telemetryApi.getMetadata.bind(telemetryApi)); | ||||
|                 var allValueMetadata = _.flatten(allMetadata.map( | ||||
|                     function getMetadataValues(metadata) { | ||||
|                         return metadata.values(); | ||||
|                     } | ||||
|                 )); | ||||
|  | ||||
|                 this.table.populateColumns(allValueMetadata); | ||||
|  | ||||
|                 var domainColumns = telemetryApi.commonValuesForHints(allMetadata, ['domain']); | ||||
|                 this.timeColumns = domainColumns.map(function (metadatum) { | ||||
|                     return metadatum.name; | ||||
|                 }); | ||||
|                 objects.forEach(function (object) { | ||||
|                     var metadataValues = telemetryApi.getMetadata(object).values(); | ||||
|                     metadataValues.forEach(function (metadatum) { | ||||
|                         this.table.addColumn(object, metadatum); | ||||
|                     }.bind(this)); | ||||
|                 }.bind(this)); | ||||
|  | ||||
|                 this.filterColumns(); | ||||
|  | ||||
|                 // Default to no sort on underlying telemetry collection. Sorting | ||||
|                 // is necessary to do bounds filtering, but this is only possible | ||||
|                 // if data matches selected time system | ||||
|                 this.telemetry.sort(undefined); | ||||
|  | ||||
|                 var timeSystem = this.openmct.time.timeSystem(); | ||||
|                 if (timeSystem !== undefined) { | ||||
|                     this.sortByTimeSystem(timeSystem); | ||||
|                 } | ||||
|  | ||||
|                 this.sortByTimeSystem(); | ||||
|             } | ||||
|  | ||||
|             return objects; | ||||
| @@ -302,7 +280,7 @@ define( | ||||
|                 /* | ||||
|                  * Process a batch of historical data | ||||
|                  */ | ||||
|                 function processData(historicalData, index, limitEvaluator) { | ||||
|                 function processData(object, historicalData, index, limitEvaluator) { | ||||
|                     if (index >= historicalData.length) { | ||||
|                         processedObjects++; | ||||
|  | ||||
| @@ -311,14 +289,13 @@ define( | ||||
|                         } | ||||
|                     } else { | ||||
|                         rowData = rowData.concat(historicalData.slice(index, index + self.batchSize) | ||||
|                             .map(self.table.getRowValues.bind(self.table, limitEvaluator))); | ||||
|  | ||||
|                             .map(self.table.getRowValues.bind(self.table, object, limitEvaluator))); | ||||
|                         /* | ||||
|                          Use timeout to yield process to other UI activities. On | ||||
|                          return, process next batch | ||||
|                          */ | ||||
|                         self.timeoutHandle = self.$timeout(function () { | ||||
|                             processData(historicalData, index + self.batchSize, limitEvaluator); | ||||
|                             processData(object, historicalData, index + self.batchSize, limitEvaluator); | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
| @@ -327,7 +304,7 @@ define( | ||||
|                     // Only process the most recent request | ||||
|                     if (requestTime === self.lastRequestTime) { | ||||
|                         var limitEvaluator = openmct.telemetry.limitEvaluator(object); | ||||
|                         processData(historicalData, 0, limitEvaluator); | ||||
|                         processData(object, historicalData, 0, limitEvaluator); | ||||
|                     } else { | ||||
|                         resolve(rowData); | ||||
|                     } | ||||
| @@ -367,7 +344,6 @@ define( | ||||
|             var telemetryCollection = this.telemetry; | ||||
|             //Set table max length to avoid unbounded growth. | ||||
|             var limitEvaluator; | ||||
|             var added = false; | ||||
|             var table = this.table; | ||||
|  | ||||
|             this.subscriptions.forEach(function (subscription) { | ||||
| @@ -377,7 +353,7 @@ define( | ||||
|  | ||||
|             function newData(domainObject, datum) { | ||||
|                 limitEvaluator = telemetryApi.limitEvaluator(domainObject); | ||||
|                 added = telemetryCollection.add([table.getRowValues(limitEvaluator, datum)]); | ||||
|                 telemetryCollection.add([table.getRowValues(domainObject, limitEvaluator, datum)]); | ||||
|             } | ||||
|  | ||||
|             objects.forEach(function (object) { | ||||
|   | ||||
| @@ -27,31 +27,52 @@ define( | ||||
|     function (Table) { | ||||
|  | ||||
|         describe("A table", function () { | ||||
|             var mockDomainObject, | ||||
|             var mockTableObject, | ||||
|                 mockTelemetryObject, | ||||
|                 mockAPI, | ||||
|                 mockTelemetryAPI, | ||||
|                 table, | ||||
|                 mockTimeAPI, | ||||
|                 mockObjectsAPI, | ||||
|                 mockModel; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockDomainObject = jasmine.createSpyObj('domainObject', | ||||
|                 mockTableObject = jasmine.createSpyObj('domainObject', | ||||
|                     ['getModel', 'useCapability', 'getCapability', 'hasCapability'] | ||||
|                 ); | ||||
|                 mockModel = {}; | ||||
|                 mockDomainObject.getModel.and.returnValue(mockModel); | ||||
|                 mockDomainObject.getCapability.and.callFake(function (name) { | ||||
|                 mockTableObject.getModel.and.returnValue(mockModel); | ||||
|                 mockTableObject.getCapability.and.callFake(function (name) { | ||||
|                     return name === 'editor' && { | ||||
|                         isEditContextRoot: function () { | ||||
|                             return true; | ||||
|                         } | ||||
|                     }; | ||||
|                 }); | ||||
|                 mockTelemetryObject = { | ||||
|                     identifier: { | ||||
|                         namespace: 'mock', | ||||
|                         key: 'domainObject' | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [ | ||||
|                     'getValueFormatter' | ||||
|                 ]); | ||||
|                 mockTimeAPI = jasmine.createSpyObj('timeAPI', [ | ||||
|                     'timeSystem' | ||||
|                 ]); | ||||
|                 mockObjectsAPI = jasmine.createSpyObj('objectsAPI', [ | ||||
|                     'makeKeyString' | ||||
|                 ]); | ||||
|                 mockObjectsAPI.makeKeyString.and.callFake(function (identifier) { | ||||
|                     return [identifier.namespace, identifier.key].join(':'); | ||||
|                 }); | ||||
|  | ||||
|                 mockAPI = { | ||||
|                     telemetry: mockTelemetryAPI | ||||
|                     telemetry: mockTelemetryAPI, | ||||
|                     time: mockTimeAPI, | ||||
|                     objects: mockObjectsAPI | ||||
|                 }; | ||||
|                 mockTelemetryAPI.getValueFormatter.and.callFake(function (metadata) { | ||||
|                     var formatter = jasmine.createSpyObj( | ||||
| @@ -69,7 +90,7 @@ define( | ||||
|                     return formatter; | ||||
|                 }); | ||||
|  | ||||
|                 table = new Table(mockDomainObject, mockAPI); | ||||
|                 table = new Table(mockTableObject, mockAPI); | ||||
|             }); | ||||
|  | ||||
|             describe("Building columns from telemetry metadata", function () { | ||||
| @@ -77,51 +98,57 @@ define( | ||||
|                     { | ||||
|                         name: 'Range 1', | ||||
|                         key: 'range1', | ||||
|                         source: 'range1', | ||||
|                         hints: { | ||||
|                             y: 1 | ||||
|                             range: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Range 2', | ||||
|                         key: 'range2', | ||||
|                         source: 'range2', | ||||
|                         hints: { | ||||
|                             y: 2 | ||||
|                             range: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Domain 1', | ||||
|                         key: 'domain1', | ||||
|                         source: 'domain1', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             x: 1 | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Domain 2', | ||||
|                         key: 'domain2', | ||||
|                         source: 'domain2', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             x: 2 | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     table.populateColumns(metadata); | ||||
|                     mockTimeAPI.timeSystem.and.returnValue({ | ||||
|                         key: 'domain1' | ||||
|                     }); | ||||
|                     metadata.forEach(function (metadatum) { | ||||
|                         table.addColumn(mockTelemetryObject, metadatum); | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 it("populates columns", function () { | ||||
|                     expect(table.columns.length).toBe(4); | ||||
|                 }); | ||||
|  | ||||
|                 it("Produces headers for each column based on title", function () { | ||||
|                     var headers, | ||||
|                         firstColumn = table.columns[0]; | ||||
|  | ||||
|                     spyOn(firstColumn, 'getTitle'); | ||||
|                     headers = table.getHeaders(); | ||||
|                     expect(headers.length).toBe(4); | ||||
|                     expect(firstColumn.getTitle).toHaveBeenCalled(); | ||||
|                 it("Produces headers for each column based on metadata name", function () { | ||||
|                     expect(table.headers.size).toBe(4); | ||||
|                     Array.from(table.headers.values).forEach(function (header, i) { | ||||
|                         expect(header).toEqual(metadata[i].name); | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 it("Provides a default configuration with all columns" + | ||||
| @@ -169,11 +196,10 @@ define( | ||||
|                                 }; | ||||
|                             } | ||||
|                         }; | ||||
|                         rowValues = table.getRowValues(limitEvaluator, datum); | ||||
|                         rowValues = table.getRowValues(mockTelemetryObject, limitEvaluator, datum); | ||||
|                     }); | ||||
|  | ||||
|                     it("Returns a value for every column", function () { | ||||
|                         expect(rowValues['Range 1'].text).toBeDefined(); | ||||
|                         expect(rowValues['Range 1'].text).toEqual(10); | ||||
|                     }); | ||||
|  | ||||
|   | ||||
| @@ -78,7 +78,8 @@ define( | ||||
|                 ]); | ||||
|  | ||||
|                 mockObjectAPI = jasmine.createSpyObj("objectAPI", [ | ||||
|                     "observe" | ||||
|                     "observe", | ||||
|                     "makeKeyString" | ||||
|                 ]); | ||||
|                 unobserve = jasmine.createSpy("unobserve"); | ||||
|                 mockObjectAPI.observe.and.returnValue(unobserve); | ||||
| @@ -184,8 +185,7 @@ define( | ||||
|                 var mockComposition, | ||||
|                     mockTelemetryObject, | ||||
|                     mockChildren, | ||||
|                     unsubscribe, | ||||
|                     done; | ||||
|                     unsubscribe; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     mockComposition = jasmine.createSpyObj("composition", [ | ||||
| @@ -207,8 +207,6 @@ define( | ||||
|                     mockTelemetryAPI.isTelemetryObject.and.callFake(function (obj) { | ||||
|                         return obj.identifier.key === mockTelemetryObject.identifier.key; | ||||
|                     }); | ||||
|  | ||||
|                     done = false; | ||||
|                 }); | ||||
|  | ||||
|                 it('fetches historical data for the time period specified by the conductor bounds', function () { | ||||
| @@ -292,40 +290,37 @@ define( | ||||
|             }); | ||||
|  | ||||
|             describe('populates table columns', function () { | ||||
|                 var domainMetadata; | ||||
|                 var allMetadata; | ||||
|                 var mockTimeSystem; | ||||
|                 var mockTimeSystem1; | ||||
|                 var mockTimeSystem2; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     domainMetadata = [{ | ||||
|                         key: "column1", | ||||
|                         name: "Column 1", | ||||
|                         hints: {} | ||||
|                     }]; | ||||
|  | ||||
|                     allMetadata = [{ | ||||
|                         key: "column1", | ||||
|                         name: "Column 1", | ||||
|                         hints: {} | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, { | ||||
|                         key: "column2", | ||||
|                         name: "Column 2", | ||||
|                         hints: {} | ||||
|                         hints: { | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     }, { | ||||
|                         key: "column3", | ||||
|                         name: "Column 3", | ||||
|                         hints: {} | ||||
|                     }]; | ||||
|  | ||||
|                     mockTimeSystem = { | ||||
|                     mockTimeSystem1 = { | ||||
|                         key: "column1" | ||||
|                     }; | ||||
|                     mockTimeSystem2 = { | ||||
|                         key: "column2" | ||||
|                     }; | ||||
|  | ||||
|                     mockTelemetryAPI.commonValuesForHints.and.callFake(function (metadata, hints) { | ||||
|                         if (_.eq(hints, ["domain"])) { | ||||
|                             return domainMetadata; | ||||
|                         } | ||||
|                     }); | ||||
|                     mockConductor.timeSystem.and.returnValue(mockTimeSystem1); | ||||
|  | ||||
|                     mockTelemetryAPI.getMetadata.and.returnValue({ | ||||
|                         values: function () { | ||||
| @@ -345,9 +340,12 @@ define( | ||||
|                 }); | ||||
|  | ||||
|                 it('and sorts by column matching time system', function () { | ||||
|                     expect(mockScope.defaultSort).not.toEqual("Column 1"); | ||||
|                     controller.sortByTimeSystem(mockTimeSystem); | ||||
|                     expect(mockScope.defaultSort).toEqual("Column 1"); | ||||
|  | ||||
|                     mockConductor.timeSystem.and.returnValue(mockTimeSystem2); | ||||
|                     controller.sortByTimeSystem(); | ||||
|  | ||||
|                     expect(mockScope.defaultSort).toEqual("Column 2"); | ||||
|                 }); | ||||
|  | ||||
|                 it('batches processing of rows for performance when receiving historical telemetry', function () { | ||||
| @@ -403,25 +401,16 @@ define( | ||||
|  | ||||
|             describe('when telemetry is added', function () { | ||||
|                 var testRows; | ||||
|                 var expectedRows; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     testRows = [{ a: 0 }, { a: 1 }, { a: 2 }]; | ||||
|                     mockScope.rows = [{ a: -1 }]; | ||||
|                     expectedRows = mockScope.rows.concat(testRows); | ||||
|  | ||||
|                     spyOn(controller.telemetry, "on").and.callThrough(); | ||||
|                     controller.registerChangeListeners(); | ||||
|  | ||||
|                     controller.telemetry.on.calls.all().forEach(function (call) { | ||||
|                         if (call.args[0] === 'added') { | ||||
|                             call.args[1](testRows); | ||||
|                         } | ||||
|                     }); | ||||
|                     controller.telemetry.add(testRows); | ||||
|                 }); | ||||
|  | ||||
|                 it("adds it to rows in scope", function () { | ||||
|                     expect(mockScope.rows).toEqual(expectedRows); | ||||
|                 it("Adds the rows to the MCTTable directive", function () { | ||||
|                     expect(mockScope.$broadcast).toHaveBeenCalledWith("add:rows", testRows); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|   | ||||
							
								
								
									
										39
									
								
								src/exporters/CSVExporter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/exporters/CSVExporter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'csv', | ||||
|     'saveAs' | ||||
| ], function (CSV, saveAs) { | ||||
|     class CSVExporter { | ||||
|         export(rows, options) { | ||||
|             let headers = (options && options.headers) || | ||||
|                 (Object.keys((rows[0] || {})).sort()); | ||||
|             let filename = (options && options.filename) || "export.csv"; | ||||
|             let csvText = new CSV(rows, { header: headers }).encode(); | ||||
|             let blob = new Blob([csvText], { type: "text/csv" }); | ||||
|             saveAs(blob, filename); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return CSVExporter; | ||||
| }); | ||||
| @@ -113,6 +113,9 @@ define([ | ||||
|     }; | ||||
|  | ||||
|     LocalTimeFormat.prototype.parse = function (text) { | ||||
|         if (typeof text === 'number') { | ||||
|             return text; | ||||
|         } | ||||
|         return moment(text, DATE_FORMATS).valueOf(); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|         <span class="t-configuration"> </span> | ||||
|         <span class="t-value-inputs"> </span> | ||||
|     </span> | ||||
|     <span class="flex-elem local-control l-condition-action-buttons-wrapper"> | ||||
|     <span class="flex-elem local-control local-controls-hidden l-condition-action-buttons-wrapper"> | ||||
|         <a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this condition"></a> | ||||
|         <a class="s-icon-button icon-trash t-delete" title="Delete this condition"></a> | ||||
|     </span> | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|         <div class="widget-rule-header"> | ||||
|             <span class="flex-elem l-widget-thumb-wrapper"> | ||||
|                 <span class="grippy-holder"> | ||||
|                     <span class="t-grippy grippy local-control"></span> | ||||
|                     <span class="t-grippy grippy local-control local-controls-hidden"></span> | ||||
|                 </span> | ||||
|                 <span class="view-control expanded"></span> | ||||
|                 <span class="t-widget-thumb widget-thumb"> | ||||
| @@ -12,7 +12,7 @@ | ||||
|             </span> | ||||
|             <span class="flex-elem rule-title">Default Title</span> | ||||
|             <span class="flex-elem rule-description grows">Rule description goes here</span> | ||||
|             <span class="flex-elem local-control l-rule-action-buttons-wrapper"> | ||||
|             <span class="flex-elem local-control local-controls-hidden l-rule-action-buttons-wrapper"> | ||||
|                 <a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this rule"></a> | ||||
|                 <a class="s-icon-button icon-trash t-delete" title="Delete this rule"></a> | ||||
|             </span> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|                 <span class="equal-to hidden"> equal to </span> | ||||
|                 <span class="t-value-inputs"></span> | ||||
|             </span> | ||||
|             <span class="flex-elem local-control l-widget-test-data-item-action-buttons-wrapper"> | ||||
|             <span class="flex-elem local-control local-controls-hidden l-widget-test-data-item-action-buttons-wrapper"> | ||||
|                 <a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this test value"></a> | ||||
|                 <a class="s-icon-button icon-trash t-delete" title="Delete this test value"></a> | ||||
|             </span> | ||||
|   | ||||
| @@ -188,7 +188,7 @@ define([ | ||||
|                 if (!this.config.values[index]) { | ||||
|                     this.config.values[index] = (inputType === 'number' ? 0 : ''); | ||||
|                 } | ||||
|                 newInput = $('<input class="sm" type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>'); | ||||
|                 newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>'); | ||||
|                 this.valueInputs.push(newInput.get(0)); | ||||
|                 inputArea.append(newInput); | ||||
|                 index += 1; | ||||
|   | ||||
							
								
								
									
										102
									
								
								src/plugins/telemetryTable/TableConfigurationComponent.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/plugins/telemetryTable/TableConfigurationComponent.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
|  define([ | ||||
|     'lodash', | ||||
|     'vue', | ||||
|     'text!./table-configuration.html', | ||||
|     './TelemetryTableConfiguration' | ||||
| ],function ( | ||||
|     _, | ||||
|     Vue,  | ||||
|     TableConfigurationTemplate, | ||||
|     TelemetryTableConfiguration | ||||
| ) { | ||||
|     return function TableConfigurationComponent(domainObject, openmct) { | ||||
|         const tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct); | ||||
|         let unlisteners = []; | ||||
|         unlisteners.push(openmct.objects.observe(domainObject, '*', (newDomainObject) => { | ||||
|             domainObject = newDomainObject; | ||||
|         })); | ||||
|  | ||||
|         function defaultConfiguration(domainObject) { | ||||
|             let configuration = domainObject.configuration; | ||||
|             configuration.table = configuration.table || { | ||||
|                 columns: {} | ||||
|             }; | ||||
|             return configuration; | ||||
|         } | ||||
|  | ||||
|         return new Vue({ | ||||
|             template: TableConfigurationTemplate, | ||||
|             data: function () { | ||||
|                 return { | ||||
|                     headers: {}, | ||||
|                     configuration: defaultConfiguration(domainObject) | ||||
|                 } | ||||
|             }, | ||||
|             methods: { | ||||
|                 updateHeaders: function (headers) { | ||||
|                     this.headers = headers; | ||||
|                 }, | ||||
|                 toggleColumn: function (key) { | ||||
|                     let isVisible = this.configuration.table.columns[key]; | ||||
|                      | ||||
|                     if (isVisible === undefined) { | ||||
|                         isVisible = true; | ||||
|                     } | ||||
|  | ||||
|                     this.configuration.table.columns[key] = !isVisible; | ||||
|                     openmct.objects.mutate(domainObject, "configuration", this.configuration); | ||||
|                 }, | ||||
|                 addObject: function (domainObject) { | ||||
|                     tableConfiguration.addColumnsForObject(domainObject, true); | ||||
|                     this.updateHeaders(tableConfiguration.getHeaders()); | ||||
|                 }, | ||||
|                 removeObject: function (objectIdentifier) { | ||||
|                     tableConfiguration.removeColumnsForObject(objectIdentifier, true); | ||||
|                     this.updateHeaders(tableConfiguration.getHeaders()); | ||||
|                 } | ||||
|  | ||||
|             }, | ||||
|             mounted: function () { | ||||
|                 let compositionCollection = openmct.composition.get(domainObject); | ||||
|  | ||||
|                 compositionCollection.load() | ||||
|                     .then((composition) => { | ||||
|                         tableConfiguration.addColumnsForAllObjects(composition); | ||||
|                         this.updateHeaders(tableConfiguration.getHeaders()); | ||||
|                          | ||||
|                         compositionCollection.on('add', this.addObject); | ||||
|                         unlisteners.push(compositionCollection.off.bind(compositionCollection, 'add', this.addObject)); | ||||
|  | ||||
|                         compositionCollection.on('remove', this.removeObject); | ||||
|                         unlisteners.push(compositionCollection.off.bind(compositionCollection, 'remove', this.removeObject)); | ||||
|                     }); | ||||
|             }, | ||||
|             destroyed: function () { | ||||
|                 tableConfiguration.destroy(); | ||||
|                 unlisteners.forEach((unlisten) => unlisten()); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  }); | ||||
							
								
								
									
										85
									
								
								src/plugins/telemetryTable/TableConfigurationViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/plugins/telemetryTable/TableConfigurationViewProvider.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     '../../api/objects/object-utils', | ||||
|     './TableConfigurationComponent' | ||||
| ], function ( | ||||
|     objectUtils, | ||||
|     TableConfigurationComponent | ||||
| ) { | ||||
|     function TableConfigurationViewProvider(openmct) { | ||||
|         let instantiateService; | ||||
|  | ||||
|         function isBeingEdited(object) { | ||||
|             let oldStyleObject = getOldStyleObject(object); | ||||
|  | ||||
|             return oldStyleObject.hasCapability('editor') && | ||||
|                 oldStyleObject.getCapability('editor').isEditContextRoot(); | ||||
|         } | ||||
|  | ||||
|         function getOldStyleObject(object) { | ||||
|             let oldFormatModel = objectUtils.toOldFormat(object); | ||||
|             let oldFormatId = objectUtils.makeKeyString(object.identifier); | ||||
|  | ||||
|             return instantiate(oldFormatModel, oldFormatId); | ||||
|         } | ||||
|  | ||||
|         function instantiate(model, id) { | ||||
|             if (!instantiateService) { | ||||
|                 instantiateService = openmct.$injector.get('instantiate'); | ||||
|             } | ||||
|             return instantiateService(model, id); | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             key: 'table-configuration', | ||||
|             name: 'Telemetry Table Configuration', | ||||
|             canView: function (selection) { | ||||
|                 let object = selection[0].context.item; | ||||
|                  | ||||
|                 return selection.length > 0 && | ||||
|                     object.type === 'vue-table' &&  | ||||
|                     isBeingEdited(object); | ||||
|             }, | ||||
|             view: function (selection) { | ||||
|                 let component; | ||||
|                 let domainObject = selection[0].context.item; | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = TableConfigurationComponent(domainObject, openmct); | ||||
|                         element.appendChild(component.$mount().$el); | ||||
|                     },  | ||||
|                     destroy: function (element) { | ||||
|                         component.$destroy(); | ||||
|                         element.removeChild(component.$el); | ||||
|                         component = undefined; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             priority: function () { | ||||
|                 return 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return TableConfigurationViewProvider; | ||||
| }); | ||||
							
								
								
									
										145
									
								
								src/plugins/telemetryTable/TelemetryTable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/plugins/telemetryTable/TelemetryTable.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'EventEmitter', | ||||
|     'lodash', | ||||
|     './collections/BoundedTableRowCollection', | ||||
|     './collections/FilteredTableRowCollection', | ||||
|     './TelemetryTableRow', | ||||
|     './TelemetryTableConfiguration' | ||||
| ], function ( | ||||
|     EventEmitter, | ||||
|     _, | ||||
|     BoundedTableRowCollection, | ||||
|     FilteredTableRowCollection, | ||||
|     TelemetryTableRow, | ||||
|     TelemetryTableConfiguration | ||||
| ) { | ||||
|     class TelemetryTable extends EventEmitter { | ||||
|         constructor(domainObject, rowCount, openmct) { | ||||
|             super(); | ||||
|  | ||||
|             this.domainObject = domainObject; | ||||
|             this.openmct = openmct; | ||||
|             this.rowCount = rowCount; | ||||
|             this.subscriptions = {}; | ||||
|             this.tableComposition = undefined; | ||||
|             this.configuration = new TelemetryTableConfiguration(domainObject, openmct); | ||||
|  | ||||
|             this.addTelemetryObject = this.addTelemetryObject.bind(this); | ||||
|             this.removeTelemetryObject = this.removeTelemetryObject.bind(this); | ||||
|  | ||||
|             this.createTableRowCollections(); | ||||
|             this.loadComposition(); | ||||
|         } | ||||
|  | ||||
|         createTableRowCollections() { | ||||
|             this.boundedRows = new BoundedTableRowCollection(this.openmct); | ||||
|  | ||||
|             //By default, sort by current time system, ascending. | ||||
|             this.filteredRows = new FilteredTableRowCollection(this.boundedRows); | ||||
|             this.filteredRows.sortBy({ | ||||
|                 key: this.openmct.time.timeSystem().key, | ||||
|                 direction: 'asc' | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         loadComposition() { | ||||
|             this.tableComposition = this.openmct.composition.get(this.domainObject); | ||||
|             this.tableComposition.load().then((composition)=>{ | ||||
|                 this.configuration.addColumnsForAllObjects(composition); | ||||
|                 composition.forEach(this.addTelemetryObject); | ||||
|  | ||||
|                 this.tableComposition.on('add', this.addTelemetryObject); | ||||
|                 this.tableComposition.on('remove', this.removeTelemetryObject); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         addTelemetryObject(telemetryObject) { | ||||
|             this.configuration.addColumnsForObject(telemetryObject, true); | ||||
|             this.requestDataFor(telemetryObject); | ||||
|             this.subscribeTo(telemetryObject); | ||||
|  | ||||
|             this.emit('object-added', telemetryObject); | ||||
|         } | ||||
|  | ||||
|         removeTelemetryObject(objectIdentifier) { | ||||
|             this.configuration.removeColumnsForObject(objectIdentifier, true); | ||||
|             let keyString = this.openmct.objects.makeKeyString(objectIdentifier); | ||||
|             this.boundedRows.removeAllRowsForObject(keyString); | ||||
|             this.unsubscribe(keyString); | ||||
|  | ||||
|             this.emit('object-removed', objectIdentifier); | ||||
|         } | ||||
|  | ||||
|         requestDataFor(telemetryObject) { | ||||
|             this.emit('loading-historical-data', true); | ||||
|             this.openmct.telemetry.request(telemetryObject) | ||||
|                 .then(telemetryData => { | ||||
|                     let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|                     let columnMap = this.getColumnMapForObject(keyString); | ||||
|                     let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|                     let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator)); | ||||
|                     this.boundedRows.add(telemetryRows); | ||||
|                     console.log('loaded ' + telemetryRows.length + ' rows'); | ||||
|                     this.emit('loading-historical-data', false); | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         getColumnMapForObject(objectKeyString) { | ||||
|             let columns = this.configuration.getColumns(); | ||||
|              | ||||
|             return columns[objectKeyString].reduce((map, column) => { | ||||
|                 map[column.getKey()] = column; | ||||
|                 return map; | ||||
|             }, {}); | ||||
|         } | ||||
|  | ||||
|         subscribeTo(telemetryObject) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|             let columnMap = this.getColumnMapForObject(keyString); | ||||
|             let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|             this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => { | ||||
|                 this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator)); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         unsubscribe(keyString) { | ||||
|             this.subscriptions[keyString](); | ||||
|             delete this.subscriptions[keyString]; | ||||
|         } | ||||
|  | ||||
|         destroy() { | ||||
|             this.boundedRows.destroy(); | ||||
|             this.filteredRows.destroy(); | ||||
|             Object.keys(this.subscriptions).forEach(this.unsubscribe, this); | ||||
|              | ||||
|             this.tableComposition.off('add', this.addTelemetryObject); | ||||
|             this.tableComposition.off('remove', this.removeTelemetryObject); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return TelemetryTable; | ||||
| }); | ||||
							
								
								
									
										57
									
								
								src/plugins/telemetryTable/TelemetryTableColumn.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/plugins/telemetryTable/TelemetryTableColumn.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| define(function () { | ||||
|     class TelemetryTableColumn { | ||||
|         constructor (openmct, metadatum) { | ||||
|             this.metadatum = metadatum; | ||||
|             this.formatter = openmct.telemetry.getValueFormatter(metadatum); | ||||
|             this.titleValue = this.metadatum.name; | ||||
|         } | ||||
|  | ||||
|         getKey() { | ||||
|             return this.metadatum.key; | ||||
|         } | ||||
|  | ||||
|         getTitle() { | ||||
|             return this.metadatum.name; | ||||
|         } | ||||
|  | ||||
|         getMetadatum() { | ||||
|             return this.metadatum; | ||||
|         } | ||||
|  | ||||
|         hasValueForDatum(telemetryDatum) { | ||||
|             return telemetryDatum.hasOwnProperty(this.metadatum.source); | ||||
|         } | ||||
|  | ||||
|         getRawValue(telemetryDatum) { | ||||
|             return telemetryDatum[this.metadatum.source]; | ||||
|         } | ||||
|  | ||||
|         getFormattedValue(telemetryDatum) { | ||||
|             return this.formatter.format(telemetryDatum); | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     return TelemetryTableColumn; | ||||
| }); | ||||
							
								
								
									
										309
									
								
								src/plugins/telemetryTable/TelemetryTableComponent.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/plugins/telemetryTable/TelemetryTableComponent.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
|  define([ | ||||
|     'lodash', | ||||
|     'vue', | ||||
|     'text!./telemetry-table.html', | ||||
|     './TelemetryTable', | ||||
|     './TelemetryTableRowComponent', | ||||
|     '../../exporters/CSVExporter' | ||||
| ],function ( | ||||
|     _, | ||||
|     Vue,  | ||||
|     TelemetryTableTemplate, | ||||
|     TelemetryTable, | ||||
|     TelemetryTableRowComponent, | ||||
|     CSVExporter | ||||
| ) { | ||||
|     const VISIBLE_ROW_COUNT = 100; | ||||
|     const ROW_HEIGHT = 17; | ||||
|     const RESIZE_POLL_INTERVAL = 200; | ||||
|     const AUTO_SCROLL_TRIGGER_HEIGHT = 20; | ||||
|  | ||||
|     return function TelemetryTableComponent(domainObject, openmct) { | ||||
|         const csvExporter = new CSVExporter(); | ||||
|         const table = new TelemetryTable(domainObject, VISIBLE_ROW_COUNT, openmct); | ||||
|         let processingScroll = false; | ||||
|         let observationUnlistener; | ||||
|  | ||||
|         function defaultConfiguration(domainObject) { | ||||
|             let configuration = domainObject.configuration; | ||||
|             configuration.table = configuration.table || { | ||||
|                 columns: {} | ||||
|             }; | ||||
|             return configuration; | ||||
|         } | ||||
|  | ||||
|         return new Vue({ | ||||
|             template: TelemetryTableTemplate, | ||||
|             components: { | ||||
|                 'telemetry-table-row': TelemetryTableRowComponent | ||||
|             }, | ||||
|             data: function () { | ||||
|                 return { | ||||
|                     headers: {}, | ||||
|                     configuration: defaultConfiguration(domainObject), | ||||
|                     headersCount: 0, | ||||
|                     visibleRows: [], | ||||
|                     columnWidths: [], | ||||
|                     sizingRows: {}, | ||||
|                     rowHeight: ROW_HEIGHT, | ||||
|                     scrollOffset: 0, | ||||
|                     totalHeight: 0, | ||||
|                     totalWidth: 0, | ||||
|                     rowOffset: 0, | ||||
|                     autoScroll: true, | ||||
|                     sortOptions: {}, | ||||
|                     filters: {}, | ||||
|                     loading: false, | ||||
|                     scrollable: undefined, | ||||
|                     tableEl: undefined, | ||||
|                     headersHolderEl: undefined, | ||||
|                     calcTableWidth: '100%' | ||||
|                 } | ||||
|             }, | ||||
|             methods: { | ||||
|                 updateVisibleRows: function () { | ||||
|                     let start = 0; | ||||
|                     let end = VISIBLE_ROW_COUNT; | ||||
|                     let filteredRows = table.filteredRows.getRows(); | ||||
|                     let filteredRowsLength = filteredRows.length; | ||||
|                      | ||||
|                     this.totalHeight = this.rowHeight * filteredRowsLength - 1; | ||||
|          | ||||
|                     if (filteredRowsLength < VISIBLE_ROW_COUNT) { | ||||
|                         end = filteredRowsLength; | ||||
|                     } else { | ||||
|                         let firstVisible = this.calculateFirstVisibleRow(); | ||||
|                         let lastVisible = this.calculateLastVisibleRow(); | ||||
|                         let totalVisible = lastVisible - firstVisible; | ||||
|  | ||||
|                         let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible; | ||||
|                         start = firstVisible - Math.floor(numberOffscreen / 2); | ||||
|                         end = lastVisible + Math.ceil(numberOffscreen / 2); | ||||
|          | ||||
|                         if (start < 0) { | ||||
|                             start = 0; | ||||
|                             end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength); | ||||
|                         } else if (end >= filteredRowsLength) { | ||||
|                             end = filteredRowsLength; | ||||
|                             start = end - VISIBLE_ROW_COUNT + 1; | ||||
|                         } | ||||
|                     } | ||||
|                     this.rowOffset = start; | ||||
|                     this.visibleRows = filteredRows.slice(start, end); | ||||
|                 }, | ||||
|                 calculateFirstVisibleRow: function () { | ||||
|                     return Math.floor(this.scrollable.scrollTop / this.rowHeight); | ||||
|                 }, | ||||
|                 calculateLastVisibleRow: function () { | ||||
|                     let bottomScroll = this.scrollable.scrollTop + this.scrollable.offsetHeight; | ||||
|                     return Math.floor(bottomScroll / this.rowHeight); | ||||
|                 }, | ||||
|                 updateHeaders: function () { | ||||
|                     let headers = table.configuration.getHeaders(); | ||||
|  | ||||
|                     Object.keys(headers).forEach((headerKey) => { | ||||
|                         if (this.configuration.table.columns[headerKey] === false) { | ||||
|                             delete headers[headerKey]; | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
|                     this.headers = headers; | ||||
|                     this.headersCount = Object.values(headers).length; | ||||
|                     Vue.nextTick().then(this.calculateColumnWidths); | ||||
|                 }, | ||||
|                 setSizingTableWidth: function () { | ||||
|                     let scrollW = this.scrollable.offsetWidth - this.scrollable.clientWidth; | ||||
|                      | ||||
|                     if (scrollW && scrollW > 0) { | ||||
|                         this.calcTableWidth = 'calc(100% - ' + scrollW + 'px)'; | ||||
|                     } | ||||
|                 }, | ||||
|                 calculateColumnWidths: function () { | ||||
|                     let columnWidths = []; | ||||
|                     let totalWidth = 0; | ||||
|                     let sizingRowEl = this.sizingTable.children[0]; | ||||
|                     let sizingCells = Array.from(sizingRowEl.children); | ||||
|  | ||||
|                     sizingCells.forEach((cell) => { | ||||
|                         let columnWidth = cell.offsetWidth; | ||||
|                         columnWidths.push(columnWidth + 'px'); | ||||
|                         totalWidth += columnWidth; | ||||
|                     }); | ||||
|  | ||||
|                     this.columnWidths = columnWidths; | ||||
|                     this.totalWidth = totalWidth; | ||||
|                 }, | ||||
|                 sortBy: function (columnKey) { | ||||
|                     // If sorting by the same column, flip the sort direction. | ||||
|                     if (this.sortOptions.key === columnKey) { | ||||
|                         if (this.sortOptions.direction === 'asc') { | ||||
|                             this.sortOptions.direction = 'desc'; | ||||
|                         } else { | ||||
|                             this.sortOptions.direction = 'asc'; | ||||
|                         } | ||||
|                     } else { | ||||
|                         this.sortOptions = { | ||||
|                             key: columnKey, | ||||
|                             direction: 'asc' | ||||
|                         } | ||||
|                     } | ||||
|                     table.filteredRows.sortBy(this.sortOptions); | ||||
|                 }, | ||||
|                 scroll: function() { | ||||
|                     if (!processingScroll) { | ||||
|                         processingScroll = true; | ||||
|                         requestAnimationFrame(() => { | ||||
|                             this.updateVisibleRows(); | ||||
|                             this.synchronizeScrollX(); | ||||
|  | ||||
|                             if (this.shouldSnapToBottom()) { | ||||
|                                 // If user scrolls away from bottom, disable auto-scroll. | ||||
|                                 // Auto-scroll will be re-enabled if user scrolls to bottom again. | ||||
|                                 this.autoScroll = true; | ||||
|                             } else { | ||||
|                                 this.autoScroll = false; | ||||
|                             } | ||||
|  | ||||
|                             processingScroll = false; | ||||
|                         }); | ||||
|                     } | ||||
|                 }, | ||||
|                 shouldSnapToBottom: function () { | ||||
|                     return this.scrollable.scrollTop >= (this.scrollable.scrollHeight - this.scrollable.offsetHeight - AUTO_SCROLL_TRIGGER_HEIGHT); | ||||
|                 }, | ||||
|                 scrollToBottom: function () { | ||||
|                     this.scrollable.scrollTop = this.totalHeight; | ||||
|                 }, | ||||
|                 synchronizeScrollX: function () { | ||||
|                     this.headersHolderEl.scrollLeft = this.scrollable.scrollLeft; | ||||
|                 }, | ||||
|                 filterChanged: function (columnKey) { | ||||
|                     table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]); | ||||
|                 }, | ||||
|                 clearFilter: function (columnKey) { | ||||
|                     this.filters[columnKey] = ''; | ||||
|                     table.filteredRows.setColumnFilter(columnKey, ''); | ||||
|                 }, | ||||
|                 rowsAdded: function (rows) { | ||||
|                     let sizingRow; | ||||
|                     if (Array.isArray(rows)) { | ||||
|                         sizingRow = rows[0]; | ||||
|                     } else { | ||||
|                         sizingRow = rows; | ||||
|                     } | ||||
|                     if (!this.sizingRows[sizingRow.objectKeyString]) { | ||||
|                         this.sizingRows[sizingRow.objectKeyString] = sizingRow; | ||||
|                         Vue.nextTick().then(this.calculateColumnWidths); | ||||
|                     } | ||||
|                     this.updateVisibleRows(); | ||||
|                      | ||||
|                     if (this.autoScroll) { | ||||
|                         this.scrollToBottom(); | ||||
|                     } | ||||
|                 }, | ||||
|                 exportAsCSV: function () { | ||||
|                     const justTheData = table.filteredRows.getRows() | ||||
|                         .map(row => row.getFormattedDatum()); | ||||
|                     const headers = Object.keys(this.headers); | ||||
|                     csvExporter.export(justTheData, { | ||||
|                         filename: table.domainObject.name + '.csv', | ||||
|                         headers: headers | ||||
|                     }); | ||||
|                 }, | ||||
|                 loadingHistoricalData: function (loading) { | ||||
|                     this.loading = loading; | ||||
|                 }, | ||||
|                 calculateTableSize: function () { | ||||
|                     this.setSizingTableWidth(); | ||||
|                     Vue.nextTick().then(this.calculateColumnWidths); | ||||
|                 }, | ||||
|                 pollForResize: function () { | ||||
|                     let el = this.$el; | ||||
|                     let width = el.clientWidth; | ||||
|                     let height = el.clientHeight; | ||||
|  | ||||
|                     this.resizePollHandle = setInterval(() => { | ||||
|                         if (el.clientWidth !== width || el.clientHeight !== height) { | ||||
|                             this.calculateTableSize(); | ||||
|                             width = el.clientWidth; | ||||
|                             height = el.clientHeight; | ||||
|                         } | ||||
|                     }, RESIZE_POLL_INTERVAL); | ||||
|                 }, | ||||
|                 updateConfiguration: function (configuration) { | ||||
|                     this.configuration = configuration; | ||||
|                     this.updateHeaders(); | ||||
|                 }, | ||||
|                 addObject: function () { | ||||
|                     this.updateHeaders(); | ||||
|                 }, | ||||
|                 removeObject: function (objectIdentifier) { | ||||
|                     let objectKeyString = openmct.objects.makeKeyString(objectIdentifier); | ||||
|                     delete this.sizingRows[objectKeyString]; | ||||
|                     this.updateHeaders(); | ||||
|                 } | ||||
|             }, | ||||
|             created: function () { | ||||
|                 this.filterChanged = _.debounce(this.filterChanged, 500); | ||||
|             }, | ||||
|             mounted: function () { | ||||
|                 table.on('object-added', this.addObject); | ||||
|                 table.on('object-removed', this.removeObject); | ||||
|                 table.on('loading-historical-data', this.loadingHistoricalData); | ||||
|  | ||||
|                 table.filteredRows.on('add', this.rowsAdded); | ||||
|                 table.filteredRows.on('remove', this.updateVisibleRows); | ||||
|                 table.filteredRows.on('sort', this.updateVisibleRows); | ||||
|                 table.filteredRows.on('filter', this.updateVisibleRows); | ||||
|                  | ||||
|                 //Default sort | ||||
|                 this.sortOptions = table.filteredRows.sortBy(); | ||||
|                 this.scrollable = this.$el.querySelector('.t-scrolling'); | ||||
|                 this.sizingTable = this.$el.querySelector('.js-sizing-table'); | ||||
|                 this.headersHolderEl = this.$el.querySelector('.mct-table-headers-w'); | ||||
|  | ||||
|                 observationUnlistener = openmct.objects.observe(domainObject, 'configuration', this.updateConfiguration); | ||||
|                  | ||||
|                 this.calculateTableSize(); | ||||
|                 this.pollForResize(); | ||||
|             }, | ||||
|             destroyed: function () { | ||||
|                 table.off('object-added', this.addObject); | ||||
|                 table.off('object-removed', this.removeObject); | ||||
|                 table.off('loading-historical-data', this.loadingHistoricalData); | ||||
|  | ||||
|                 table.filteredRows.off('add', this.updateVisibleRows); | ||||
|                 table.filteredRows.off('remove', this.updateVisibleRows); | ||||
|                 table.filteredRows.off('sort', this.updateVisibleRows); | ||||
|                 table.filteredRows.off('filter', this.updateVisibleRows); | ||||
|                 clearInterval(this.resizePollHandle); | ||||
|  | ||||
|                 observationUnlistener(); | ||||
|                  | ||||
|                 table.destroy(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  }); | ||||
							
								
								
									
										94
									
								
								src/plugins/telemetryTable/TelemetryTableConfiguration.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/plugins/telemetryTable/TelemetryTableConfiguration.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'EventEmitter', | ||||
|     './TelemetryTableColumn', | ||||
| ], function (EventEmitter, TelemetryTableColumn) { | ||||
|  | ||||
|     class TelemetryTableConfiguration extends EventEmitter{ | ||||
|         constructor(domainObject, openmct) { | ||||
|             super(); | ||||
|  | ||||
|             this.domainObject = domainObject; | ||||
|             this.openmct = openmct; | ||||
|             this.columns = {}; | ||||
|  | ||||
|             this.addColumnsForObject = this.addColumnsForObject.bind(this); | ||||
|             this.removeColumnsForObject = this.removeColumnsForObject.bind(this); | ||||
|         } | ||||
|  | ||||
|         addColumnsForAllObjects(objects) { | ||||
|             objects.forEach(composee => this.addColumnsForObject(composee, false)); | ||||
|         } | ||||
|  | ||||
|         addColumnsForObject(telemetryObject) { | ||||
|             let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values(); | ||||
|             let objectKeyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|             this.columns[objectKeyString] = []; | ||||
|  | ||||
|             metadataValues.forEach(metadatum => { | ||||
|                 let column = new TelemetryTableColumn(this.openmct, metadatum); | ||||
|                 this.columns[objectKeyString].push(column); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         removeColumnsForObject(objectIdentifier) { | ||||
|             let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier); | ||||
|             let columnsToRemove = this.columns[objectKeyString]; | ||||
|  | ||||
|             delete this.columns[objectKeyString]; | ||||
|             columnsToRemove.forEach((column) => { | ||||
|                 //There may be more than one column with the same key (eg. time system columns) | ||||
|                 if (!this.hasColumnWithKey(column.key)) { | ||||
|                     // If there are no more columns with this key, delete any configuration, and trigger | ||||
|                     // a column refresh. | ||||
|                     delete this.domainObject.configuration.table.columns[column.getKey()]; | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         hasColumnWithKey(columnKey) { | ||||
|             return _.flatten(Object.values(this.columns)) | ||||
|                 .findIndex(column => column.getKey() === columnKey) !== -1; | ||||
|         } | ||||
|  | ||||
|         getColumns() { | ||||
|             return this.columns; | ||||
|         } | ||||
|  | ||||
|         getHeaders() { | ||||
|             let flattenedColumns = _.flatten(Object.values(this.columns)); | ||||
|             let headers = _.uniq(flattenedColumns, false, column => column.getKey()) | ||||
|                 .reduce(fromColumnsToHeadersMap, {}); | ||||
|  | ||||
|             function fromColumnsToHeadersMap(headersMap, column){ | ||||
|                 headersMap[column.getKey()] = column.getTitle(); | ||||
|                 return headersMap; | ||||
|             } | ||||
|  | ||||
|             return headers; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return TelemetryTableConfiguration; | ||||
| }); | ||||
							
								
								
									
										82
									
								
								src/plugins/telemetryTable/TelemetryTableRow.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/plugins/telemetryTable/TelemetryTableRow.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([], function () { | ||||
|     class TelemetryTableRow { | ||||
|         constructor(datum, columns, objectKeyString, limitEvaluator) { | ||||
|             this.columns = columns; | ||||
|  | ||||
|             this.datum = createNormalizedDatum(datum, columns); | ||||
|             this.limitEvaluator = limitEvaluator; | ||||
|             this.objectKeyString = objectKeyString; | ||||
|         } | ||||
|          | ||||
|         getFormattedDatum() { | ||||
|             return Object.values(this.columns) | ||||
|                 .reduce((formattedDatum, column) => { | ||||
|                     formattedDatum[column.getKey()] = this.getFormattedValue(column.getKey()); | ||||
|                     return formattedDatum; | ||||
|                 }, {}); | ||||
|         } | ||||
|  | ||||
|         getFormattedValue(key) { | ||||
|             let column = this.columns[key]; | ||||
|             return column.getFormattedValue(this.datum[key]); | ||||
|         } | ||||
|  | ||||
|         getRowLimitClass() { | ||||
|             if (!this.rowLimitClass) { | ||||
|                 let limitEvaluation = this.limitEvaluator.evaluate(this.datum);  | ||||
|                 this.rowLimitClass = limitEvaluation && limitEvaluation.cssClass; | ||||
|             } | ||||
|             return this.rowLimitClass; | ||||
|         } | ||||
|  | ||||
|         getCellLimitClasses() { | ||||
|             if (!this.cellLimitClasses) { | ||||
|                 this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => { | ||||
|                     let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum()); | ||||
|                     alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass; | ||||
|                      | ||||
|                     return alarmStateMap; | ||||
|                 }, {}); | ||||
|             } | ||||
|             return this.cellLimitClasses; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Normalize the structure of datums to assist sorting and merging of columns. | ||||
|      * Maps all sources to keys. | ||||
|      * @private | ||||
|      * @param {*} telemetryDatum | ||||
|      * @param {*} metadataValues  | ||||
|      */ | ||||
|     function createNormalizedDatum(datum, columns) { | ||||
|         return Object.values(columns).reduce((normalizedDatum, column) => { | ||||
|             normalizedDatum[column.getKey()] = column.getRawValue(datum); | ||||
|             return normalizedDatum; | ||||
|         }, {}); | ||||
|     } | ||||
|  | ||||
|     return TelemetryTableRow; | ||||
| }); | ||||
							
								
								
									
										90
									
								
								src/plugins/telemetryTable/TelemetryTableRowComponent.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/plugins/telemetryTable/TelemetryTableRowComponent.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
|  define([ | ||||
|     'text!./telemetry-table-row.html', | ||||
| ],function ( | ||||
|     TelemetryTableRowTemplate | ||||
| ) { | ||||
|     return { | ||||
|         template: TelemetryTableRowTemplate, | ||||
|         data: function () { | ||||
|             return { | ||||
|                 rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px', | ||||
|                 formattedRow: this.row.getFormattedDatum(), | ||||
|                 rowLimitClass: this.row.getRowLimitClass(), | ||||
|                 cellLimitClasses: this.row.getCellLimitClasses() | ||||
|             } | ||||
|         }, | ||||
|         props: { | ||||
|             headers: { | ||||
|                 type: Object, | ||||
|                 required: true | ||||
|             }, | ||||
|             row: { | ||||
|                 type: Object, | ||||
|                 required: true | ||||
|             }, | ||||
|             columnWidths: { | ||||
|                 type: Array, | ||||
|                 required: false, | ||||
|                 default: [], | ||||
|             }, | ||||
|             rowIndex: { | ||||
|                 type: Number, | ||||
|                 required: false, | ||||
|                 default: undefined | ||||
|             }, | ||||
|             rowOffset: { | ||||
|                 type: Number, | ||||
|                 required: false, | ||||
|                 default: 0 | ||||
|             }, | ||||
|             rowHeight: { | ||||
|                 type: Number, | ||||
|                 required: false, | ||||
|                 default: 0 | ||||
|             }, | ||||
|             configuration: { | ||||
|                 type: Object, | ||||
|                 required: true | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
|             calculateRowTop: function (rowOffset) { | ||||
|                 this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px'; | ||||
|             }, | ||||
|             formatRow: function (row) { | ||||
|                 this.formattedRow = row.getFormattedDatum(); | ||||
|                 this.rowLimitClass = row.getRowLimitClass(); | ||||
|                 this.cellLimitClasses = row.getCellLimitClasses(); | ||||
|             } | ||||
|         }, | ||||
|         watch: { | ||||
|             rowOffset: 'calculateRowTop', | ||||
|             row: { | ||||
|                 handler: 'formatRow',  | ||||
|                 deep: false | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  }); | ||||
							
								
								
									
										37
									
								
								src/plugins/telemetryTable/TelemetryTableType.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/plugins/telemetryTable/TelemetryTableType.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define(function () { | ||||
|     function TelemetryTableType() { | ||||
|         return { | ||||
|             name: 'Vue Telemetry Table', | ||||
|             description: 'Display telemetry values for the current time bounds in tabular form. Supports filtering and sorting.', | ||||
|             creatable: true, | ||||
|             cssClass: 'icon-tabular-realtime', | ||||
|             initialize: function (domainObject) { | ||||
|                 domainObject.composition = []; | ||||
|                 domainObject.configuration = {}; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return TelemetryTableType; | ||||
| }); | ||||
							
								
								
									
										52
									
								
								src/plugins/telemetryTable/TelemetryTableViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/plugins/telemetryTable/TelemetryTableViewProvider.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define(['./TelemetryTableComponent'], function (TelemetryTableComponent) { | ||||
|     function TelemetryTableViewProvider(openmct) { | ||||
|         return { | ||||
|             key: 'vue-table', | ||||
|             name: 'Telemetry Table', | ||||
|             editable: true, | ||||
|             canView: function (domainObject) { | ||||
|                 return domainObject.type === 'vue-table'; | ||||
|             }, | ||||
|             view: function (domainObject) { | ||||
|                 let component; | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = TelemetryTableComponent(domainObject, openmct); | ||||
|                         element.appendChild(component.$mount().$el); | ||||
|                     },  | ||||
|                     destroy: function (element) { | ||||
|                         component.$destroy(); | ||||
|                         element.removeChild(component.$el); | ||||
|                         component = undefined; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             priority: function () { | ||||
|                 return 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return TelemetryTableViewProvider; | ||||
| }); | ||||
| @@ -0,0 +1,139 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         'lodash', | ||||
|         './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()); | ||||
|                 openmct.time.on('timeSystem', this.sortByTimeSystem); | ||||
|  | ||||
|                 this.lastBounds = openmct.time.bounds(); | ||||
|                 openmct.time.on('bounds', this.bounds); | ||||
|             } | ||||
|  | ||||
|             addOne (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 = item.datum[this.sortOptions.key] < this.lastBounds.start; | ||||
|                 let afterEndOfBounds = item.datum[this.sortOptions.key] > 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'}); | ||||
|                 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); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             destroy() { | ||||
|                 this.openmct.time.off('timeSystem', this.sortByTimeSystem); | ||||
|                 this.openmct.time.off('bounds', this.bounds); | ||||
|             } | ||||
|         } | ||||
|     return BoundedTableRowCollection; | ||||
| }); | ||||
| @@ -0,0 +1,112 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         './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'); | ||||
|             } | ||||
|  | ||||
|             /** | ||||
|              * @private | ||||
|              */ | ||||
|             getRowsToFilter(columnKey, filter) { | ||||
|                 if (this.isSubsetOfCurrentFilter(columnKey, filter)) { | ||||
|                     return this.getRows(); | ||||
|                 } else { | ||||
|                     return this.masterCollection.getRows(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             /** | ||||
|              * @private | ||||
|              */ | ||||
|             isSubsetOfCurrentFilter(columnKey, filter) { | ||||
|                 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; | ||||
|                 for (const key in this.columnFilters) { | ||||
|                     if (!this.rowHasColumn(row, key)) { | ||||
|                         return false; | ||||
|                     } else { | ||||
|                         let formattedValue = row.getFormattedValue(key).toLowerCase(); | ||||
|                         doesMatchFilters = doesMatchFilters &&  | ||||
|                             formattedValue.indexOf(this.columnFilters[key]) !== -1;     | ||||
|                     } | ||||
|                 } | ||||
|                 return doesMatchFilters; | ||||
|             } | ||||
|  | ||||
|             rowHasColumn(row, key) { | ||||
|                 return row.columns.hasOwnProperty(key); | ||||
|             } | ||||
|  | ||||
|             destroy() { | ||||
|                 this.masterCollection.off('add', this.add); | ||||
|                 this.masterCollection.off('remove', this.remove); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return FilteredTableRowCollection; | ||||
|     }); | ||||
| @@ -0,0 +1,212 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         'lodash', | ||||
|         'EventEmitter' | ||||
|     ], | ||||
|     function ( | ||||
|         _, | ||||
|         EventEmitter | ||||
|     ) { | ||||
|         const LESS_THAN = -1; | ||||
|         const EQUAL = 0; | ||||
|         const GREATER_THAN = 1; | ||||
|  | ||||
|         /** | ||||
|          * @constructor | ||||
|          */ | ||||
|         class SortedTableRowCollection extends EventEmitter { | ||||
|             constructor () { | ||||
|                 super(); | ||||
|  | ||||
|                 this.dupeCheck = false; | ||||
|                 this.rows = []; | ||||
|  | ||||
|                 this.add = this.add.bind(this); | ||||
|                 this.remove = this.remove.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; | ||||
|  | ||||
|                     let rowsAdded = rows.filter(this.addOne, this); | ||||
|                     if (rowsAdded.length > 0) { | ||||
|                         this.emit('add', rowsAdded); | ||||
|                     } | ||||
|                     this.dupeCheck = true;     | ||||
|                 } else { | ||||
|                     let wasAdded = this.addOne(rows); | ||||
|                     if (wasAdded) { | ||||
|                         this.emit('add', rows); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             /** | ||||
|              * @private | ||||
|              */ | ||||
|             addOne(row) { | ||||
|                 if (this.sortOptions === undefined) { | ||||
|                     throw 'Please specify sort options'; | ||||
|                 } | ||||
|  | ||||
|                 let isDuplicate = false; | ||||
|  | ||||
|                 // 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 | ||||
|                 // based on time stamp 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 = _.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 = _.findIndex(potentialDupes, _.isEqual.bind(undefined, row)) > -1; | ||||
|                 } | ||||
|  | ||||
|                 if (!isDuplicate) { | ||||
|                     this.rows.splice(endIx || startIx, 0, row); | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             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) { | ||||
|                 const sortOptionsKey = this.sortOptions.key; | ||||
|                 lodashFunction = lodashFunction || _.sortedIndex; | ||||
|  | ||||
|                 if (this.sortOptions.direction === 'asc') { | ||||
|                     return lodashFunction(rows, testRow, (thisRow) => { | ||||
|                         return thisRow.datum[sortOptionsKey]; | ||||
|                     }); | ||||
|                 } else { | ||||
|                     const testRowValue = testRow.datum[this.sortOptions.key]; | ||||
|                     // Use a custom comparison function to support descending sort. | ||||
|                     return lodashFunction(rows, testRow, (thisRow) => { | ||||
|                         const thisRowValue = thisRow.datum[sortOptionsKey]; | ||||
|                         if (testRowValue === thisRowValue) { | ||||
|                             return EQUAL; | ||||
|                         } else if (testRowValue < thisRowValue) { | ||||
|                             return LESS_THAN; | ||||
|                         } else { | ||||
|                             return GREATER_THAN; | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             /** | ||||
|              * Sorts the telemetry collection based on the provided sort field | ||||
|              * specifier. Subsequent inserts are sorted to maintain specified sport | ||||
|              * order. | ||||
|              * | ||||
|              * @example | ||||
|              * // First build some mock telemetry for the purpose of an example | ||||
|              * let now = Date.now(); | ||||
|              * let telemetry = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(function (value) { | ||||
|              *     return { | ||||
|              *         // define an object property to demonstrate nested paths | ||||
|              *         timestamp: { | ||||
|              *             ms: now - value * 1000, | ||||
|              *             text: | ||||
|              *         }, | ||||
|              *         value: value | ||||
|              *     } | ||||
|              * }); | ||||
|              * let collection = new TelemetryCollection(); | ||||
|              * | ||||
|              * collection.add(telemetry); | ||||
|              * | ||||
|              * // Sort by telemetry value | ||||
|              * collection.sortBy({ | ||||
|              *  key: 'value', direction: 'asc' | ||||
|              * }); | ||||
|              * | ||||
|              * // Sort by ms since epoch | ||||
|              * collection.sort({ | ||||
|              *  key: 'timestamp.ms', | ||||
|              *  direction: 'asc' | ||||
|              * }); | ||||
|              * | ||||
|              * // Sort by 'text' attribute, descending | ||||
|              * collection.sort("timestamp.text"); | ||||
|              * | ||||
|              * | ||||
|              * @param {object} sortOptions An object specifying a sort key, and direction. | ||||
|              */ | ||||
|             sortBy(sortOptions) { | ||||
|                 if (arguments.length > 0) { | ||||
|                     this.sortOptions = sortOptions; | ||||
|                     this.rows = _.sortByOrder(this.rows, 'datum.' + sortOptions.key, sortOptions.direction); | ||||
|                     this.emit('sort'); | ||||
|                 } | ||||
|                 // Return duplicate to avoid direct modification of underlying object | ||||
|                 return Object.assign({}, this.sortOptions);  | ||||
|             } | ||||
|  | ||||
|             removeAllRowsForObject(objectKeyString) { | ||||
|                 let removed = []; | ||||
|                 this.rows = this.rows.filter(row => { | ||||
|                     if (row.objectKeyString === objectKeyString) { | ||||
|                         removed.push(row); | ||||
|                         return false; | ||||
|                     } | ||||
|                     return true; | ||||
|                 }); | ||||
|                 this.emit('remove', removed); | ||||
|             } | ||||
|  | ||||
|             remove(removedRows) { | ||||
|                 this.rows = this.rows.filter(row => { | ||||
|                     return removedRows.indexOf(row) === -1; | ||||
|                 }); | ||||
|                 this.emit('remove', removedRows); | ||||
|             } | ||||
|  | ||||
|             getRows () { | ||||
|                 return this.rows; | ||||
|             } | ||||
|         } | ||||
|     return SortedTableRowCollection; | ||||
| }); | ||||
							
								
								
									
										39
									
								
								src/plugins/telemetryTable/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/plugins/telemetryTable/plugin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
|  define([ | ||||
|      './TelemetryTableViewProvider', | ||||
|      './TableConfigurationViewProvider', | ||||
|      './TelemetryTableType' | ||||
|     ], function ( | ||||
|         TelemetryTableViewProvider, | ||||
|         TableConfigurationViewProvider, | ||||
|         TelemetryTableType | ||||
|     ) { | ||||
|     return function plugin() { | ||||
|         return function install(openmct) { | ||||
|             openmct.objectViews.addProvider(new TelemetryTableViewProvider(openmct)); | ||||
|             openmct.inspectorViews.addProvider(new TableConfigurationViewProvider(openmct)); | ||||
|             openmct.types.addType('vue-table', TelemetryTableType()); | ||||
|         }; | ||||
|     }; | ||||
|  }); | ||||
							
								
								
									
										11
									
								
								src/plugins/telemetryTable/table-configuration.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/plugins/telemetryTable/table-configuration.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <div class="grid-properties"> | ||||
|     <!--form class="form" --> | ||||
|         <ul class="l-inspector-part"> | ||||
|             <h2>Table Columns</h2> | ||||
|             <li class="grid-row" v-for="(title, key) in headers"> | ||||
|                 <div class="grid-cell label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div> | ||||
|                 <div class="grid-cell value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.table.columns[key] !== false" @change="toggleColumn(key)"></div> | ||||
|             </li> | ||||
|         </ul> | ||||
|     <!--/form --> | ||||
| </div> | ||||
							
								
								
									
										6
									
								
								src/plugins/telemetryTable/telemetry-table-row.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/plugins/telemetryTable/telemetry-table-row.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <tr :style="{ top: rowTop }" :class="rowLimitClass"> | ||||
|     <td v-for="(title, key, headerIndex) in headers"  | ||||
|         :style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}"  | ||||
|         :title="formattedRow[key]" | ||||
|         :class="cellLimitClasses[key]">{{formattedRow[key]}}</td> | ||||
| </tr> | ||||
							
								
								
									
										60
									
								
								src/plugins/telemetryTable/telemetry-table.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/plugins/telemetryTable/telemetry-table.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| <div class="tabular-holder l-sticky-headers has-control-bar" :class="{'loading': loading}"> | ||||
|     <div class="l-control-bar"> | ||||
|         <a class="s-button t-export icon-download labeled" | ||||
|            v-on:click="exportAsCSV()" | ||||
|            title="Export This View's Data"> | ||||
|             Export As CSV | ||||
|         </a> | ||||
|     </div> | ||||
|     <!-- Headers table --> | ||||
|     <div class="mct-table-headers-w"> | ||||
|         <table class="mct-table l-tabular-headers filterable" :style="{ 'max-width': totalWidth + 'px'}"> | ||||
|             <thead> | ||||
|                 <tr> | ||||
|                     <th v-for="(title, key, headerIndex) in headers" | ||||
|                         v-on:click="sortBy(key)" | ||||
|                         :class="['sortable', sortOptions.key === key ? 'sort' : '', sortOptions.direction].join(' ')" | ||||
|                         :style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}">{{title}}</th> | ||||
|                 </tr> | ||||
|                 <tr class="s-filters"> | ||||
|                     <th v-for="(title, key, headerIndex) in headers" | ||||
|                         :style="{ | ||||
|                             width: columnWidths[headerIndex], | ||||
|                             'max-width': columnWidths[headerIndex], | ||||
|                         }"> | ||||
|                         <div class="holder l-filter flex-elem grows" :class="{active: filters[key]}"> | ||||
|                             <input type="text" v-model="filters[key]" v-on:input="filterChanged(key)" /> | ||||
|                             <a class="clear-icon clear-input icon-x-in-circle" :class="{show: filters[key]}" @click="clearFilter(key)"></a> | ||||
|                         </div> | ||||
|                     </th> | ||||
|                     </tr> | ||||
|             </thead> | ||||
|         </table> | ||||
|     </div> | ||||
|     <!-- Content table --> | ||||
|     <div v-on:scroll="scroll()" class="l-tabular-body t-scrolling vscroll--persist"> | ||||
|         <table class="mct-table js-telemetry-table" :style="{ height: totalHeight + 'px', 'max-width': totalWidth + 'px'}"> | ||||
|             <tbody> | ||||
|                 <telemetry-table-row v-for="(row, rowIndex) in visibleRows"  | ||||
|                     :headers="headers" | ||||
|                     :columnWidths="columnWidths" | ||||
|                     :rowIndex="rowIndex" | ||||
|                     :rowOffset="rowOffset" | ||||
|                     :rowHeight="rowHeight" | ||||
|                     :row="row" | ||||
|                     > | ||||
|                 </telemetry-table-row> | ||||
|             </tbody> | ||||
|         </table> | ||||
|     </div> | ||||
|     <!-- Sizing table --> | ||||
|     <table class="mct-sizing-table t-sizing-table js-sizing-table" :style="{width: calcTableWidth}"> | ||||
|         <tr> | ||||
|             <th v-for="(title, key, headerIndex) in headers">{{title}}</th> | ||||
|         </tr> | ||||
|         <telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows" | ||||
|             :headers="headers" | ||||
|             :row="sizingRowData"> | ||||
|         </telemetry-table-row> | ||||
|     </table> | ||||
| </div> | ||||
		Reference in New Issue
	
	Block a user