Compare commits

..

2 Commits

Author SHA1 Message Date
d3825fc554 rescope objects 2023-08-18 14:52:46 -07:00
d42b2dd023 mappin 2023-08-17 11:23:42 -07:00
11 changed files with 939 additions and 933 deletions

View File

@ -23,5 +23,5 @@ module.exports = merge(common, {
__OPENMCT_ROOT_RELATIVE__: '""' __OPENMCT_ROOT_RELATIVE__: '""'
}) })
], ],
devtool: "source-map" devtool: "eval-source-map"
}); });

View File

@ -1,138 +1,150 @@
define(['lodash'], function (_) { define([
var METADATA_BY_TYPE = { 'lodash'
generator: { ], function (
values: [ _
{ ) {
key: 'name',
name: 'Name', var METADATA_BY_TYPE = {
format: 'string' 'generator': {
values: [
{
key: "name",
name: "Name",
format: "string"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "wavelengths",
name: "Wavelength",
unit: "nm",
format: 'string[]',
hints: {
range: 4
}
},
// Need to enable "LocalTimeSystem" plugin to make use of this
// {
// key: "local",
// name: "Time",
// format: "local-format",
// source: "utc",
// hints: {
// domain: 3
// }
// },
{
key: "sin",
name: "Sine",
unit: "Hz",
formatString: '%0.2f',
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
formatString: '%0.2f',
hints: {
range: 2
}
},
{
key: "intensities",
name: "Intensities",
format: 'number[]',
hints: {
range: 3
}
}
]
}, },
{ 'example.state-generator': {
key: 'utc', values: [
name: 'Time', {
format: 'utc', key: "name",
hints: { name: "Name",
domain: 1 format: "string"
} },
}, {
{ key: "utc",
key: 'yesterday', name: "Time",
name: 'Yesterday', format: "utc",
format: 'utc', hints: {
hints: { domain: 1
domain: 2 }
} },
}, {
{ key: "local",
key: 'wavelengths', name: "Time",
name: 'Wavelength', format: "utc",
unit: 'nm', source: "utc",
format: 'string[]', hints: {
hints: { domain: 2
range: 4 }
} },
}, {
// Need to enable "LocalTimeSystem" plugin to make use of this key: "state",
// { source: "value",
// key: "local", name: "State",
// name: "Time", format: "enum",
// format: "local-format", enumerations: [
// source: "utc", {
// hints: { value: 0,
// domain: 3 string: "OFF"
// } },
// }, {
{ value: 1,
key: 'sin', string: "ON"
name: 'Sine', }
unit: 'Hz', ],
formatString: '%0.2f', hints: {
hints: { range: 1
range: 1 }
} },
}, {
{ key: "value",
key: 'cos', name: "Value",
name: 'Cosine', hints: {
unit: 'deg', range: 2
formatString: '%0.2f', }
hints: { }
range: 2 ]
}
},
{
key: 'intensities',
name: 'Intensities',
format: 'number[]',
hints: {
range: 3
}
} }
] };
},
'example.state-generator': { function GeneratorMetadataProvider() {
values: [
{
key: 'name',
name: 'Name',
format: 'string'
},
{
key: 'utc',
name: 'Time',
format: 'utc',
hints: {
domain: 1
}
},
{
key: 'local',
name: 'Time',
format: 'utc',
source: 'utc',
hints: {
domain: 2
}
},
{
key: 'state',
source: 'value',
name: 'State',
format: 'enum',
enumerations: [
{
value: 0,
string: 'OFF'
},
{
value: 1,
string: 'ON'
}
],
hints: {
range: 1
}
},
{
key: 'value',
name: 'Value',
hints: {
range: 2
}
}
]
} }
};
function GeneratorMetadataProvider() {} GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
};
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) { GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type); return Object.assign(
}; {},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) { return GeneratorMetadataProvider;
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
};
return GeneratorMetadataProvider;
}); });

View File

