Compare commits
	
		
			19 Commits
		
	
	
		
			table-colu
			...
			latest-tel
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					12a4b2395a | ||
| 
						 | 
					c179098227 | ||
| 
						 | 
					600d24e3af | ||
| 
						 | 
					9e360fb226 | ||
| 
						 | 
					6687a83ca5 | ||
| 
						 | 
					e9643ad07f | ||
| 
						 | 
					ca16892237 | ||
| 
						 | 
					a2b0d350d8 | ||
| 
						 | 
					534bdbae50 | ||
| 
						 | 
					831873e7de | ||
| 
						 | 
					622d246fdd | ||
| 
						 | 
					4a1ca9f299 | ||
| 
						 | 
					4e1de2678c | ||
| 
						 | 
					33a4792531 | ||
| 
						 | 
					37dd4856a6 | ||
| 
						 | 
					6a9cf3389d | ||
| 
						 | 
					2da2395473 | ||
| 
						 | 
					00ecd27bb3 | ||
| 
						 | 
					0fa4486dcf | 
							
								
								
									
										61
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								API.md
									
									
									
									
									
								
							@@ -52,7 +52,6 @@
 | 
			
		||||
    - [The URL Status Indicator](#the-url-status-indicator)
 | 
			
		||||
    - [Creating a Simple Indicator](#creating-a-simple-indicator)
 | 
			
		||||
    - [Custom Indicators](#custom-indicators)
 | 
			
		||||
  - [Included Plugins](#included-plugins)
 | 
			
		||||
 | 
			
		||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
 | 
			
		||||
 | 
			
		||||
@@ -994,7 +993,7 @@ A common use case for indicators is to convey the state of some external system
 | 
			
		||||
persistence backend or HTTP server. So long as this system is accessible via HTTP request, 
 | 
			
		||||
Open MCT provides a general purpose indicator to show whether the server is available and 
 | 
			
		||||
returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See
 | 
			
		||||
[Included Plugins](#included-plugins) below for details on how to install and configure the 
 | 
			
		||||
the [documentation](./src/plugins/URLIndicatorPlugin) for details on how to install and configure the 
 | 
			
		||||
URL Status Indicator.
 | 
			
		||||
 | 
			
		||||
### Creating a Simple Indicator
 | 
			
		||||
@@ -1045,60 +1044,4 @@ A completely custom indicator can be added by simple providing a DOM element to
 | 
			
		||||
    openmct.indicators.add({
 | 
			
		||||
        element: domNode
 | 
			
		||||
    });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Included Plugins
 | 
			
		||||
 | 
			
		||||
Open MCT is packaged along with a few general-purpose plugins:
 | 
			
		||||
 | 
			
		||||
* `openmct.plugins.Conductor` provides a user interface for working with time
 | 
			
		||||
within the application. If activated, configuration must be provided. This is 
 | 
			
		||||
detailed in the section on [Time Conductor Configuration](#time-conductor-configuration).
 | 
			
		||||
* `openmct.plugins.CouchDB` is an adapter for using CouchDB for persistence
 | 
			
		||||
  of user-created objects. This is a constructor that takes the URL for the
 | 
			
		||||
  CouchDB database as a parameter, e.g.
 | 
			
		||||
```javascript
 | 
			
		||||
openmct.install(openmct.plugins.CouchDB('http://localhost:5984/openmct'))
 | 
			
		||||
```
 | 
			
		||||
* `openmct.plugins.Elasticsearch` is an adapter for using Elasticsearch for
 | 
			
		||||
  persistence of user-created objects. This is a
 | 
			
		||||
  constructor that takes the URL for the Elasticsearch instance as a
 | 
			
		||||
  parameter. eg.
 | 
			
		||||
```javascript
 | 
			
		||||
openmct.install(openmct.plugins.CouchDB('http://localhost:9200'))
 | 
			
		||||
```
 | 
			
		||||
* `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different
 | 
			
		||||
  themes (dark and light) available for Open MCT. Note that at least one
 | 
			
		||||
  of these themes must be installed for Open MCT to appear correctly.
 | 
			
		||||
* `openmct.plugins.URLIndicator` adds an indicator which shows the
 | 
			
		||||
availability of a URL with the following options: 
 | 
			
		||||
  - `url` : URL to indicate the status of
 | 
			
		||||
  - `iconClass`: Icon to show in the status bar, defaults to `icon-database`, [list of all icons](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home?view=items)
 | 
			
		||||
  - `interval`: Interval between checking the connection, defaults to `10000`
 | 
			
		||||
  - `label` Name showing up as text in the status bar, defaults to url
 | 
			
		||||
```javascript
 | 
			
		||||
openmct.install(openmct.plugins.URLIndicator({
 | 
			
		||||
  url: 'http://localhost:8080',
 | 
			
		||||
    iconClass: 'check',
 | 
			
		||||
    interval: 10000,
 | 
			
		||||
    label: 'Localhost'
 | 
			
		||||
 })
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
* `openmct.plugins.LocalStorage` provides persistence of user-created
 | 
			
		||||
  objects in browser-local storage. This is particularly useful in
 | 
			
		||||
  development environments.
 | 
			
		||||
* `openmct.plugins.MyItems` adds a top-level folder named "My Items"
 | 
			
		||||
  when the application is first started, providing a place for a
 | 
			
		||||
  user to store created items.
 | 
			
		||||
* `openmct.plugins.UTCTimeSystem` provides a default time system for Open MCT.
 | 
			
		||||
 | 
			
		||||
Generally, you will want to either install these plugins, or install
 | 
			
		||||
different plugins that provide persistence and an initial folder
 | 
			
		||||
hierarchy.
 | 
			
		||||
 | 
			
		||||
eg.
 | 
			
		||||
```javascript
 | 
			
		||||
openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
```
 | 
			
		||||
```
 | 
			
		||||
@@ -20,14 +20,8 @@ API. Open MCT is also being refactored to minimize the dependencies that using
 | 
			
		||||
Open MCT imposes on developers, such as the current requirement to use
 | 
			
		||||
AngularJS.
 | 
			
		||||
 | 
			
		||||
This new API has not yet been heavily used and is likely to contain defects.
 | 
			
		||||
You can help by trying it out, and reporting any issues you encounter
 | 
			
		||||
using our GitHub issue tracker. Such issues may include bugs, suggestions,
 | 
			
		||||
missing documentation, or even just requests for help if you're having
 | 
			
		||||
trouble.
 | 
			
		||||
 | 
			
		||||
We want Open MCT to be as easy to use, install, run, and develop for as
 | 
			
		||||
possible, and your feedback will help us get there!
 | 
			
		||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues), or by emailing us at [arc-dl-openmct@nasa.gov](mailto:arc-dl-openmct@nasa.gov).
 | 
			
		||||
 | 
			
		||||
## Building and Running Open MCT Locally
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ var gulp = require('gulp'),
 | 
			
		||||
    git = require('git-rev-sync'),
 | 
			
		||||
    moment = require('moment'),
 | 
			
		||||
    project = require('./package.json'),
 | 
			
		||||
    _ = require('lodash'),
 | 
			
		||||
    paths = {
 | 
			
		||||
        main: 'openmct.js',
 | 
			
		||||
        dist: 'dist',
 | 
			
		||||
@@ -140,7 +139,7 @@ gulp.task('checkstyle', function () {
 | 
			
		||||
    var mkdirp = require('mkdirp');
 | 
			
		||||
    var reportName = 'jscs-html-report.html';
 | 
			
		||||
    var reportPath = path.resolve(paths.reports, 'checkstyle', reportName);
 | 
			
		||||
    var moveReport = fs.rename.bind(fs, reportName, reportPath, _.noop);
 | 
			
		||||
    var moveReport = fs.rename.bind(fs, reportName, reportPath, function () {});
 | 
			
		||||
 | 
			
		||||
    mkdirp.sync(path.resolve(paths.reports, 'checkstyle'));
 | 
			
		||||
 | 
			
		||||
@@ -179,4 +178,4 @@ gulp.task('install', [ 'assets', 'scripts' ]);
 | 
			
		||||
 | 
			
		||||
gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
 | 
			
		||||
 | 
			
		||||
gulp.task('build', [ 'verify', 'install' ]);
 | 
			
		||||
gulp.task('build', [ 'verify', 'install' ]);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								index.html
									
									
									
									
									
								
							@@ -45,8 +45,17 @@
 | 
			
		||||
                openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
                openmct.install(openmct.plugins.ImportExport());
 | 
			
		||||
                openmct.install(openmct.plugins.AutoflowView({
 | 
			
		||||
                    type: "telemetry.panel"
 | 
			
		||||
                    type: "view.autoflow"
 | 
			
		||||
                }));
 | 
			
		||||
                openmct.types.addType("view.autoflow", {
 | 
			
		||||
                    key: "view.autoflow",
 | 
			
		||||
                    name: "Autoflow",
 | 
			
		||||
                    creatable: true,
 | 
			
		||||
                    initialize: function (model) {
 | 
			
		||||
                        model.composition = [];
 | 
			
		||||
                        return model
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                openmct.install(openmct.plugins.Conductor({
 | 
			
		||||
                    menuOptions: [
 | 
			
		||||
                        {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								openmct.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								openmct.js
									
									
									
									
									
								
							@@ -41,14 +41,14 @@ requirejs.config({
 | 
			
		||||
        "lodash": "bower_components/lodash/lodash",
 | 
			
		||||
        "d3-selection": "node_modules/d3-selection/dist/d3-selection.min",
 | 
			
		||||
        "d3-scale": "node_modules/d3-scale/build/d3-scale.min",
 | 
			
		||||
        "d3-axis": "node_modules/d3-axis/build/d3-axis.min",
 | 
			
		||||
        "d3-array": "node_modules/d3-array/build/d3-array.min",
 | 
			
		||||
        "d3-collection": "node_modules/d3-collection/build/d3-collection.min",
 | 
			
		||||
        "d3-axis": "node_modules/d3-axis/dist/d3-axis.min",
 | 
			
		||||
        "d3-array": "node_modules/d3-array/dist/d3-array.min",
 | 
			
		||||
        "d3-collection": "node_modules/d3-collection/dist/d3-collection.min",
 | 
			
		||||
        "d3-color": "node_modules/d3-color/build/d3-color.min",
 | 
			
		||||
        "d3-format": "node_modules/d3-format/build/d3-format.min",
 | 
			
		||||
        "d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
 | 
			
		||||
        "d3-time": "node_modules/d3-time/build/d3-time.min",
 | 
			
		||||
        "d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
 | 
			
		||||
        "d3-time": "node_modules/d3-time/dist/d3-time.min",
 | 
			
		||||
        "d3-time-format": "node_modules/d3-time-format/dist/d3-time-format.min",
 | 
			
		||||
        "html2canvas": "node_modules/html2canvas/dist/html2canvas.min",
 | 
			
		||||
        "painterro": "node_modules/painterro/build/painterro.min",
 | 
			
		||||
        "printj": "node_modules/printj/dist/printj.min"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "openmct",
 | 
			
		||||
  "version": "0.14.0-SNAPSHOT",
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "description": "The Open MCT core platform.",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "d3-array": "1.2.x",
 | 
			
		||||
    "d3-axis": "1.0.x",
 | 
			
		||||
@@ -43,7 +43,6 @@
 | 
			
		||||
    "karma-html-reporter": "^0.2.7",
 | 
			
		||||
    "karma-jasmine": "^1.1.2",
 | 
			
		||||
    "karma-requirejs": "^1.1.0",
 | 
			
		||||
    "lodash": "^3.10.1",
 | 
			
		||||
    "markdown-toc": "^0.11.7",
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "merge-stream": "^1.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ define([
 | 
			
		||||
    /**
 | 
			
		||||
     * Perform this action.
 | 
			
		||||
     */
 | 
			
		||||
    RemoveAction.prototype.perform = function () {
 | 
			
		||||
    RemoveAction.prototype.perform = function (skipWarning) {
 | 
			
		||||
        var dialog,
 | 
			
		||||
            dialogService = this.dialogService,
 | 
			
		||||
            domainObject = this.domainObject,
 | 
			
		||||
@@ -115,12 +115,18 @@ define([
 | 
			
		||||
            return parent.useCapability('mutation', doMutate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Pass in the function to remove the domain object so it can be
 | 
			
		||||
         * associated with an 'OK' button press
 | 
			
		||||
         */
 | 
			
		||||
        dialog = new RemoveDialog(dialogService, domainObject, removeFromContext);
 | 
			
		||||
        dialog.show();
 | 
			
		||||
        if (skipWarning) {
 | 
			
		||||
 | 
			
		||||
            removeFromContext(domainObject);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            /*
 | 
			
		||||
            * Pass in the function to remove the domain object so it can be
 | 
			
		||||
            * associated with an 'OK' button press
 | 
			
		||||
            */
 | 
			
		||||
            dialog = new RemoveDialog(dialogService, domainObject, removeFromContext);
 | 
			
		||||
            dialog.show();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Object needs to have a parent for Remove to be applicable
 | 
			
		||||
 
 | 
			
		||||
@@ -124,6 +124,17 @@ define(
 | 
			
		||||
                expect(mockParent.useCapability).not.toHaveBeenCalledWith("mutation", jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not show a blocking message dialog when true is passed to perform", function () {
 | 
			
		||||
                mockParent = jasmine.createSpyObj(
 | 
			
		||||
                    "parent",
 | 
			
		||||
                    ["getModel", "getCapability", "useCapability"]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                action.perform(true);
 | 
			
		||||
 | 
			
		||||
                expect(mockDialogService.showBlockingMessage).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("after the remove callback is triggered", function () {
 | 
			
		||||
                var mockChildContext,
 | 
			
		||||
                    mockChildObject,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								platform/commonUI/themes/espresso/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								platform/commonUI/themes/espresso/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
# Espresso Theme
 | 
			
		||||
Dark theme for the Open MCT user interface.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.Espresso());
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										7
									
								
								platform/commonUI/themes/snow/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								platform/commonUI/themes/snow/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
# Espresso Theme
 | 
			
		||||
A light colored theme for the Open MCT user interface.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.Snow());
 | 
			
		||||
```
 | 
			
		||||
@@ -102,14 +102,14 @@ define(
 | 
			
		||||
         * @returns {Action[]} an array of matching actions
 | 
			
		||||
         * @memberof platform/core.ActionCapability#
 | 
			
		||||
         */
 | 
			
		||||
        ActionCapability.prototype.perform = function (context) {
 | 
			
		||||
        ActionCapability.prototype.perform = function (context, flag) {
 | 
			
		||||
            // Alias to getActions(context)[0].perform, with a
 | 
			
		||||
            // check for empty arrays.
 | 
			
		||||
            var actions = this.getActions(context);
 | 
			
		||||
 | 
			
		||||
            return this.$q.when(
 | 
			
		||||
                (actions && actions.length > 0) ?
 | 
			
		||||
                    actions[0].perform() :
 | 
			
		||||
                    actions[0].perform(flag) :
 | 
			
		||||
                    undefined
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ define(
 | 
			
		||||
                .then(function () {
 | 
			
		||||
                    return object
 | 
			
		||||
                        .getCapability('action')
 | 
			
		||||
                        .perform('remove');
 | 
			
		||||
                        .perform('remove', true);
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -224,10 +224,11 @@ define(
 | 
			
		||||
                            locationPromise.resolve();
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("removes object from parent", function () {
 | 
			
		||||
                        it("removes object from parent without user warning dialog", function () {
 | 
			
		||||
                            expect(actionCapability.perform)
 | 
			
		||||
                                .toHaveBeenCalledWith('remove');
 | 
			
		||||
                                .toHaveBeenCalledWith('remove', true);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
@@ -244,9 +245,9 @@ define(
 | 
			
		||||
                            .toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("removes object from parent", function () {
 | 
			
		||||
                    it("removes object from parent without user warning dialog", function () {
 | 
			
		||||
                        expect(actionCapability.perform)
 | 
			
		||||
                            .toHaveBeenCalledWith('remove');
 | 
			
		||||
                            .toHaveBeenCalledWith('remove', true);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,8 @@ define(
 | 
			
		||||
                'setDisplayedValue',
 | 
			
		||||
                'subscribeToObject',
 | 
			
		||||
                'unsubscribe',
 | 
			
		||||
                'updateView'
 | 
			
		||||
                'updateView',
 | 
			
		||||
                'setSelection'
 | 
			
		||||
            ].forEach(function (name) {
 | 
			
		||||
                self[name] = self[name].bind(self);
 | 
			
		||||
            });
 | 
			
		||||
@@ -224,7 +225,7 @@ define(
 | 
			
		||||
            // Respond to external bounds changes
 | 
			
		||||
            this.openmct.time.on("bounds", updateDisplayBounds);
 | 
			
		||||
 | 
			
		||||
            this.openmct.selection.on('change', this.setSelection.bind(this));
 | 
			
		||||
            this.openmct.selection.on('change', this.setSelection);
 | 
			
		||||
            this.$element.on('click', this.bypassSelection.bind(this));
 | 
			
		||||
            this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
 | 
			
		||||
                this.newDomainObject = JSON.parse(JSON.stringify(obj));
 | 
			
		||||
@@ -233,6 +234,9 @@ define(
 | 
			
		||||
 | 
			
		||||
            this.updateElementPositions(this.newDomainObject.layoutGrid);
 | 
			
		||||
            refreshElements();
 | 
			
		||||
 | 
			
		||||
            //force a click, to initialize Fixed Position Controller on SelectionAPI
 | 
			
		||||
            $element[0].click();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FixedController.prototype.updateElementPositions = function (layoutGrid) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								platform/features/my-items/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								platform/features/my-items/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
# My Items plugin
 | 
			
		||||
Defines top-level folder named "My Items" to store user-created items. Enabled by default, this can be disabled in a 
 | 
			
		||||
read-only deployment with no user-editable objects.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
```
 | 
			
		||||
@@ -35,28 +35,30 @@ mct-table {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mct-table {
 | 
			
		||||
        tr {
 | 
			
		||||
            display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define
 | 
			
		||||
            height: 18px; // Needed when a row has empty values in its cells
 | 
			
		||||
            align-items: stretch;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        td, th {
 | 
			
		||||
            box-sizing: border-box;
 | 
			
		||||
            display: block;
 | 
			
		||||
            flex: 1 0 auto;
 | 
			
		||||
            white-space: nowrap;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        thead {
 | 
			
		||||
            display: block;
 | 
			
		||||
            tr {
 | 
			
		||||
                display: block;
 | 
			
		||||
                white-space: nowrap;
 | 
			
		||||
                th {
 | 
			
		||||
                    display: inline-block;
 | 
			
		||||
                    box-sizing: border-box;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tbody {
 | 
			
		||||
            tr {
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                white-space: nowrap;
 | 
			
		||||
                display: block;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            td {
 | 
			
		||||
                white-space: nowrap;
 | 
			
		||||
                overflow: hidden;
 | 
			
		||||
                box-sizing: border-box;
 | 
			
		||||
                display: inline-block;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								platform/import-export/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								platform/import-export/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
# Import / Export Plugin
 | 
			
		||||
The Import/Export plugin allows objects to be exported as JSON files. This allows for sharing of objects between users 
 | 
			
		||||
who are not using a shared persistence store. It also allows object trees to be backed up. Additionally, object trees 
 | 
			
		||||
exported using this tool can then be exposed as read-only static root trees using the 
 | 
			
		||||
[Static Root Plugin](../../src/plugins/staticRootPlugin/README.md).
 | 
			
		||||
 | 
			
		||||
Upon installation it will add two new context menu actions to allow import and export of objects. Initiating the Export 
 | 
			
		||||
action on an object will produce a JSON file that includes the object and all of its composed children. Selecting Import 
 | 
			
		||||
on an object will allow the user to import a previously exported object tree as a child of the selected object.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.ImportExport())
 | 
			
		||||
```
 | 
			
		||||
@@ -133,7 +133,7 @@ define(['lodash'], function (_) {
 | 
			
		||||
        copyOfChild.location = parentId;
 | 
			
		||||
        parent.composition[index] = copyOfChild.identifier;
 | 
			
		||||
        this.tree[newIdString] = copyOfChild;
 | 
			
		||||
        this.tree[parentId].composition[index] = newIdString;
 | 
			
		||||
        this.tree[parentId].composition[index] = copyOfChild.identifier;
 | 
			
		||||
 | 
			
		||||
        return copyOfChild;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -79,16 +79,18 @@ define(['zepto'], function ($) {
 | 
			
		||||
            var parentModel = parent.getModel();
 | 
			
		||||
            var newObj;
 | 
			
		||||
 | 
			
		||||
            seen.push(parent.getId());
 | 
			
		||||
            seen.push(this.getKeyString(parent.getId()));
 | 
			
		||||
            parentModel.composition.forEach(function (childId, index) {
 | 
			
		||||
                if (!tree[childId] || seen.includes(childId)) {
 | 
			
		||||
                var childIdString = this.getKeyString(childId);
 | 
			
		||||
                if (!tree[childIdString] || seen.includes(childIdString)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                newObj = this.instantiate(tree[childId], childId);
 | 
			
		||||
                parent.getCapability("composition").add(newObj);
 | 
			
		||||
                newObj.getCapability("location")
 | 
			
		||||
                    .setPrimaryLocation(tree[childId].location);
 | 
			
		||||
                newObj = this.instantiate(tree[childIdString], childIdString);
 | 
			
		||||
                // New object has not been persisted yet so clear persisted
 | 
			
		||||
                // timestamp from copied model.
 | 
			
		||||
                delete newObj.getModel().persisted;
 | 
			
		||||
                newObj.getCapability('persistence').persist();
 | 
			
		||||
                this.deepInstantiate(newObj, tree, seen);
 | 
			
		||||
            }, this);
 | 
			
		||||
        }
 | 
			
		||||
@@ -103,6 +105,10 @@ define(['zepto'], function ($) {
 | 
			
		||||
        return tree;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ImportAsJSONAction.prototype.getKeyString = function (identifier) {
 | 
			
		||||
        return this.openmct.objects.makeKeyString(identifier);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rewrites all instances of a given id in the tree with a newly generated
 | 
			
		||||
     * replacement to prevent collision.
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,12 @@ define(
 | 
			
		||||
                uniqueId = 0;
 | 
			
		||||
                newObjects = [];
 | 
			
		||||
                openmct = {
 | 
			
		||||
                    $injector: jasmine.createSpyObj('$injector', ['get'])
 | 
			
		||||
                    $injector: jasmine.createSpyObj('$injector', ['get']),
 | 
			
		||||
                    objects: {
 | 
			
		||||
                        makeKeyString: function (identifier) {
 | 
			
		||||
                            return identifier.key;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                mockInstantiate = jasmine.createSpy('instantiate').and.callFake(
 | 
			
		||||
                    function (model, id) {
 | 
			
		||||
@@ -154,7 +159,7 @@ define(
 | 
			
		||||
                            body: JSON.stringify({
 | 
			
		||||
                                "openmct": {
 | 
			
		||||
                                    "infiniteParent": {
 | 
			
		||||
                                        "composition": ["infinteChild"],
 | 
			
		||||
                                        "composition": [{key: "infinteChild", namespace: ""}],
 | 
			
		||||
                                        "name": "1",
 | 
			
		||||
                                        "type": "folder",
 | 
			
		||||
                                        "modified": 1503598129176,
 | 
			
		||||
@@ -162,7 +167,7 @@ define(
 | 
			
		||||
                                        "persisted": 1503598129176
 | 
			
		||||
                                    },
 | 
			
		||||
                                    "infinteChild": {
 | 
			
		||||
                                        "composition": ["infiniteParent"],
 | 
			
		||||
                                        "composition": [{key: "infinteParent", namespace: ""}],
 | 
			
		||||
                                        "name": "2",
 | 
			
		||||
                                        "type": "folder",
 | 
			
		||||
                                        "modified": 1503598132428,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,8 @@
 | 
			
		||||
This bundle implements a connection to an external CouchDB persistence 
 | 
			
		||||
store in Open MCT.
 | 
			
		||||
# Couch DB Persistence Plugin
 | 
			
		||||
An adapter for using CouchDB for persistence of user-created objects. The plugin installation function takes the URL 
 | 
			
		||||
for the CouchDB database as a parameter.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.CouchDB('http://localhost:5984/openmct'))
 | 
			
		||||
```
 | 
			
		||||
@@ -1,2 +1,8 @@
 | 
			
		||||
This bundle implements a connection to an external ElasticSearch persistence 
 | 
			
		||||
store in Open MCT.
 | 
			
		||||
# Elasticsearch Persistence Provider
 | 
			
		||||
An adapter for using Elastic for persistence of user-created objects. The installation function takes the URL for an 
 | 
			
		||||
Elasticsearch server as a parameter.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.Elasticsearch('http://localhost:9200'))
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										9
									
								
								platform/persistence/local/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								platform/persistence/local/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
# Local Storage Plugin
 | 
			
		||||
Provides persistence of user-created objects in browser Local Storage. Objects persisted in this way will only be 
 | 
			
		||||
available from the browser and machine on which they were persisted. For shared persistence, consider the 
 | 
			
		||||
[Elasticsearch](../elastic/) and [CouchDB](../couch/) persistence plugins.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
```
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
// Converts all templateUrl references in bundle.js files to
 | 
			
		||||
// plain template references, loading said templates with the
 | 
			
		||||
// RequireJS text plugin.
 | 
			
		||||
 | 
			
		||||
var glob = require('glob'),
 | 
			
		||||
    fs = require('fs');
 | 
			
		||||
 | 
			
		||||
function migrate(file) {
 | 
			
		||||
    var sourceCode = fs.readFileSync(file, 'utf8'),
 | 
			
		||||
        lines = sourceCode.split('\n')
 | 
			
		||||
            .filter(function (line) {
 | 
			
		||||
                return !(/^\W*['"]use strict['"];\W*$/.test(line));
 | 
			
		||||
            })
 | 
			
		||||
            .filter(function (line) {
 | 
			
		||||
                return line.indexOf("/*global") !== 0;
 | 
			
		||||
            });
 | 
			
		||||
    fs.writeFileSync(file, lines.join('\n'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glob('@(src|platform)/**/*.js', {}, function (err, files) {
 | 
			
		||||
    if (err) {
 | 
			
		||||
        console.log(err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    files.forEach(migrate);
 | 
			
		||||
});
 | 
			
		||||
@@ -1,106 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
// Converts all templateUrl references in bundle.js files to
 | 
			
		||||
// plain template references, loading said templates with the
 | 
			
		||||
// RequireJS text plugin.
 | 
			
		||||
 | 
			
		||||
var glob = require('glob'),
 | 
			
		||||
    fs = require('fs'),
 | 
			
		||||
    path = require('path'),
 | 
			
		||||
    _ = require('lodash');
 | 
			
		||||
 | 
			
		||||
function toTemplateName(templateUrl) {
 | 
			
		||||
    var parts = templateUrl.split('/');
 | 
			
		||||
    return _.camelCase(parts[parts.length - 1].replace(".html", "")) +
 | 
			
		||||
            "Template";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTemplateUrl(sourceLine) {
 | 
			
		||||
    return _.trim(sourceLine.split(":")[1], "\", ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hasTemplateUrl(sourceLine) {
 | 
			
		||||
    return sourceLine.indexOf("templateUrl") !== -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findTemplateURLs(sourceCode) {
 | 
			
		||||
    return sourceCode.split('\n')
 | 
			
		||||
        .map(_.trim)
 | 
			
		||||
        .filter(hasTemplateUrl)
 | 
			
		||||
        .map(getTemplateUrl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function injectRequireArgument(sourceCode, templateUrls) {
 | 
			
		||||
    var lines = sourceCode.split('\n'),
 | 
			
		||||
        index;
 | 
			
		||||
 | 
			
		||||
    templateUrls = _.uniq(templateUrls);
 | 
			
		||||
 | 
			
		||||
    // Add arguments for source paths...
 | 
			
		||||
    index = lines.map(_.trim).indexOf("'legacyRegistry'");
 | 
			
		||||
    lines = lines.slice(0, index).concat(templateUrls.map(function (url) {
 | 
			
		||||
        return "    \"text!./res/" + url + "\",";
 | 
			
		||||
    }).concat(lines.slice(index)));
 | 
			
		||||
 | 
			
		||||
    /// ...and for arguments
 | 
			
		||||
    index = lines.map(_.trim).indexOf("legacyRegistry");
 | 
			
		||||
    lines = lines.slice(0, index).concat(templateUrls.map(function (url) {
 | 
			
		||||
        return "    " + toTemplateName(url) + ",";
 | 
			
		||||
    }).concat(lines.slice(index)));
 | 
			
		||||
 | 
			
		||||
    return lines.join('\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function rewriteUrl(sourceLine) {
 | 
			
		||||
    return [
 | 
			
		||||
        sourceLine.substring(0, sourceLine.indexOf(sourceLine.trim())),
 | 
			
		||||
        "\"template\": " + toTemplateName(getTemplateUrl(sourceLine)),
 | 
			
		||||
        _.endsWith(sourceLine, ",") ? "," : ""
 | 
			
		||||
    ].join('');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function rewriteLine(sourceLine) {
 | 
			
		||||
    return hasTemplateUrl(sourceLine) ?
 | 
			
		||||
        rewriteUrl(sourceLine.replace("templateUrl", "template")) :
 | 
			
		||||
        sourceLine;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function rewriteTemplateUrls(sourceCode) {
 | 
			
		||||
    return sourceCode.split('\n').map(rewriteLine).join('\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function migrate(file) {
 | 
			
		||||
    var sourceCode = fs.readFileSync(file, 'utf8');
 | 
			
		||||
    fs.writeFileSync(file, rewriteTemplateUrls(
 | 
			
		||||
        injectRequireArgument(sourceCode, findTemplateURLs(sourceCode))
 | 
			
		||||
    ), 'utf8');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glob('platform/**/bundle.js', {}, function (err, files) {
 | 
			
		||||
    if (err) {
 | 
			
		||||
        console.log(err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    files.forEach(migrate);
 | 
			
		||||
});
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    <%= implPaths %>
 | 
			
		||||
    'legacyRegistry'
 | 
			
		||||
], function (
 | 
			
		||||
    <%= implNames %>
 | 
			
		||||
    legacyRegistry
 | 
			
		||||
) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    legacyRegistry.register("<%= bundleName %>", <%= bundleContents %>);
 | 
			
		||||
});
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
// Temporary utility script to rewrite bundle.json
 | 
			
		||||
// files as bundle.js files.
 | 
			
		||||
 | 
			
		||||
var glob = require('glob'),
 | 
			
		||||
    fs = require('fs'),
 | 
			
		||||
    path = require('path'),
 | 
			
		||||
    _ = require('lodash'),
 | 
			
		||||
    template = _.template(
 | 
			
		||||
        fs.readFileSync(path.resolve(__dirname, 'rebundle-template.txt'), 'utf8')
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
function indent(str, depth) {
 | 
			
		||||
    return _.trimLeft(str.split('\n').map(function (line) {
 | 
			
		||||
        return _.repeat('    ', depth || 1) + line;
 | 
			
		||||
    }).filter(function (line) {
 | 
			
		||||
        return line.trim().length > 0;
 | 
			
		||||
    }).join('\n'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findImpls(bundleContents) {
 | 
			
		||||
    return _(bundleContents.extensions || {})
 | 
			
		||||
        .map()
 | 
			
		||||
        .flatten()
 | 
			
		||||
        .pluck('implementation')
 | 
			
		||||
        .filter()
 | 
			
		||||
        .uniq()
 | 
			
		||||
        .value();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toIdentifier(impl) {
 | 
			
		||||
    var parts = impl.replace(".js", "").split('/');
 | 
			
		||||
    return parts[parts.length - 1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toPath(impl) {
 | 
			
		||||
    return "\"./src/" + impl.replace(".js", "") + "\"";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function replaceImpls(bundleText) {
 | 
			
		||||
    var rx = /"implementation": "([^"]*)"/;
 | 
			
		||||
    return bundleText.split('\n').map(function (line) {
 | 
			
		||||
        var m = line.match(rx);
 | 
			
		||||
        return m !== null ?
 | 
			
		||||
                line.replace(rx, '"implementation": ' + toIdentifier(m[1])) :
 | 
			
		||||
                line;
 | 
			
		||||
    }).join('\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function rebundle(file) {
 | 
			
		||||
    var plainJson = fs.readFileSync(file, 'utf8'),
 | 
			
		||||
        bundleContents = JSON.parse(plainJson),
 | 
			
		||||
        impls = findImpls(bundleContents),
 | 
			
		||||
        bundleName = file.replace("/bundle.json", ""),
 | 
			
		||||
        outputFile = file.replace(".json", ".js"),
 | 
			
		||||
        contents = template({
 | 
			
		||||
            bundleName: bundleName,
 | 
			
		||||
            implPaths: indent(impls.map(toPath).concat([""]).join(",\n")),
 | 
			
		||||
            implNames: indent(impls.map(toIdentifier).concat([""]).join(",\n")),
 | 
			
		||||
            bundleContents: indent(replaceImpls(JSON.stringify(bundleContents, null, 4)))
 | 
			
		||||
        });
 | 
			
		||||
    fs.writeFileSync(outputFile, contents, 'utf8');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glob('**/bundle.json', {}, function (err, files) {
 | 
			
		||||
    if (err) {
 | 
			
		||||
        console.log(err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    files.forEach(rebundle);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -24,12 +24,14 @@ define([
 | 
			
		||||
    './TelemetryMetadataManager',
 | 
			
		||||
    './TelemetryValueFormatter',
 | 
			
		||||
    './DefaultMetadataProvider',
 | 
			
		||||
    './latestValueSubscription',
 | 
			
		||||
    '../objects/object-utils',
 | 
			
		||||
    'lodash'
 | 
			
		||||
], function (
 | 
			
		||||
    TelemetryMetadataManager,
 | 
			
		||||
    TelemetryValueFormatter,
 | 
			
		||||
    DefaultMetadataProvider,
 | 
			
		||||
    latestValueSubscription,
 | 
			
		||||
    objectUtils,
 | 
			
		||||
    _
 | 
			
		||||
) {
 | 
			
		||||
@@ -335,6 +337,41 @@ define([
 | 
			
		||||
        }.bind(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Subscribe to receive the latest telemetry value for a given domain
 | 
			
		||||
     * object. The callback will be called whenever newer data is received from
 | 
			
		||||
     * a realtime provider.  If a LAD provider is available, Open MCT will use
 | 
			
		||||
     * it to provide an initial value for the latest data subscriber.
 | 
			
		||||
     *
 | 
			
		||||
     * Using this method will ensure that you only receive telemetry values in
 | 
			
		||||
     * order, according to the current time system.  If openmct receives a new
 | 
			
		||||
     * telemetry value from a provider that occurs out of order, i.e. the
 | 
			
		||||
     * timestamp is less than the last received timestamp, then it will discard
 | 
			
		||||
     * the message instead of notifying the callback.  In a telemetry system
 | 
			
		||||
     * where data may be processed out of order, this guarantees that the end
 | 
			
		||||
     * user is always viewing the latest data.
 | 
			
		||||
     *
 | 
			
		||||
     * If the user changes the time system, Open MCT will attempt to provide
 | 
			
		||||
     * a new value from the LAD data provider and continue to provide new values
 | 
			
		||||
     * via realtime providers.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {module:openmct.DomainObject} domainObject the object
 | 
			
		||||
     *        which has associated telemetry
 | 
			
		||||
     * @param {Function} callback the callback to invoke with new data, as
 | 
			
		||||
     *        it becomes available
 | 
			
		||||
     * @returns {Function} a function which may be called to terminate
 | 
			
		||||
     *          the subscription
 | 
			
		||||
     */
 | 
			
		||||
    TelemetryAPI.prototype.latest = function (domainObject, callback) {
 | 
			
		||||
        return latestValueSubscription(
 | 
			
		||||
            domainObject,
 | 
			
		||||
            callback,
 | 
			
		||||
            this,
 | 
			
		||||
            this.openmct
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get telemetry metadata for a given domain object.  Returns a telemetry
 | 
			
		||||
     * metadata manager which provides methods for interrogating telemetry
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										177
									
								
								src/api/telemetry/latestValueSubscription.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/api/telemetry/latestValueSubscription.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 openmct 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 openmct 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 (
 | 
			
		||||
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Subscribe to receive the latest telemetry value for a given domain
 | 
			
		||||
     * object. The callback will be called whenever newer data is received from
 | 
			
		||||
     * a realtime provider.  If a LAD provider is available, Open MCT will use
 | 
			
		||||
     * it to provide an initial value for the latest data subscriber.
 | 
			
		||||
     *
 | 
			
		||||
     * Using this method will ensure that you only receive telemetry values in
 | 
			
		||||
     * order, according to the current time system.  If openmct receives a new
 | 
			
		||||
     * telemetry value from a provider that occurs out of order, i.e. the
 | 
			
		||||
     * timestamp is less than the last received timestamp, then it will discard
 | 
			
		||||
     * the message instead of notifying the callback.  In a telemetry system
 | 
			
		||||
     * where data may be processed out of order, this guarantees that the end
 | 
			
		||||
     * user is always viewing the latest data.
 | 
			
		||||
     *
 | 
			
		||||
     * If the user changes the time system, Open MCT will attempt to provide
 | 
			
		||||
     * a new value from the LAD data provider and continue to provide new values
 | 
			
		||||
     * via realtime providers.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {module:openmct.DomainObject} domainObject the object
 | 
			
		||||
     *        which has associated telemetry
 | 
			
		||||
     * @param {Function} callback the callback to invoke with new data, as
 | 
			
		||||
     *        it becomes available
 | 
			
		||||
     * @returns {Function} a function which may be called to terminate
 | 
			
		||||
     *          the subscription
 | 
			
		||||
     */
 | 
			
		||||
    function latestValueSubscription(
 | 
			
		||||
        domainObject,
 | 
			
		||||
        callback,
 | 
			
		||||
        telemetryAPI,
 | 
			
		||||
        openmct
 | 
			
		||||
    ) {
 | 
			
		||||
        var latestDatum;
 | 
			
		||||
        var pendingRealtimeDatum;
 | 
			
		||||
        var currentRequest = 0;
 | 
			
		||||
        var metadata = telemetryAPI.getMetadata(domainObject);
 | 
			
		||||
        var formatters = telemetryAPI.getFormatMap(metadata);
 | 
			
		||||
        var timeFormatter;
 | 
			
		||||
        var active = true;
 | 
			
		||||
        var restrictToBounds = false;
 | 
			
		||||
 | 
			
		||||
        function isLater(a, b) {
 | 
			
		||||
            return !timeFormatter || timeFormatter.parse(a) > timeFormatter.parse(b);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function applyBoundsFilter(datum) {
 | 
			
		||||
            if (withinBounds(datum)) {
 | 
			
		||||
                callback(datum);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function withinBounds(datum) {
 | 
			
		||||
            if (!restrictToBounds || !timeFormatter) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            var timestamp = timeFormatter.parse(datum);
 | 
			
		||||
            var bounds = openmct.time.bounds();
 | 
			
		||||
            if (timestamp >= bounds.start && timestamp <= bounds.end) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function updateClock(clock) {
 | 
			
		||||
            // We restrict to bounds if there is no clock (aka fixed mode) to
 | 
			
		||||
            // prevent users from seeing data they don't expect.
 | 
			
		||||
            restrictToBounds = !clock;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function callbackIfLatest(datum) {
 | 
			
		||||
            if (!active) {
 | 
			
		||||
                return; // prevent LAD notify after unsubscribe.
 | 
			
		||||
            }
 | 
			
		||||
            // If we don't have latest data, store datum for later processing.
 | 
			
		||||
            if (typeof latestDatum === 'undefined') {
 | 
			
		||||
                if (typeof pendingRealtimeDatum === 'undefined' || (
 | 
			
		||||
                        isLater(datum, pendingRealtimeDatum) &&
 | 
			
		||||
                        withinBounds(datum)
 | 
			
		||||
                    )) {
 | 
			
		||||
 | 
			
		||||
                    pendingRealtimeDatum = datum;
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // If there is no latest data, or datum is latest, then notify
 | 
			
		||||
            // subscriber.
 | 
			
		||||
            if (latestDatum === false || isLater(datum, latestDatum)) {
 | 
			
		||||
                latestDatum = datum;
 | 
			
		||||
                applyBoundsFilter(datum);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function updateTimeSystem(timeSystem) {
 | 
			
		||||
            // Reset subscription state, request new latest data and wait for
 | 
			
		||||
            // response before filtering lad data.
 | 
			
		||||
            latestDatum = undefined;
 | 
			
		||||
            pendingRealtimeDatum = undefined;
 | 
			
		||||
            timeFormatter = formatters[timeSystem.key];
 | 
			
		||||
 | 
			
		||||
            currentRequest++;
 | 
			
		||||
            var thisRequest = currentRequest;
 | 
			
		||||
            telemetryAPI.request(domainObject, {strategy: 'latest', size: 1})
 | 
			
		||||
                .then(function (results) {
 | 
			
		||||
                    return results[results.length - 1];
 | 
			
		||||
                }, function (error) {
 | 
			
		||||
                    return undefined;
 | 
			
		||||
                })
 | 
			
		||||
                .then(function (datum) {
 | 
			
		||||
                    if (currentRequest !== thisRequest) {
 | 
			
		||||
                        return; // prevent race.
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!datum || !withinBounds(datum)) {
 | 
			
		||||
                        latestDatum = false;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        latestDatum = datum;
 | 
			
		||||
                        applyBoundsFilter(datum);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (pendingRealtimeDatum) {
 | 
			
		||||
                        callbackIfLatest(pendingRealtimeDatum);
 | 
			
		||||
                        pendingRealtimeDatum = undefined;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function reloadInFixedMode(bounds, isTick) {
 | 
			
		||||
            if (isTick || openmct.time.clock()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            updateTimeSystem(openmct.time.timeSystem());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        openmct.time.on('clock', updateClock);
 | 
			
		||||
        openmct.time.on('timeSystem', updateTimeSystem);
 | 
			
		||||
        openmct.time.on('bounds', reloadInFixedMode);
 | 
			
		||||
        var internalUnsubscribe = telemetryAPI.subscribe(domainObject, callbackIfLatest);
 | 
			
		||||
 | 
			
		||||
        updateClock(openmct.time.clock());
 | 
			
		||||
        updateTimeSystem(openmct.time.timeSystem());
 | 
			
		||||
 | 
			
		||||
        return function unsubscribe() {
 | 
			
		||||
            active = false;
 | 
			
		||||
            internalUnsubscribe();
 | 
			
		||||
            openmct.time.off('clock', updateClock);
 | 
			
		||||
            openmct.time.off('timeSystem', updateTimeSystem);
 | 
			
		||||
            openmct.time.off('bounds', reloadInFixedMode);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return latestValueSubscription
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										670
									
								
								src/api/telemetry/latestValueSubscriptionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										670
									
								
								src/api/telemetry/latestValueSubscriptionSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,670 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 openmct 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 openmct 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([
 | 
			
		||||
    './latestValueSubscription'
 | 
			
		||||
], function (
 | 
			
		||||
    latestValueSubscription
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    function slowPromise() {
 | 
			
		||||
        // Emprically, using setTimeout to resolve a promise results in a
 | 
			
		||||
        // promise that will resolve after every other promise.  This is a
 | 
			
		||||
        // simple way to defer code.
 | 
			
		||||
        return new Promise(function (resolve, reject) {
 | 
			
		||||
            setTimeout(resolve);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    describe("latestValueSubscription", function () {
 | 
			
		||||
 | 
			
		||||
        var openmct;
 | 
			
		||||
        var telemetryAPI;
 | 
			
		||||
        var telemetryMetadata;
 | 
			
		||||
        var formatMap;
 | 
			
		||||
        var pendingRequests;
 | 
			
		||||
        var subscriptions;
 | 
			
		||||
        var domainObject;
 | 
			
		||||
        var callback;
 | 
			
		||||
        var unsubscribe;
 | 
			
		||||
        var triggerTimeEvent;
 | 
			
		||||
 | 
			
		||||
        var noLADTestcases = [
 | 
			
		||||
            {
 | 
			
		||||
                name: "empty LAD",
 | 
			
		||||
                trigger: function () {
 | 
			
		||||
                    pendingRequests[0].resolve([]);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                name: "rejected LAD",
 | 
			
		||||
                trigger: function () {
 | 
			
		||||
                    pendingRequests[0].reject();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        var outOfBoundsLADTestcases = [
 | 
			
		||||
            {
 | 
			
		||||
                name: "future LAD",
 | 
			
		||||
                trigger: function () {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 1123}]);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                name: "prehistoric LAD",
 | 
			
		||||
                trigger: function () {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: -150}]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        beforeEach(function () {
 | 
			
		||||
            openmct = {
 | 
			
		||||
                time: jasmine.createSpyObj('timeAPI', [
 | 
			
		||||
                    'clock',
 | 
			
		||||
                    'timeSystem',
 | 
			
		||||
                    'bounds',
 | 
			
		||||
                    'on',
 | 
			
		||||
                    'off'
 | 
			
		||||
                ])
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            openmct.time.timeSystem.and.returnValue({key: 'test'});
 | 
			
		||||
 | 
			
		||||
            telemetryAPI = jasmine.createSpyObj('telemetryAPI', [
 | 
			
		||||
                'getMetadata',
 | 
			
		||||
                'getFormatMap',
 | 
			
		||||
                'request',
 | 
			
		||||
                'subscribe'
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            telemetryMetadata = jasmine.createSpyObj('metadata', [
 | 
			
		||||
                'values'
 | 
			
		||||
            ]);
 | 
			
		||||
            telemetryAPI.getMetadata.and.returnValue(telemetryMetadata);
 | 
			
		||||
 | 
			
		||||
            formatMap = {
 | 
			
		||||
                test: jasmine.createSpyObj('testFormatter', ['parse']),
 | 
			
		||||
                other: jasmine.createSpyObj('otherFormatter', ['parse'])
 | 
			
		||||
            };
 | 
			
		||||
            formatMap.test.parse.and.callFake(function (datum) {
 | 
			
		||||
                return datum.test;
 | 
			
		||||
            });
 | 
			
		||||
            formatMap.other.parse.and.callFake(function (datum) {
 | 
			
		||||
                return datum.other;
 | 
			
		||||
            });
 | 
			
		||||
            telemetryAPI.getFormatMap.and.returnValue(formatMap);
 | 
			
		||||
 | 
			
		||||
            pendingRequests = [];
 | 
			
		||||
            telemetryAPI.request.and.callFake(function (domainObject, options) {
 | 
			
		||||
                var request = {
 | 
			
		||||
                    domainObject: domainObject,
 | 
			
		||||
                    options: options
 | 
			
		||||
                };
 | 
			
		||||
                request.promise = new Promise(function (resolve, reject) {
 | 
			
		||||
                    request.resolve = resolve;
 | 
			
		||||
                    request.reject = reject;
 | 
			
		||||
                });
 | 
			
		||||
                pendingRequests.push(request);
 | 
			
		||||
                return request.promise;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            subscriptions = [];
 | 
			
		||||
            telemetryAPI.subscribe.and.callFake(function (domainObject, callback) {
 | 
			
		||||
                var subscription = {
 | 
			
		||||
                    domainObject: domainObject,
 | 
			
		||||
                    callback: callback,
 | 
			
		||||
                    unsubscribe: jasmine.createSpy('unsubscribe')
 | 
			
		||||
                };
 | 
			
		||||
                subscriptions.push(subscription);
 | 
			
		||||
                return subscription.unsubscribe;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            callback = jasmine.createSpy('callback');
 | 
			
		||||
 | 
			
		||||
            domainObject = {};
 | 
			
		||||
 | 
			
		||||
            triggerTimeEvent = function (event, args) {
 | 
			
		||||
                openmct.time.on.calls.allArgs().filter(function (callArgs) {
 | 
			
		||||
                    return callArgs[0] === event;
 | 
			
		||||
                })[0][1].apply(null, args);
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // A simple test case to make sure we have appropriate mocks.
 | 
			
		||||
        it("requests, subscribes, and unsubscribes", function () {
 | 
			
		||||
            var unsubscribe = latestValueSubscription(
 | 
			
		||||
                domainObject,
 | 
			
		||||
                callback,
 | 
			
		||||
                telemetryAPI,
 | 
			
		||||
                openmct
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            expect(unsubscribe).toEqual(jasmine.any(Function));
 | 
			
		||||
            expect(telemetryAPI.request)
 | 
			
		||||
                .toHaveBeenCalledWith(domainObject, jasmine.any(Object))
 | 
			
		||||
            expect(telemetryAPI.subscribe)
 | 
			
		||||
                .toHaveBeenCalledWith(domainObject, jasmine.any(Function))
 | 
			
		||||
            expect(subscriptions[0].unsubscribe).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
            unsubscribe();
 | 
			
		||||
 | 
			
		||||
            expect(subscriptions[0].unsubscribe).toHaveBeenCalled();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /** TODO:
 | 
			
		||||
         * test lad response inside bounds, outside bounds, no response.
 | 
			
		||||
         * test realtime should wait until lad response (all cases);
 | 
			
		||||
         * realtime should only notify if later than latest (or no latest).
 | 
			
		||||
         *
 | 
			
		||||
         * timesystem change should clear and re-request LAD.
 | 
			
		||||
         * clock change should enable/disable bounds filtering.
 | 
			
		||||
         * non-tick bounds change should clear and
 | 
			
		||||
         *
 | 
			
		||||
         *
 | 
			
		||||
         * should receive lad response
 | 
			
		||||
         * should receive realtime if later than lad.
 | 
			
		||||
         * should receive lad response (unless outside)
 | 
			
		||||
         * subscriptions should wait for lad response
 | 
			
		||||
         *
 | 
			
		||||
        */
 | 
			
		||||
        describe("no clock (AKA fixed)", function () {
 | 
			
		||||
            var unsubscribe;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct.time.clock.and.returnValue(undefined);
 | 
			
		||||
                openmct.time.timeSystem.and.returnValue({key: 'test'});
 | 
			
		||||
                openmct.time.bounds.and.returnValue({start: 0, end: 1000});
 | 
			
		||||
                unsubscribe = latestValueSubscription(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    telemetryAPI,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("nominal LAD response", function () {
 | 
			
		||||
                it("provides LAD datum on resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("sends realtime values synchronously after resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        subscriptions[0].callback({test: 456})
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("holds realtime values until resolved", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456})
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("only sends latest realtime value after resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456});
 | 
			
		||||
                    subscriptions[0].callback({test: 567});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("filters realtime values before latest", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 456}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 123})
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("filters realtime values outside bounds", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 456}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 1123})
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("doesn't override pending value with one outside bounds", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456});
 | 
			
		||||
                    subscriptions[0].callback({test: 1123});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("doesn't send out of order realtime value", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        subscriptions[0].callback({test: 567});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            outOfBoundsLADTestcases.concat(noLADTestcases).forEach(function (testCase) {
 | 
			
		||||
                describe(testCase.name, function () {
 | 
			
		||||
                    it("does not provide LAD datum", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalled();
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("sends realtime values synchronously after resolve", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 456})
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("holds realtime values until resolved", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456})
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("only sends latest realtime value after resolve", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 567});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("filters realtime values outside bounds", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 1123})
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("doesn't override pending value with one outside bounds", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 1123});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("doesn't send out of order realtime value", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 567});
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                            subscriptions[0].callback({test: 456});
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("with clock (AKA realtime)", function () {
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct.time.clock.and.returnValue({});
 | 
			
		||||
                openmct.time.timeSystem.and.returnValue({key: 'test'});
 | 
			
		||||
                openmct.time.bounds.and.returnValue({start: 0, end: 1000});
 | 
			
		||||
                unsubscribe = latestValueSubscription(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    telemetryAPI,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("nominal LAD response", function () {
 | 
			
		||||
                it("provides LAD datum on resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("sends realtime values synchronously after resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        subscriptions[0].callback({test: 456})
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("holds realtime values until resolved", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456})
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("only sends latest realtime value after resolve", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456});
 | 
			
		||||
                    subscriptions[0].callback({test: 567});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("filters realtime values before latest", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 456}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 123})
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not filter realtime values outside bounds", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 456}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 1123})
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("overrides pending realtime value with one outside bounds", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 456});
 | 
			
		||||
                    subscriptions[0].callback({test: 1123});
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("doesn't send out of order realtime value", function (done) {
 | 
			
		||||
                    pendingRequests[0].resolve([{test: 123}]);
 | 
			
		||||
                    slowPromise().then(function () {
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 123});
 | 
			
		||||
                        subscriptions[0].callback({test: 567});
 | 
			
		||||
                        expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        done();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            noLADTestcases.forEach(function (testCase) {
 | 
			
		||||
                describe(testCase.name, function () {
 | 
			
		||||
                    it("does not provide LAD datum", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalled();
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("sends realtime values synchronously after resolve", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 456})
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("holds realtime values until resolved", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456})
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("only sends latest realtime value after resolve", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 567});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                        expect(callback).not.toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("doesn't filter realtime values outside bounds", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 1123})
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("doesn't filter pending realtime values outside bounds", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        subscriptions[0].callback({test: 456});
 | 
			
		||||
                        subscriptions[0].callback({test: 1123});
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("doesn't send out of order realtime value", function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            subscriptions[0].callback({test: 567});
 | 
			
		||||
                            expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                            subscriptions[0].callback({test: 456});
 | 
			
		||||
                            expect(callback).not.toHaveBeenCalledWith({test: 456});
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("out of bounds LAD", function () {
 | 
			
		||||
                outOfBoundsLADTestcases.forEach(function (testCase) {
 | 
			
		||||
                    it(`provides ${testCase} datum`, function (done) {
 | 
			
		||||
                        testCase.trigger();
 | 
			
		||||
                        slowPromise().then(function () {
 | 
			
		||||
                            expect(callback).toHaveBeenCalled();
 | 
			
		||||
                            done();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("clock changes", function () {
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct.time.timeSystem.and.returnValue({key: 'test'});
 | 
			
		||||
                openmct.time.bounds.and.returnValue({start: 0, end: 1000});
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("starts bounds filtering when clock is cleared", function (done) {
 | 
			
		||||
                openmct.time.clock.and.returnValue({});
 | 
			
		||||
                unsubscribe = latestValueSubscription(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    telemetryAPI,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
                pendingRequests[0].resolve([]);
 | 
			
		||||
                slowPromise().then(function () {
 | 
			
		||||
                    subscriptions[0].callback({test: 1123});
 | 
			
		||||
                    expect(callback).toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                    triggerTimeEvent('clock', undefined);
 | 
			
		||||
                    subscriptions[0].callback({test: 1223});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 1223});
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("stops bounds filtering when clock is set", function (done) {
 | 
			
		||||
                openmct.time.clock.and.returnValue(undefined);
 | 
			
		||||
                unsubscribe = latestValueSubscription(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    telemetryAPI,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
                pendingRequests[0].resolve([]);
 | 
			
		||||
                slowPromise().then(function () {
 | 
			
		||||
                    subscriptions[0].callback({test: 1123});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 1123});
 | 
			
		||||
                    triggerTimeEvent('clock', [{}]);
 | 
			
		||||
                    subscriptions[0].callback({test: 1223});
 | 
			
		||||
                    expect(callback).toHaveBeenCalledWith({test: 1223});
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("timesystem changes", function () {
 | 
			
		||||
            it("requeries lad and uses new keys.", function (done) {
 | 
			
		||||
                openmct.time.clock.and.returnValue(undefined);
 | 
			
		||||
                openmct.time.timeSystem.and.returnValue({key: 'test'});
 | 
			
		||||
                openmct.time.bounds.and.returnValue({start: 0, end: 1000});
 | 
			
		||||
                unsubscribe = latestValueSubscription(
 | 
			
		||||
                    domainObject,
 | 
			
		||||
                    callback,
 | 
			
		||||
                    telemetryAPI,
 | 
			
		||||
                    openmct
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                expect(pendingRequests.length).toBe(1);
 | 
			
		||||
                expect(subscriptions.length).toBe(1);
 | 
			
		||||
                pendingRequests[0].resolve([{test: 234}]);
 | 
			
		||||
                slowPromise().then(function () {
 | 
			
		||||
                    expect(callback).toHaveBeenCalledWith({test: 234});
 | 
			
		||||
                    triggerTimeEvent('timeSystem', [{key: 'other'}]);
 | 
			
		||||
                    expect(pendingRequests.length).toBe(2);
 | 
			
		||||
                    expect(subscriptions.length).toBe(1);
 | 
			
		||||
                    pendingRequests[1].resolve([{test: 123, other: 456}]);
 | 
			
		||||
                    return slowPromise(); // wait for new lad to resolve.
 | 
			
		||||
                }).then(function() {
 | 
			
		||||
                    expect(callback).toHaveBeenCalledWith({test: 123, other: 456});
 | 
			
		||||
                    // should have synchronous callbacks when other is greater.
 | 
			
		||||
                    subscriptions[0].callback({test: 234, other: 567});
 | 
			
		||||
                    expect(callback).toHaveBeenCalledWith({test: 234, other:567});
 | 
			
		||||
                    // should filter out when other is less.
 | 
			
		||||
                    subscriptions[0].callback({test: 345, other: 345});
 | 
			
		||||
                    expect(callback).not.toHaveBeenCalledWith({test: 345, other: 345});
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("does not filter when no value matches timesystem", function (done) {
 | 
			
		||||
            openmct.time.clock.and.returnValue(undefined);
 | 
			
		||||
            openmct.time.timeSystem.and.returnValue({key: 'blah'});
 | 
			
		||||
            openmct.time.bounds.and.returnValue({start: 0, end: 1000});
 | 
			
		||||
            unsubscribe = latestValueSubscription(
 | 
			
		||||
                domainObject,
 | 
			
		||||
                callback,
 | 
			
		||||
                telemetryAPI,
 | 
			
		||||
                openmct
 | 
			
		||||
            );
 | 
			
		||||
            pendingRequests[0].resolve([{test: 1234}]);
 | 
			
		||||
            slowPromise().then(function () {
 | 
			
		||||
                expect(callback).toHaveBeenCalledWith({test: 1234});
 | 
			
		||||
                subscriptions[0].callback({test: 567});
 | 
			
		||||
                expect(callback).toHaveBeenCalledWith({test: 567});
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("on bounds event", function () {
 | 
			
		||||
            // TODO: test cases for what happens when bounds changes.
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										21
									
								
								src/plugins/URLIndicatorPlugin/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/plugins/URLIndicatorPlugin/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
# URL Indicator
 | 
			
		||||
Adds an indicator which shows the availability of a URL, with success based on receipt of a 200 HTTP code. Can be used 
 | 
			
		||||
for monitoring the availability of web services.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.URLIndicator({
 | 
			
		||||
  url: 'http://localhost:8080',
 | 
			
		||||
    iconClass: 'check',
 | 
			
		||||
    interval: 10000,
 | 
			
		||||
    label: 'Localhost'
 | 
			
		||||
 })
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Options
 | 
			
		||||
* __url__: URL to indicate the status of
 | 
			
		||||
* __iconClass__: Icon to show in the status bar, defaults to icon-database. See the [Style Guide](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home/glyphs?view=styleguide.glyphs) for more icon options.
 | 
			
		||||
* __interval__: Interval between checking the connection, defaults to 10000
 | 
			
		||||
* __label__: Name showing up as text in the status bar, defaults to url
 | 
			
		||||
 | 
			
		||||
@@ -25,15 +25,6 @@ define([
 | 
			
		||||
], function (
 | 
			
		||||
    AutoflowTabularView
 | 
			
		||||
) {
 | 
			
		||||
    /**
 | 
			
		||||
     * This plugin provides an Autoflow Tabular View for domain objects
 | 
			
		||||
     * in Open MCT.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Object} options
 | 
			
		||||
     * @param {String} [options.type] the domain object type for which
 | 
			
		||||
     *        this view should be available; if omitted, this view will
 | 
			
		||||
     *        be available for all objects
 | 
			
		||||
     */
 | 
			
		||||
    return function (options) {
 | 
			
		||||
        return function (openmct) {
 | 
			
		||||
            var views = (openmct.mainViews || openmct.objectViews);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,7 @@ define([
 | 
			
		||||
            spyOn(mockmct.telemetry, 'getMetadata');
 | 
			
		||||
            spyOn(mockmct.telemetry, 'getValueFormatter');
 | 
			
		||||
            spyOn(mockmct.telemetry, 'limitEvaluator');
 | 
			
		||||
            spyOn(mockmct.telemetry, 'request');
 | 
			
		||||
            spyOn(mockmct.telemetry, 'subscribe');
 | 
			
		||||
            spyOn(mockmct.telemetry, 'latest');
 | 
			
		||||
 | 
			
		||||
            var plugin = new AutoflowTabularPlugin({ type: testType });
 | 
			
		||||
            plugin(mockmct);
 | 
			
		||||
@@ -140,15 +139,11 @@ define([
 | 
			
		||||
                        return mockFormatter;
 | 
			
		||||
                    });
 | 
			
		||||
                    mockmct.telemetry.limitEvaluator.and.returnValue(mockEvaluator);
 | 
			
		||||
                    mockmct.telemetry.subscribe.and.callFake(function (obj, callback) {
 | 
			
		||||
                    mockmct.telemetry.latest.and.callFake(function (obj, callback) {
 | 
			
		||||
                        var key = obj.identifier.key;
 | 
			
		||||
                        callbacks[key] = callback;
 | 
			
		||||
                        return mockUnsubscribes[key];
 | 
			
		||||
                    });
 | 
			
		||||
                    mockmct.telemetry.request.and.callFake(function (obj, request) {
 | 
			
		||||
                        var key = obj.identifier.key;
 | 
			
		||||
                        return Promise.resolve([testHistories[key]]);
 | 
			
		||||
                    });
 | 
			
		||||
                    mockMetadata.valuesForHints.and.callFake(function (hints) {
 | 
			
		||||
                        return [{ hint: hints[0] }];
 | 
			
		||||
                    });
 | 
			
		||||
@@ -232,19 +227,6 @@ define([
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("displays historical telemetry", function () {
 | 
			
		||||
                    function rowTextDefined() {
 | 
			
		||||
                        return $(testContainer).find(".l-autoflow-item").filter(".r").text() !== "";
 | 
			
		||||
                    }
 | 
			
		||||
                    return domObserver.when(rowTextDefined).then(function () {
 | 
			
		||||
                        testKeys.forEach(function (key, index) {
 | 
			
		||||
                            var datum = testHistories[key];
 | 
			
		||||
                            var $cell = $(testContainer).find(".l-autoflow-row").eq(index).find(".r");
 | 
			
		||||
                            expect($cell.text()).toEqual(String(datum.range));
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("displays incoming telemetry", function () {
 | 
			
		||||
                    var testData = testKeys.map(function (key, index) {
 | 
			
		||||
                        return { key: key, range: index * 100, domain: key + index };
 | 
			
		||||
 
 | 
			
		||||
@@ -66,19 +66,10 @@ define([], function () {
 | 
			
		||||
     * Activate this controller; begin listening for changes.
 | 
			
		||||
     */
 | 
			
		||||
    AutoflowTabularRowController.prototype.activate = function () {
 | 
			
		||||
        this.unsubscribe = this.openmct.telemetry.subscribe(
 | 
			
		||||
        this.unsubscribe = this.openmct.telemetry.latest(
 | 
			
		||||
            this.domainObject,
 | 
			
		||||
            this.updateRowData.bind(this)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.openmct.telemetry.request(
 | 
			
		||||
            this.domainObject,
 | 
			
		||||
            { size: 1 }
 | 
			
		||||
        ).then(function (history) {
 | 
			
		||||
            if (!this.initialized && history.length > 0) {
 | 
			
		||||
                this.updateRowData(history[history.length - 1]);
 | 
			
		||||
            }
 | 
			
		||||
        }.bind(this));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/plugins/autoflow/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/plugins/autoflow/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# Autoflow View
 | 
			
		||||
 | 
			
		||||
This plugin provides the Autoflow View for domain objects in Open MCT. This view allows users to visualize the latest 
 | 
			
		||||
values of a collection of telemetry points in a condensed list.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
``` js
 | 
			
		||||
    openmct.install(openmct.plugins.AutoflowView({
 | 
			
		||||
        type: "telemetry.fixed"
 | 
			
		||||
    }));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Options
 | 
			
		||||
* `type`: The object type to add the Autoflow View to. Currently supports a single value. If not provided, will make the 
 | 
			
		||||
Autoflow view available for all objects (which is probably not what you want).
 | 
			
		||||
							
								
								
									
										10
									
								
								src/plugins/plot/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/plugins/plot/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
# Plot Plugin
 | 
			
		||||
 | 
			
		||||
Enables plot visualization of telemetry data. This plugin adds a plot view that is available from the view switcher for 
 | 
			
		||||
all telemetry objects. Two user createble objects are also added by this plugin, for Overlay and Stacked Plots. 
 | 
			
		||||
Telemetry objects can be added to Overlay and Stacked Plots via drag and drop.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
``` js
 | 
			
		||||
openmct.install(openmct.plugins.Plot());
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										19
									
								
								src/plugins/staticRootPlugin/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/plugins/staticRootPlugin/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
# Static Root Plugin
 | 
			
		||||
 | 
			
		||||
This plugin takes an object tree as JSON and exposes it as a non-editable root level tree. This can be useful if you 
 | 
			
		||||
have static non-editable content that you wish to expose, such as a standard set of displays that should not be edited 
 | 
			
		||||
(but which can be copied and then modified if desired).
 | 
			
		||||
 | 
			
		||||
Any object tree in Open MCT can be exported as JSON after installing the 
 | 
			
		||||
[Import/Export plugin](../../../platform/import-export/README.md).
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
``` js
 | 
			
		||||
openmct.install(openmct.plugins.StaticRootPlugin('mission', 'data/static-objects.json'));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Parameters
 | 
			
		||||
The StaticRootPlugin takes two parameters:
 | 
			
		||||
1. __namespace__: This should be a name that uniquely identifies this collection of objects.
 | 
			
		||||
2. __path__: The file that the static tree should be exposed from. This will need to be a path that is reachable by a web 
 | 
			
		||||
browser, ie not a path on the local file system.
 | 
			
		||||
							
								
								
									
										10
									
								
								src/plugins/summaryWidget/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/plugins/summaryWidget/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
# Summary Widget Plugin
 | 
			
		||||
Summary widgets can be used to provide visual indication of state based on telemetry data. They allow rules to be 
 | 
			
		||||
defined that can then be used to change the appearance of the summary widget element based on data. For example, a 
 | 
			
		||||
summary widget could be defined that is green when a temparature reading is between `0` and `100` centigrade, red when 
 | 
			
		||||
it's above `100`, and orange when it's below `0`.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.SummaryWidget());
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										10
									
								
								test-main.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								test-main.js
									
									
									
									
									
								
							@@ -67,14 +67,14 @@ requirejs.config({
 | 
			
		||||
        "lodash": "bower_components/lodash/lodash",
 | 
			
		||||
        "d3-selection": "node_modules/d3-selection/dist/d3-selection.min",
 | 
			
		||||
        "d3-scale": "node_modules/d3-scale/build/d3-scale.min",
 | 
			
		||||
        "d3-axis": "node_modules/d3-axis/build/d3-axis.min",
 | 
			
		||||
        "d3-array": "node_modules/d3-array/build/d3-array.min",
 | 
			
		||||
        "d3-collection": "node_modules/d3-collection/build/d3-collection.min",
 | 
			
		||||
        "d3-axis": "node_modules/d3-axis/dist/d3-axis.min",
 | 
			
		||||
        "d3-array": "node_modules/d3-array/dist/d3-array.min",
 | 
			
		||||
        "d3-collection": "node_modules/d3-collection/dist/d3-collection.min",
 | 
			
		||||
        "d3-color": "node_modules/d3-color/build/d3-color.min",
 | 
			
		||||
        "d3-format": "node_modules/d3-format/build/d3-format.min",
 | 
			
		||||
        "d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
 | 
			
		||||
        "d3-time": "node_modules/d3-time/build/d3-time.min",
 | 
			
		||||
        "d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
 | 
			
		||||
        "d3-time": "node_modules/d3-time/dist/d3-time.min",
 | 
			
		||||
        "d3-time-format": "node_modules/d3-time-format/dist/d3-time-format.min",
 | 
			
		||||
        "html2canvas": "node_modules/html2canvas/dist/html2canvas.min",
 | 
			
		||||
        "painterro": "node_modules/painterro/build/painterro.min",
 | 
			
		||||
        "printj": "node_modules/printj/dist/printj.min"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user