[Time Conductor] Misc. bug fixes, additional documentation for conductor elements, moved and renamed LAD tick source

This commit is contained in:
Henry 2017-04-29 16:02:25 -07:00
parent 6628f93823
commit 31897ec520
10 changed files with 176 additions and 97 deletions

View File

@ -26,14 +26,6 @@ define(
'./TimeConductorValidation'
],
function (moment, TimeConductorValidation) {
var SEARCH = {
MODE: 'tc.mode',
TIME_SYSTEM: 'tc.timeSystem',
START_BOUND: 'tc.startBound',
END_BOUND: 'tc.endBound',
START_DELTA: 'tc.startDelta',
END_DELTA: 'tc.endDelta'
};
var timeUnitsMegastructure = [
["Decades", function (r) {
@ -63,8 +55,10 @@ define(
];
/**
* Controller for the Time Conductor UI element. The Time Conductor includes form fields for specifying time
* bounds and relative time offsets for queries, as well as controls for selection mode, time systems, and zooming.
* Controller for the Time Conductor UI element. The Time Conductor
* includes form fields for specifying time bounds and relative time
* offsets for queries, as well as controls for selection mode,
* time systems, and zooming.
* @memberof platform.features.conductor
* @constructor
*/
@ -77,14 +71,20 @@ define(
config
) {
var self = this;
//Bind all class functions to 'this'
Object.keys(TimeConductorController.prototype).filter(function (key) {
return typeof TimeConductorController.prototype[key] === 'function';
}).forEach(function (key) {
self[key] = self[key].bind(self);
});
//Bind functions that are used as callbacks to 'this'.
[
"selectMenuOption",
"onPan",
"onPanStop",
"setViewFromBounds",
"setViewFromClock",
"setViewFromOffsets",
"setViewFromTimeSystem",
"setTimeSystemFromView",
"destroy"
].forEach(function (name) {
this[name] = this[name].bind(this);
}.bind(this));
this.$scope = $scope;
this.$window = $window;
@ -142,13 +142,24 @@ define(
this.$scope.$on('$destroy', this.destroy);
}
/**
* Given a key for a clock, retrieve the clock object.
* @private
* @param key
* @returns {Clock}
*/
TimeConductorController.prototype.getClock = function (key) {
return this.timeAPI.getAllClocks().filter(function (clock) {
return clock.key === key;
})[0];
};
/**
* Given a key for a time system, retrieve the time system object.
* @private
* @param key
* @returns {TimeSystem}
*/
TimeConductorController.prototype.getTimeSystem = function (key) {
return this.timeAPI.getAllTimeSystems().filter(function (timeSystem) {
return timeSystem.key === key;
@ -156,6 +167,10 @@ define(
};
/**
* Activate the selected menu option. Menu options correspond to clocks.
* A distinction is made to avoid confusion between the menu options and
* their metadata, and actual {@link Clock} objects.
*
* @private
* @param newOption
* @param oldOption
@ -163,8 +178,13 @@ define(
TimeConductorController.prototype.selectMenuOption = function (newOption, oldOption){
if (newOption !== oldOption) {
var config = this.getConfig(this.timeAPI.timeSystem(), newOption.clock);
/*
* If there is no configuration defined for the selected clock
* and time system default to the first time system that
* configuration is available for.
*/
if (config === undefined) {
//Default to first time system available if the current one is not compatible with the new clock
var timeSystem = this.timeSystemsForClocks[newOption.key][0];
this.$scope.timeSystemModel.selected = timeSystem;
this.setTimeSystemFromView(timeSystem.key);
@ -180,11 +200,15 @@ define(
};
/**
* From the provided configuration, build the available menu options.
* @private
* @param config
* @returns {*[]}
*/
TimeConductorController.prototype.optionsFromConfig = function (config) {
/*
* "Fixed Mode" is always the first available option.
*/
var options = [{
key: 'fixed',
name: 'Fixed Timespan Mode',
@ -202,6 +226,8 @@ define(
var timeSystem = this.getTimeSystem(menuOption.timeSystem);
if (timeSystem !== undefined) {
if (clock !== undefined) {
// Use an associative array to built a set of unique
// clocks
clocks[clock.key] = clock;
clocksForTimeSystem[timeSystem.key] = clocksForTimeSystem[timeSystem.key] || [];
clocksForTimeSystem[timeSystem.key].push(clock);
@ -213,6 +239,9 @@ define(
}
}.bind(this));
/*
* Populate the clocks menu with metadata from the available clocks
*/
Object.values(clocks).forEach(function (clock) {
options.push({
key: clock.key,
@ -228,23 +257,8 @@ define(
};
/**
* @private
*/
TimeConductorController.prototype.destroy = function () {
this.timeAPI.off('bounds', this.setViewFromBounds);
this.timeAPI.off('timeSystem', this.setViewFromTimeSystem);
this.timeAPI.off('clock', this.setViewFromClock);
this.timeAPI.off('follow', this.setFollow);
this.timeAPI.off('clockOffsets', this.setViewFromOffsets);
this.conductorViewService.off('pan', this.onPan);
this.conductorViewService.off('pan-stop', this.onPanStop);
};
/**
* Called when the bounds change in the time conductor. Synchronizes
* the bounds values in the time conductor with those in the form
* @param {TimeConductorBounds}
* When bounds change, set UI values from the new bounds.
* @param {TimeBounds} bounds the bounds
*/
TimeConductorController.prototype.setViewFromBounds = function (bounds) {
if (!this.zooming && !this.panning) {
@ -257,6 +271,10 @@ define(
this.toTimeUnits(bounds.end - bounds.start);
}
/*
Ensure that a digest occurs, capped at the browser's refresh
rate.
*/
if (!this.pendingUpdate) {
this.pendingUpdate = true;
this.$window.requestAnimationFrame(function () {
@ -268,10 +286,13 @@ define(
};
/**
* Retrieve any configuration defined for the provided time system and
* clock
* @private
* @param timeSystem
* @param clock
* @returns {T}
* @returns {object} The Time Conductor configuration corresponding to
* the provided combination of time system and clock
*/
TimeConductorController.prototype.getConfig = function (timeSystem, clock) {
var clockKey = clock && clock.key;
@ -284,7 +305,8 @@ define(
};
/**
* When the offsets change, update the values in the UI
* When the clock offsets change, update the values in the UI
* @param {ClockOffsets} offsets
* @private
*/
TimeConductorController.prototype.setViewFromOffsets = function (offsets) {
@ -293,8 +315,9 @@ define(
};
/**
* Called when form values are changed.
* @param formModel
* When form values for bounds change, update the bounds in the Time API
* to trigger an application-wide bounds change.
* @param {object} boundsModel
*/
TimeConductorController.prototype.setBoundsFromView = function (boundsModel) {
var bounds = this.timeAPI.bounds();
@ -307,8 +330,9 @@ define(
};
/**
* Called when form values are changed.
* @param formModel
* When form values for bounds change, update the bounds in the Time API
* to trigger an application-wide bounds change.
* @param {object} formModel
*/
TimeConductorController.prototype.setOffsetsFromView = function (boundsModel) {
if (this.validation.validateStartOffset(boundsModel.startOffset) && this.validation.validateEndOffset(boundsModel.endOffset)) {
@ -335,13 +359,14 @@ define(
};
/**
* Change the selected Time Conductor mode. This will call destroy
* and initialization functions on the relevant modes, setting
* default values for bound and offsets in the form.
* Update the UI state to reflect a change in clock. Provided conductor
* configuration will be checked for compatibility between the new clock
* and the currently selected time system. If configuration is not available,
* an attempt will be made to default to a time system that is compatible
* with the new clock
*
* @private
* @param newClockKey
* @param oldModeKey
* @param {Clock} clock
*/
TimeConductorController.prototype.setViewFromClock = function (clock) {
var newClockKey = clock && clock.key;
@ -411,13 +436,20 @@ define(
*/
if (clock === undefined) {
bounds = config.bounds;
this.timeAPI.timeSystem(timeSystem, bounds);
} else {
bounds = {
start: clock.currentValue() + config.clockOffsets.start,
end: clock.currentValue() + config.clockOffsets.end
};
//Has time system change resulted in offsets change (based on config)?
this.timeAPI.timeSystem(timeSystem, bounds);
var configOffsets = config.clockOffsets;
var apiOffsets = this.timeAPI.clockOffsets();
if (configOffsets.start !== apiOffsets.start || configOffsets.end !== apiOffsets.end) {
this.timeAPI.clockOffsets(configOffsets);
}
}
this.timeAPI.timeSystem(timeSystem, bounds);
};
/**
@ -450,6 +482,7 @@ define(
/**
* Takes a time span and calculates a slider increment value, used
* to set the horizontal offset of the slider.
* @private
* @param {number} timeSpan a duration of time, in ms
* @returns {number} a value between 0.01 and 0.99, in increments of .01
*/
@ -486,6 +519,7 @@ define(
var timeSpan = Math.pow((1 - sliderValue), 4) * (config.zoomOutLimit - config.zoomInLimit);
var zoom = this.conductorViewService.zoom(timeSpan);
this.zooming = true;
this.$scope.boundsModel.start = zoom.bounds.start;
this.$scope.boundsModel.end = zoom.bounds.end;
@ -506,10 +540,12 @@ define(
* @fires platform.features.conductor.TimeConductorController~zoomStop
*/
TimeConductorController.prototype.onZoomStop = function () {
if (this.timeAPI.clock() !== undefined) {
this.setOffsetsFromView(this.$scope.boundsModel);
}
this.setBoundsFromView(this.$scope.boundsModel);
this.setOffsetsFromView(this.$scope.boundsModel);
this.zooming = false;
this.zooming = false;
this.conductorViewService.emit('zoom-stop');
};
@ -534,6 +570,20 @@ define(
this.panning = false;
};
/**
* @private
*/
TimeConductorController.prototype.destroy = function () {
this.timeAPI.off('bounds', this.setViewFromBounds);
this.timeAPI.off('timeSystem', this.setViewFromTimeSystem);
this.timeAPI.off('clock', this.setViewFromClock);
this.timeAPI.off('follow', this.setFollow);
this.timeAPI.off('clockOffsets', this.setViewFromOffsets);
this.conductorViewService.off('pan', this.onPan);
this.conductorViewService.off('pan-stop', this.onPanStop);
};
return TimeConductorController;
}
);

View File

@ -27,16 +27,20 @@ define(
function (EventEmitter) {
/**
* A class representing the state of the time conductor view. This
* exposes details of the UI that are not represented on the
* TimeConductor API itself such as modes and offsets.
* The TimeConductorViewService acts as an event bus between different
* elements of the Time Conductor UI. Zooming and panning occur via this
* service, as they are specific behaviour of the UI, and not general
* functions of the time API.
*
* Synchronization of conductor state between the Time API and the URL
* also occurs from the conductor view service, whose lifecycle persists
* between view changes.
*
* @memberof platform.features.conductor
* @param conductor
* @param timeSystems
* @constructor
*/
function TimeConductorViewService(openmct, timeSystems) {
function TimeConductorViewService(openmct) {
EventEmitter.call(this);
@ -45,21 +49,6 @@ define(
TimeConductorViewService.prototype = Object.create(EventEmitter.prototype);
TimeConductorViewService.prototype.calculateBoundsFromOffsets = function (offsets) {
var oldEnd = this.timeAPI.bounds().end;
if (offsets && offsets.end !== undefined) {
//Calculate the previous raw end value (without delta)
oldEnd = oldEnd - offsets.end;
}
var bounds = {
start: oldEnd - offsets.start,
end: oldEnd + offsets.end
};
return bounds;
};
/**
* An event to indicate that zooming is taking place
* @event platform.features.conductor.TimeConductorViewService~zoom
@ -81,10 +70,16 @@ define(
// important. Calculate zoom based on 'now'.
if (this.timeAPI.clock() !== undefined) {
zoom.offsets = {
start: timeSpan,
start: -timeSpan,
end: this.timeAPI.clockOffsets().end
};
zoom.bounds = this.calculateBoundsFromOffsets(zoom.offsets);
var currentVal = this.timeAPI.clock().currentValue();
zoom.bounds = {
start: currentVal + zoom.offsets.start,
end: currentVal + zoom.offsets.end
};
} else {
var bounds = this.timeAPI.bounds();
var center = bounds.start + ((bounds.end - bounds.start)) / 2;

View File

@ -22,20 +22,21 @@
define(['../../../src/plugins/utcTimeSystem/LocalClock'], function (LocalClock) {
/**
* @implements TickSource
* A {@link Clock} that mocks a "latest available data" type tick source.
* This is for testing purposes only, and behaves identically to a local clock.
* It DOES NOT tick on receipt of data.
* @constructor
*/
function LADTickSource (period) {
function LADClock (period) {
LocalClock.call(this, period);
this.key = 'test-lad';
this.mode = 'lad';
this.cssClass = 'icon-database';
this.label = 'Latest Available Data';
this.name = 'Latest available data';
this.description = "Updates when when new data is available";
}
LADTickSource.prototype = Object.create(LocalClock.prototype);
LADClock.prototype = Object.create(LocalClock.prototype);
return LADTickSource;
return LADClock;
});

View File

@ -0,0 +1,33 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"./LADClock"
], function (
LADClock
) {
return function () {
return function (openmct) {
openmct.time.addClock(new LADClock());
}
};
});

View File

@ -20,12 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'../../../src/plugins/utcTimeSystem/LocalClock',
'./LADTickSource'
], function (LocalClock, LADTickSource) {
var THIRTY_MINUTES = 30 * 60 * 1000,
DEFAULT_PERIOD = 1000;
define([], function () {
/**
* This time system supports UTC dates and provides a ticking clock source.

View File

@ -22,17 +22,14 @@
define([
"./LocalTimeSystem",
"./LocalTimeFormat",
"./LADTickSource"
"./LocalTimeFormat"
], function (
LocalTimeSystem,
LocalTimeFormat,
LADTickSource
LocalTimeFormat
) {
return function () {
return function (openmct) {
openmct.time.addTimeSystem(new LocalTimeSystem());
openmct.time.addClock(new LADTickSource());
openmct.legacyExtension('formats', {
key: 'local-format',

View File

@ -22,7 +22,10 @@
define(['EventEmitter'], function (EventEmitter) {
/**
* @implements TickSource
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
* application based on UTC time values provided by a ticking local clock,
* with the periodicity specified.
* @param {number} period The periodicity with which the clock should tick
* @constructor
*/
function LocalClock(period) {
@ -33,7 +36,6 @@ define(['EventEmitter'], function (EventEmitter) {
*/
this.key = 'local';
this.cssClass = 'icon-clock';
this.label = 'Local Clock';
this.name = 'Local Clock';
this.description = "Updates every second, providing UTC timestamps from " +
"user's local computer.";
@ -62,6 +64,9 @@ define(['EventEmitter'], function (EventEmitter) {
}
};
/**
* @private
*/
LocalClock.prototype.tick = function () {
var now = Date.now();
this.emit("tick", now);
@ -102,6 +107,9 @@ define(['EventEmitter'], function (EventEmitter) {
return result;
};
/**
* @returns {number} The last value provided for a clock tick
*/
LocalClock.prototype.currentValue = function () {
return this.lastTick;
};

View File

@ -22,24 +22,21 @@
define([], function () {
/**
* This time system supports UTC dates and provides a ticking clock source.
* This time system supports UTC dates.
* @implements TimeSystem
* @constructor
*/
function UTCTimeSystem() {
/**
* Some metadata, which will be used to identify the time system in
* Metadata used to identify the time system in
* the UI
* @type {{key: string, name: string, cssClass: string}}
*/
this.key = 'utc';
this.name = 'UTC';
this.cssClass = 'icon-clock';
this.timeFormat = 'utc';
this.durationFormat = 'duration';
this.isUTCBased = true;
}

View File

@ -27,7 +27,10 @@ define([
UTCTimeSystem,
LocalClock
) {
var ONE_DAY = 24 * 60 * 60 * 1000;
/**
* Install a time system that supports UTC times. It also installs a local
* clock source that ticks every 100ms, providing UTC times.
*/
return function () {
return function (openmct) {
var timeSystem = new UTCTimeSystem();