@ -20,86 +20,87 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['./WorkerInterface'], function (WorkerInterface) { define([
var REQUEST_DEFAULTS = { './WorkerInterface'
amplitude: 1, ], function (
period: 10, WorkerInterface
offset: 0, ) {
dataRateInHz: 1,
randomness: 0,
phase: 0,
loadDelay: 0,
infinityValues: false,
veryLargeValues: false
};
function GeneratorProvider(openmct, StalenessProvider) { var REQUEST_DEFAULTS = {
this.openmct = openmct; amplitude: 1,
this.workerInterface = new WorkerInterface(openmct, StalenessProvider); period: 10,
} offset: 0,
dataRateInHz: 1,
randomness: 0,
phase: 0,
loadDelay: 0,
infinityValues: false
};
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) { function GeneratorProvider(openmct, StalenessProvider) {
return domainObject.type === 'generator'; this.openmct = openmct;
}; this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
}
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe = GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
GeneratorProvider.prototype.canProvideTelemetry; return domainObject.type === 'generator';
};
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) { GeneratorProvider.prototype.supportsRequest =
var props = [ GeneratorProvider.prototype.supportsSubscribe =
'amplitude', GeneratorProvider.prototype.canProvideTelemetry;
'period',
'offset',
'dataRateInHz',
'randomness',
'phase',
'loadDelay',
'infinityValues',
'veryLargeValues'
];
request = request || {}; GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',
'period',
'offset',
'dataRateInHz',
'randomness',
'phase',
'loadDelay',
'infinityValues'
];
var workerRequest = {}; request = request || {};
props.forEach(function (prop) { var workerRequest = {};
if (
domainObject.telemetry &&
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
) {
workerRequest[prop] = domainObject.telemetry[prop];
}
if (request && Object.prototype.hasOwnProperty.call(request, prop)) { props.forEach(function (prop) {
workerRequest[prop] = request[prop]; if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
} workerRequest[prop] = domainObject.telemetry[prop];
}
if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) { if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
workerRequest[prop] = REQUEST_DEFAULTS[prop]; workerRequest[prop] = request[prop];
} }
workerRequest[prop] = Number(workerRequest[prop]); if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
}); workerRequest[prop] = REQUEST_DEFAULTS[prop];
}
workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier); workerRequest[prop] = Number(workerRequest[prop]);
workerRequest.name = domainObject.name; });
return workerRequest; workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier);
}; workerRequest.name = domainObject.name;
GeneratorProvider.prototype.request = function (domainObject, request) { return workerRequest;
var workerRequest = this.makeWorkerRequest(domainObject, request); };
workerRequest.start = request.start;
workerRequest.end = request.end;
return this.workerInterface.request(workerRequest); GeneratorProvider.prototype.request = function (domainObject, request) {
}; var workerRequest = this.makeWorkerRequest(domainObject, request);
workerRequest.start = request.start;
workerRequest.end = request.end;
GeneratorProvider.prototype.subscribe = function (domainObject, callback) { return this.workerInterface.request(workerRequest);
var workerRequest = this.makeWorkerRequest(domainObject, {}); };
return this.workerInterface.subscribe(workerRequest, callback); GeneratorProvider.prototype.subscribe = function (domainObject, callback) {
}; var workerRequest = this.makeWorkerRequest(domainObject, {});
return GeneratorProvider; return this.workerInterface.subscribe(workerRequest, callback);
};
return GeneratorProvider;
}); });

View File

@ -20,147 +20,155 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { define([
var PURPLE = {
sin: 2.2, ], function (
cos: 2.2
}, ) {
RED = {
sin: 0.9, var PURPLE = {
cos: 0.9 sin: 2.2,
}, cos: 2.2
ORANGE = { },
sin: 0.7, RED = {
cos: 0.7 sin: 0.9,
}, cos: 0.9
YELLOW = { },
sin: 0.5, ORANGE = {
cos: 0.5 sin: 0.7,
}, cos: 0.7
CYAN = { },
sin: 0.45, YELLOW = {
cos: 0.45 sin: 0.5,
}, cos: 0.5
LIMITS = { },
rh: { CYAN = {
cssClass: 'is-limit--upr is-limit--red', sin: 0.45,
low: RED, cos: 0.45
high: Number.POSITIVE_INFINITY, },
name: 'Red High' LIMITS = {
}, rh: {
rl: { cssClass: "is-limit--upr is-limit--red",
cssClass: 'is-limit--lwr is-limit--red', low: RED,
high: -RED, high: Number.POSITIVE_INFINITY,
low: Number.NEGATIVE_INFINITY, name: "Red High"
name: 'Red Low' },
}, rl: {
yh: { cssClass: "is-limit--lwr is-limit--red",
cssClass: 'is-limit--upr is-limit--yellow', high: -RED,
low: YELLOW, low: Number.NEGATIVE_INFINITY,
high: RED, name: "Red Low"
name: 'Yellow High' },
}, yh: {
yl: { cssClass: "is-limit--upr is-limit--yellow",
cssClass: 'is-limit--lwr is-limit--yellow', low: YELLOW,
low: -RED, high: RED,
high: -YELLOW, name: "Yellow High"
name: 'Yellow Low' },
} yl: {
cssClass: "is-limit--lwr is-limit--yellow",
low: -RED,
high: -YELLOW,
name: "Yellow Low"
}
};
function SinewaveLimitProvider() {
}
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
return domainObject.type === 'generator';
}; };
function SinewaveLimitProvider() {} SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
return {
evaluate: function (datum, valueMetadata) {
var range = valueMetadata && valueMetadata.key;
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) { if (datum[range] > RED[range]) {
return domainObject.type === 'generator'; return LIMITS.rh;
}; }
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) { if (datum[range] < -RED[range]) {
return { return LIMITS.rl;
evaluate: function (datum, valueMetadata) { }
var range = valueMetadata && valueMetadata.key;
if (datum[range] > RED[range]) { if (datum[range] > YELLOW[range]) {
return LIMITS.rh; return LIMITS.yh;
} }
if (datum[range] < -RED[range]) { if (datum[range] < -YELLOW[range]) {
return LIMITS.rl; return LIMITS.yl;
} }
}
if (datum[range] > YELLOW[range]) { };
return LIMITS.yh;
}
if (datum[range] < -YELLOW[range]) {
return LIMITS.yl;
}
}
}; };
};
SinewaveLimitProvider.prototype.getLimits = function (domainObject) { SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
return {
limits: function () { return {
return Promise.resolve({ limits: function () {
WATCH: { return Promise.resolve({
low: { WATCH: {
color: 'cyan', low: {
sin: -CYAN.sin, color: "cyan",
cos: -CYAN.cos sin: -CYAN.sin,
}, cos: -CYAN.cos
high: { },
color: 'cyan', high: {
...CYAN color: "cyan",
...CYAN
}
},
WARNING: {
low: {
color: "yellow",
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
color: "yellow",
...YELLOW
}
},
DISTRESS: {
low: {
color: "orange",
sin: -ORANGE.sin,
cos: -ORANGE.cos
},
high: {
color: "orange",
...ORANGE
}
},
CRITICAL: {
low: {
color: "red",
sin: -RED.sin,
cos: -RED.cos
},
high: {
color: "red",
...RED
}
},
SEVERE: {
low: {
color: "purple",
sin: -PURPLE.sin,
cos: -PURPLE.cos
},
high: {
color: "purple",
...PURPLE
}
}
});
} }
}, };
WARNING: {
low: {
color: 'yellow',
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
color: 'yellow',
...YELLOW
}
},
DISTRESS: {
low: {
color: 'orange',
sin: -ORANGE.sin,
cos: -ORANGE.cos
},
high: {
color: 'orange',
...ORANGE
}
},
CRITICAL: {
low: {
color: 'red',
sin: -RED.sin,
cos: -RED.cos
},
high: {
color: 'red',
...RED
}
},
SEVERE: {
low: {
color: 'purple',
sin: -PURPLE.sin,
cos: -PURPLE.cos
},
high: {
color: 'purple',
...PURPLE
}
}
});
}
}; };
};
return SinewaveLimitProvider; return SinewaveLimitProvider;
}); });

