Compare commits

..

41 Commits

Author SHA1 Message Date
7ce9bd969a [API] Telemetry JSdoc 2016-09-06 10:14:04 -07:00
8cafd2da7e Merge remote-tracking branch 'origin/api-tutorial/telemetry' into api-1110 2016-09-06 10:06:30 -07:00
6264ab75f3 Merge remote-tracking branch 'origin/api-tutorials' into api-1110
Conflicts:
	src/MCT.js
	src/api/composition/CompositionCollection.js
	src/api/composition/DefaultCompositionProvider.js
	src/api/objects/MutableObject.js
2016-09-06 10:03:59 -07:00
7a5cad20ec [API] Add JSDoc for Dialog 2016-09-06 09:58:08 -07:00
4de069b393 [API] JSDoc for Selection 2016-09-06 09:54:51 -07:00
70abd5c1f9 [API] Document start event 2016-09-06 09:47:06 -07:00
2a3a61da86 [API] Fix event memberofs in TimeConductor 2016-09-06 09:45:24 -07:00
018bd022cc [API] Document View API 2016-09-06 09:42:04 -07:00
4739b36bc3 [API] Add metadata to View jsdoc 2016-09-06 09:14:04 -07:00
c9b1035a6d [API] Document Type.check 2016-09-06 09:13:01 -07:00
6768328475 [API] Document MutableObject 2016-09-06 08:43:01 -07:00
60c179eac3 [API] Add missing parameter names 2016-09-06 08:32:18 -07:00
a20e8d69b5 [API] More Objects doc 2016-09-06 08:31:42 -07:00
1abcb248fe [API] Include API.md as main page 2016-09-06 08:13:26 -07:00
a8151f5f22 [API] Document CompositionProvider 2016-09-06 08:09:03 -07:00
cdf21f3763 [API] Mark Composition as instance method 2016-09-06 07:58:52 -07:00
341bceb4e2 [API] Document composition API 2016-09-06 07:57:44 -07:00
0470a02272 [API] Reference ObjectAPI 2016-09-02 16:04:38 -07:00
e3dc26c130 [API] Clean up docs 2016-09-02 15:25:28 -07:00
96c3d1cac2 [API] Use JSDoc config 2016-09-02 15:19:20 -07:00
2af778145d [API] Clean up JSDoc slightly 2016-09-02 15:16:11 -07:00
5743eeb33a [API] Reference EventEmitter 2016-09-02 15:03:12 -07:00
f06f714bdc [API] Document some public fields 2016-09-02 15:02:18 -07:00
d592bd1035 [API] Give up on borrows-style documentation 2016-09-02 14:58:06 -07:00
b5f62541ce [API] Add watch for API docs 2016-09-02 14:54:27 -07:00
33ced4bccf [API] Expose MCT on openmct 2016-09-01 15:51:25 -07:00
e37510dbab [API] Ignore internal API after processing 2016-09-01 15:13:52 -07:00
f27c41014d [API] JSDoc for openmct.start() 2016-09-01 14:50:18 -07:00
bd796f2beb [API] Simple constructor documentation 2016-09-01 14:44:24 -07:00
bcc32c76d0 [API] Module-level JSDoc 2016-09-01 14:42:55 -07:00
ff2ec6690a [API] Rename module to openmct 2016-09-01 14:35:52 -07:00
1e0fb3611d [API] Render API docs to HTML 2016-09-01 12:34:54 -07:00
1d4f36a7d9 [API] Include JSDoc for mct namespace only 2016-08-26 11:48:27 -07:00
0f96fbdd62 [API] Treat mct as a namespace 2016-08-26 11:31:56 -07:00
e05fb57fe4 [API] Quasi-sensible JSDoc starting point 2016-08-26 09:47:50 -07:00
185cdcab08 [API] Begin adding mct.js
...which will provide an instance of OpenMCT at startup,
as well as house documentation for entry point to public API.
2016-08-26 09:31:12 -07:00
50ccad5aaa [API] Rename MCT to OpenMCT 2016-08-26 09:26:12 -07:00
6a23df9454 [API] Add JSDoc for MCT 2016-08-25 14:54:07 -07:00
ab5b1d3754 [API] Add JSDoc task 2016-08-25 13:49:21 -07:00
b309f26b56 [API] Add gulp-jsdoc-to-markdown dep
...to aid in generating API docs as a measure of API completeness
and consistency, for #1110 and #1111
2016-08-25 13:38:44 -07:00
bccd018d97 Telemetry Draft 2016-07-01 10:26:49 -07:00
20 changed files with 945 additions and 30 deletions

View File

@ -0,0 +1,13 @@
module.exports = {
handlers: {
processingComplete: function (e) {
e.doclets.forEach(function (doclet) {
var memberof = doclet.memberof || "";
var longname = doclet.longname || "";
doclet.ignore = longname !== 'module:openmct' &&
memberof.indexOf('module:openmct') !== 0;
});
}
}
};

View File

