mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 07:16:39 +00:00
c5161887e2
Change label to name, log a warning to console if a typeDef is registered with a label. Fixes https://github.com/nasa/openmct/issues/1568.
863 lines
36 KiB
Markdown
863 lines
36 KiB
Markdown
# Building Applications With Open MCT
|
|
|
|
## Scope and purpose of this document
|
|
|
|
This document is intended to serve as a reference for developing an application
|
|
based on Open MCT. It will provide details of the API functions necessary to extend the
|
|
Open MCT platform meet common use cases such as integrating with a telemetry source.
|
|
|
|
The best place to start is with the [Open MCT Tutorials](https://github.com/nasa/openmct-tutorial).
|
|
These will walk you through the process of getting up and running with Open MCT,
|
|
as well as addressing some common developer use cases.
|
|
|
|
## Building From Source
|
|
|
|
The latest version of Open MCT is available from [our GitHub repository](https://github.com/nasa/openmct).
|
|
If you have `git`, and `node` installed, you can build Open MCT with the commands
|
|
```
|
|
git clone https://github.com/nasa/openmct.git
|
|
cd openmct
|
|
npm install
|
|
```
|
|
|
|
These commands will fetch the Open MCT source from our GitHub repository, and build
|
|
a minified version that can be included in your application. The output of the
|
|
build process is placed in a `dist` folder under the openmct source directory,
|
|
which can be copied out to another location as needed. The contents of this
|
|
folder will include a minified javascript file named `openmct.js` as well as
|
|
assets such as html, css, and images necessary for the UI.
|
|
|
|
## Starting an Open MCT application
|
|
|
|
To start a minimally functional Open MCT application, it is necessary to include
|
|
the Open MCT distributable, enable some basic plugins, and bootstrap the application.
|
|
The tutorials walk through the process of getting Open MCT up and running from scratch,
|
|
but provided below is a minimal HTML template that includes Open MCT, installs
|
|
some basic plugins, and bootstraps the application. It assumes that Open MCT is
|
|
installed under an `openmct` subdirectory, as described in [Building From Source](#building-from-source).
|
|
|
|
This approach includes openmct using a simple script tag, resulting in a global
|
|
variable named `openmct`. This `openmct` object is used subsequently to make API
|
|
calls.
|
|
|
|
Open MCT is packaged as a UMD (Universal Module Definition) module, so common
|
|
script loaders are also supported.
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Open MCT</title>
|
|
<script src="openmct.js"></script>
|
|
</head>
|
|
<body>
|
|
<script>
|
|
openmct.setAssetPath('openmct/dist');
|
|
openmct.install(openmct.plugins.LocalStorage());
|
|
openmct.install(openmct.plugins.MyItems());
|
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
|
openmct.install(openmct.plugins.Espresso());
|
|
openmct.start();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
The Open MCT library included above requires certain assets such as html templates,
|
|
images, and css. If you installed Open MCT from GitHub as described in the section
|
|
on [Building from Source](#building-from-source) then these assets will have been
|
|
downloaded along with the Open MCT javascript library. You can specify the
|
|
location of these assets by calling `openmct.setAssetPath()`. Typically this will
|
|
be the same location as the `openmct.js` library is included from.
|
|
|
|
There are some plugins bundled with the application that provide UI, persistence,
|
|
and other default configuration which are necessary to be able to do anything with
|
|
the application initially. Any of these plugins can, in principle, be replaced with a custom
|
|
plugin. The included plugins are documented in the [Included Plugins](#included-plugins)
|
|
section.
|
|
|
|
## Plugins
|
|
|
|
### Defining and Installing a New Plugin
|
|
|
|
```javascript
|
|
openmct.install(function install(openmctAPI) {
|
|
// Do things here
|
|
// ...
|
|
});
|
|
```
|
|
|
|
New plugins are installed in Open MCT by calling `openmct.install`, and providing
|
|
a plugin installation function. This function will be invoked on application
|
|
startup with one parameter - the openmct API object. A common approach used in
|
|
the Open MCT codebase is to define a plugin as a function that returns this
|
|
installation function. This allows configuration to be specified when the plugin is included.
|
|
|
|
eg.
|
|
```javascript
|
|
openmct.install(openmct.plugins.Elasticsearch("http://localhost:8002/openmct"));
|
|
```
|
|
This approach can be seen in all of the [plugins provided with Open MCT](https://github.com/nasa/openmct/blob/master/src/plugins/plugins.js).
|
|
|
|
## Domain Objects and Identifiers
|
|
|
|
_Domain Objects_ are the basic entities that represent domain knowledge in Open MCT.
|
|
The temperature sensor on a solar panel, an overlay plot comparing
|
|
the results of all temperature sensors, the command dictionary for a spacecraft,
|
|
the individual commands in that dictionary, the "My Items" folder:
|
|
All of these things are domain objects.
|
|
|
|
A _Domain Object_ is simply a javascript object with some standard attributes.
|
|
An example of a _Domain Object_ is the "My Items" object which is a folder in
|
|
which a user can persist any objects that they create. The My Items object
|
|
looks like this:
|
|
|
|
```javascript
|
|
{
|
|
identifier: {
|
|
namespace: ""
|
|
key: "mine"
|
|
}
|
|
name:"My Items",
|
|
type:"folder",
|
|
location:"ROOT",
|
|
composition: []
|
|
}
|
|
```
|
|
|
|
### Object Attributes
|
|
|
|
The main attributes to note are the `identifier`, and `type` attributes.
|
|
* `identifier`: A composite key that provides a universally unique identifier for
|
|
this object. The `namespace` and `key` are used to identify the object. The `key`
|
|
must be unique within the namespace.
|
|
* `type`: All objects in Open MCT have a type. Types allow you to form an
|
|
ontology of knowledge and provide an abstraction for grouping, visualizing, and
|
|
interpreting data. Details on how to define a new object type are provided below.
|
|
|
|
Open MCT uses a number of builtin types. Typically you are going to want to
|
|
define your own if extending Open MCT.
|
|
|
|
### Domain Object Types
|
|
|
|
Custom types may be registered via the `addType` function on the opencmt Type
|
|
registry.
|
|
|
|
eg.
|
|
```javascript
|
|
openmct.types.addType('my-type', {
|
|
name: "My Type",
|
|
description: "This is a type that I added!",
|
|
creatable: true
|
|
});
|
|
```
|
|
|
|
The `addType` function accepts two arguments:
|
|
* A `string` key identifying the type. This key is used when specifying a type
|
|
for an object.
|
|
* An object type specification. An object type definition supports the following
|
|
attributes
|
|
* `name`: a `string` naming this object type
|
|
* `description`: a `string` specifying a longer-form description of this type
|
|
* `initialize`: a `function` which initializes the model for new domain objects
|
|
of this type. This can be used for setting default values on an object when
|
|
it is instantiated.
|
|
* `creatable`: A `boolean` indicating whether users should be allowed to create
|
|
this type (default: `false`). This will determine whether the type appears
|
|
in the `Create` menu.
|
|
* `cssClass`: A `string` specifying a CSS class to apply to each representation
|
|
of this object. This is used for specifying an icon to appear next to each
|
|
object of this type.
|
|
|
|
The [Open MCT Tutorials](https://github.com/openmct/openmct-tutorial) provide a
|
|
step-by-step examples of writing code for Open MCT that includes a [section on
|
|
defining a new object type](https://github.com/nasa/openmct-tutorial#step-3---providing-objects).
|
|
|
|
## Root Objects
|
|
|
|
In many cases, you'd like a certain object (or a certain hierarchy of objects)
|
|
to be accessible from the top level of the application (the tree on the left-hand
|
|
side of Open MCT.) For example, it is typical to expose a telemetry dictionary
|
|
as a hierarchy of telemetry-providing domain objects in this fashion.
|
|
|
|
To do so, use the `addRoot` method of the object API.
|
|
|
|
eg.
|
|
```javascript
|
|
openmct.objects.addRoot({
|
|
namespace: "my-namespace",
|
|
key: "my-key"
|
|
});
|
|
```
|
|
|
|
The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
|
|
as an argument.
|
|
|
|
Root objects are loaded just like any other objects, i.e. via an object
|
|
provider.
|
|
|
|
## Object Providers
|
|
|
|
An Object Provider is used to build _Domain Objects_, typically retrieved from
|
|
some source such as a persistence store or telemetry dictionary. In order to
|
|
integrate telemetry from a new source an object provider will need to be created
|
|
that can build objects representing telemetry points exposed by the telemetry
|
|
source. The API call to define a new object provider is fairly straightforward.
|
|
Here's a very simple example:
|
|
|
|
```javascript
|
|
openmct.objects.addProvider('example.namespace', {
|
|
get: function (identifier) {
|
|
return Promise.resolve({
|
|
identifier: identifier,
|
|
name: 'Example Object',
|
|
type: 'example-object-type'
|
|
});
|
|
}
|
|
});
|
|
```
|
|
The `addProvider` function takes two arguments:
|
|
|
|
* `namespace`: A `string` representing the namespace that this object provider
|
|
will provide objects for.
|
|
* `provider`: An `object` with a single function, `get`. This function accepts an
|
|
[Identifier](#domain-objects-and-identifiers) for the object to be provided.
|
|
It is expected that the `get` function will return a
|
|
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
|
that resolves with the object being requested.
|
|
|
|
In future, object providers will support other methods to enable other operations
|
|
with persistence stores, such as creating, updating, and deleting objects.
|
|
|
|
## Composition Providers
|
|
|
|
The _composition_ of a domain object is the list of objects it contains, as shown
|
|
(for example) in the tree for browsing. Open MCT provides a
|
|
[default solution](#default-composition-provider) for composition, but there
|
|
may be cases where you want to provide the composition of a certain object
|
|
(or type of object) dynamically.
|
|
|
|
### Adding Composition Providers
|
|
|
|
You may want to populate a hierarchy under a custom root-level object based on
|
|
the contents of a telemetry dictionary. To do this, you can add a new
|
|
Composition Provider:
|
|
|
|
```javascript
|
|
openmct.composition.addProvider({
|
|
appliesTo: function (domainObject) {
|
|
return domainObject.type === 'my-type';
|
|
},
|
|
load: function (domainObject) {
|
|
return Promise.resolve(myDomainObjects);
|
|
}
|
|
});
|
|
```
|
|
The `addProvider` function accepts a Composition Provider object as its sole
|
|
argument. A Composition Provider is a javascript object exposing two functions:
|
|
* `appliesTo`: A `function` that accepts a `domainObject` argument, and returns
|
|
a `boolean` value indicating whether this composition provider applies to the
|
|
given object.
|
|
* `load`: A `function` that accepts a `domainObject` as an argument, and returns
|
|
a `Promise` that resolves with an array of [Identifier](#domain-objects-and-identifiers).
|
|
These identifiers will be used to fetch Domain Objects from an [Object Provider](#object-provider)
|
|
|
|
### Default Composition Provider
|
|
|
|
The default composition provider applies to any domain object with a `composition`
|
|
property. The value of `composition` should be an array of identifiers, e.g.:
|
|
|
|
```javascript
|
|
var domainObject = {
|
|
name: "My Object",
|
|
type: 'folder',
|
|
composition: [
|
|
{
|
|
id: '412229c3-922c-444b-8624-736d85516247',
|
|
namespace: 'foo'
|
|
},
|
|
{
|
|
key: 'd6e0ce02-5b85-4e55-8006-a8a505b64c75',
|
|
namespace: 'foo'
|
|
}
|
|
]
|
|
};
|
|
```
|
|
|
|
## Telemetry Providers
|
|
|
|
When connecting to a new telemetry source, you will need to register a new
|
|
_Telemetry Provider_. A _Telemetry Provider_ retrieves telemetry data from some telemetry
|
|
source, and exposes them in a way that can be used by Open MCT. A telemetry
|
|
provider typically can support a one off __request__ for a batch of telemetry data,
|
|
or it can provide the ability to __subscribe__ to receive new telemetry data when
|
|
it becomes available, or both.
|
|
|
|
```javascript
|
|
openmct.telemetry.addProvider({
|
|
supportsRequest: function (domainObject) {
|
|
//...
|
|
},
|
|
supportsSubscribe: function (domainObject) {
|
|
//...
|
|
},
|
|
request: function (domainObject, options) {
|
|
//...
|
|
},
|
|
subscribe: function (domainObject, callback, options) {
|
|
//...
|
|
}
|
|
})
|
|
```
|
|
|
|
A telemetry provider is an object with the following functions defined:
|
|
|
|
* `supportsRequest`: An __optional__ `function` that accepts a
|
|
[Domain Object](#domain-objects-and-identifiers) and returns a `boolean` value
|
|
indicating whether or not this provider supports telemetry requests for the
|
|
given object. If this returns `true` then a `request` function must be defined.
|
|
* `supportsSubscribe`: An __optional__ `function` that accepts a
|
|
[Domain Object](#domain-objects-and-identifiers) and returns a `boolean` value
|
|
indicating whether or not this provider supports telemetry subscriptions. If this
|
|
returns `true` then a `subscribe` function must also be defined. As with `request`,
|
|
the return value will typically be conditional, and based on attributes of
|
|
`domainObject` such as its identifier.
|
|
* `request`: A `function` that returns a `Promise` that will resolve with an `Array`
|
|
of telemetry in a single query. This function accepts as arguments a
|
|
[Domain Object](#domain-objects-and-identifiers) and an object containing some
|
|
[request options](#telemetry-requests).
|
|
* `subscribe`: A `function` that accepts a [Domain Object](#domain-objects-and-identifiers),
|
|
a callback `function`, and a [telemetry request](#telemetry-requests). The
|
|
callback is invoked whenever telemetry is available, and
|
|
|
|
|
|
The implementations for `request` and `subscribe` can vary depending on the
|
|
nature of the endpoint which will provide telemetry. In the example above,
|
|
it is assumed that `myAdapter` contains the implementation details
|
|
(such as HTTP requests, WebSocket connections, etc.) associated with some telemetry
|
|
source.
|
|
|
|
For a step-by-step guide to building a telemetry adapter, please see the
|
|
[Open MCT Tutorials](https://github.com/larkin/openmct-tutorial).
|
|
|
|
### Telemetry Requests
|
|
Telemetry requests support time bounded queries. A call to a _Telemetry Provider_'s
|
|
`request` function will include an `options` argument. These are simply javascript
|
|
objects with attributes for the request parameters. An example of a telemetry
|
|
request object with a start and end time is included below:
|
|
```javascript
|
|
{
|
|
start: 1487981997240,
|
|
end: 1487982897240
|
|
}
|
|
```
|
|
|
|
|
|
### Telemetry Formats
|
|
|
|
Telemetry format objects define how to interpret and display telemetry data.
|
|
They have a simple structure:
|
|
|
|
* `key`: A `string` that uniquely identifies this formatter.
|
|
* `format`: A `function` that takes a raw telemetry value, and returns a human-readable
|
|
`string` representation of that value. It has one required argument, and three
|
|
optional arguments that provide context and can be used for returning scaled
|
|
representations of a value. An example of this is representing time values in a
|
|
scale such as the time conductor scale. There are multiple ways of representing
|
|
a point in time, and by providing a minimum scale value, maximum scale value,
|
|
and a count, it's possible to provide more useful representations of time given
|
|
the provided limitations.
|
|
* `value`: The raw telemetry value in its native type.
|
|
* `minValue`: An __optional__ argument specifying the minimum displayed value.
|
|
* `maxValue`: An __optional__ argument specifying the maximum displayed value.
|
|
* `count`: An __optional__ argument specifying the number of displayed values.
|
|
* `parse`: A `function` that takes a `string` representation of a telemetry value,
|
|
and returns the value in its native type. It accepts one argument:
|
|
* `text`: A `string` representation of a telemetry value.
|
|
* `validate`: A `function` that takes a `string` representation of a telemetry
|
|
value, and returns a `boolean` value indicating whether the provided string can be
|
|
parsed.
|
|
|
|
#### Registering Formats
|
|
|
|
Formats are registered with the Telemetry API using the `addFormat` function. eg.
|
|
|
|
``` javascript
|
|
openmct.telemetry.addFormat({
|
|
key: 'number-to-string',
|
|
format: function (number) {
|
|
return number + '';
|
|
},
|
|
parse: function (text) {
|
|
return Number(text);
|
|
},
|
|
validate: function (text) {
|
|
return !isNaN(text);
|
|
}
|
|
});
|
|
```
|
|
|
|
#### Examples of Formats in Use
|
|
* The [NumberFormat](https://github.com/nasa/openmct/blob/time-api-redo/platform/features/conductor/core/src/ui/NumberFormat.js)
|
|
provides an example of a simple format available by default
|
|
in OpenMCT.
|
|
* The [UTCTimeFormat](https://github.com/nasa/openmct/blob/master/src/plugins/utcTimeSystem/UTCTimeFormat.js)
|
|
is a more complex implementation of a format that uses the optional context
|
|
arguments in `format` to provide scale-appropriate values.
|
|
|
|
### Telemetry Data
|
|
|
|
Telemetry data is provided to Open MCT by _[Telemetry Providers](#telemetry-providers)_
|
|
in the form of javascript objects. A collection of telemetry values (for example,
|
|
retrieved in response to a `request`) is represented by an `Array` of javascript
|
|
objects. These telemetry javascript objects are simply key value pairs.
|
|
|
|
Typically a telemetry datum will have some timestamp associated with it. This
|
|
time stamp should have a key that corresponds to some time system supported by
|
|
Open MCT. If the `UTCTimeSystem` plugin is installed, then the key `utc` can be used.
|
|
|
|
An example of a telemetry provider request function that returns a collection of
|
|
mock telemtry data is below:
|
|
|
|
```javascript
|
|
openmct.telemetry.addProvider({
|
|
supportsRequest: function (domainObject) {
|
|
return true
|
|
},
|
|
request: function (domainObject, options) {
|
|
return Promise.resolve([
|
|
{
|
|
'utc': Date.now() - 2000,
|
|
'value': 1,
|
|
},
|
|
{
|
|
'utc': Date.now() - 1000,
|
|
'value': 2,
|
|
},
|
|
{
|
|
'utc': Date.now(),
|
|
'value': 3,
|
|
}
|
|
]);
|
|
}
|
|
})
|
|
```
|
|
|
|
## Time API
|
|
Open MCT provides API for managing the temporal state of the application. Central
|
|
to this is the concept of "time bounds". Views in Open MCT will typically show
|
|
telemetry data for some prescribed date range, and the Time API provides a way
|
|
to centrally manage these bounds.
|
|
|
|
The Time API exposes a number of methods for querying and setting the temporal
|
|
state of the application, and emits events to inform listeners when the state changes.
|
|
|
|
Because the data displayed tends to be time domain data, Open MCT must always
|
|
have at least one time system installed and activated. When you download Open MCT,
|
|
it will be pre-configured to use the UTC time system, which is installed and
|
|
activated, along with other default plugins, in `index.html`. Installing and
|
|
activating a time system is simple, and is covered
|
|
[in the next section](#defining-and-registering-time-systems).
|
|
|
|
### Time Systems and Bounds
|
|
#### Defining and Registering Time Systems
|
|
The time bounds of an Open MCT application are defined as numbers, and a Time
|
|
System gives meaning and context to these numbers so that they can be correctly
|
|
interpreted. Time Systems are javscript objects that provide some information
|
|
about the current time reference frame. An example of defining and registering
|
|
a new time system is given below:
|
|
|
|
``` javascript
|
|
openmct.time.addTimeSystem({
|
|
key: 'utc',
|
|
name: 'UTC Time',
|
|
cssClass = 'icon-clock',
|
|
timeFormat = 'utc',
|
|
durationFormat = 'duration',
|
|
isUTCBased = true
|
|
});
|
|
```
|
|
|
|
The example above defines a new utc based time system. In fact, this time system
|
|
is configured and activated by default from `index.html` in the default
|
|
installation of Open MCT if you download the source from GitHub. Some details of
|
|
each of the required properties is provided below.
|
|
|
|
* `key`: A `string` that uniquely identifies this time system.
|
|
* `name`: A `string` providing a brief human readable label. If the [Time Conductor](#the-time-conductor)
|
|
plugin is enabled, this name will identify the time system in a dropdown menu.
|
|
* `cssClass`: A class name `string` that will be applied to the time system when
|
|
it appears in the UI. This will be used to represent the time system with an icon.
|
|
There are a number of built-in icon classes [available in Open MCT](https://github.com/nasa/openmct/blob/master/platform/commonUI/general/res/sass/_glyphs.scss),
|
|
or a custom class can be used here.
|
|
* `timeFormat`: A `string` corresponding to the key of a registered
|
|
[telemetry time format](#telemetry-formats). The format will be used for
|
|
displaying discrete timestamps from telemetry streams when this time system is
|
|
activated. If the [UTCTimeSystem](#included-plugins) is enabled, then the `utc`
|
|
format can be used if this is a utc-based time system
|
|
* `durationFormat`: A `string` corresponding to the key of a registered
|
|
[telemetry time format](#telemetry-formats). The format will be used for
|
|
displaying time ranges, for example `00:15:00` might be used to represent a time
|
|
period of fifteen minutes. These are used by the Time Conductor plugin to specify
|
|
relative time offsets. If the [UTCTimeSystem](#included-plugins) is enabled,
|
|
then the `duration` format can be used if this is a utc-based time system
|
|
* `isUTCBased`: A `boolean` that defines whether this time system represents
|
|
numbers in UTC terrestrial time.
|
|
|
|
#### Getting and Setting the Active Time System
|
|
|
|
Once registered, a time system can be activated using a key, or an instance of
|
|
the time system itself.
|
|
|
|
```javascript
|
|
openmct.time.timeSystem('utc');
|
|
```
|
|
|
|
A time system can be immediately activated upon registration:
|
|
|
|
```javascript
|
|
var utcTimeSystem = {
|
|
key: 'utc',
|
|
name: 'UTC Time',
|
|
cssClass = 'icon-clock',
|
|
timeFormat = 'utc',
|
|
durationFormat = 'duration',
|
|
isUTCBased = true
|
|
};
|
|
openmct.time.addTimeSystem(utcTimeSystem);
|
|
openmct.time.timeSystem(utcTimeSystem);
|
|
```
|
|
|
|
Setting the active time system will trigger a [time system event](#time-events).
|
|
|
|
### Time Bounds
|
|
|
|
The TimeAPI provides a getter/setter for querying and setting time bounds. Time
|
|
bounds are simply an object with a `start` and an end `end` attribute.
|
|
|
|
* `start`: A `number` representing a moment in time in the active [Time System](#defining-and-registering-time-systems).
|
|
This will be used as the beginning of the time period displayed by time-responsive
|
|
telemetry views.
|
|
* `end`: A `number` representing a moment in time in the active [Time System](#defining-and-registering-time-systems).
|
|
This will be used as the end of the time period displayed by time-responsive
|
|
telemetry views.
|
|
|
|
If invoked with bounds, it will set the new time bounds system-wide. If invoked
|
|
without any parameters, it will return the current application-wide time bounds.
|
|
|
|
``` javascript
|
|
const ONE_HOUR = 60 * 60 * 1000;
|
|
let now = Date.now();
|
|
openmct.time.bounds({start: now - ONE_HOUR, now);
|
|
```
|
|
|
|
To respond to bounds change events, simply register a callback against the `bounds`
|
|
event. For more information on the bounds event, please see the section on [Time Events](#time-events).
|
|
|
|
## Clocks
|
|
|
|
The Time API can be set to follow a clock source which will cause the bounds
|
|
to be updated automatically whenever the clock source "ticks". A clock is simply
|
|
an object that supports registration of listeners and periodically invokes its
|
|
listeners with a number. Open MCT supports registration of new clock sources that
|
|
tick on almost anything. A tick occurs when the clock invokes callback functions
|
|
registered by its listeners with a new time value.
|
|
|
|
An example of a clock source is the [LocalClock](https://github.com/nasa/openmct/blob/master/src/plugins/utcTimeSystem/LocalClock.js)
|
|
which emits the current time in UTC every 100ms. Clocks can tick on anything. For
|
|
example, a clock could be defined to provide the timestamp of any new data
|
|
received via a telemetry subscription. This would have the effect of advancing
|
|
the bounds of views automatically whenever data is received. A clock could also
|
|
be defined to tick on some remote timing source.
|
|
|
|
The values provided by clocks are simple `number`s, which are interpreted in the
|
|
context of the active [Time System](#defining-and-registering-time-systems).
|
|
|
|
### Defining and registering clocks
|
|
A clock is an object that defines certain required metadata and functions:
|
|
|
|
* `key`: A `string` uniquely identifying this clock. This can be used later to
|
|
reference the clock in places such as the [Time Conductor configuration](#time-conductor-configuration)
|
|
* `cssClass`: A `string` identifying a CSS class to apply to this clock when it's
|
|
displayed in the UI. This will be used to represent the time system with an icon.
|
|
There are a number of built-in icon classes [available in Open MCT](https://github.com/nasa/openmct/blob/master/platform/commonUI/general/res/sass/_glyphs.scss),
|
|
or a custom class can be used here.
|
|
* `name`: A `string` providing a human-readable identifier for the clock source.
|
|
This will be displayed in the clock selector menu in the Time Conductor UI
|
|
component, if active.
|
|
* `description`: An __optional__ `string` providing a longer description of the
|
|
clock. The description will be visible in the clock selection menu in the Time
|
|
Conductor plugin.
|
|
* `on`: A `function` supporting registration of a new callback that will be
|
|
invoked when the clock next ticks. It will be invoked with two arguments:
|
|
* `eventName`: A `string` specifying the event to listen on. For now, clocks
|
|
support one event - `tick`.
|
|
* `callback`: A `function` that will be invoked when this clock ticks. The
|
|
function must be invoked with one parameter - a `number` representing a valid
|
|
time in the current time system.
|
|
* `off`: A `function` that allows deregistration of a tick listener. It accepts
|
|
the same arguments as `on`.
|
|
* `currentValue`: A `function` that returns a `number` representing a point in
|
|
time in the active time system. It should be the last value provided by a tick,
|
|
or some default value if no ticking has yet occurred.
|
|
|
|
A new clock can be registered using the `addClock` function exposed by the Time
|
|
API:
|
|
|
|
```javascript
|
|
var someClock = {
|
|
key: 'someClock',
|
|
cssClass: 'icon-clock',
|
|
name: 'Some clock',
|
|
description: "Presumably does something useful",
|
|
on: function (event, callback) {
|
|
// Some function that registers listeners, and updates them on a tick
|
|
},
|
|
off: function (event, callback) {
|
|
// Some function that unregisters listeners.
|
|
},
|
|
currentValue: function () {
|
|
// A function that returns the last ticked value for the clock
|
|
}
|
|
}
|
|
|
|
openmct.time.addClock(someClock);
|
|
```
|
|
|
|
An example clock implementation is provided in the form of the [LocalClock](https://github.com/nasa/openmct/blob/master/src/plugins/utcTimeSystem/LocalClock.js)
|
|
|
|
#### Getting and setting active clock
|
|
|
|
Once registered a clock can be activated by calling the `clock` function on the
|
|
Time API passing in the key or instance of a registered clock. Only one clock
|
|
may be active at once, so activating a clock will deactivate any currently
|
|
active clock. Setting the clock will also trigger a ['clock' event](#time-events).
|
|
|
|
```
|
|
openmct.time.clock(someClock);
|
|
```
|
|
|
|
Upon being activated, a clock's `on` function will be immediately called to subscribe
|
|
to `tick` events.
|
|
|
|
The currently active clock (if any) can be retrieved by calling the same
|
|
function without any arguments.
|
|
|
|
#### Stopping an active clock
|
|
|
|
The `stopClock` method can be used to stop an active clock, and to clear it. It
|
|
will stop the clock from ticking, and set the active clock to `undefined`.
|
|
|
|
``` javascript
|
|
openmct.time.stopClock();
|
|
```
|
|
|
|
#### Clock Offsets
|
|
|
|
When a clock is active, the time bounds of the application will be updated
|
|
automatically each time the clock "ticks". The bounds are calculated based on
|
|
the current value provided by the active clock (via its `tick` event, or its
|
|
`currentValue()` method).
|
|
|
|
Unlike bounds, which represent absolute time values, clock offsets represent
|
|
relative time spans. Offsets are defined as an object with two properties:
|
|
|
|
* `start`: A `number` that must be < 0 and which is used to calculate the start
|
|
bounds on each clock tick. The start offset will be calculated relative to the
|
|
value provided by a clock's tick callback, or its `currentValue()` function.
|
|
* `end`: A `number` that must be >=0 and which is used to calculate the end
|
|
bounds on each clock tick.
|
|
|
|
The `clockOffsets` function can be used to get or set clock offsets. For example,
|
|
to show the last fifteen minutes in a ms-based time system:
|
|
|
|
```javascript
|
|
var FIFTEEN_MINUTES = 15 * 60 * 1000;
|
|
|
|
openmct.time.clockOffsets({
|
|
start: -FIFTEEN_MINUTES,
|
|
end: 0
|
|
})
|
|
```
|
|
|
|
__Note:__ Setting the clock offsets will trigger an immediate bounds change, as
|
|
new bounds will be calculated based on the `currentValue()` of the active clock.
|
|
Clock offsets are only relevant when a clock source is active.
|
|
|
|
## Time Events
|
|
The time API supports the registration of listeners that will be invoked when the
|
|
application's temporal state changes. Events listeners can be registered using
|
|
the `on` function. They can be deregistered using the `off` function. The arguments
|
|
accepted by the `on` and `off` functions are:
|
|
|
|
* `event`: A `string` naming the event to subscribe to. Event names correspond to
|
|
the property of the Time API you're interested in. A [full list of time events](#list-of-time-events)
|
|
is provided later.
|
|
|
|
As an example, the code to listen to bounds change events looks like:
|
|
|
|
``` javascript
|
|
openmct.time.on('bounds', function callback (newBounds, tick) {
|
|
// Do something with new bounds
|
|
});
|
|
```
|
|
|
|
#### List of Time Events
|
|
The events supported by the Time API are:
|
|
|
|
* `bounds`: Listen for changes to current bounds. The callback will be invoked
|
|
with two arguments:
|
|
* `bounds`: A [bounds](#getting-and-setting-bounds) bounds object representing
|
|
a new time period bound by the specified start and send times.
|
|
* `tick`: A `boolean` indicating whether or not this bounds change is due to a
|
|
"tick" from a [clock source](#clocks). This information can be useful when
|
|
determining a strategy for fetching telemetry data in response to a bounds
|
|
change event. For example, if the bounds change was automatic, and is due
|
|
to a tick then it's unlikely that you would need to perform a historical
|
|
data query. It should be sufficient to just show any new telemetry received
|
|
via subscription since the last tick, and optionally to discard any older
|
|
data that now falls outside of the currently set bounds. If `tick` is false,
|
|
then the bounds change was not due to an automatic tick, and a query for
|
|
historical data may be necessary, depending on your data caching strategy,
|
|
and how significantly the start bound has changed.
|
|
* `timeSystem`: Listen for changes to the active [time system](#defining-and-registering-time-systems).
|
|
The callback will be invoked with a single argument - the newly active time system.
|
|
* `timeSystem`: The newly active [time system](#defining-and-registering-time-systems) object.
|
|
* `clock`: Listen for changes to the active clock. When invoked, the callback
|
|
will be provided with the new clock.
|
|
* `clock`: The newly active [clock](#clocks), or `undefined` if an active clock
|
|
has been deactivated.
|
|
* `clockOffsets`: Listen for changes to active clock offsets. When invoked the
|
|
callback will be provided with the new clock offsets.
|
|
* `clockOffsets`: A [clock offsets](#clock-offsets) object.
|
|
|
|
|
|
## The Time Conductor
|
|
The Time Conductor provides a user interface for managing time bounds in Open MCT.
|
|
It allows a user to select from configured time systems and clocks, and to set bounds
|
|
and clock offsets.
|
|
|
|
If activated, the time conductor must be provided with configuration options,
|
|
detailed below.
|
|
|
|
#### Time Conductor Configuration
|
|
The time conductor is configured by specifying the options that will be
|
|
available to the user from the menus in the time conductor. These will determine
|
|
the clocks available from the conductor, the time systems available for each
|
|
clock, and some default bounds and clock offsets for each combination of clock
|
|
and time system. By default, the conductor always supports a `fixed` mode where
|
|
no clock is active. To specify configuration for fixed mode, simply leave out a
|
|
`clock` attribute in the configuration entry object.
|
|
|
|
Configuration is provided as an `array` of menu options. Each entry of the
|
|
array is an object with some properties specifying configuration. The configuration
|
|
options specified are slightly different depending on whether or not it is for
|
|
an active clock mode.
|
|
|
|
__Configuration for Fixed Time Mode (no active clock)__
|
|
|
|
* `timeSystem`: A `string`, the key for the time system that this configuration
|
|
relates to.
|
|
* `bounds`: A [`Time Bounds`](#time-bounds) object. These bounds will be applied
|
|
when the user selects the time system specified in the previous `timeSystem`
|
|
property.
|
|
* `zoomOutLimit`: An __optional__ `number` specifying the longest period of time
|
|
that can be represented by the conductor when zooming. If a `zoomOutLimit` is
|
|
provided, then a `zoomInLimit` must also be provided. If provided, the zoom
|
|
slider will automatically become available in the Time Conductor UI.
|
|
* `zoomInLimit`: An __optional__ `number` specifying the shortest period of time
|
|
that can be represented by the conductor when zooming. If a `zoomInLimit` is
|
|
provided, then a `zoomOutLimit` must also be provided. If provided, the zoom
|
|
slider will automatically become available in the Time Conductor UI.
|
|
|
|
__Configuration for Active Clock__
|
|
|
|
* `clock`: A `string`, the `key` of the clock that this configuration applies to.
|
|
* `timeSystem`: A `string`, the key for the time system that this configuration
|
|
relates to. Separate configuration must be provided for each time system that you
|
|
wish to be available to users when they select the specified clock.
|
|
* `clockOffsets`: A [`clockOffsets`](#clock-offsets) object that will be
|
|
automatically applied when the combination of clock and time system specified in
|
|
this configuration is selected from the UI.
|
|
|
|
#### Example conductor configuration
|
|
An example time conductor configuration is provided below. It sets up some
|
|
default options for the [UTCTimeSystem](https://github.com/nasa/openmct/blob/master/src/plugins/utcTimeSystem/UTCTimeSystem.js)
|
|
and [LocalTimeSystem](https://github.com/nasa/openmct/blob/master/src/plugins/localTimeSystem/LocalTimeSystem.js),
|
|
in both fixed mode, and for the [LocalClock](https://github.com/nasa/openmct/blob/master/src/plugins/utcTimeSystem/LocalClock.js)
|
|
source. In this configutation, the local clock supports both the UTCTimeSystem
|
|
and LocalTimeSystem. Configuration for fixed bounds mode is specified by omitting
|
|
a clock key.
|
|
|
|
``` javascript
|
|
const ONE_YEAR = 365 * 24 * 60 * 60 * 1000;
|
|
const ONE_MINUTE = 60 * 1000;
|
|
|
|
openmct.install(openmct.plugins.Conductor({
|
|
menuOptions: [
|
|
// 'Fixed' bounds mode configuation for the UTCTimeSystem
|
|
{
|
|
timeSystem: 'utc',
|
|
bounds: {start: Date.now() - 30 * ONE_MINUTE, end: Date.now()},
|
|
zoomOutLimit: ONE_YEAR,
|
|
zoomInLimit: ONE_MINUTE
|
|
},
|
|
// Configuration for the LocalClock in the UTC time system
|
|
{
|
|
clock: 'local',
|
|
timeSystem: 'utc',
|
|
clockOffsets: {start: - 30 * ONE_MINUTE, end: 0},
|
|
zoomOutLimit: ONE_YEAR,
|
|
zoomInLimit: ONE_MINUTE
|
|
},
|
|
//Configuration for the LocaLClock in the Local time system
|
|
{
|
|
clock: 'local',
|
|
timeSystem: 'local',
|
|
clockOffsets: {start: - 15 * ONE_MINUTE, end: 0}
|
|
}
|
|
]
|
|
}));
|
|
```
|
|
|
|
## 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.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());
|
|
```
|