View File

@ -23,135 +23,135 @@
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
export default class SinewaveLimitProvider extends EventEmitter { export default class SinewaveLimitProvider extends EventEmitter {
#openmct; #openmct;
#observingStaleness; #observingStaleness;
#watchingTheClock; #watchingTheClock;
#isRealTime; #isRealTime;
constructor(openmct) { constructor(openmct) {
super(); super();
this.#openmct = openmct; this.#openmct = openmct;
this.#observingStaleness = {}; this.#observingStaleness = {};
this.#watchingTheClock = false; this.#watchingTheClock = false;
this.#isRealTime = undefined; this.#isRealTime = undefined;
}
supportsStaleness(domainObject) {
return domainObject.type === 'generator';
}
isStale(domainObject, options) {
if (!this.#providingStaleness(domainObject)) {
return;
} }
const id = this.#getObjectKeyString(domainObject); supportsStaleness(domainObject) {
return domainObject.type === 'generator';
if (!this.#observerExists(id)) {
this.#createObserver(id);
} }
return Promise.resolve({ isStale(domainObject, options) {
isStale: this.#observingStaleness[id].isStale, if (!this.#providingStaleness(domainObject)) {
utc: Date.now() return;
}); }
}
subscribeToStaleness(domainObject, callback) { const id = this.#getObjectKeyString(domainObject);
const id = this.#getObjectKeyString(domainObject);
if (this.#isRealTime === undefined) { if (!this.#observerExists(id)) {
this.#updateRealTime(this.#openmct.time.getMode()); this.#createObserver(id);
}
return Promise.resolve({
isStale: this.#observingStaleness[id].isStale,
utc: Date.now()
});
} }
this.#handleClockUpdate(); subscribeToStaleness(domainObject, callback) {
const id = this.#getObjectKeyString(domainObject);
if (this.#observerExists(id)) { if (this.#isRealTime === undefined) {
this.#addCallbackToObserver(id, callback); this.#updateRealTime(this.#openmct.time.clock());
} else { }
this.#createObserver(id, callback);
this.#handleClockUpdate();
if (this.#observerExists(id)) {
this.#addCallbackToObserver(id, callback);
} else {
this.#createObserver(id, callback);
}
const intervalId = setInterval(() => {
if (this.#providingStaleness(domainObject)) {
this.#updateStaleness(id, !this.#observingStaleness[id].isStale);
}
}, 10000);
return () => {
clearInterval(intervalId);
this.#updateStaleness(id, false);
this.#handleClockUpdate();
this.#destroyObserver(id);
};
} }
const intervalId = setInterval(() => { #handleClockUpdate() {
if (this.#providingStaleness(domainObject)) { let observers = Object.values(this.#observingStaleness).length > 0;
this.#updateStaleness(id, !this.#observingStaleness[id].isStale);
}
}, 10000);
return () => { if (observers && !this.#watchingTheClock) {
clearInterval(intervalId); this.#watchingTheClock = true;
this.#updateStaleness(id, false); this.#openmct.time.on('clock', this.#updateRealTime, this);
this.#handleClockUpdate(); } else if (!observers && this.#watchingTheClock) {
this.#destroyObserver(id); this.#watchingTheClock = false;
}; this.#openmct.time.off('clock', this.#updateRealTime, this);
} }
#handleClockUpdate() {
let observers = Object.values(this.#observingStaleness).length > 0;
if (observers && !this.#watchingTheClock) {
this.#watchingTheClock = true;
this.#openmct.time.on('modeChanged', this.#updateRealTime, this);
} else if (!observers && this.#watchingTheClock) {
this.#watchingTheClock = false;
this.#openmct.time.off('modeChanged', this.#updateRealTime, this);
} }
}
#updateRealTime(mode) { #updateRealTime(clock) {
this.#isRealTime = mode !== 'fixed'; this.#isRealTime = clock !== undefined;
if (!this.#isRealTime) { if (!this.#isRealTime) {
Object.keys(this.#observingStaleness).forEach((id) => { Object.keys(this.#observingStaleness).forEach((id) => {
this.#updateStaleness(id, false); this.#updateStaleness(id, false);
}); });
}
} }
}
#updateStaleness(id, isStale) { #updateStaleness(id, isStale) {
this.#observingStaleness[id].isStale = isStale; this.#observingStaleness[id].isStale = isStale;
this.#observingStaleness[id].utc = Date.now(); this.#observingStaleness[id].utc = Date.now();
this.#observingStaleness[id].callback({ this.#observingStaleness[id].callback({
isStale: this.#observingStaleness[id].isStale, isStale: this.#observingStaleness[id].isStale,
utc: this.#observingStaleness[id].utc utc: this.#observingStaleness[id].utc
}); });
this.emit('stalenessEvent', { this.emit('stalenessEvent', {
id, id,
isStale: this.#observingStaleness[id].isStale isStale: this.#observingStaleness[id].isStale
}); });
}
#createObserver(id, callback) {
this.#observingStaleness[id] = {
isStale: false,
utc: Date.now()
};
if (typeof callback === 'function') {
this.#addCallbackToObserver(id, callback);
} }
}
#destroyObserver(id) { #createObserver(id, callback) {
if (this.#observingStaleness[id]) { this.#observingStaleness[id] = {
delete this.#observingStaleness[id]; isStale: false,
utc: Date.now()
};
if (typeof callback === 'function') {
this.#addCallbackToObserver(id, callback);
}
} }
}
#providingStaleness(domainObject) { #destroyObserver(id) {
return domainObject.telemetry?.staleness === true && this.#isRealTime; if (this.#observingStaleness[id]) {
} delete this.#observingStaleness[id];
}
}
#getObjectKeyString(object) { #providingStaleness(domainObject) {
return this.#openmct.objects.makeKeyString(object.identifier); return domainObject.telemetry?.staleness === true && this.#isRealTime;
} }
#addCallbackToObserver(id, callback) { #getObjectKeyString(object) {
this.#observingStaleness[id].callback = callback; return this.#openmct.objects.makeKeyString(object.identifier);
} }
#observerExists(id) { #addCallbackToObserver(id, callback) {
return this.#observingStaleness?.[id]; this.#observingStaleness[id].callback = callback;
} }
#observerExists(id) {
return this.#observingStaleness?.[id];
}
} }