@ -145,6 +145,21 @@ gulp.task('watch', function () {
return gulp.watch(paths.scss, ['stylesheets', 'assets']);
});
gulp.task('api', function () {
var jsdoc2md = require('gulp-jsdoc-to-markdown');
var concat = require('gulp-concat');
var markdown = require('gulp-markdown');
return gulp.src('src/**/*.js')
.pipe(concat('api.md'))
.pipe(jsdoc2md(require('./jsdoc.json')))
.pipe(markdown())
.pipe(gulp.dest(paths.dist));
});
gulp.task('api-watch', function () {
return gulp.watch('src/**/*.js', ['api']);
});
gulp.task('serve', function () {
console.log('Running development server with all defaults');
var app = require('./app.js');

View File

@ -1,12 +1,13 @@
{
"source": {
"include": [
"platform/"
"src/"
],
"includePattern": "platform/.+\\.js$",
"includePattern": "src/.+\\.js$",
"excludePattern": ".+\\Spec\\.js$|lib/.+"
},
"plugins": [
"plugins/markdown"
"plugins/markdown",
"build/jsdoc/plugins/mct-only"
]
}

View File

@ -12,8 +12,11 @@
"git-rev-sync": "^1.4.0",
"glob": ">= 3.0.0",
"gulp": "^3.9.0",
"gulp-concat": "^2.6.0",
"gulp-jscs": "^3.0.2",
"gulp-jsdoc-to-markdown": "^1.2.2",
"gulp-jshint": "^2.0.0",
"gulp-markdown": "^1.2.0",
"gulp-rename": "^1.2.2",
"gulp-replace-task": "^0.11.0",
"gulp-requirejs-optimize": "^0.3.1",
@ -47,7 +50,7 @@
"test": "karma start --single-run",
"jshint": "jshint platform example",
"watch": "karma start",
"jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api",
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d target/docs/api",
"otherdoc": "node docs/gendocs.js --in docs/src --out target/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
"docs": "npm run jsdoc ; npm run otherdoc",
"prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"

View File

@ -5,7 +5,8 @@ define([
'./api/api',
'text!./adapter/templates/edit-object-replacement.html',
'./Selection',
'./api/objects/object-utils'
'./api/objects/object-utils',
'./api/TimeConductor'
], function (
EventEmitter,
legacyRegistry,
@ -13,8 +14,17 @@ define([
api,
editObjectTemplate,
Selection,
objectUtils
objectUtils,
TimeConductor
) {
/**
* The Open MCT application. This may be configured by installing plugins
* or registering extensions before the application is started.
* @class MCT
* @memberof module:openmct
* @augments {EventEmitter}
*/
function MCT() {
EventEmitter.call(this);
this.legacyBundle = { extensions: {
@ -28,7 +38,23 @@ define([
]
} };
/**
*
* @type {module:openmct.Selection}
* @memberof module:openmct.MCT#
* @name selection
*/
this.selection = new Selection();
/**
*
* @type {module:openmct.TimeConductor}
* @memberof module:openmct.MCT#
* @name conductor
*/
this.conductor = new TimeConductor();
this.TimeConductor = this.conductor; // compatibility for prototype
this.on('navigation', this.selection.clear.bind(this.selection));
}
@ -39,6 +65,25 @@ define([
});
MCT.prototype.MCT = MCT;
/**
* An interface for interacting with domain objects and the domain
* object hierarchy.
*
* @type {module:openmct.ObjectAPI}
* @memberof module:openmct.MCT#
* @name Objects
*/
MCT.Objects = api.Objects;
/**
* An interface for retrieving and interpreting telemetry data associated
* with a domain object.
*
* @type {module:openmct.TelemetryAPI}
* @memberof module:openmct.MCT#
* @name Telemetry
*/
MCT.prototype.legacyExtension = function (category, extension) {
this.legacyBundle.extensions[category] =
this.legacyBundle.extensions[category] || [];
@ -47,6 +92,8 @@ define([
/**
* Set path to where assets are hosted. This should be the path to main.js.
* @memberof module:openmct.MCT#
* @method setAssetPath
*/
MCT.prototype.setAssetPath = function (path) {
this.legacyExtension('constants', {
@ -58,8 +105,10 @@ define([
/**
* Register a new type of view.
*
* @param region the region identifier (see mct.regions)
* @param {ViewDefinition} definition the definition for this view
* @param {string} region the region identifier (see mct.regions)
* @param {module:openmct.ViewProvider} provider the provider for this view
* @method view
* @memberof module:openmct.MCT#
*/
MCT.prototype.view = function (region, definition) {
var viewKey = region + uuid();
@ -103,6 +152,13 @@ define([
});
};
/**
* Register a new [type]{@link module:openmct.Type} of domain object.
* @param {string} key a unique identifier for this type of object
* @param {module:openmct.Type} type the new type
* @memberof module:openmct.MCT#
* @method type
*/
MCT.prototype.type = function (key, type) {
var legacyDef = type.toLegacyDefinition();
legacyDef.key = key;
@ -117,6 +173,13 @@ define([
});
};
/**
* Start running Open MCT. This should be called only after any plugins
* have been installed.
* @fires module:openmct.MCT~start
* @memberof module:openmct.MCT#
* @method start
*/
MCT.prototype.start = function () {
this.legacyExtension('runs', {
depends: ['navigationService'],
@ -128,14 +191,22 @@ define([
legacyRegistry.register('adapter', this.legacyBundle);
legacyRegistry.enable('adapter');
/**
* Fired by [MCT]{@link module:openmct.MCT} when the application
* is started.
* @event start
* @memberof module:openmct.MCT~
*/
this.emit('start');
};
/**
* Install a plugin in MCT.
*
* @param `Function` plugin -- a plugin install function which will be
* @param {Function} plugin a plugin install function which will be
* invoked with the mct instance.
* @memberof module:openmct.MCT#
*/
MCT.prototype.install = function (plugin) {
plugin(this);

View File

@ -1,17 +1,44 @@
define(['EventEmitter'], function (EventEmitter) {
/**
* Maintains selection state for the application.
* @interface
* @memberof module:openmct
* @extends {EventEmitter}
*/
function Selection() {
EventEmitter.call(this);
this.selectedValues = [];
}
/**
* Fired whenever the current selection state changes.
* @event change
* @memberof module:openmct.Selection~
*/
Selection.prototype = Object.create(EventEmitter.prototype);
/**
* Add a new value to the current selection state.
* @param {*} value the newly selected value
* @returns {Function} a function to deselect this value
* @method select
* @memberof module:openmct.Selection#
* @fires module:openmct.Selection~change
*/
Selection.prototype.select = function (value) {
this.selectedValues.push(value);
this.emit('change', this.selectedValues);
return this.deselect.bind(this, value);
};
/**
* Remove a value from the current selection state.
* @param {*} value the value to deselect
* @method deselect
* @memberof module:openmct.Selection#
* @fires module:openmct.Selection~change
*/
Selection.prototype.deselect = function (value) {
this.selectedValues = this.selectedValues.filter(function (v) {
return v !== value;
@ -19,10 +46,23 @@ define(['EventEmitter'], function (EventEmitter) {
this.emit('change', this.selectedValues);
};
/**
* Get the current selection state.
* @param {*} value the newly selected value
* @returns {Array} all currently-selected objects
* @method selected
* @memberof module:openmct.Selection#
*/
Selection.prototype.selected = function () {
return this.selectedValues;
};
/**
* Deselect all currently-selected objects.
* @method clear
* @memberof module:openmct.Selection#
* @fires module:openmct.Selection~change
*/
Selection.prototype.clear = function () {
this.selectedValues = [];
this.emit('change', this.selectedValues);

View File

@ -32,7 +32,8 @@ define(['EventEmitter'], function (EventEmitter) {
* The TimeConductor extends the EventEmitter class. A number of events are
* fired when properties of the time conductor change, which are
* documented below.
* @constructor
* @interface
* @memberof module:openmct
*/
function TimeConductor() {
EventEmitter.call(this);
@ -58,6 +59,8 @@ define(['EventEmitter'], function (EventEmitter) {
* bounds, for example by views validating user inputs.
* @param bounds The start and end time of the conductor.
* @returns {string | true} A validation error, or true if valid
* @memberof module:openmct.TimeConductor#
* @method validateBounds
*/
TimeConductor.prototype.validateBounds = function (bounds) {
if ((bounds.start === undefined) ||
@ -83,18 +86,21 @@ define(['EventEmitter'], function (EventEmitter) {
* time conductor ticks, regularly updating the bounds from a timing
* source appropriate to the selected time system and mode of the time
* conductor.
* @fires TimeConductor#follow
* @fires module:openmct.TimeConductor~follow
* @param {boolean} followMode
* @returns {boolean}
* @memberof module:openmct.TimeConductor#
* @method follow
*/
TimeConductor.prototype.follow = function (followMode) {
if (arguments.length > 0) {
this.followMode = followMode;
/**
* @event TimeConductor#follow The TimeConductor has toggled
* into or out of follow mode.
* The TimeConductor has toggled into or out of follow mode.
* @event follow
* @memberof module:openmct.TimeConductor~
* @property {boolean} followMode true if follow mode is
* enabled, otherwise false.
* enabled, otherwise false.
*/
this.emit('follow', this.followMode);
}
@ -105,23 +111,28 @@ define(['EventEmitter'], function (EventEmitter) {
* @typedef {Object} TimeConductorBounds
* @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system
* @property {number} end The end time displayed by the time conductor in ms since epoch.
* @memberof module:openmct.TimeConductor~
*/
/**
* Get or set the start and end time of the time conductor. Basic validation
* of bounds is performed.
*
* @param {TimeConductorBounds} newBounds
* @param {module:openmct.TimeConductorBounds~TimeConductorBounds} newBounds
* @throws {Error} Validation error
* @fires TimeConductor#bounds
* @returns {TimeConductorBounds}
* @fires module:openmct.TimeConductor~bounds
* @returns {module:openmct.TimeConductorBounds~TimeConductorBounds}
* @memberof module:openmct.TimeConductor#
* @method bounds
*/
TimeConductor.prototype.bounds = function (newBounds) {
if (arguments.length > 0) {
throwOnError(this.validateBounds(newBounds));
this.boundsVal = newBounds;
/**
* @event TimeConductor#bounds The start time, end time, or
* both have been updated
* The start time, end time, or both have been updated.
* @event bounds
* @memberof module:openmct.TimeConductor~
* @property {TimeConductorBounds} bounds
*/
this.emit('bounds', this.boundsVal);
@ -134,17 +145,21 @@ define(['EventEmitter'], function (EventEmitter) {
* units, epoch, and other aspects of time representation. When changing
* the time system in use, new valid bounds must also be provided.
* @param {TimeSystem} newTimeSystem
* @param {TimeConductorBounds} bounds
* @fires TimeConductor#timeSystem
* @param {module:openmct.TimeConductor~TimeConductorBounds} bounds
* @fires module:openmct.TimeConductor~timeSystem
* @returns {TimeSystem} The currently applied time system
* @memberof module:openmct.TimeConductor#
* @method timeSystem
*/
TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) {
if (arguments.length >= 2) {
this.system = newTimeSystem;
/**
* @event TimeConductor#timeSystem The time system used by the time
* The time system used by the time
* conductor has changed. A change in Time System will always be
* followed by a bounds event specifying new query bounds
* followed by a bounds event specifying new query bounds.
*
* @event module:openmct.TimeConductor~timeSystem
* @property {TimeSystem} The value of the currently applied
* Time System
* */
@ -163,15 +178,19 @@ define(['EventEmitter'], function (EventEmitter) {
* Get or set the Time of Interest. The Time of Interest is the temporal
* focus of the current view. It can be manipulated by the user from the
* time conductor or from other views.
* @fires TimeConductor#timeOfInterest
* @fires module:openmct.TimeConductor~timeOfInterest
* @param newTOI
* @returns {number} the current time of interest
* @memberof module:openmct.TimeConductor#
* @method timeOfInterest
*/
TimeConductor.prototype.timeOfInterest = function (newTOI) {
if (arguments.length > 0) {
this.toi = newTOI;
/**
* @event TimeConductor#timeOfInterest The Time of Interest has moved.
* The Time of Interest has moved.
* @event timeOfInterest
* @memberof module:openmct.TimeConductor~
* @property {number} Current time of interest
*/
this.emit('timeOfInterest', this.toi);

View File

@ -1,6 +1,7 @@
define(function () {
/**
* @typedef TypeDefinition
* @memberof module:openmct.Type~
* @property {Metadata} metadata displayable metadata about this type
* @property {function (object)} [initialize] a function which initializes
* the model for new domain objects of this type
@ -9,9 +10,12 @@ define(function () {
*/
/**
* A Type describes a kind of domain object that may appear or be
* created within Open MCT.
*
* @param {TypeDefinition} definition
* @constructor
* @param {module:opemct.Type~TypeDefinition} definition
* @class Type
* @memberof module:openmct
*/
function Type(definition) {
this.definition = definition;
@ -21,6 +25,8 @@ define(function () {
* Check if a domain object is an instance of this type.
* @param domainObject
* @returns {boolean} true if the domain object is of this type
* @memberof module:openmct.Type#
* @method check
*/
Type.prototype.check = function (domainObject) {
// Depends on assignment from MCT.

View File

@ -5,6 +5,7 @@ define([], function () {
* associated life cycle events.
*
* @interface
* @memberof module:openmct
*/
function View() {
@ -18,6 +19,8 @@ define([], function () {
* the contents of this view up-to-date.
*
* @param {HTMLElement} container the DOM element to populate
* @method show
* @memberof module:openmct.View#
*/
View.prototype.show = function (container) {
@ -29,10 +32,60 @@ define([], function () {
* View implementations should use this method to detach any
* listeners or release other resources that are no longer necessary
* once a view is no longer used.
*
* @method destroy
* @memberof module:openmct.View#
*/
View.prototype.destroy = function () {
};
/**
* Exposes types of views in Open MCT.
*
* @interface ViewProvider
* @memberof module:openmct
*/
/**
* Check if this provider can supply views for a domain object.
* @method canView
* @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed
* @returns {boolean} true if this domain object can be viewed using
* this provider
*/
/**
* Provide a view of this domain object.
* @method view
* @memberof module:openmct.ViewProvider#
* @param {module:openmct.DomainObject} domainObject the domain object
* to be viewed
* @returns {module:openmct.View} a view of this domain object
*/
/**
* Get metadata associated with this view provider. This may be used
* to populate the user interface with options associated with this
* view provider.
*
* @method metadata
* @memberof module:openmct.ViewProvider#
* @returns {module:openmct.ViewProvider~ViewMetadata} view metadata
*/
/**
* @typedef ViewMetadata
* @memberof module:openmct.ViewProvider~
* @property {string} name the human-readable name of this view
* @property {string} key a machine-readable name for this view
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of view
* @property {string} cssclass the CSS class to apply to labels for this
* view (to add icons, for instance)
*/
return View;
});

View File

@ -15,7 +15,6 @@ define([
) {
return {
Type: Type,
TimeConductor: new TimeConductor(),
View: View,
Objects: ObjectAPI,
Composition: CompositionAPI,

View File

@ -18,6 +18,15 @@ define([
});
};
/**
* Retrieve the composition (if any) of this domain object. The
* composition of a domain object is the list of other domain objects
* it "contains" (for instance, that should be displayed beneath it
* in the tree.)
* @method Composition
* @returns {module:openmct.CompositionCollection}
* @memberof module:openmct.MCT#
*/
function composition(object) {
var provider = getProvider(object);

View File

@ -8,6 +8,20 @@ define([
objectUtils
) {
/**
* A CompositionCollection represents the list of domain objects contained
* by another domain object. It provides methods for loading this
* list asynchronously, and for modifying this list.
*
* @class CompositionCollection
* @param {module:openmct.DomainObject} domainObject the domain object
* whose composition will be contained
* @param {module:openmct.CompositionProvider} provider the provider
* to use to retrieve other domain objects
* @memberof module:openmct
* @augments EventEmitter
*/
function CompositionCollection(domainObject, provider) {
EventEmitter.call(this);
this.domainObject = domainObject;
@ -38,16 +52,53 @@ define([
this.remove(child, true);
};
/**
* Get the index of a domain object within this composition. If the
* domain object is not contained here, -1 will be returned.
*
* A call to [load]{@link module:openmct.CompositionCollection#load}
* must have resolved before using this method.
*
* @param {module:openmct.DomainObject} child the domain object for which
* an index should be retrieved
* @returns {number} the index of that domain object
* @memberof module:openmct.CompositionCollection#
* @name indexOf
*/
CompositionCollection.prototype.indexOf = function (child) {
return _.findIndex(this._children, function (other) {
return objectUtils.equals(child, other);
});
};
/**
* Get the index of a domain object within this composition.
*
* A call to [load]{@link module:openmct.CompositionCollection#load}
* must have resolved before using this method.
*
* @param {module:openmct.DomainObject} child the domain object for which
* containment should be checked
* @returns {boolean} true if the domain object is contained here
* @memberof module:openmct.CompositionCollection#
* @name contains
*/
CompositionCollection.prototype.contains = function (child) {
return this.indexOf(child) !== -1;
};
/**
* Add a domain object to this composition.
*
* A call to [load]{@link module:openmct.CompositionCollection#load}
* must have resolved before using this method.
*
* @param {module:openmct.DomainObject} child the domain object to add
* @param {boolean} skipMutate true if the underlying provider should
* not be updated
* @memberof module:openmct.CompositionCollection#
* @name add
*/
CompositionCollection.prototype.add = function (child, skipMutate) {
if (!this._children) {
throw new Error("Must load composition before you can add!");
@ -69,6 +120,14 @@ define([
}
};
/**
* Load the domain objects in this composition.
*
* @returns {Promise.<Array.<module:openmct.DomainObject>>} a promise for
* the domain objects in this composition
* @memberof {module:openmct.CompositionCollection#}
* @name load
*/
CompositionCollection.prototype.load = function () {
return this.provider.load(this.domainObject)
.then(function (children) {
@ -81,6 +140,18 @@ define([
}.bind(this));
};
/**
* Remove a domain object from this composition.
*
* A call to [load]{@link module:openmct.CompositionCollection#load}
* must have resolved before using this method.
*
* @param {module:openmct.DomainObject} child the domain object to remove
* @param {boolean} skipMutate true if the underlying provider should
* not be updated
* @memberof module:openmct.CompositionCollection#
* @name remove
*/
CompositionCollection.prototype.remove = function (child, skipMutate) {
if (!this.contains(child)) {
if (skipMutate) {
@ -97,10 +168,23 @@ define([
}
};
/**
* Check if this composition can contain this domain object.
* @name canContain
* @memberof module:openmct.CompositionCollection
* @param {module:openmct.DomainObject} the domain object to contain
* @returns {boolean} true if containment is allowed
*/
CompositionCollection.prototype.canContain = function (domainObject) {
return this.provider.canContain(this.domainObject, domainObject);
};
/**
* Stop using this composition collection. This will release any resources
* associated with this collection.
* @name destroy
* @memberof module:openmct.CompositionCollection
*/
CompositionCollection.prototype.destroy = function () {
if (this.provider.off) {
this.provider.off(

View File

@ -9,6 +9,14 @@ define([
ObjectAPI,
objectUtils
) {
/**
* A CompositionProvider provides the underlying implementation of
* composition-related behavior for certain types of domain object.
*
* @interface CompositionProvider
* @memberof module:openmct
* @augments EventEmitter
*/
function makeEventName(domainObject, event) {
return event + ':' + objectUtils.makeKeyString(domainObject.key);
@ -21,10 +29,30 @@ define([
DefaultCompositionProvider.prototype =
Object.create(EventEmitter.prototype);
/**
* Check if this provider should be used to load composition for a
* particular domain object.
* @param {module:openmct.DomainObject} domainObject the domain object
* to check
* @returns {boolean} true if this provider can provide
* composition for a given domain object
* @memberof {module:openmct.CompositionProvider#}
* @name appliesTo
*/
DefaultCompositionProvider.prototype.appliesTo = function (domainObject) {
return !!domainObject.composition;
};
/**
* Load any domain objects contained in the composition of this domain
* object.
* @param {module:openmct.DomainObjcet} domainObject the domain object
* for which to load composition
* @returns {Promise.<Array.<module:openmct.DomainObject>>} a promise for
* the domain objects in this composition
* @memberof {module:openmct.CompositionProvider#}
* @name load
*/
DefaultCompositionProvider.prototype.load = function (domainObject) {
return Promise.all(domainObject.composition.map(ObjectAPI.get));
};
@ -59,10 +87,27 @@ define([
);
};
/**
* Check if one domain object can contain another.
* @param {module:openmct.DomainObject} domainObject the domain object
* which will act as the container
* @param {module:openmct.DomainObject} child the domain object to be
* contained
* @returns {boolean} true if this is allowed
*/
DefaultCompositionProvider.prototype.canContain = function (domainObject, child) {
return true;
};
/**
* Remove a domain object from another domain object's composition.
*
* @param {module:openmct.DomainObject} domainObject the domain object
* which should have its composition modified
* @param {module:openmct.DomainObject} child the domain object to remove
* @memberof module:openmct.CompositionProvider#
* @name remove
*/
DefaultCompositionProvider.prototype.remove = function (domainObject, child) {
// TODO: this needs to be synchronized via mutation
var index = domainObject.composition.indexOf(child);
@ -70,6 +115,15 @@ define([
this.emit(makeEventName(domainObject, 'remove'), child);
};
/**
* Add a domain object to another domain object's composition.
*
* @param {module:openmct.DomainObject} domainObject the domain object
* which should have its composition modified
* @param {module:openmct.DomainObject} child the domain object to add
* @memberof module:openmct.CompositionProvider#
* @name add
*/
DefaultCompositionProvider.prototype.add = function (domainObject, child) {
// TODO: this needs to be synchronized via mutation
domainObject.composition.push(child.key);

View File

@ -5,7 +5,6 @@ define([
_,
objectEventEmitter
) {
var ANY_OBJECT_EVENT = "mutation";
/**
@ -13,7 +12,8 @@ define([
* setters for
* @param eventEmitter
* @param object
* @constructor
* @interface MutableObject
* @memberof module:openmct
*/
function MutableObject(object) {
this.object = object;
@ -30,12 +30,27 @@ define([
})
};
/**
* Observe changes to this domain object.
* @param {string} path the property to observe
* @param {Function} callback a callback to invoke when new values for
* this property are observed
* @method on
* @memberof module:openmct.MutableObject#
*/
MutableObject.prototype.on = function(path, callback) {
var fullPath = qualifiedEventName(this.object, path);
objectEventEmitter.on(fullPath, callback);
this.unlisteners.push(objectEventEmitter.off.bind(objectEventEmitter, fullPath, callback));
};
/**
* Modify this domain object.
* @param {string} path the property to modify
* @param {*} value the new value for this property
* @method set
* @memberof module:openmct.MutableObject#
*/
MutableObject.prototype.set = function (path, value) {
_.set(this.object, path, value);

View File

@ -13,6 +13,12 @@ define([
A new Object API.
*/
/**
* Utilities for loading, saving, and manipulating domain objects.
* @interface ObjectAPI
* @memberof module:openmct
* @implements {module:openmct.ObjectProvider}
*/
var Objects = {},
ROOT_REGISTRY = [],
PROVIDER_REGISTRY = {},
@ -41,10 +47,61 @@ define([
return PROVIDER_REGISTRY[key.namespace] || FALLBACK_PROVIDER;
}
/**
* Register a new object provider for a particular namespace.
*
* @param {string} namespace the namespace for which to provide objects
* @param {module:openmct.ObjectProvider} provider the provider which
* will handle loading domain objects from this namespace
* @memberof {module:openmct.ObjectAPI#}
* @name addProvider
*/
Objects.addProvider = function (namespace, provider) {
PROVIDER_REGISTRY[namespace] = provider;
};
/**
* Provides the ability to read, write, and delete domain objects.
*
* When registering a new object provider, all methods on this interface
* are optional.
*
* @interface ObjectProvider
* @memberof module:openmct
*/
/**
* Save this domain object in its current state.
*
* @method save
* @memberof module:openmct.ObjectProvider#
* @param {module:openmct.DomainObject} domainObject the domain object to
* save
* @returns {Promise} a promise which will resolve when the domain object
* has been saved, or be rejected if it cannot be saved
*/
/**
* Delete this domain object.
*
* @method delete
* @memberof module:openmct.ObjectProvider#
* @param {module:openmct.DomainObject} domainObject the domain object to
* delete
* @returns {Promise} a promise which will resolve when the domain object
* has been deleted, or be rejected if it cannot be deleted
*/
/**
* Get a domain object.
*
* @method get
* @memberof module:openmct.ObjectProvider#
* @param {string} key the key for the domain object to load
* @returns {Promise} a promise which will resolve when the domain object
* has been saved, or be rejected if it cannot be saved
*/
[
'save',
'delete',
@ -66,10 +123,24 @@ define([
};
});
/**
* Add a root-level object.
* @param {module:openmct.Identifier} id the identifier of the root-level
* object to add.
* @method addRoot
* @memberof module:openmct.ObjectAPI#
*/
Objects.addRoot = function (key) {
ROOT_REGISTRY.unshift(key);
};
/**
* Remove a root-level object.
* @param {module:openmct.Identifier} id the identifier of the root-level
* object to remove.
* @method removeRoot
* @memberof module:openmct.ObjectAPI#
*/
Objects.removeRoot = function (key) {
ROOT_REGISTRY = ROOT_REGISTRY.filter(function (k) {
return (
@ -79,9 +150,28 @@ define([
});
};
/**
* Get an interface for observing and mutating this domain object.
* @param {module:openmct.DomainObject} object the object to mutate
* @returns {module:openmct.MutableObject} an interface for mutating
* and observing this domain object
* @method getMutable
* @memberof module:openmct.ObjectAPI#
*/
Objects.getMutable = function (object) {
return new MutableObject(object);
};
/**
* Uniquely identifies a domain object.
*
* @typedef Identifier
* @memberof module:openmct
* @property {string} namespace the namespace to/from which this domain
* object should be loaded/stored.
* @property {string} key a unique identifier for the domain object
* within that namespace
*/
return Objects;
});

View File

@ -0,0 +1,71 @@
# Telemetry API - Overview
The Telemetry API provides basic methods for retrieving historical and realtime telemetry data, retrieving telemetry metadata, and registering additional telemetry providers.
The Telemetry API also provides a set of helpers built upon these basics-- TelemetryFormatters help you format telemetry values for display purposes, LimitEvaluators help you display evaluate and display alarm states, while TelemetryCollections provide a method for seamlessly combining historical and realtime data, while supporting more advanced client side filtering and interactivity.
## Getting Telemetry Data
### `MCT.telemetry.request(domainObject, options)`
Request historical telemetry for a domain object. Options allows you to specify filters (start, end, etc.), sort order, and strategies for retrieving telemetry (aggregation, latest available, etc.).
Returns a `Promise` for an array of telemetry values.
### `MCT.telemetry.subscribe(domainObject, callback, options)`
Subscribe to realtime telemetry for a specific domain object. callback will be called whenever data is received from a realtime provider. Options allows you to specify ???
## Understanding Telemetry
### `MCT.telemetry.getMetadata(domainObject)`
Retrieve telemetry metadata for a domain object. Telemetry metadata helps you understand the sort of telemetry data a domain object can provide-- for instances, the possible enumerations or states, the units, and more.
### `MCT.telemetry.Formatter`
Telemetry formatters help you format telemetry values for display. Under the covers, they use telemetry metadata to interpret your telemetry data, and then they use the format API to format that data for display.
### `MCT.telemetry.LimitEvaluator`
Limit Evaluators help you evaluate limit and alarm status of individual telemetry datums for display purposes without having to interact directly with the Limit API.
## Adding new telemetry sources
### `MCT.telemetry.registerProvider(telemetryProvider)`
Register a telemetry provider with the telemetry service. This allows you to connect alternative telemetry sources to For more information, see the `MCT.telemetry.BaseProvider`
### `MCT.telemetry.BaseProvider`
The base provider is a great starting point for developers who would like to implement their own telemetry provider. At the same time, you can implement your own telemetry provider as long as it meets the TelemetryProvider (see other docs).
## Other tools
### `MCT.telemetry.TelemetryCollection`
The TelemetryCollection is a useful tool for building advanced displays. It helps you seamlessly handle both historical and realtime telemetry data, while making it easier to deal with large data sets and interactive displays that need to frequently requery data.
# API Reference (TODO)
* Telemetry Metadata
* Request Options
-- start
-- end
-- sort
-- ???
-- strategies -- specify which strategies you want. an array provides for fallback strategies without needing decoration. Design fallbacks into API.
### `MCT.telemetry.request(domainObject, options)`
### `MCT.telemetry.subscribe(domainObject, callback, options)`
### `MCT.telemetry.getMetadata(domainObject)`
### `MCT.telemetry.Formatter`
### `MCT.telemetry.LimitEvaluator`
### `MCT.telemetry.registerProvider(telemetryProvider)`
### `MCT.telemetry.BaseProvider`
### `MCT.telemetry.TelemetryCollection`

View File

@ -0,0 +1,295 @@
/*global define,window,console,MCT*/
/**
var key = '114ced6c-deb7-4169-ae71-68c571665514';
MCT.objects.getObject([key])
.then(function (results) {
console.log('got results');
return results[key];
})
.then(function (domainObject) {
console.log('got object');
MCT.telemetry.subscribe(domainObject, function (datum) {
console.log('gotData!', datum);
});
});
});
*/
define([
'lodash',
'eventemitter2'
], function (
_,
EventEmitter
) {
// format map is a placeholder until we figure out format service.
var FORMAT_MAP = {
generic: function (range) {
return function (datum) {
return datum[range.key];
};
},
enum: function (range) {
var enumMap = _.indexBy(range.enumerations, 'value');
return function (datum) {
try {
return enumMap[datum[range.valueKey]].text;
} catch (e) {
return datum[range.valueKey];
}
};
}
};
FORMAT_MAP.number =
FORMAT_MAP.float =
FORMAT_MAP.integer =
FORMAT_MAP.ascii =
FORMAT_MAP.generic;
/**
* An interface for retrieving telemetry data associated with a domain
* object.
* @interface TelemetryAPI
* @memberof module:openmct
*/
function TelemetryAPI(
formatService
) {
var FORMATTER_CACHE = new WeakMap(),
EVALUATOR_CACHE = new WeakMap();
function testAPI() {
var key = '114ced6c-deb7-4169-ae71-68c571665514';
window.MCT.objects.getObjects([key])
.then(function (results) {
console.log('got results');
return results[key];
})
.then(function (domainObject) {
var formatter = new MCT.telemetry.Formatter(domainObject);
console.log('got object');
window.MCT.telemetry.subscribe(domainObject, function (datum) {
var formattedValues = {};
Object.keys(datum).forEach(function (key) {
formattedValues[key] = formatter.format(datum, key);
});
console.log(
'datum:',
datum,
'formatted:',
formattedValues
);
});
});
}
function getFormatter(range) {
if (FORMAT_MAP[range.type]) {
return FORMAT_MAP[range.type](range);
}
try {
var format = formatService.getFormat(range.type).format.bind(
formatService.getFormat(range.type)
),
formatter = function (datum) {
return format(datum[range.key]);
};
return formatter;
} catch (e) {
console.log('could not retrieve format', range, e, e.message);
return FORMAT_MAP.generic(range);
}
}
function TelemetryFormatter(domainObject) {
this.metadata = domainObject.getCapability('telemetry').getMetadata();
this.formats = {};
var ranges = this.metadata.ranges.concat(this.metadata.domains);
ranges.forEach(function (range) {
this.formats[range.key] = getFormatter(range);
}, this);
}
/**
* Retrieve the 'key' from the datum and format it accordingly to
* telemetry metadata in domain object.
*/
TelemetryFormatter.prototype.format = function (datum, key) {
return this.formats[key](datum);
};
function LimitEvaluator(domainObject) {
this.domainObject = domainObject;
this.evaluator = domainObject.getCapability('limit');
if (!this.evaluator) {
this.evalute = function () {
return '';
}
}
}
/** TODO: Do we need a telemetry parser, or do we assume telemetry
is numeric by default? */
LimitEvaluator.prototype.evaluate = function (datum, key) {
return this.evaluator.evaluate(datum, key);
};
/** Basic telemetry collection, needs more magic. **/
function TelemetryCollection(domainObject) {
this.domainObject = domainObject;
this.data = [];
}
_.extend(TelemetryCollection.prototype, EventEmitter.prototype);
TelemetryCollection.prototype.request = function (options) {
request(this.domainObject, options).then(function (data) {
data.forEach(function (datum) {
this.addDatum(datum);
}, this);
}.bind(this));
};
TelemetryCollection.prototype.addDatum = function (datum) {
this.data.push(datum);
this.emit('add', datum);
};
TelemetryCollection.prototype.subscribe = function (options) {
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
this.unsubscribe = subscribe(
this.domainObject,
function (telemetrySeries) {
telemetrySeries.getData().forEach(this.addDatum, this);
}.bind(this),
options
);
};
function registerProvider(provider) {
// Not yet implemented.
console.log('registering provider', provider);
}
function registerEvaluator(evaluator) {
// not yet implemented.
console.log('registering evaluator', evaluator);
}
function request(domainObject, options) {
return domainObject.getCapability('telemetry')
.requestData(options)
.then(function (telemetrySeries) {
return telemetrySeries.getData();
});
}
function subscribe(domainObject, callback, options) {
return domainObject.getCapability('telemetry')
.subscribe(function (series) {
series.getData().forEach(callback);
}, options);
}
var Telemetry = {
/**
* Register a new telemetry provider.
* @method registerProvider
* @memberof module:openmct.TelemetryAPI#
*/
registerProvider: registerProvider,
/**
* Register a new limit evaluator.
* @method registerEvaluator
* @memberof module:openmct.TelemetryAPI#
*/
registerEvaluator: registerEvaluator,
/**
* Request historical telemetry data.
* @method request
* @memberof module:openmct.TelemetryAPI#
*/
request: request,
/**
* Subscribe to updates to telemetry data.
* @method subscribe
* @memberof module:openmct.TelemetryAPI#
*/
subscribe: subscribe,
/**
* Get metadata associated with telemetry for this domain object.
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to request telemetry
* @returns {module:openmct.TelemetryAPI~TelemetryMetadata}
* telemetry metadata
* @method getMetadata
* @memberof module:openmct.TelemetryAPI#
*/
getMetadata: function (domainObject) {
return domainObject.getCapability('telemetry').getMetadata();
},
/**
* Get a telemetry formatter for this domain object.
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to format telemetry
* @returns {module:openmct.TelemetryAPI~TelemetryFormatter}
* @method Formatter
* @memberof module:openmct.TelemetryAPI#
*/
Formatter: function (domainObject) {
if (!FORMATTER_CACHE.has(domainObject)) {
FORMATTER_CACHE.set(
domainObject,
new TelemetryFormatter(domainObject)
);
}
return FORMATTER_CACHE.get(domainObject);
},
/**
* Get a limit evaluator for this domain object.
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to evaluate limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method LimitEvaluator
* @memberof module:openmct.TelemetryAPI#
*/
LimitEvaluator: function (domainObject) {
if (!EVALUATOR_CACHE.has(domainObject)) {
EVALUATOR_CACHE.set(
domainObject,
new LimitEvaluator(domainObject)
);
}
return EVALUATOR_CACHE.get(domainObject);
}
};
window.MCT = window.MCT || {};
window.MCT.telemetry = Telemetry;
window.testAPI = testAPI;
return Telemetry;
}
return TelemetryAPI;
});

View File

@ -0,0 +1,46 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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([
'./TelemetryAPI',
'legacyRegistry'
], function (
TelemetryAPI,
legacyRegistry
) {
legacyRegistry.register('api/telemetry-api', {
name: 'Telemetry API',
description: 'The public Telemetry API',
extensions: {
runs: [
{
key: "TelemetryAPI",
implementation: TelemetryAPI,
depends: [
'formatService'
]
}
]
}
});
});

View File

@ -1,4 +1,12 @@
define(['text!./dialog.html', 'zepto'], function (dialogTemplate, $) {
/**
* A dialog may be displayed to show blocking content to users.
* @param {module:openmct.View} view the view to show in the dialog
* @param {string [title] the title for this dialog
* @constructor
* @memberof module:openmct
*/
function Dialog(view, title) {
this.view = view;
this.title = title;
@ -6,6 +14,13 @@ define(['text!./dialog.html', 'zepto'], function (dialogTemplate, $) {
this.enabledState = true;
}
/**
* Display this dialog.
* @returns {Promise} a promise that will be resolved if the user
* chooses "OK", an rejected if the user chooses "cancel"
* @method show
* @memberof module:openmct.Dialog#
*/
Dialog.prototype.show = function () {
if (this.showing) {
throw new Error("Dialog already showing.");

16
src/openmct.js Normal file
View File

@ -0,0 +1,16 @@
define(['./MCT', './api/Type'], function (MCT, Type) {
/**
* Open MCT is an extensible web application for building mission
* control user interfaces. This module is itself an instance of
* [MCT]{@link module:openmct.MCT}, which provides an interface for
* configuring and executing the application.
*
* @exports openmct
*/
var openmct = new MCT();
openmct.MCT = MCT;
openmct.Type = Type;
return openmct;
});