mirror of
https://github.com/nasa/openmct.git
synced 2025-04-11 05:10:21 +00:00
[Plugin] Remote Clock based off Telemetry (#3998)
* Remote-clock plugin * Added a default clock class that can be extended by other classes * Updated local clock to use the parent default clock class * added a period check to make sure its been a certain amount of time before emitting Co-authored-by: John Hill <jchill2.spam@gmail.com>
This commit is contained in:
parent
94c7b2343a
commit
ca66898e51
@ -23,6 +23,7 @@
|
||||
define([
|
||||
'lodash',
|
||||
'./utcTimeSystem/plugin',
|
||||
'./remoteClock/plugin',
|
||||
'./localTimeSystem/plugin',
|
||||
'./ISOTimeFormat/plugin',
|
||||
'../../example/generator/plugin',
|
||||
@ -69,6 +70,7 @@ define([
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
RemoteClock,
|
||||
LocalTimeSystem,
|
||||
ISOTimeFormat,
|
||||
GeneratorPlugin,
|
||||
@ -129,6 +131,7 @@ define([
|
||||
|
||||
plugins.UTCTimeSystem = UTCTimeSystem;
|
||||
plugins.LocalTimeSystem = LocalTimeSystem;
|
||||
plugins.RemoteClock = RemoteClock.default;
|
||||
|
||||
plugins.ImportExport = ImportExport;
|
||||
|
||||
|
132
src/plugins/remoteClock/RemoteClock.js
Normal file
132
src/plugins/remoteClock/RemoteClock.js
Normal file
@ -0,0 +1,132 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
import DefaultClock from '../../utils/clock/DefaultClock';
|
||||
|
||||
/**
|
||||
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
|
||||
* application based on a time providing telemetry domainObject.
|
||||
*
|
||||
* @param {openmct} Object Instance of OpenMCT
|
||||
* @param {module:openmct.ObjectAPI~Identifier} identifier An object identifier for
|
||||
* the time providing telemetry domainObject
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
export default class RemoteClock extends DefaultClock {
|
||||
constructor(openmct, identifier) {
|
||||
super();
|
||||
|
||||
this.key = 'remote-clock';
|
||||
|
||||
this.openmct = openmct;
|
||||
this.identifier = identifier;
|
||||
|
||||
this.name = 'Remote Clock';
|
||||
this.description = "Provides telemetry based timestamps from a configurable source.";
|
||||
|
||||
this.timeTelemetryObject = undefined;
|
||||
this.parseTime = undefined;
|
||||
this.metadata = undefined;
|
||||
|
||||
this.lastTick = 0;
|
||||
|
||||
this._processDatum = this._processDatum.bind(this);
|
||||
}
|
||||
|
||||
start() {
|
||||
this.openmct.time.on('timeSystem', this._timeSystemChange);
|
||||
this.openmct.objects.get(this.identifier).then((domainObject) => {
|
||||
this.timeTelemetryObject = domainObject;
|
||||
this.metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||
this._timeSystemChange();
|
||||
this._requestLatest();
|
||||
this._subscribe();
|
||||
}).catch((error) => {
|
||||
throw new Error(error);
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.openmct.time.off('timeSystem', this._timeSystemChange);
|
||||
if (this._unsubscribe) {
|
||||
this._unsubscribe();
|
||||
}
|
||||
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will start a subscription to the timeTelemetryObject as well
|
||||
* handle the unsubscribe callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_subscribe() {
|
||||
this._unsubscribe = this.openmct.telemetry.subscribe(
|
||||
this.timeTelemetryObject,
|
||||
this._processDatum
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will request the latest data for the timeTelemetryObject
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_requestLatest() {
|
||||
this.openmct.telemetry.request(this.timeTelemetryObject, {
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
}).then(data => {
|
||||
this._processDatum(data[data.length - 1]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to parse the datum from the timeTelemetryObject as well
|
||||
* as check if it's valid, calls "tick"
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_processDatum(datum) {
|
||||
let time = this.parseTime(datum);
|
||||
|
||||
if (time > this.lastTick) {
|
||||
this.tick(time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for timeSystem change events
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_timeSystemChange() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let timeKey = timeSystem.key;
|
||||
let metadataValue = this.metadata.value(timeKey);
|
||||
let timeFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||
this.parseTime = (datum) => {
|
||||
return timeFormatter.parse(datum);
|
||||
};
|
||||
}
|
||||
}
|
149
src/plugins/remoteClock/RemoteClockSpec.js
Normal file
149
src/plugins/remoteClock/RemoteClockSpec.js
Normal file
@ -0,0 +1,149 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
createOpenMct,
|
||||
resetApplicationState
|
||||
} from 'utils/testing';
|
||||
|
||||
const REMOTE_CLOCK_KEY = 'remote-clock';
|
||||
const TIME_TELEMETRY_ID = {
|
||||
namespace: 'remote',
|
||||
key: 'telemetry'
|
||||
};
|
||||
const TIME_VALUE = 12345;
|
||||
const REQ_OPTIONS = {
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
};
|
||||
const OFFSET_START = -10;
|
||||
const OFFSET_END = 1;
|
||||
|
||||
describe("the RemoteClock plugin", () => {
|
||||
let openmct;
|
||||
let object = {
|
||||
name: 'remote-telemetry',
|
||||
identifier: TIME_TELEMETRY_ID
|
||||
};
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
openmct.on('start', done);
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
describe('once installed', () => {
|
||||
let remoteClock;
|
||||
let boundsCallback;
|
||||
let metadataValue = { some: 'value' };
|
||||
let timeSystem = { key: 'utc' };
|
||||
let metadata = {
|
||||
value: () => metadataValue
|
||||
};
|
||||
let reqDatum = {
|
||||
key: TIME_VALUE
|
||||
};
|
||||
|
||||
let formatter = {
|
||||
parse: (datum) => datum.key
|
||||
};
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct.install(openmct.plugins.RemoteClock(TIME_TELEMETRY_ID));
|
||||
|
||||
let clocks = openmct.time.getAllClocks();
|
||||
remoteClock = clocks.filter(clock => clock.key === REMOTE_CLOCK_KEY)[0];
|
||||
|
||||
boundsCallback = jasmine.createSpy("boundsCallback");
|
||||
openmct.time.on('bounds', boundsCallback);
|
||||
|
||||
spyOn(remoteClock, '_timeSystemChange').and.callThrough();
|
||||
spyOn(openmct.telemetry, 'getMetadata').and.returnValue(metadata);
|
||||
spyOn(openmct.telemetry, 'getValueFormatter').and.returnValue(formatter);
|
||||
spyOn(openmct.telemetry, 'subscribe').and.callThrough();
|
||||
spyOn(openmct.time, 'on').and.callThrough();
|
||||
spyOn(openmct.time, 'timeSystem').and.returnValue(timeSystem);
|
||||
spyOn(metadata, 'value').and.callThrough();
|
||||
|
||||
let requestPromiseResolve;
|
||||
let requestPromise = new Promise((resolve) => {
|
||||
requestPromiseResolve = resolve;
|
||||
});
|
||||
spyOn(openmct.telemetry, 'request').and.callFake(() => {
|
||||
requestPromiseResolve([reqDatum]);
|
||||
|
||||
return requestPromise;
|
||||
});
|
||||
|
||||
let objectPromiseResolve;
|
||||
let objectPromise = new Promise((resolve) => {
|
||||
objectPromiseResolve = resolve;
|
||||
});
|
||||
spyOn(openmct.objects, 'get').and.callFake(() => {
|
||||
objectPromiseResolve(object);
|
||||
|
||||
return objectPromise;
|
||||
});
|
||||
|
||||
openmct.time.clock(REMOTE_CLOCK_KEY, {
|
||||
start: OFFSET_START,
|
||||
end: OFFSET_END
|
||||
});
|
||||
|
||||
Promise.all([objectPromiseResolve, requestPromise])
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('is available and sets up initial values and listeners', () => {
|
||||
expect(remoteClock.key).toEqual(REMOTE_CLOCK_KEY);
|
||||
expect(remoteClock.identifier).toEqual(TIME_TELEMETRY_ID);
|
||||
expect(openmct.time.on).toHaveBeenCalledWith('timeSystem', remoteClock._timeSystemChange);
|
||||
expect(remoteClock._timeSystemChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('will request/store the object based on the identifier passed in', () => {
|
||||
expect(remoteClock.timeTelemetryObject).toEqual(object);
|
||||
});
|
||||
|
||||
it('will request metadata and set up formatters', () => {
|
||||
expect(remoteClock.metadata).toEqual(metadata);
|
||||
expect(metadata.value).toHaveBeenCalled();
|
||||
expect(openmct.telemetry.getValueFormatter).toHaveBeenCalledWith(metadataValue);
|
||||
});
|
||||
|
||||
it('will request the latest datum for the object it received and process the datum returned', () => {
|
||||
expect(openmct.telemetry.request).toHaveBeenCalledWith(remoteClock.timeTelemetryObject, REQ_OPTIONS);
|
||||
expect(boundsCallback).toHaveBeenCalledWith({ start: TIME_VALUE + OFFSET_START, end: TIME_VALUE + OFFSET_END }, true);
|
||||
});
|
||||
|
||||
it('will set up subscriptions correctly', () => {
|
||||
expect(remoteClock._unsubscribe).toBeDefined();
|
||||
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(remoteClock.timeTelemetryObject, remoteClock._processDatum);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
32
src/plugins/remoteClock/plugin.js
Normal file
32
src/plugins/remoteClock/plugin.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import RemoteClock from "./RemoteClock";
|
||||
/**
|
||||
* Install a clock that uses a configurable telemetry endpoint.
|
||||
*/
|
||||
|
||||
export default function (identifier) {
|
||||
return function (openmct) {
|
||||
openmct.time.addClock(new RemoteClock(openmct, identifier));
|
||||
};
|
||||
}
|
@ -20,22 +20,20 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
/**
|
||||
* 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) {
|
||||
EventEmitter.call(this);
|
||||
import DefaultClock from "../../utils/clock/DefaultClock";
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export default class LocalClock extends DefaultClock {
|
||||
constructor(period = 100) {
|
||||
super();
|
||||
|
||||
/*
|
||||
Metadata fields
|
||||
*/
|
||||
this.key = 'local';
|
||||
this.cssClass = 'icon-clock';
|
||||
this.name = 'Local Clock';
|
||||
this.description = "Provides UTC timestamps every second from the local system clock.";
|
||||
|
||||
@ -44,75 +42,21 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
this.lastTick = Date.now();
|
||||
}
|
||||
|
||||
LocalClock.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
LocalClock.prototype.start = function () {
|
||||
start() {
|
||||
this.timeoutHandle = setTimeout(this.tick.bind(this), this.period);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
LocalClock.prototype.stop = function () {
|
||||
stop() {
|
||||
if (this.timeoutHandle) {
|
||||
clearTimeout(this.timeoutHandle);
|
||||
this.timeoutHandle = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
LocalClock.prototype.tick = function () {
|
||||
tick() {
|
||||
const now = Date.now();
|
||||
this.emit("tick", now);
|
||||
this.lastTick = now;
|
||||
super.tick(now);
|
||||
this.timeoutHandle = setTimeout(this.tick.bind(this), this.period);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for the local clock. When it ticks, the local
|
||||
* clock will provide the current local system time
|
||||
*
|
||||
* @param listener
|
||||
* @returns {function} a function for deregistering the provided listener
|
||||
*/
|
||||
LocalClock.prototype.on = function (event) {
|
||||
const result = EventEmitter.prototype.on.apply(this, arguments);
|
||||
|
||||
if (this.listeners(event).length === 1) {
|
||||
this.start();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a listener for the local clock. When it ticks, the local
|
||||
* clock will provide the current local system time
|
||||
*
|
||||
* @param listener
|
||||
* @returns {function} a function for deregistering the provided listener
|
||||
*/
|
||||
LocalClock.prototype.off = function (event) {
|
||||
const result = EventEmitter.prototype.off.apply(this, arguments);
|
||||
|
||||
if (this.listeners(event).length === 0) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {number} The last value provided for a clock tick
|
||||
*/
|
||||
LocalClock.prototype.currentValue = function () {
|
||||
return this.lastTick;
|
||||
};
|
||||
|
||||
return LocalClock;
|
||||
});
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ define([
|
||||
return function (openmct) {
|
||||
const timeSystem = new UTCTimeSystem();
|
||||
openmct.time.addTimeSystem(timeSystem);
|
||||
openmct.time.addClock(new LocalClock(100));
|
||||
openmct.time.addClock(new LocalClock.default(100));
|
||||
};
|
||||
};
|
||||
});
|
||||
|
89
src/utils/clock/DefaultClock.js
Normal file
89
src/utils/clock/DefaultClock.js
Normal file
@ -0,0 +1,89 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2021, 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.
|
||||
*****************************************************************************/
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
/**
|
||||
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
|
||||
* application based on values provided by a ticking clock,
|
||||
* with the periodicity specified (optionally).
|
||||
* @param {number} period The periodicity with which the clock should tick
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
export default class DefaultClock extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.key = 'clock';
|
||||
|
||||
this.cssClass = 'icon-clock';
|
||||
this.name = 'Clock';
|
||||
this.description = "A default clock for openmct.";
|
||||
}
|
||||
|
||||
tick(tickValue) {
|
||||
this.emit("tick", tickValue);
|
||||
this.lastTick = tickValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for the clock. When it ticks, the
|
||||
* clock will provide the time from the configured endpoint
|
||||
*
|
||||
* @param listener
|
||||
* @returns {function} a function for deregistering the provided listener
|
||||
*/
|
||||
on(event) {
|
||||
let result = super.on.apply(this, arguments);
|
||||
|
||||
if (this.listeners(event).length === 1) {
|
||||
this.start();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for the clock. When it ticks, the
|
||||
* clock will provide the current local system time
|
||||
*
|
||||
* @param listener
|
||||
* @returns {function} a function for deregistering the provided listener
|
||||
*/
|
||||
off(event) {
|
||||
let result = super.off.apply(this, arguments);
|
||||
|
||||
if (this.listeners(event).length === 0) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The last value provided for a clock tick
|
||||
*/
|
||||
currentValue() {
|
||||
return this.lastTick;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user