View File

@ -20,56 +20,64 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define([], function () { define([
function StateGeneratorProvider() {}
function pointForTimestamp(timestamp, duration, name) { ], function (
return {
name: name,
utc: Math.floor(timestamp / duration) * duration,
value: Math.floor(timestamp / duration) % 2
};
}
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) { ) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) { function StateGeneratorProvider() {
var duration = domainObject.telemetry.duration * 1000;
var interval = setInterval(function () {
var now = Date.now();
var datum = pointForTimestamp(now, duration, domainObject.name);
datum.value = String(datum.value);
callback(datum);
}, duration);
return function () {
clearInterval(interval);
};
};
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.request = function (domainObject, options) {
var start = options.start;
var end = Math.min(Date.now(), options.end); // no future values
var duration = domainObject.telemetry.duration * 1000;
if (options.strategy === 'latest' || options.size === 1) {
start = end;
} }
var data = []; function pointForTimestamp(timestamp, duration, name) {
while (start <= end && data.length < 5000) { return {
data.push(pointForTimestamp(start, duration, domainObject.name)); name: name,
start += duration; utc: Math.floor(timestamp / duration) * duration,
value: Math.floor(timestamp / duration) % 2
};
} }
return Promise.resolve(data); StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
}; return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
var duration = domainObject.telemetry.duration * 1000;
var interval = setInterval(function () {
var now = Date.now();
var datum = pointForTimestamp(now, duration, domainObject.name);
datum.value = String(datum.value);
callback(datum);
}, duration);
return function () {
clearInterval(interval);
};
};
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
return domainObject.type === 'example.state-generator';
};
StateGeneratorProvider.prototype.request = function (domainObject, options) {
var start = options.start;
var end = Math.min(Date.now(), options.end); // no future values
var duration = domainObject.telemetry.duration * 1000;
if (options.strategy === 'latest' || options.size === 1) {
start = end;
}
var data = [];
while (start <= end && data.length < 5000) {
data.push(pointForTimestamp(start, duration, domainObject.name));
start += duration;
}
return Promise.resolve(data);
};
return StateGeneratorProvider;
return StateGeneratorProvider;
}); });

View File

@ -20,88 +20,93 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['uuid'], function ({ v4: uuid }) { define([
function WorkerInterface(openmct, StalenessProvider) { 'uuid'
// eslint-disable-next-line no-undef ], function (
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`; { v4: uuid }
this.StalenessProvider = StalenessProvider; ) {
this.worker = new Worker(workerUrl); function WorkerInterface(openmct, StalenessProvider) {
this.worker.onmessage = this.onMessage.bind(this); // eslint-disable-next-line no-undef
this.callbacks = {}; const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
this.staleTelemetryIds = {}; this.StalenessProvider = StalenessProvider;
this.worker = new Worker(workerUrl);
this.worker.onmessage = this.onMessage.bind(this);
this.callbacks = {};
this.staleTelemetryIds = {};
this.watchStaleness(); this.watchStaleness();
}
WorkerInterface.prototype.watchStaleness = function () {
this.StalenessProvider.on('stalenessEvent', ({ id, isStale }) => {
this.staleTelemetryIds[id] = isStale;
});
};
WorkerInterface.prototype.onMessage = function (message) {
message = message.data;
var callback = this.callbacks[message.id];
if (callback) {
callback(message);
} }
};
WorkerInterface.prototype.dispatch = function (request, data, callback) { WorkerInterface.prototype.watchStaleness = function () {
var message = { this.StalenessProvider.on('stalenessEvent', ({ id, isStale}) => {
request: request, this.staleTelemetryIds[id] = isStale;
data: data, });
id: uuid()
}; };
if (callback) { WorkerInterface.prototype.onMessage = function (message) {
this.callbacks[message.id] = callback; message = message.data;
} var callback = this.callbacks[message.id];
if (callback) {
callback(message);
}
};
this.worker.postMessage(message); WorkerInterface.prototype.dispatch = function (request, data, callback) {
var message = {
request: request,
data: data,
id: uuid()
};
return message.id; if (callback) {
}; this.callbacks[message.id] = callback;
}
WorkerInterface.prototype.request = function (request) { this.worker.postMessage(message);
var deferred = {};
var promise = new Promise(function (resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
var messageId;
let self = this; return message.id;
function callback(message) { };
if (message.error) {
deferred.reject(message.error);
} else {
deferred.resolve(message.data);
}
delete self.callbacks[messageId]; WorkerInterface.prototype.request = function (request) {
} var deferred = {};
var promise = new Promise(function (resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
var messageId;
messageId = this.dispatch('request', request, callback.bind(this)); let self = this;
function callback(message) {
if (message.error) {
deferred.reject(message.error);
} else {
deferred.resolve(message.data);
}
return promise; delete self.callbacks[messageId];
};
WorkerInterface.prototype.subscribe = function (request, cb) { }
const { id, loadDelay } = request;
const messageId = this.dispatch('subscribe', request, (message) => {
if (!this.staleTelemetryIds[id]) {
setTimeout(() => cb(message.data), Math.max(loadDelay, 0));
}
});
return function () { messageId = this.dispatch('request', request, callback.bind(this));
this.dispatch('unsubscribe', {
id: messageId
});
delete this.callbacks[messageId];
}.bind(this);
};
return WorkerInterface; return promise;
};
WorkerInterface.prototype.subscribe = function (request, cb) {
const id = request.id;
const messageId = this.dispatch('subscribe', request, (message) => {
if (!this.staleTelemetryIds[id]) {
cb(message.data);
}
});
return function () {
this.dispatch('unsubscribe', {
id: messageId
});
delete this.callbacks[messageId];
}.bind(this);
};
return WorkerInterface;
}); });

View File

@ -21,254 +21,204 @@
*****************************************************************************/ *****************************************************************************/
(function () { (function () {
var FIFTEEN_MINUTES = 15 * 60 * 1000;
var handlers = { var FIFTEEN_MINUTES = 15 * 60 * 1000;
subscribe: onSubscribe,
unsubscribe: onUnsubscribe,
request: onRequest
};
var subscriptions = {}; var handlers = {
subscribe: onSubscribe,
unsubscribe: onUnsubscribe,
request: onRequest
};
function workSubscriptions(timestamp) { var subscriptions = {};
var now = Date.now();
var nextWork = Math.min.apply(
Math,
Object.values(subscriptions).map(function (subscription) {
return subscription(now);
})
);
var wait = nextWork - now;
if (wait < 0) {
wait = 0;
}
if (Number.isFinite(wait)) { function workSubscriptions(timestamp) {
setTimeout(workSubscriptions, wait); var now = Date.now();
} var nextWork = Math.min.apply(Math, Object.values(subscriptions).map(function (subscription) {
} return subscription(now);
}));
function onSubscribe(message) { var wait = nextWork - now;
var data = message.data; if (wait < 0) {
wait = 0;
// Keep
var start = Date.now();
var step = 1000 / data.dataRateInHz;
var nextStep = start - (start % step) + step;
let work;
if (data.spectra) {
work = function (now) {
while (nextStep < now) {
const messageCopy = Object.create(message);
message.data.start = nextStep - 60 * 1000;
message.data.end = nextStep;
onRequest(messageCopy);
nextStep += step;
} }
return nextStep; if (Number.isFinite(wait)) {
}; setTimeout(workSubscriptions, wait);
} else { }
work = function (now) { }
while (nextStep < now) {
self.postMessage({ function onSubscribe(message) {
var data = message.data;
// Keep
var start = Date.now();
var step = 1000 / data.dataRateInHz;
var nextStep = start - (start % step) + step;
let work;
if (data.spectra) {
work = function (now) {
while (nextStep < now) {
const messageCopy = Object.create(message);
message.data.start = nextStep - (60 * 1000);
message.data.end = nextStep;
onRequest(messageCopy);
nextStep += step;
}
return nextStep;
};
} else {
work = function (now) {
while (nextStep < now) {
self.postMessage({
id: message.id,
data: {
name: data.name,
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness, data.infinityValues),
wavelengths: wavelengths(),
intensities: intensities(),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness, data.infinityValues)
}
});
nextStep += step;
}
return nextStep;
};
}
subscriptions[message.id] = work;
workSubscriptions();
}
function onUnsubscribe(message) {
delete subscriptions[message.data.id];
}
function onRequest(message) {
var request = message.data;
if (request.end === undefined) {
request.end = Date.now();
}
if (request.start === undefined) {
request.start = request.end - FIFTEEN_MINUTES;
}
var now = Date.now();
var start = request.start;
var end = request.end > now ? now : request.end;
var amplitude = request.amplitude;
var period = request.period;
var offset = request.offset;
var dataRateInHz = request.dataRateInHz;
var phase = request.phase;
var randomness = request.randomness;
var loadDelay = Math.max(request.loadDelay, 0);
var infinityValues = request.infinityValues;
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
var data = [];
for (; nextStep < end && data.length < 5000; nextStep += step) {
data.push({
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness, infinityValues),
wavelengths: wavelengths(),
intensities: intensities(),
cos: cos(nextStep, period, amplitude, offset, phase, randomness, infinityValues)
});
}
if (loadDelay === 0) {
postOnRequest(message, request, data);
} else {
setTimeout(() => postOnRequest(message, request, data), loadDelay);
}
}
function postOnRequest(message, request, data) {
self.postMessage({
id: message.id, id: message.id,
data: { data: request.spectra ? {
name: data.name, wavelength: data.map((item) => {
utc: nextStep, return item.wavelength;
yesterday: nextStep - 60 * 60 * 24 * 1000, }),
sin: sin( cos: data.map((item) => {
nextStep, return item.cos;
data.period, })
data.amplitude, } : data
data.offset, });
data.phase, }
data.randomness,
data.infinityValues, function cos(timestamp, period, amplitude, offset, phase, randomness, infinityValues) {
data.veryLargeValues if (infinityValues && Math.random() > 0.5) {
), return Number.POSITIVE_INFINITY;
wavelengths: wavelengths(), }
intensities: intensities(),
cos: cos( return amplitude
nextStep, * Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
data.period, }
data.amplitude,
data.offset, function sin(timestamp, period, amplitude, offset, phase, randomness, infinityValues) {
data.phase, if (infinityValues && Math.random() > 0.5) {
data.randomness, return Number.POSITIVE_INFINITY;
data.infinityValues, }
data.veryLargeValues
) return amplitude
* Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
function wavelengths() {
let values = [];
while (values.length < 5) {
const randomValue = Math.random() * 100;
if (!values.includes(randomValue)) {
values.push(String(randomValue));
} }
});
nextStep += step;
} }
return nextStep; return values;
};
} }
subscriptions[message.id] = work; function intensities() {
workSubscriptions(); let values = [];
} while (values.length < 5) {
const randomValue = Math.random() * 10;
function onUnsubscribe(message) { if (!values.includes(randomValue)) {
delete subscriptions[message.data.id]; values.push(String(randomValue));
} }
function onRequest(message) {
var request = message.data;
if (request.end === undefined) {
request.end = Date.now();
}
if (request.start === undefined) {
request.start = request.end - FIFTEEN_MINUTES;
}
var now = Date.now();
var start = request.start;
var end = request.end > now ? now : request.end;
var amplitude = request.amplitude;
var period = request.period;
var offset = request.offset;
var dataRateInHz = request.dataRateInHz;
var phase = request.phase;
var randomness = request.randomness;
var loadDelay = Math.max(request.loadDelay, 0);
var infinityValues = request.infinityValues;
var veryLargeValues = request.veryLargeValues;
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
var data = [];
for (; nextStep < end && data.length < 5000; nextStep += step) {
data.push({
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness, infinityValues, veryLargeValues),
wavelengths: wavelengths(),
intensities: intensities(),
cos: cos(nextStep, period, amplitude, offset, phase, randomness, infinityValues, veryLargeValues)
});
}
if (loadDelay === 0) {
postOnRequest(message, request, data);
} else {
setTimeout(() => postOnRequest(message, request, data), loadDelay);
}
}
function postOnRequest(message, request, data) {
self.postMessage({
id: message.id,
data: request.spectra
? {
wavelength: data.map((item) => {
return item.wavelength;
}),
cos: data.map((item) => {
return item.cos;
})
}
: data
});
}
function cos(timestamp, period, amplitude, offset, phase, randomness, infinityValues, veryLargeValues) {
const randomValue = Math.random();
if (infinityValues && veryLargeValues) {
if (randomValue < 0.33) return Number.POSITIVE_INFINITY;
if (randomValue < 0.66) return (randomValue > 0.5 ? -1 : 1) * 1e+177;
// if neither condition is met, it just proceeds to the normal return value
} else {
if (infinityValues && randomValue > 0.5) {
return Number.POSITIVE_INFINITY;
} }
if (veryLargeValues && randomValue > 0.5) {
return (randomValue > 0.5 ? -1 : 1) * 1e+177; return values;
}
function sendError(error, message) {
self.postMessage({
error: error.name + ': ' + error.message,
message: message,
id: message.id
});
}
self.onmessage = function handleMessage(event) {
var message = event.data;
var handler = handlers[message.request];
if (!handler) {
sendError(new Error('unknown message type'), message);
} else {
try {
handler(message);
} catch (e) {
sendError(e, message);
}
} }
} };
return ( }());
amplitude * Math.cos(phase + (timestamp / period / 1000) * Math.PI * 2) +
amplitude * Math.random() * randomness +
offset
);
}
function sin(timestamp, period, amplitude, offset, phase, randomness, infinityValues, veryLargeValues) {
const randomValue = Math.random();
if (infinityValues && veryLargeValues) {
if (randomValue < 0.33) return Number.POSITIVE_INFINITY;
if (randomValue < 0.66) return (randomValue > 0.5 ? -1 : 1) * 1e+177;
// if neither condition is met, it just proceeds to the normal return value
} else {
if (infinityValues && randomValue > 0.5) {
return Number.POSITIVE_INFINITY;
}
if (veryLargeValues && randomValue > 0.5) {
return (randomValue > 0.5 ? -1 : 1) * 1e+177;
}
}
return (
amplitude * Math.sin(phase + (timestamp / period / 1000) * Math.PI * 2) +
amplitude * Math.random() * randomness +
offset
);
}
function wavelengths() {
let values = [];
while (values.length < 5) {
const randomValue = Math.random() * 100;
if (!values.includes(randomValue)) {
values.push(String(randomValue));
}
}
return values;
}
function intensities() {
let values = [];
while (values.length < 5) {
const randomValue = Math.random() * 10;
if (!values.includes(randomValue)) {
values.push(String(randomValue));
}
}
return values;
}
function sendError(error, message) {
self.postMessage({
error: error.name + ': ' + error.message,
message: message,
id: message.id
});
}
self.onmessage = function handleMessage(event) {
var message = event.data;
var handler = handlers[message.request];
if (!handler) {
sendError(new Error('unknown message type'), message);
} else {
try {
handler(message);
} catch (e) {
sendError(e, message);
}
}
};
})();

View File

@ -20,142 +20,163 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import GeneratorMetadataProvider from './GeneratorMetadataProvider'; import GeneratorProvider from "./GeneratorProvider";
import GeneratorProvider from './GeneratorProvider'; import SinewaveLimitProvider from "./SinewaveLimitProvider";
import SinewaveLimitProvider from './SinewaveLimitProvider'; import SinewaveStalenessProvider from "./SinewaveStalenessProvider";
import SinewaveStalenessProvider from './SinewaveStalenessProvider'; import StateGeneratorProvider from "./StateGeneratorProvider";
import StateGeneratorProvider from './StateGeneratorProvider'; import GeneratorMetadataProvider from "./GeneratorMetadataProvider";
export default function (openmct) { export default function (openmct) {
openmct.types.addType('example.state-generator', {
name: 'State Generator',
description:
'For development use. Generates example enumerated telemetry by cycling through a given set of states.',
cssClass: 'icon-generator-telemetry',
creatable: true,
form: [
{
name: 'State Duration (seconds)',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'duration',
required: true,
property: ['telemetry', 'duration']
}
],
initialize: function (object) {
object.telemetry = {
duration: 5
};
}
});
openmct.telemetry.addProvider(new StateGeneratorProvider()); openmct.types.addType("example.state-generator", {
name: "State Generator",
description: "For development use. Generates example enumerated telemetry by cycling through a given set of states.",
cssClass: "icon-generator-telemetry",
creatable: true,
form: [
{
name: "State Duration (seconds)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "duration",
required: true,
property: [
"telemetry",
"duration"
]
}
],
initialize: function (object) {
object.telemetry = {
duration: 5
};
}
});
openmct.types.addType('generator', { openmct.telemetry.addProvider(new StateGeneratorProvider());
name: 'Sine Wave Generator',
description:
'For development use. Generates example streaming telemetry data using a simple sine wave algorithm.',
cssClass: 'icon-generator-telemetry',
creatable: true,
form: [
{
name: 'Period',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'period',
required: true,
property: ['telemetry', 'period']
},
{
name: 'Amplitude',
control: 'numberfield',
cssClass: 'l-numeric',
key: 'amplitude',
required: true,
property: ['telemetry', 'amplitude']
},
{
name: 'Offset',
control: 'numberfield',
cssClass: 'l-numeric',
key: 'offset',
required: true,
property: ['telemetry', 'offset']
},
{
name: 'Data Rate (hz)',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'dataRateInHz',
required: true,
property: ['telemetry', 'dataRateInHz']
},
{
name: 'Phase (radians)',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'phase',
required: true,
property: ['telemetry', 'phase']
},
{
name: 'Randomness',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'randomness',
required: true,
property: ['telemetry', 'randomness']
},
{
name: 'Loading Delay (ms)',
control: 'numberfield',
cssClass: 'l-input-sm l-numeric',
key: 'loadDelay',
required: true,
property: ['telemetry', 'loadDelay']
},
{
name: 'Include Infinity Values',
control: 'toggleSwitch',
cssClass: 'l-input',
key: 'infinityValues',
property: ['telemetry', 'infinityValues']
},
{
name: 'Include Very Large Values',
control: 'toggleSwitch',
cssClass: 'l-input',
key: 'veryLargeValues',
property: ['telemetry', 'veryLargeValues']
},
{
name: 'Provide Staleness Updates',
control: 'toggleSwitch',
cssClass: 'l-input',
key: 'staleness',
property: ['telemetry', 'staleness']
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0,
randomness: 0,
loadDelay: 0,
infinityValues: false,
veryLargeValues: false,
staleness: false
};
}
});
const stalenessProvider = new SinewaveStalenessProvider(openmct);
openmct.telemetry.addProvider(new GeneratorProvider(openmct, stalenessProvider)); openmct.types.addType("generator", {
openmct.telemetry.addProvider(new GeneratorMetadataProvider()); name: "Sine Wave Generator",
openmct.telemetry.addProvider(new SinewaveLimitProvider()); description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
openmct.telemetry.addProvider(stalenessProvider); cssClass: "icon-generator-telemetry",
creatable: true,
form: [
{
name: "Period",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "period",
required: true,
property: [
"telemetry",
"period"
]
},
{
name: "Amplitude",
control: "numberfield",
cssClass: "l-numeric",
key: "amplitude",
required: true,
property: [
"telemetry",
"amplitude"
]
},
{
name: "Offset",
control: "numberfield",
cssClass: "l-numeric",
key: "offset",
required: true,
property: [
"telemetry",
"offset"
]
},
{
name: "Data Rate (hz)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "dataRateInHz",
required: true,
property: [
"telemetry",
"dataRateInHz"
]
},
{
name: "Phase (radians)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "phase",
required: true,
property: [
"telemetry",
"phase"
]
},
{
name: "Randomness",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "randomness",
required: true,
property: [
"telemetry",
"randomness"
]
},
{
name: "Loading Delay (ms)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "loadDelay",
required: true,
property: [
"telemetry",
"loadDelay"
]
},
{
name: "Include Infinity Values",
control: "toggleSwitch",
cssClass: "l-input",
key: "infinityValues",
property: [
"telemetry",
"infinityValues"
]
},
{
name: "Provide Staleness Updates",
control: "toggleSwitch",
cssClass: "l-input",
key: "staleness",
property: [
"telemetry",
"staleness"
]
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0,
randomness: 0,
loadDelay: 0,
infinityValues: false,
staleness: false
};
}
});
const stalenessProvider = new SinewaveStalenessProvider(openmct);
openmct.telemetry.addProvider(new GeneratorProvider(openmct, stalenessProvider));
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
openmct.telemetry.addProvider(new SinewaveLimitProvider());
openmct.telemetry.addProvider(stalenessProvider);
} }

View File

@ -57,9 +57,7 @@ define([
if (isIdentifier(keyString)) { if (isIdentifier(keyString)) {
// TODO REMOVE FOR OMM-RELEASE-5.0 // TODO REMOVE FOR OMM-RELEASE-5.0
if (!keyString.namespace && keyString.key.includes(':')) { if (!keyString.namespace && keyString.key.includes(':')) {
console.warn(`smushed key: ${keyString.key}`); console.error(`smushed key: ${keyString.key}`);
return parseKeyString(keyString.key);
} }
return keyString; return keyString;

View File

@ -35,34 +35,34 @@ export default class DuplicateAction {
} }
invoke(objectPath) { invoke(objectPath) {
this.object = objectPath[0]; const object = objectPath[0];
this.parent = objectPath[1]; const parent = objectPath[1];
this.showForm(this.object, this.parent); this.showForm(object, parent);
} }
inNavigationPath() { inNavigationPath(object) {
return this.openmct.router.path return this.openmct.router.path
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, this.object.identifier)); .some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, object.identifier));
} }
async onSave(changes) { async onSave(object, parent, changes) {
this.startTransaction(); this.startTransaction();
let inNavigationPath = this.inNavigationPath(); let inNavigationPath = this.inNavigationPath(object);
if (inNavigationPath && this.openmct.editor.isEditing()) { if (inNavigationPath && this.openmct.editor.isEditing()) {
this.openmct.editor.save(); this.openmct.editor.save();
} }
let duplicationTask = new DuplicateTask(this.openmct); let duplicationTask = new DuplicateTask(this.openmct);
if (changes.name && (changes.name !== this.object.name)) { if (changes.name && (changes.name !== object.name)) {
duplicationTask.changeName(changes.name); duplicationTask.changeName(changes.name);
} }
const parentDomainObjectpath = changes.location || [this.parent]; const parentDomainObjectpath = changes.location || [parent];
const parent = parentDomainObjectpath[0]; const parentObject = parentDomainObjectpath[0];
await duplicationTask.duplicate(this.object, parent); await duplicationTask.duplicate(object, parentObject);
return this.saveTransaction(); return this.saveTransaction();
} }
@ -88,7 +88,7 @@ export default class DuplicateAction {
control: "locator", control: "locator",
required: true, required: true,
parent: parentDomainObject, parent: parentDomainObject,
validate: this.validate(parentDomainObject), validate: this.validate(domainObject, parentDomainObject),
key: 'location' key: 'location'
} }
] ]
@ -97,16 +97,19 @@ export default class DuplicateAction {
}; };
this.openmct.forms.showForm(formStructure) this.openmct.forms.showForm(formStructure)
.then(this.onSave.bind(this)); .then(changes => {
let onSave = this.onSave.bind(this);
onSave(domainObject, parentDomainObject, changes);
});
} }
validate(currentParent) { validate(domainObject, currentParent) {
return (data) => { return (data) => {
const parentCandidate = data.value[0]; const parentCandidate = data.value[0];
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); let objectKeystring = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) { if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
return false; return false;
@ -125,7 +128,7 @@ export default class DuplicateAction {
return false; return false;
} }
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object); return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, domainObject);
}; };
} }