mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
[Staleness] API and Component Functionality (#6108)
* initial telemetry api updates for staleness support * modifying staleness to a subsription style * fixing variable name * debuggin * put the subscribe in the wrong place * stale class for object views * temp cyan border for testing * added staleness to swg, working on stacked plot staleness * WIP: stacked plot staleness * reverting, going a different route * staleness on stacked plots * plot legend staleness * remove debug code * staleness for alphanumerics * lad table and table set staleness * overlay plot staleness * remove debug code * hardened lad staleness functionality fixed plots without composition bug * adding staleness to gauges * renaming telemetry age check functionality so it does not conflict with new staleness functionality * couple one-off fixes here and there, and WIP for condition sets, moving to telemetry tables to facilitate styling of completed components * small fix on lad tables, added staleness functionality to tables * finishing up condition sets * some cleaning up * adding border to condition sets when an item is stale * fixing dub sub * addressing PR comment, moving repeated code to a function * robustified the SWG stalenes provider, little fixes here and there as far as cleaning up listeners and... whatnot * removing debug code * typo fixes * cleanin up debug code * created a simple stalenes mixin for more basic usage in components * more robustification, if a new staleness subscription happens, will now send the current staleness value if we have it, beefed up example stalenes swg provider * beefed up staleness mixin a bit to give it more use * copyright * cleanin up ladtable code * cleanin up ladtable code * cleaning up lad table sets * some minor updates * Closes #6109 - New staleness glyph and font CSS added. * Closes #6109 - Normalized staleness colors as theme constants. - New mixins for staleness application to view elements. - Applied staleness styling to all relevant view elements. - TODO: smoke-test in Show theme. * adding staleness utils helper, mixin and isStale functionalirty for telemtry api * Closes #6109 - Refined style for Snow theme. * need to have one domainObject per stalenes utility * making sure we handle domains correctly while dealing with staleness * couple fixes * moving abort controller logic to a spot where it makes more sense * added some more info for the StalenesProvider interface docs * returning undefinded for ifStale requests with no provider * debuggin * debuggin * missed "isStale" call in condtioncollections * removing debug code and using mixin unsubscribe in gauge * fixing tests * more targeted tree item click Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com> Co-authored-by: Andrew Henry <akhenry@gmail.com> Co-authored-by: Scott Bell <scott@traclabs.com>
This commit is contained in:
parent
1b71a3bf33
commit
8847c862fa
@ -197,11 +197,16 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
||||
page.goto('./#/browse/mine?hideTree=false'),
|
||||
page.click('.c-disclosure-triangle')
|
||||
]);
|
||||
// Click Clock
|
||||
await page.click(`text=${clock.name}`);
|
||||
|
||||
const treePane = page.locator('#tree-pane');
|
||||
// Click Clock
|
||||
await treePane.getByRole('treeitem', {
|
||||
name: clock.name
|
||||
}).click();
|
||||
// Click Notebook
|
||||
await page.click(`text=${notebook.name}`);
|
||||
await page.getByRole('treeitem', {
|
||||
name: notebook.name
|
||||
}).click();
|
||||
|
||||
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
|
||||
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
|
||||
|
@ -32,7 +32,7 @@ test.use({
|
||||
}
|
||||
});
|
||||
|
||||
test.describe('ExportAsJSON', () => {
|
||||
test.fixme('ExportAsJSON', () => {
|
||||
test('User can set autoscale with a valid range @snapshot', async ({ page, openmctConfig }) => {
|
||||
const { myItemsFolderName } = openmctConfig;
|
||||
|
||||
|
@ -37,8 +37,9 @@ define([
|
||||
infinityValues: false
|
||||
};
|
||||
|
||||
function GeneratorProvider(openmct) {
|
||||
this.workerInterface = new WorkerInterface(openmct);
|
||||
function GeneratorProvider(openmct, StalenessProvider) {
|
||||
this.openmct = openmct;
|
||||
this.workerInterface = new WorkerInterface(openmct, StalenessProvider);
|
||||
}
|
||||
|
||||
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
@ -81,6 +82,7 @@ define([
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
|
||||
workerRequest.id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
workerRequest.name = domainObject.name;
|
||||
|
||||
return workerRequest;
|
||||
|
151
example/generator/SinewaveStalenessProvider.js
Normal file
151
example/generator/SinewaveStalenessProvider.js
Normal file
@ -0,0 +1,151 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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';
|
||||
|
||||
export default class SinewaveLimitProvider extends EventEmitter {
|
||||
|
||||
constructor(openmct) {
|
||||
super();
|
||||
|
||||
this.openmct = openmct;
|
||||
this.observingStaleness = {};
|
||||
this.watchingTheClock = false;
|
||||
this.isRealTime = undefined;
|
||||
}
|
||||
|
||||
supportsStaleness(domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
}
|
||||
|
||||
isStale(domainObject, options) {
|
||||
if (!this.providingStaleness(domainObject)) {
|
||||
return Promise.resolve({
|
||||
isStale: false,
|
||||
utc: 0
|
||||
});
|
||||
}
|
||||
|
||||
const id = this.getObjectKeyString(domainObject);
|
||||
|
||||
if (!this.observerExists(id)) {
|
||||
this.createObserver(id);
|
||||
}
|
||||
|
||||
return Promise.resolve(this.observingStaleness[id].isStale);
|
||||
}
|
||||
|
||||
subscribeToStaleness(domainObject, callback) {
|
||||
const id = this.getObjectKeyString(domainObject);
|
||||
|
||||
if (this.isRealTime === undefined) {
|
||||
this.updateRealTime(this.openmct.time.clock());
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
handleClockUpdate() {
|
||||
let observers = Object.values(this.observingStaleness).length > 0;
|
||||
|
||||
if (observers && !this.watchingTheClock) {
|
||||
this.watchingTheClock = true;
|
||||
this.openmct.time.on('clock', this.updateRealTime, this);
|
||||
} else if (!observers && this.watchingTheClock) {
|
||||
this.watchingTheClock = false;
|
||||
this.openmct.time.off('clock', this.updateRealTime, this);
|
||||
}
|
||||
}
|
||||
|
||||
updateRealTime(clock) {
|
||||
this.isRealTime = clock !== undefined;
|
||||
|
||||
if (!this.isRealTime) {
|
||||
Object.keys(this.observingStaleness).forEach((id) => {
|
||||
this.updateStaleness(id, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateStaleness(id, isStale) {
|
||||
this.observingStaleness[id].isStale = isStale;
|
||||
this.observingStaleness[id].utc = Date.now();
|
||||
this.observingStaleness[id].callback({
|
||||
isStale: this.observingStaleness[id].isStale,
|
||||
utc: this.observingStaleness[id].utc
|
||||
});
|
||||
this.emit('stalenessEvent', {
|
||||
id,
|
||||
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) {
|
||||
delete this.observingStaleness[id];
|
||||
}
|
||||
|
||||
providingStaleness(domainObject) {
|
||||
return domainObject.telemetry?.staleness === true && this.isRealTime;
|
||||
}
|
||||
|
||||
getObjectKeyString(object) {
|
||||
return this.openmct.objects.makeKeyString(object.identifier);
|
||||
}
|
||||
|
||||
addCallbackToObserver(id, callback) {
|
||||
this.observingStaleness[id].callback = callback;
|
||||
}
|
||||
|
||||
observerExists(id) {
|
||||
return this.observingStaleness?.[id];
|
||||
}
|
||||
}
|
@ -25,14 +25,24 @@ define([
|
||||
], function (
|
||||
{ v4: uuid }
|
||||
) {
|
||||
function WorkerInterface(openmct) {
|
||||
function WorkerInterface(openmct, StalenessProvider) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
|
||||
this.StalenessProvider = StalenessProvider;
|
||||
this.worker = new Worker(workerUrl);
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.callbacks = {};
|
||||
this.staleTelemetryIds = {};
|
||||
|
||||
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];
|
||||
@ -83,11 +93,12 @@ define([
|
||||
};
|
||||
|
||||
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||
function callback(message) {
|
||||
cb(message.data);
|
||||
}
|
||||
|
||||
var messageId = this.dispatch('subscribe', request, callback);
|
||||
const id = request.id;
|
||||
const messageId = this.dispatch('subscribe', request, (message) => {
|
||||
if (!this.staleTelemetryIds[id]) {
|
||||
cb(message.data);
|
||||
}
|
||||
});
|
||||
|
||||
return function () {
|
||||
this.dispatch('unsubscribe', {
|
||||
|
@ -20,158 +20,163 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./GeneratorProvider",
|
||||
"./SinewaveLimitProvider",
|
||||
"./StateGeneratorProvider",
|
||||
"./GeneratorMetadataProvider"
|
||||
], function (
|
||||
GeneratorProvider,
|
||||
SinewaveLimitProvider,
|
||||
StateGeneratorProvider,
|
||||
GeneratorMetadataProvider
|
||||
) {
|
||||
import GeneratorProvider from "./GeneratorProvider";
|
||||
import SinewaveLimitProvider from "./SinewaveLimitProvider";
|
||||
import SinewaveStalenessProvider from "./SinewaveStalenessProvider";
|
||||
import StateGeneratorProvider from "./StateGeneratorProvider";
|
||||
import GeneratorMetadataProvider from "./GeneratorMetadataProvider";
|
||||
|
||||
return 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.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.telemetry.addProvider(new StateGeneratorProvider());
|
||||
|
||||
openmct.types.addType("generator", {
|
||||
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"
|
||||
]
|
||||
}
|
||||
],
|
||||
initialize: function (object) {
|
||||
object.telemetry = {
|
||||
period: 10,
|
||||
amplitude: 1,
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
phase: 0,
|
||||
randomness: 0,
|
||||
loadDelay: 0,
|
||||
infinityValues: false
|
||||
};
|
||||
openmct.types.addType("generator", {
|
||||
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: "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));
|
||||
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
|
||||
openmct.telemetry.addProvider(new SinewaveLimitProvider());
|
||||
};
|
||||
|
||||
});
|
||||
openmct.telemetry.addProvider(new GeneratorProvider(openmct, stalenessProvider));
|
||||
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
|
||||
openmct.telemetry.addProvider(new SinewaveLimitProvider());
|
||||
openmct.telemetry.addProvider(stalenessProvider);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ export default class TelemetryAPI {
|
||||
this.formatMapCache = new WeakMap();
|
||||
this.formatters = new Map();
|
||||
this.limitProviders = [];
|
||||
this.stalenessProviders = [];
|
||||
this.metadataCache = new WeakMap();
|
||||
this.metadataProviders = [new DefaultMetadataProvider(this.openmct)];
|
||||
this.noRequestProviderForAllObjects = false;
|
||||
@ -114,6 +115,10 @@ export default class TelemetryAPI {
|
||||
if (provider.supportsLimits) {
|
||||
this.limitProviders.unshift(provider);
|
||||
}
|
||||
|
||||
if (provider.supportsStaleness) {
|
||||
this.stalenessProviders.unshift(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,7 +130,7 @@ export default class TelemetryAPI {
|
||||
return provider.supportsSubscribe.apply(provider, args);
|
||||
}
|
||||
|
||||
return this.subscriptionProviders.filter(supportsDomainObject)[0];
|
||||
return this.subscriptionProviders.find(supportsDomainObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,25 +143,25 @@ export default class TelemetryAPI {
|
||||
return provider.supportsRequest.apply(provider, args);
|
||||
}
|
||||
|
||||
return this.requestProviders.filter(supportsDomainObject)[0];
|
||||
return this.requestProviders.find(supportsDomainObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
#findMetadataProvider(domainObject) {
|
||||
return this.metadataProviders.filter(function (p) {
|
||||
return p.supportsMetadata(domainObject);
|
||||
})[0];
|
||||
return this.metadataProviders.find((provider) => {
|
||||
return provider.supportsMetadata(domainObject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
#findLimitEvaluator(domainObject) {
|
||||
return this.limitProviders.filter(function (p) {
|
||||
return p.supportsLimits(domainObject);
|
||||
})[0];
|
||||
return this.limitProviders.find((provider) => {
|
||||
return provider.supportsLimits(domainObject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -351,6 +356,101 @@ export default class TelemetryAPI {
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to staleness updates for a specific domain object.
|
||||
* The callback will be called whenever staleness changes.
|
||||
*
|
||||
* @method subscribeToStaleness
|
||||
* @memberof module:openmct.TelemetryAPI~StalenessProvider#
|
||||
* @param {module:openmct.DomainObject} domainObject the object
|
||||
* to watch for staleness updates
|
||||
* @param {Function} callback the callback to invoke with staleness data,
|
||||
* as it is received: ex.
|
||||
* {
|
||||
* isStale: <Boolean>,
|
||||
* timestamp: <timestamp>
|
||||
* }
|
||||
* @returns {Function} a function which may be called to terminate
|
||||
* the subscription to staleness updates
|
||||
*/
|
||||
subscribeToStaleness(domainObject, callback) {
|
||||
const provider = this.#findStalenessProvider(domainObject);
|
||||
|
||||
if (!this.stalenessSubscriberCache) {
|
||||
this.stalenessSubscriberCache = {};
|
||||
}
|
||||
|
||||
const keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let stalenessSubscriber = this.stalenessSubscriberCache[keyString];
|
||||
|
||||
if (!stalenessSubscriber) {
|
||||
stalenessSubscriber = this.stalenessSubscriberCache[keyString] = {
|
||||
callbacks: [callback]
|
||||
};
|
||||
if (provider) {
|
||||
stalenessSubscriber.unsubscribe = provider
|
||||
.subscribeToStaleness(domainObject, (stalenessResponse) => {
|
||||
stalenessSubscriber.callbacks.forEach((cb) => {
|
||||
cb(stalenessResponse);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
stalenessSubscriber.unsubscribe = () => {};
|
||||
}
|
||||
} else {
|
||||
stalenessSubscriber.callbacks.push(callback);
|
||||
}
|
||||
|
||||
return function unsubscribe() {
|
||||
stalenessSubscriber.callbacks = stalenessSubscriber.callbacks.filter((cb) => {
|
||||
return cb !== callback;
|
||||
});
|
||||
if (stalenessSubscriber.callbacks.length === 0) {
|
||||
stalenessSubscriber.unsubscribe();
|
||||
delete this.stalenessSubscriberCache[keyString];
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request telemetry staleness for a domain object.
|
||||
*
|
||||
* @method isStale
|
||||
* @memberof module:openmct.TelemetryAPI~StalenessProvider#
|
||||
* @param {module:openmct.DomainObject} domainObject the object
|
||||
* which has associated telemetry staleness
|
||||
* @returns {Promise.<StalenessResponseObject>} a promise for a StalenessResponseObject
|
||||
* or undefined if no provider exists
|
||||
*/
|
||||
async isStale(domainObject) {
|
||||
const provider = this.#findStalenessProvider(domainObject);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
const options = { signal: abortController.signal };
|
||||
this.requestAbortControllers.add(abortController);
|
||||
|
||||
try {
|
||||
const staleness = await provider.isStale(domainObject, options);
|
||||
|
||||
return staleness;
|
||||
} finally {
|
||||
this.requestAbortControllers.delete(abortController);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
#findStalenessProvider(domainObject) {
|
||||
return this.stalenessProviders.find((provider) => {
|
||||
return provider.supportsStaleness(domainObject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get telemetry metadata for a given domain object. Returns a telemetry
|
||||
* metadata manager which provides methods for interrogating telemetry
|
||||
@ -661,6 +761,29 @@ export default class TelemetryAPI {
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides telemetry staleness data. To subscribe to telemetry stalenes,
|
||||
* new StalenessProvider implementations should be
|
||||
* [registered]{@link module:openmct.TelemetryAPI#addProvider}.
|
||||
*
|
||||
* @interface StalenessProvider
|
||||
* @property {function} supportsStaleness receieves a domainObject and
|
||||
* returns a boolean to indicate it will provide staleness
|
||||
* @property {function} subscribeToStaleness receieves a domainObject to
|
||||
* be subscribed to and a callback to invoke with a StalenessResponseObject
|
||||
* @property {function} isStale an asynchronous method called with a domainObject
|
||||
* and an options object which currently has an abort signal, ex.
|
||||
* { signal: <AbortController.signal> }
|
||||
* this method should return a current StalenessResponseObject
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} StalenessResponseObject
|
||||
* @property {Boolean} isStale boolean representing the staleness state
|
||||
* @property {Number} timestamp Unix timestamp in milliseconds
|
||||
*/
|
||||
|
||||
/**
|
||||
* An interface for retrieving telemetry data associated with a domain
|
||||
* object.
|
||||
|
@ -29,7 +29,7 @@
|
||||
<td class="js-second-data">{{ formattedTimestamp }}</td>
|
||||
<td
|
||||
class="js-third-data"
|
||||
:class="valueClass"
|
||||
:class="valueClasses"
|
||||
>{{ value }}</td>
|
||||
<td
|
||||
v-if="hasUnits"
|
||||
@ -63,6 +63,12 @@ export default {
|
||||
hasUnits: {
|
||||
type: Boolean,
|
||||
requred: true
|
||||
},
|
||||
isStale: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -81,14 +87,22 @@ export default {
|
||||
|
||||
return this.formats[this.valueKey].format(this.datum);
|
||||
},
|
||||
valueClass() {
|
||||
if (!this.datum) {
|
||||
return '';
|
||||
valueClasses() {
|
||||
let classes = [];
|
||||
|
||||
if (this.isStale) {
|
||||
classes.push('is-stale');
|
||||
}
|
||||
|
||||
const limit = this.limitEvaluator.evaluate(this.datum, this.valueMetadata);
|
||||
if (this.datum) {
|
||||
const limit = this.limitEvaluator.evaluate(this.datum, this.valueMetadata);
|
||||
|
||||
return limit ? limit.cssClass : '';
|
||||
if (limit) {
|
||||
classes.push(limit.cssClass);
|
||||
}
|
||||
}
|
||||
|
||||
return classes;
|
||||
|
||||
},
|
||||
formattedTimestamp() {
|
||||
|
@ -21,7 +21,10 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-lad-table-wrapper u-style-receiver js-style-receiver">
|
||||
<div
|
||||
class="c-lad-table-wrapper u-style-receiver js-style-receiver"
|
||||
:class="staleClass"
|
||||
>
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -38,6 +41,7 @@
|
||||
:domain-object="ladRow.domainObject"
|
||||
:path-to-table="objectPath"
|
||||
:has-units="hasUnits"
|
||||
:is-stale="staleObjects.includes(ladRow.key)"
|
||||
@rowContextClick="updateViewContext"
|
||||
/>
|
||||
</tbody>
|
||||
@ -46,7 +50,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import LadRow from './LADRow.vue';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -66,7 +72,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
viewContext: {}
|
||||
viewContext: {},
|
||||
staleObjects: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -80,6 +87,13 @@ export default {
|
||||
});
|
||||
|
||||
return itemsWithUnits.length !== 0;
|
||||
},
|
||||
staleClass() {
|
||||
if (this.staleObjects.length !== 0) {
|
||||
return 'is-stale';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -88,11 +102,17 @@ export default {
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.reorder);
|
||||
this.composition.load();
|
||||
this.stalenessSubscription = {};
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.reorder);
|
||||
|
||||
Object.values(this.stalenessSubscription).forEach(stalenessSubscription => {
|
||||
stalenessSubscription.unsubscribe();
|
||||
stalenessSubscription.stalenessUtils.destroy();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
addItem(domainObject) {
|
||||
@ -101,23 +121,55 @@ export default {
|
||||
item.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
this.items.push(item);
|
||||
|
||||
this.stalenessSubscription[item.key] = {};
|
||||
this.stalenessSubscription[item.key].stalenessUtils = new StalenessUtils(this.openmct, domainObject);
|
||||
this.openmct.telemetry.isStale(domainObject).then((stalenessResponse) => {
|
||||
if (stalenessResponse !== undefined) {
|
||||
this.handleStaleness(item.key, stalenessResponse);
|
||||
}
|
||||
});
|
||||
const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness(domainObject, (stalenessResponse) => {
|
||||
this.handleStaleness(item.key, stalenessResponse);
|
||||
});
|
||||
|
||||
this.stalenessSubscription[item.key].unsubscribe = stalenessSubscription;
|
||||
},
|
||||
removeItem(identifier) {
|
||||
let index = this.items.findIndex(item => this.openmct.objects.makeKeyString(identifier) === item.key);
|
||||
const SKIP_CHECK = true;
|
||||
const keystring = this.openmct.objects.makeKeyString(identifier);
|
||||
const index = this.items.findIndex(item => keystring === item.key);
|
||||
|
||||
this.items.splice(index, 1);
|
||||
|
||||
this.stalenessSubscription[keystring].unsubscribe();
|
||||
this.handleStaleness(keystring, { isStale: false }, SKIP_CHECK);
|
||||
},
|
||||
reorder(reorderPlan) {
|
||||
let oldItems = this.items.slice();
|
||||
const oldItems = this.items.slice();
|
||||
reorderPlan.forEach((reorderEvent) => {
|
||||
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]);
|
||||
});
|
||||
},
|
||||
metadataHasUnits(valueMetadatas) {
|
||||
let metadataWithUnits = valueMetadatas.filter(metadatum => metadatum.unit);
|
||||
const metadataWithUnits = valueMetadatas.filter(metadatum => metadatum.unit);
|
||||
|
||||
return metadataWithUnits.length > 0;
|
||||
},
|
||||
handleStaleness(id, stalenessResponse, skipCheck = false) {
|
||||
if (skipCheck || this.stalenessSubscription[id].stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
const index = this.staleObjects.indexOf(id);
|
||||
if (stalenessResponse.isStale) {
|
||||
if (index === -1) {
|
||||
this.staleObjects.push(id);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
this.staleObjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateViewContext(rowContext) {
|
||||
this.viewContext.row = rowContext;
|
||||
},
|
||||
|
@ -21,42 +21,50 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Value</th>
|
||||
<th v-if="hasUnits">Unit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template
|
||||
v-for="ladTable in ladTableObjects"
|
||||
>
|
||||
<tr
|
||||
:key="ladTable.key"
|
||||
class="c-table__group-header js-lad-table-set__table-headers"
|
||||
>
|
||||
<td colspan="10">
|
||||
{{ ladTable.domainObject.name }}
|
||||
</td>
|
||||
<div
|
||||
class="c-lad-table-wrapper u-style-receiver js-style-receiver"
|
||||
:class="staleClass"
|
||||
>
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Value</th>
|
||||
<th v-if="hasUnits">Unit</th>
|
||||
</tr>
|
||||
<lad-row
|
||||
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
||||
:key="ladRow.key"
|
||||
:domain-object="ladRow.domainObject"
|
||||
:path-to-table="ladTable.objectPath"
|
||||
:has-units="hasUnits"
|
||||
@rowContextClick="updateViewContext"
|
||||
/>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template
|
||||
v-for="ladTable in ladTableObjects"
|
||||
>
|
||||
<tr
|
||||
:key="ladTable.key"
|
||||
class="c-table__group-header js-lad-table-set__table-headers"
|
||||
>
|
||||
<td colspan="10">
|
||||
{{ ladTable.domainObject.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<lad-row
|
||||
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
||||
:key="ladRow.key"
|
||||
:domain-object="ladRow.domainObject"
|
||||
:path-to-table="ladTable.objectPath"
|
||||
:has-units="hasUnits"
|
||||
:is-stale="staleObjects.includes(ladRow.key)"
|
||||
@rowContextClick="updateViewContext"
|
||||
/>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import LadRow from './LADRow.vue';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -74,7 +82,8 @@ export default {
|
||||
ladTableObjects: [],
|
||||
ladTelemetryObjects: {},
|
||||
compositions: [],
|
||||
viewContext: {}
|
||||
viewContext: {},
|
||||
staleObjects: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -95,6 +104,13 @@ export default {
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
staleClass() {
|
||||
if (this.staleObjects.length !== 0) {
|
||||
return 'is-stale';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -103,6 +119,8 @@ export default {
|
||||
this.composition.on('remove', this.removeLadTable);
|
||||
this.composition.on('reorder', this.reorderLadTables);
|
||||
this.composition.load();
|
||||
|
||||
this.stalenessSubscription = {};
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addLadTable);
|
||||
@ -112,6 +130,11 @@ export default {
|
||||
c.composition.off('add', c.addCallback);
|
||||
c.composition.off('remove', c.removeCallback);
|
||||
});
|
||||
|
||||
Object.values(this.stalenessSubscription).forEach(stalenessSubscription => {
|
||||
stalenessSubscription.unsubscribe();
|
||||
stalenessSubscription.stalenessUtils.destroy();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
addLadTable(domainObject) {
|
||||
@ -160,18 +183,57 @@ export default {
|
||||
telemetryObjects.push(telemetryObject);
|
||||
|
||||
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects);
|
||||
|
||||
// if tracking already, possibly in another table, return
|
||||
if (this.stalenessSubscription[telemetryObject.key]) {
|
||||
return;
|
||||
} else {
|
||||
this.stalenessSubscription[telemetryObject.key] = {};
|
||||
this.stalenessSubscription[telemetryObject.key].stalenessUtils = new StalenessUtils(this.openmct, domainObject);
|
||||
}
|
||||
|
||||
this.openmct.telemetry.isStale(domainObject).then((stalenessResponse) => {
|
||||
if (stalenessResponse !== undefined) {
|
||||
this.handleStaleness(telemetryObject.key, stalenessResponse);
|
||||
}
|
||||
});
|
||||
const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness(domainObject, (stalenessResponse) => {
|
||||
this.handleStaleness(telemetryObject.key, stalenessResponse);
|
||||
});
|
||||
|
||||
this.stalenessSubscription[telemetryObject.key].unsubscribe = stalenessSubscription;
|
||||
};
|
||||
},
|
||||
removeTelemetryObject(ladTable) {
|
||||
return (identifier) => {
|
||||
const SKIP_CHECK = true;
|
||||
const keystring = this.openmct.objects.makeKeyString(identifier);
|
||||
let telemetryObjects = this.ladTelemetryObjects[ladTable.key];
|
||||
let index = telemetryObjects.findIndex(telemetryObject => this.openmct.objects.makeKeyString(identifier) === telemetryObject.key);
|
||||
let index = telemetryObjects.findIndex(telemetryObject => keystring === telemetryObject.key);
|
||||
|
||||
telemetryObjects.splice(index, 1);
|
||||
|
||||
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects);
|
||||
|
||||
this.stalenessSubscription[keystring].unsubscribe();
|
||||
this.stalenessSubscription[keystring].stalenessUtils.destroy();
|
||||
this.handleStaleness(keystring, { isStale: false }, SKIP_CHECK);
|
||||
};
|
||||
},
|
||||
handleStaleness(id, stalenessResponse, skipCheck = false) {
|
||||
if (skipCheck || this.stalenessSubscription[id].stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
const index = this.staleObjects.indexOf(id);
|
||||
if (stalenessResponse.isStale) {
|
||||
if (index === -1) {
|
||||
this.staleObjects.push(id);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
this.staleObjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateViewContext(rowContext) {
|
||||
this.viewContext.row = rowContext;
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||
import { evaluateResults } from './utils/evaluator';
|
||||
import { getLatestTimestamp } from './utils/time';
|
||||
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
||||
import {TRIGGER_CONJUNCTION, TRIGGER_LABEL} from "./utils/constants";
|
||||
import { TRIGGER_CONJUNCTION, TRIGGER_LABEL } from "./utils/constants";
|
||||
|
||||
/*
|
||||
* conditionConfiguration = {
|
||||
@ -160,7 +160,8 @@ export default class Condition extends EventEmitter {
|
||||
}
|
||||
|
||||
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||
criterion.on('telemetryIsStale', (obj) => this.handleStaleCriterion(obj));
|
||||
criterion.on('telemetryIsOld', (obj) => this.handleOldTelemetryCriterion(obj));
|
||||
criterion.on('telemetryStaleness', () => this.handleTelemetryStaleness());
|
||||
if (!this.criteria) {
|
||||
this.criteria = [];
|
||||
}
|
||||
@ -191,12 +192,14 @@ export default class Condition extends EventEmitter {
|
||||
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
||||
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
||||
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||
newCriterion.on('telemetryIsStale', (obj) => this.handleStaleCriterion(obj));
|
||||
newCriterion.on('telemetryIsOld', (obj) => this.handleOldTelemetryCriterion(obj));
|
||||
newCriterion.on('telemetryStaleness', () => this.handleTelemetryStaleness());
|
||||
|
||||
let criterion = found.item;
|
||||
criterion.unsubscribe();
|
||||
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||
criterion.off('telemetryIsStale', (obj) => this.handleStaleCriterion(obj));
|
||||
criterion.off('telemetryIsOld', (obj) => this.handleOldTelemetryCriterion(obj));
|
||||
newCriterion.off('telemetryStaleness', () => this.handleTelemetryStaleness());
|
||||
this.criteria.splice(found.index, 1, newCriterion);
|
||||
}
|
||||
}
|
||||
@ -205,12 +208,9 @@ export default class Condition extends EventEmitter {
|
||||
let found = this.findCriterion(id);
|
||||
if (found) {
|
||||
let criterion = found.item;
|
||||
criterion.off('criterionUpdated', (obj) => {
|
||||
this.handleCriterionUpdated(obj);
|
||||
});
|
||||
criterion.off('telemetryIsStale', (obj) => {
|
||||
this.handleStaleCriterion(obj);
|
||||
});
|
||||
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||
criterion.off('telemetryIsOld', (obj) => this.handleOldTelemetryCriterion(obj));
|
||||
criterion.off('telemetryStaleness', () => this.handleTelemetryStaleness());
|
||||
criterion.destroy();
|
||||
this.criteria.splice(found.index, 1);
|
||||
|
||||
@ -227,7 +227,7 @@ export default class Condition extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
handleStaleCriterion(updatedCriterion) {
|
||||
handleOldTelemetryCriterion(updatedCriterion) {
|
||||
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
||||
let latestTimestamp = {};
|
||||
latestTimestamp = getLatestTimestamp(
|
||||
@ -239,6 +239,11 @@ export default class Condition extends EventEmitter {
|
||||
this.conditionManager.updateCurrentCondition(latestTimestamp);
|
||||
}
|
||||
|
||||
handleTelemetryStaleness() {
|
||||
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
||||
this.conditionManager.updateCurrentCondition();
|
||||
}
|
||||
|
||||
updateDescription() {
|
||||
const triggerDescription = this.getTriggerDescription();
|
||||
let description = '';
|
||||
|
@ -82,8 +82,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import Condition from './Condition.vue';
|
||||
import ConditionManager from '../ConditionManager';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -139,6 +141,13 @@ export default {
|
||||
if (this.stopObservingForChanges) {
|
||||
this.stopObservingForChanges();
|
||||
}
|
||||
|
||||
if (this.stalenessSubscription) {
|
||||
Object.values(this.stalenessSubscription).forEach(stalenessSubscription => {
|
||||
stalenessSubscription.unsubscribe();
|
||||
stalenessSubscription.stalenessUtils.destroy();
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
@ -150,6 +159,7 @@ export default {
|
||||
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
|
||||
this.conditionManager.on('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
|
||||
this.updateDefaultCondition();
|
||||
this.stalenessSubscription = {};
|
||||
},
|
||||
methods: {
|
||||
handleConditionSetResultUpdated(data) {
|
||||
@ -210,19 +220,57 @@ export default {
|
||||
return arr;
|
||||
},
|
||||
addTelemetryObject(domainObject) {
|
||||
const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
this.telemetryObjs.push(domainObject);
|
||||
this.$emit('telemetryUpdated', this.telemetryObjs);
|
||||
|
||||
if (!this.stalenessSubscription[keyString]) {
|
||||
this.stalenessSubscription[keyString] = {};
|
||||
}
|
||||
|
||||
this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils(this.openmct, domainObject);
|
||||
|
||||
this.openmct.telemetry.isStale(domainObject).then((stalenessResponse) => {
|
||||
this.hanldeStaleness(keyString, stalenessResponse);
|
||||
});
|
||||
const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness(domainObject, (stalenessResponse) => {
|
||||
this.hanldeStaleness(keyString, stalenessResponse);
|
||||
});
|
||||
|
||||
this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription;
|
||||
},
|
||||
removeTelemetryObject(identifier) {
|
||||
let index = this.telemetryObjs.findIndex(obj => {
|
||||
const keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
const index = this.telemetryObjs.findIndex(obj => {
|
||||
let objId = this.openmct.objects.makeKeyString(obj.identifier);
|
||||
let id = this.openmct.objects.makeKeyString(identifier);
|
||||
|
||||
return objId === id;
|
||||
return objId === keyString;
|
||||
});
|
||||
|
||||
if (index > -1) {
|
||||
this.telemetryObjs.splice(index, 1);
|
||||
}
|
||||
|
||||
if (this.stalenessSubscription[keyString]) {
|
||||
this.stalenessSubscription[keyString].unsubscribe();
|
||||
this.stalenessSubscription[keyString].stalenessUtils.destroy();
|
||||
this.emitStaleness({
|
||||
keyString,
|
||||
isStale: false
|
||||
});
|
||||
}
|
||||
},
|
||||
hanldeStaleness(keyString, stalenessResponse) {
|
||||
if (this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
this.emitStaleness({
|
||||
keyString,
|
||||
isStale: stalenessResponse.isStale
|
||||
});
|
||||
}
|
||||
},
|
||||
emitStaleness(stalenessObject) {
|
||||
this.$emit('telemetryStaleness', stalenessObject);
|
||||
},
|
||||
addCondition() {
|
||||
this.conditionManager.addCondition();
|
||||
|
@ -21,7 +21,10 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-cs">
|
||||
<div
|
||||
class="c-cs"
|
||||
:class="{'is-stale': isStale }"
|
||||
>
|
||||
<section class="c-cs__current-output c-section">
|
||||
<div class="c-cs__content c-cs__current-output-value">
|
||||
<span class="c-cs__current-output-value__label">Current Output</span>
|
||||
@ -50,6 +53,7 @@
|
||||
@conditionSetResultUpdated="updateCurrentOutput"
|
||||
@updateDefaultOutput="updateDefaultOutput"
|
||||
@telemetryUpdated="updateTelemetry"
|
||||
@telemetryStaleness="handleStaleness"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -73,9 +77,15 @@ export default {
|
||||
currentConditionOutput: '',
|
||||
defaultConditionOutput: '',
|
||||
telemetryObjs: [],
|
||||
testData: {}
|
||||
testData: {},
|
||||
staleObjects: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isStale() {
|
||||
return this.staleObjects.length !== 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.testData = {
|
||||
@ -95,6 +105,18 @@ export default {
|
||||
},
|
||||
updateTestData(testData) {
|
||||
this.testData = testData;
|
||||
},
|
||||
handleStaleness({ keyString, isStale }) {
|
||||
const index = this.staleObjects.indexOf(keyString);
|
||||
if (isStale) {
|
||||
if (index === -1) {
|
||||
this.staleObjects.push(keyString);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
this.staleObjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -94,7 +94,7 @@
|
||||
>
|
||||
<span v-if="inputIndex < inputCount-1">and</span>
|
||||
</span>
|
||||
<span v-if="criterion.metadata === 'dataReceived'">seconds</span>
|
||||
<span v-if="criterion.metadata === 'dataReceived' && criterion.operation.name === IS_OLD_KEY">seconds</span>
|
||||
</template>
|
||||
<span v-else>
|
||||
<span
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
<script>
|
||||
import { OPERATIONS, INPUT_TYPES } from '../utils/operations';
|
||||
import {TRIGGER_CONJUNCTION} from "../utils/constants";
|
||||
import { TRIGGER_CONJUNCTION, IS_OLD_KEY, IS_STALE_KEY } from "../utils/constants";
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -153,7 +153,8 @@ export default {
|
||||
rowLabel: '',
|
||||
operationFormat: '',
|
||||
enumerations: [],
|
||||
inputTypes: INPUT_TYPES
|
||||
inputTypes: INPUT_TYPES,
|
||||
IS_OLD_KEY
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -164,7 +165,7 @@ export default {
|
||||
},
|
||||
filteredOps: function () {
|
||||
if (this.criterion.metadata === 'dataReceived') {
|
||||
return this.operations.filter(op => op.name === 'isStale');
|
||||
return this.operations.filter(op => op.name === IS_OLD_KEY || op.name === IS_STALE_KEY);
|
||||
} else {
|
||||
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
|
||||
}
|
||||
|
@ -54,6 +54,10 @@
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
|
||||
/************************** CONDITION SET LAYOUT */
|
||||
&__current-output {
|
||||
flex: 0 0 auto;
|
||||
|
@ -21,8 +21,9 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import TelemetryCriterion from './TelemetryCriterion';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
import { evaluateResults } from "../utils/evaluator";
|
||||
import {getLatestTimestamp, subscribeForStaleness} from '../utils/time';
|
||||
import { getLatestTimestamp, checkIfOld } from '../utils/time';
|
||||
import { getOperatorText } from "@/plugins/condition/utils/operations";
|
||||
|
||||
export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
@ -38,13 +39,41 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
initialize() {
|
||||
this.telemetryObjects = { ...this.telemetryDomainObjectDefinition.telemetryObjects };
|
||||
this.telemetryDataCache = {};
|
||||
if (this.isValid() && this.isStalenessCheck() && this.isValidInput()) {
|
||||
this.subscribeForStaleData(this.telemetryObjects || {});
|
||||
|
||||
if (this.isValid() && this.isOldCheck() && this.isValidInput()) {
|
||||
this.checkForOldData(this.telemetryObjects || {});
|
||||
}
|
||||
|
||||
if (this.isValid() && this.isStalenessCheck()) {
|
||||
this.subscribeToStaleness(this.telemetryObjects || {});
|
||||
}
|
||||
}
|
||||
|
||||
subscribeForStaleData(telemetryObjects) {
|
||||
checkForOldData(telemetryObjects) {
|
||||
if (!this.ageCheck) {
|
||||
this.ageCheck = {};
|
||||
}
|
||||
|
||||
Object.values(telemetryObjects).forEach((telemetryObject) => {
|
||||
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
if (!this.ageCheck[id]) {
|
||||
this.ageCheck[id] = checkIfOld((data) => {
|
||||
this.handleOldTelemetry(id, data);
|
||||
}, this.input[0] * 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleOldTelemetry(id, data) {
|
||||
if (this.telemetryDataCache) {
|
||||
this.telemetryDataCache[id] = true;
|
||||
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||
}
|
||||
|
||||
this.emitEvent('telemetryIsOld', data);
|
||||
}
|
||||
|
||||
subscribeToStaleness(telemetryObjects) {
|
||||
if (!this.stalenessSubscription) {
|
||||
this.stalenessSubscription = {};
|
||||
}
|
||||
@ -52,20 +81,27 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
Object.values(telemetryObjects).forEach((telemetryObject) => {
|
||||
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
if (!this.stalenessSubscription[id]) {
|
||||
this.stalenessSubscription[id] = subscribeForStaleness((data) => {
|
||||
this.handleStaleTelemetry(id, data);
|
||||
}, this.input[0] * 1000);
|
||||
this.stalenessSubscription[id] = {};
|
||||
this.stalenessSubscription[id].stalenessUtils = new StalenessUtils(this.openmct, telemetryObject);
|
||||
this.stalenessSubscription[id].unsubscribe = this.openmct.telemetry.subscribeToStaleness(
|
||||
telemetryObject,
|
||||
(stalenessResponse) => {
|
||||
this.handleStaleTelemetry(id, stalenessResponse);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleStaleTelemetry(id, data) {
|
||||
handleStaleTelemetry(id, stalenessResponse) {
|
||||
if (this.telemetryDataCache) {
|
||||
this.telemetryDataCache[id] = true;
|
||||
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||
}
|
||||
if (this.stalenessSubscription[id].stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
this.telemetryDataCache[id] = stalenessResponse.isStale;
|
||||
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||
|
||||
this.emitEvent('telemetryIsStale', data);
|
||||
this.emitEvent('telemetryStaleness');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isValid() {
|
||||
@ -75,8 +111,13 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
updateTelemetryObjects(telemetryObjects) {
|
||||
this.telemetryObjects = { ...telemetryObjects };
|
||||
this.removeTelemetryDataCache();
|
||||
if (this.isValid() && this.isStalenessCheck() && this.isValidInput()) {
|
||||
this.subscribeForStaleData(this.telemetryObjects || {});
|
||||
|
||||
if (this.isValid() && this.isOldCheck() && this.isValidInput()) {
|
||||
this.checkForOldData(this.telemetryObjects || {});
|
||||
}
|
||||
|
||||
if (this.isValid() && this.isStalenessCheck()) {
|
||||
this.subscribeToStaleness(this.telemetryObjects || {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +132,9 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
});
|
||||
telemetryCacheIds.forEach(id => {
|
||||
delete (this.telemetryDataCache[id]);
|
||||
delete (this.ageCheck[id]);
|
||||
this.stalenessSubscription[id].unsubscribe();
|
||||
this.stalenessSubscription[id].stalenessUtils.destroy();
|
||||
delete (this.stalenessSubscription[id]);
|
||||
});
|
||||
}
|
||||
@ -125,10 +169,10 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
updateResult(data, telemetryObjects) {
|
||||
const validatedData = this.isValid() ? data : {};
|
||||
|
||||
if (validatedData) {
|
||||
if (this.isStalenessCheck()) {
|
||||
if (this.stalenessSubscription && this.stalenessSubscription[validatedData.id]) {
|
||||
this.stalenessSubscription[validatedData.id].update(validatedData);
|
||||
if (validatedData && !this.isStalenessCheck()) {
|
||||
if (this.isOldCheck()) {
|
||||
if (this.ageCheck?.[validatedData.id]) {
|
||||
this.ageCheck[validatedData.id].update(validatedData);
|
||||
}
|
||||
|
||||
this.telemetryDataCache[validatedData.id] = false;
|
||||
@ -226,9 +270,17 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||
destroy() {
|
||||
delete this.telemetryObjects;
|
||||
delete this.telemetryDataCache;
|
||||
|
||||
if (this.ageCheck) {
|
||||
Object.values(this.ageCheck).forEach((subscription) => subscription.clear);
|
||||
delete this.ageCheck;
|
||||
}
|
||||
|
||||
if (this.stalenessSubscription) {
|
||||
Object.values(this.stalenessSubscription).forEach((subscription) => subscription.clear);
|
||||
delete this.stalenessSubscription;
|
||||
Object.values(this.stalenessSubscription).forEach(subscription => {
|
||||
subscription.unsubscribe();
|
||||
subscription.stalenessUtils.destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,10 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
import { IS_OLD_KEY, IS_STALE_KEY } from '../utils/constants';
|
||||
import { OPERATIONS, getOperatorText } from '../utils/operations';
|
||||
import { subscribeForStaleness } from "../utils/time";
|
||||
import { checkIfOld } from "../utils/time";
|
||||
|
||||
export default class TelemetryCriterion extends EventEmitter {
|
||||
|
||||
@ -44,7 +46,8 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
this.input = telemetryDomainObjectDefinition.input;
|
||||
this.metadata = telemetryDomainObjectDefinition.metadata;
|
||||
this.result = undefined;
|
||||
this.stalenessSubscription = undefined;
|
||||
this.ageCheck = undefined;
|
||||
this.unsubscribeFromStaleness = undefined;
|
||||
|
||||
this.initialize();
|
||||
this.emitEvent('criterionUpdated', this);
|
||||
@ -57,8 +60,13 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
}
|
||||
|
||||
this.updateTelemetryObjects(this.telemetryDomainObjectDefinition.telemetryObjects);
|
||||
if (this.isValid() && this.isStalenessCheck() && this.isValidInput()) {
|
||||
this.subscribeForStaleData();
|
||||
|
||||
if (this.isValid() && this.isOldCheck() && this.isValidInput()) {
|
||||
this.checkForOldData();
|
||||
}
|
||||
|
||||
if (this.isValid() && this.isStalenessCheck()) {
|
||||
this.subscribeToStaleness();
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,25 +74,52 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
return this.telemetryObjectIdAsString && (this.telemetryObjectIdAsString === id);
|
||||
}
|
||||
|
||||
subscribeForStaleData() {
|
||||
if (this.stalenessSubscription) {
|
||||
this.stalenessSubscription.clear();
|
||||
checkForOldData() {
|
||||
if (this.ageCheck) {
|
||||
this.ageCheck.clear();
|
||||
}
|
||||
|
||||
this.stalenessSubscription = subscribeForStaleness(this.handleStaleTelemetry.bind(this), this.input[0] * 1000);
|
||||
this.ageCheck = checkIfOld(this.handleOldTelemetry.bind(this), this.input[0] * 1000);
|
||||
}
|
||||
|
||||
handleStaleTelemetry(data) {
|
||||
handleOldTelemetry(data) {
|
||||
this.result = true;
|
||||
this.emitEvent('telemetryIsStale', data);
|
||||
this.emitEvent('telemetryIsOld', data);
|
||||
}
|
||||
|
||||
subscribeToStaleness() {
|
||||
if (this.unsubscribeFromStaleness) {
|
||||
this.unsubscribeFromStaleness();
|
||||
}
|
||||
|
||||
if (!this.stalenessUtils) {
|
||||
this.stalenessUtils = new StalenessUtils(this.openmct, this.telemetryObject);
|
||||
}
|
||||
|
||||
this.openmct.telemetry.isStale(this.telemetryObject).then(this.handleStaleTelemetry.bind(this));
|
||||
this.unsubscribeFromStaleness = this.openmct.telemetry.subscribeToStaleness(
|
||||
this.telemetryObject,
|
||||
this.handleStaleTelemetry.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
handleStaleTelemetry(stalenessResponse) {
|
||||
if (stalenessResponse !== undefined && this.stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
this.result = stalenessResponse.isStale;
|
||||
this.emitEvent('telemetryStaleness');
|
||||
}
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return this.telemetryObject && this.metadata && this.operation;
|
||||
}
|
||||
|
||||
isOldCheck() {
|
||||
return this.metadata && this.metadata === 'dataReceived' && this.operation === IS_OLD_KEY;
|
||||
}
|
||||
|
||||
isStalenessCheck() {
|
||||
return this.metadata && this.metadata === 'dataReceived';
|
||||
return this.metadata && this.metadata === 'dataReceived' && this.operation === IS_STALE_KEY;
|
||||
}
|
||||
|
||||
isValidInput() {
|
||||
@ -93,8 +128,13 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
|
||||
updateTelemetryObjects(telemetryObjects) {
|
||||
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
||||
if (this.isValid() && this.isStalenessCheck() && this.isValidInput()) {
|
||||
this.subscribeForStaleData();
|
||||
|
||||
if (this.isValid() && this.isOldCheck() && this.isValidInput()) {
|
||||
this.checkForOldData();
|
||||
}
|
||||
|
||||
if (this.isValid() && this.isStalenessCheck()) {
|
||||
this.subscribeToStaleness();
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,14 +170,17 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
|
||||
updateResult(data) {
|
||||
const validatedData = this.isValid() ? data : {};
|
||||
if (this.isStalenessCheck()) {
|
||||
if (this.stalenessSubscription) {
|
||||
this.stalenessSubscription.update(validatedData);
|
||||
}
|
||||
|
||||
this.result = false;
|
||||
} else {
|
||||
this.result = this.computeResult(validatedData);
|
||||
if (!this.isStalenessCheck()) {
|
||||
if (this.isOldCheck()) {
|
||||
if (this.ageCheck) {
|
||||
this.ageCheck.update(validatedData);
|
||||
}
|
||||
|
||||
this.result = false;
|
||||
} else {
|
||||
this.result = this.computeResult(validatedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,8 +311,17 @@ export default class TelemetryCriterion extends EventEmitter {
|
||||
destroy() {
|
||||
delete this.telemetryObject;
|
||||
delete this.telemetryObjectIdAsString;
|
||||
if (this.stalenessSubscription) {
|
||||
delete this.stalenessSubscription;
|
||||
|
||||
if (this.ageCheck) {
|
||||
delete this.ageCheck;
|
||||
}
|
||||
|
||||
if (this.stalenessUtils) {
|
||||
this.stalenessUtils.destroy();
|
||||
}
|
||||
|
||||
if (this.unsubscribeFromStaleness) {
|
||||
this.unsubscribeFromStaleness();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import Vue from 'vue';
|
||||
import {getApplicableStylesForItem} from "./utils/styleUtils";
|
||||
import ConditionManager from "@/plugins/condition/ConditionManager";
|
||||
import StyleRuleManager from "./StyleRuleManager";
|
||||
import { IS_OLD_KEY } from "./utils/constants";
|
||||
|
||||
describe('the plugin', function () {
|
||||
let conditionSetDefinition;
|
||||
@ -642,7 +643,7 @@ describe('the plugin', function () {
|
||||
|
||||
});
|
||||
|
||||
describe('the condition check for staleness', () => {
|
||||
describe('the condition check if old', () => {
|
||||
let conditionSetDomainObject;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -660,13 +661,13 @@ describe('the plugin', function () {
|
||||
"id": "39584410-cbf9-499e-96dc-76f27e69885d",
|
||||
"configuration": {
|
||||
"name": "Unnamed Condition",
|
||||
"output": "Any stale telemetry",
|
||||
"output": "Any old telemetry",
|
||||
"trigger": "all",
|
||||
"criteria": [
|
||||
{
|
||||
"id": "35400132-63b0-425c-ac30-8197df7d5862",
|
||||
"telemetry": "any",
|
||||
"operation": "isStale",
|
||||
"operation": IS_OLD_KEY,
|
||||
"input": [
|
||||
"0.2"
|
||||
],
|
||||
@ -674,7 +675,7 @@ describe('the plugin', function () {
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": "Match if all criteria are met: Any telemetry is stale after 5 seconds"
|
||||
"summary": "Match if all criteria are met: Any telemetry is old after 5 seconds"
|
||||
},
|
||||
{
|
||||
"isDefault": true,
|
||||
@ -708,7 +709,7 @@ describe('the plugin', function () {
|
||||
};
|
||||
});
|
||||
|
||||
it('should evaluate as stale when telemetry is not received in the allotted time', (done) => {
|
||||
it('should evaluate as old when telemetry is not received in the allotted time', (done) => {
|
||||
let conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
||||
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
||||
conditionMgr.telemetryObjects = {
|
||||
@ -717,7 +718,7 @@ describe('the plugin', function () {
|
||||
conditionMgr.updateConditionTelemetryObjects();
|
||||
setTimeout(() => {
|
||||
expect(mockListener).toHaveBeenCalledWith({
|
||||
output: 'Any stale telemetry',
|
||||
output: 'Any old telemetry',
|
||||
id: {
|
||||
namespace: '',
|
||||
key: 'cf4456a9-296a-4e6b-b182-62ed29cd15b9'
|
||||
@ -729,7 +730,7 @@ describe('the plugin', function () {
|
||||
}, 400);
|
||||
});
|
||||
|
||||
it('should not evaluate as stale when telemetry is received in the allotted time', (done) => {
|
||||
it('should not evaluate as old when telemetry is received in the allotted time', (done) => {
|
||||
const date = 1;
|
||||
conditionSetDomainObject.configuration.conditionCollection[0].configuration.criteria[0].input = ["0.4"];
|
||||
let conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
||||
|
@ -59,3 +59,6 @@ export const ERROR = {
|
||||
errorText: 'Condition not found'
|
||||
}
|
||||
};
|
||||
|
||||
export const IS_OLD_KEY = 'isStale';
|
||||
export const IS_STALE_KEY = 'isStale.new';
|
||||
|
@ -20,6 +20,8 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { IS_OLD_KEY, IS_STALE_KEY } from "./constants";
|
||||
|
||||
function convertToNumbers(input) {
|
||||
let numberInputs = [];
|
||||
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
||||
@ -295,7 +297,7 @@ export const OPERATIONS = [
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'isStale',
|
||||
name: IS_OLD_KEY,
|
||||
operation: function () {
|
||||
return false;
|
||||
},
|
||||
@ -305,6 +307,18 @@ export const OPERATIONS = [
|
||||
getDescription: function (values) {
|
||||
return ` is older than ${values[0] || ''} seconds`;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: IS_STALE_KEY,
|
||||
operation: function () {
|
||||
return false;
|
||||
},
|
||||
text: 'is stale',
|
||||
appliesTo: ["number"],
|
||||
inputCount: 0,
|
||||
getDescription: function () {
|
||||
return ' is stale';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@ -316,5 +330,5 @@ export const INPUT_TYPES = {
|
||||
export function getOperatorText(operationName, values) {
|
||||
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
||||
|
||||
return found ? found.getDescription(values) : '';
|
||||
return found?.getDescription(values) ?? '';
|
||||
}
|
||||
|
@ -51,26 +51,26 @@ export function getLatestTimestamp(
|
||||
return latest;
|
||||
}
|
||||
|
||||
export function subscribeForStaleness(callback, timeout) {
|
||||
let stalenessTimer = setTimeout(() => {
|
||||
clearTimeout(stalenessTimer);
|
||||
export function checkIfOld(callback, timeout) {
|
||||
let oldCheckTimer = setTimeout(() => {
|
||||
clearTimeout(oldCheckTimer);
|
||||
callback();
|
||||
}, timeout);
|
||||
|
||||
return {
|
||||
update: (data) => {
|
||||
if (stalenessTimer) {
|
||||
clearTimeout(stalenessTimer);
|
||||
if (oldCheckTimer) {
|
||||
clearTimeout(oldCheckTimer);
|
||||
}
|
||||
|
||||
stalenessTimer = setTimeout(() => {
|
||||
clearTimeout(stalenessTimer);
|
||||
oldCheckTimer = setTimeout(() => {
|
||||
clearTimeout(oldCheckTimer);
|
||||
callback(data);
|
||||
}, timeout);
|
||||
},
|
||||
clear: () => {
|
||||
if (stalenessTimer) {
|
||||
clearTimeout(stalenessTimer);
|
||||
if (oldCheckTimer) {
|
||||
clearTimeout(oldCheckTimer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -19,7 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { subscribeForStaleness } from "./time";
|
||||
import { checkIfOld } from "./time";
|
||||
|
||||
describe('time related utils', () => {
|
||||
let subscription;
|
||||
@ -27,11 +27,11 @@ describe('time related utils', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockListener = jasmine.createSpy('listener');
|
||||
subscription = subscribeForStaleness(mockListener, 100);
|
||||
subscription = checkIfOld(mockListener, 100);
|
||||
});
|
||||
|
||||
describe('subscribe for staleness', () => {
|
||||
it('should call listeners when stale', (done) => {
|
||||
describe('check if old', () => {
|
||||
it('should call listeners when old', (done) => {
|
||||
setTimeout(() => {
|
||||
expect(mockListener).toHaveBeenCalled();
|
||||
done();
|
||||
|
@ -31,7 +31,7 @@
|
||||
<div
|
||||
v-if="domainObject"
|
||||
class="c-telemetry-view u-style-receiver"
|
||||
:class="[statusClass]"
|
||||
:class="[itemClasses]"
|
||||
:style="styleObject"
|
||||
:data-font-size="item.fontSize"
|
||||
:data-font="item.font"
|
||||
@ -73,6 +73,7 @@
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue';
|
||||
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import { getDefaultNotebook, getNotebookSectionAndPage } from '@/plugins/notebook/utils/notebook-storage.js';
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
|
||||
@ -102,7 +103,7 @@ export default {
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
mixins: [conditionalStylesMixin],
|
||||
mixins: [conditionalStylesMixin, stalenessMixin],
|
||||
inject: ['openmct', 'objectPath', 'currentView'],
|
||||
props: {
|
||||
item: {
|
||||
@ -137,8 +138,18 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
statusClass() {
|
||||
return (this.status) ? `is-status--${this.status}` : '';
|
||||
itemClasses() {
|
||||
let classes = [];
|
||||
|
||||
if (this.status) {
|
||||
classes.push(`is-status--${this.status}`);
|
||||
}
|
||||
|
||||
if (this.isStale) {
|
||||
classes.push('is-stale');
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
showLabel() {
|
||||
let displayMode = this.item.displayMode;
|
||||
@ -310,6 +321,7 @@ export default {
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.immediatelySelect || this.initSelect);
|
||||
delete this.immediatelySelect;
|
||||
this.subscribeToStaleness(this.domainObject);
|
||||
},
|
||||
updateTelemetryFormat(format) {
|
||||
this.customStringformatter.setFormat(format);
|
||||
|
@ -25,6 +25,12 @@
|
||||
margin-right: $interiorMargin;
|
||||
}
|
||||
|
||||
&.is-stale {
|
||||
.c-telemetry-view__value {
|
||||
@include isStaleElement();
|
||||
}
|
||||
}
|
||||
|
||||
.c-frame & {
|
||||
@include abs();
|
||||
border: 1px solid transparent;
|
||||
|
@ -22,7 +22,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-gauge__wrapper js-gauge-wrapper"
|
||||
:class="`c-gauge--${gaugeType}`"
|
||||
:class="gaugeClasses"
|
||||
:title="gaugeTitle"
|
||||
>
|
||||
<template v-if="typeDial">
|
||||
@ -347,12 +347,14 @@
|
||||
|
||||
<script>
|
||||
import { DIAL_VALUE_DEG_OFFSET, getLimitDegree } from '../gauge-limit-util';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
const LIMIT_PADDING_IN_PERCENT = 10;
|
||||
const DEFAULT_CURRENT_VALUE = '--';
|
||||
|
||||
export default {
|
||||
name: 'Gauge',
|
||||
mixins: [stalenessMixin],
|
||||
inject: ['openmct', 'domainObject', 'composition'],
|
||||
data() {
|
||||
let gaugeController = this.domainObject.configuration.gaugeController;
|
||||
@ -403,6 +405,15 @@ export default {
|
||||
|
||||
return VIEWBOX_STR.replace('X', this.digits * DIGITS_RATIO);
|
||||
},
|
||||
gaugeClasses() {
|
||||
let classes = [`c-gauge--${this.gaugeType}`];
|
||||
|
||||
if (this.isStale) {
|
||||
classes.push('is-stale');
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
rangeFontSize() {
|
||||
const CHAR_THRESHOLD = 3;
|
||||
const START_PERC = 8.5;
|
||||
@ -553,6 +564,8 @@ export default {
|
||||
this.telemetryObject = domainObject;
|
||||
this.request();
|
||||
this.subscribe();
|
||||
|
||||
this.subscribeToStaleness(domainObject);
|
||||
},
|
||||
addedToComposition(domainObject) {
|
||||
if (this.telemetryObject) {
|
||||
@ -611,6 +624,8 @@ export default {
|
||||
this.unsubscribe = null;
|
||||
}
|
||||
|
||||
this.triggerUnsubscribeFromStaleness();
|
||||
|
||||
this.curVal = DEFAULT_CURRENT_VALUE;
|
||||
this.formats = null;
|
||||
this.limitHigh = '';
|
||||
|
@ -32,6 +32,15 @@ $meterNeedleBorderRadius: 5px;
|
||||
&__wrapper {
|
||||
@include abs();
|
||||
overflow: hidden;
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
|
||||
[class*=__current-value-text] {
|
||||
fill: $colorTelemStale;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__current-value-text-wrapper {
|
||||
|
@ -23,6 +23,7 @@
|
||||
<div
|
||||
ref="plotWrapper"
|
||||
class="c-plot holder holder-plot has-control-bar"
|
||||
:class="staleClass"
|
||||
>
|
||||
<div
|
||||
ref="plotContainer"
|
||||
@ -50,6 +51,7 @@ import eventHelpers from './lib/eventHelpers';
|
||||
import ImageExporter from '../../exporters/ImageExporter';
|
||||
import MctPlot from './MctPlot.vue';
|
||||
import ProgressBar from "../../ui/components/ProgressBar.vue";
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -74,21 +76,95 @@ export default {
|
||||
cursorGuide: false,
|
||||
gridLines: !this.options.compact,
|
||||
loading: false,
|
||||
status: ''
|
||||
status: '',
|
||||
staleObjects: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
staleClass() {
|
||||
if (this.staleObjects.length !== 0) {
|
||||
return 'is-stale';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
eventHelpers.extend(this);
|
||||
this.imageExporter = new ImageExporter(this.openmct);
|
||||
this.loadComposition();
|
||||
this.stalenessSubscription = {};
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroy();
|
||||
},
|
||||
methods: {
|
||||
loadComposition() {
|
||||
this.compositionCollection = this.openmct.composition.get(this.domainObject);
|
||||
|
||||
if (this.compositionCollection) {
|
||||
this.compositionCollection.on('add', this.addItem);
|
||||
this.compositionCollection.on('remove', this.removeItem);
|
||||
this.compositionCollection.load();
|
||||
}
|
||||
|
||||
},
|
||||
addItem(object) {
|
||||
const keystring = this.openmct.objects.makeKeyString(object.identifier);
|
||||
|
||||
if (!this.stalenessSubscription[keystring]) {
|
||||
this.stalenessSubscription[keystring] = {};
|
||||
this.stalenessSubscription[keystring].stalenessUtils = new StalenessUtils(this.openmct, object);
|
||||
}
|
||||
|
||||
this.openmct.telemetry.isStale(object).then((stalenessResponse) => {
|
||||
if (stalenessResponse !== undefined) {
|
||||
this.handleStaleness(keystring, stalenessResponse);
|
||||
}
|
||||
});
|
||||
const unsubscribeFromStaleness = this.openmct.telemetry.subscribeToStaleness(object, (stalenessResponse) => {
|
||||
this.handleStaleness(keystring, stalenessResponse);
|
||||
});
|
||||
|
||||
this.stalenessSubscription[keystring].unsubscribe = unsubscribeFromStaleness;
|
||||
},
|
||||
removeItem(object) {
|
||||
const SKIP_CHECK = true;
|
||||
const keystring = this.openmct.objects.makeKeyString(object);
|
||||
this.stalenessSubscription[keystring].unsubscribe();
|
||||
this.stalenessSubscription[keystring].stalenessUtils.destroy();
|
||||
this.handleStaleness(keystring, { isStale: false }, SKIP_CHECK);
|
||||
},
|
||||
handleStaleness(id, stalenessResponse, skipCheck = false) {
|
||||
if (skipCheck || this.stalenessSubscription[id].stalenessUtils.shouldUpdateStaleness(stalenessResponse, id)) {
|
||||
const index = this.staleObjects.indexOf(id);
|
||||
if (stalenessResponse.isStale) {
|
||||
if (index === -1) {
|
||||
this.staleObjects.push(id);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
this.staleObjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
loadingUpdated(loading) {
|
||||
this.loading = loading;
|
||||
},
|
||||
destroy() {
|
||||
if (this.stalenessSubscription) {
|
||||
Object.values(this.stalenessSubscription).forEach(stalenessSubscription => {
|
||||
stalenessSubscription.unsubscribe();
|
||||
stalenessSubscription.stalenessUtils.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.compositionCollection) {
|
||||
this.compositionCollection.off('add', this.addItem);
|
||||
this.compositionCollection.off('remove', this.removeItem);
|
||||
}
|
||||
|
||||
this.stopListening();
|
||||
},
|
||||
exportJPG() {
|
||||
|
@ -23,6 +23,7 @@
|
||||
<div
|
||||
class="plot-legend-item"
|
||||
:class="{
|
||||
'is-stale': isStale,
|
||||
'is-status--missing': isMissing
|
||||
}"
|
||||
@mouseover="toggleHover(true)"
|
||||
@ -55,8 +56,10 @@
|
||||
|
||||
import {getLimitClass} from "@/plugins/plot/chart/limitUtil";
|
||||
import eventHelpers from "../lib/eventHelpers";
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
valueToShowWhenCollapsed: {
|
||||
@ -112,6 +115,7 @@ export default {
|
||||
this.listenTo(this.seriesObject, 'change:name', () => {
|
||||
this.updateName();
|
||||
}, this);
|
||||
this.subscribeToStaleness(this.seriesObject.domainObject);
|
||||
this.initialize();
|
||||
},
|
||||
beforeDestroy() {
|
||||
@ -120,6 +124,7 @@ export default {
|
||||
methods: {
|
||||
initialize(highlightedObject) {
|
||||
const seriesObject = highlightedObject ? highlightedObject.series : this.seriesObject;
|
||||
|
||||
this.isMissing = seriesObject.domainObject.status === 'missing';
|
||||
this.colorAsHexString = seriesObject.get('color').asHexString();
|
||||
this.nameWithUnit = seriesObject.nameWithUnit();
|
||||
|
@ -23,6 +23,7 @@
|
||||
<tr
|
||||
class="plot-legend-item"
|
||||
:class="{
|
||||
'is-stale': isStale,
|
||||
'is-status--missing': isMissing
|
||||
}"
|
||||
@mouseover="toggleHover(true)"
|
||||
@ -81,8 +82,10 @@
|
||||
<script>
|
||||
import {getLimitClass} from "@/plugins/plot/chart/limitUtil";
|
||||
import eventHelpers from "@/plugins/plot/lib/eventHelpers";
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [stalenessMixin],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
seriesObject: {
|
||||
@ -149,6 +152,7 @@ export default {
|
||||
this.listenTo(this.seriesObject, 'change:name', () => {
|
||||
this.updateName();
|
||||
}, this);
|
||||
this.subscribeToStaleness(this.seriesObject.domainObject);
|
||||
this.initialize();
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
@ -27,12 +27,13 @@
|
||||
import MctPlot from '../MctPlot.vue';
|
||||
import Vue from "vue";
|
||||
import conditionalStylesMixin from "./mixins/objectStyles-mixin";
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import configStore from "@/plugins/plot/configuration/ConfigStore";
|
||||
import PlotConfigurationModel from "@/plugins/plot/configuration/PlotConfigurationModel";
|
||||
import ProgressBar from "../../../ui/components/ProgressBar.vue";
|
||||
|
||||
export default {
|
||||
mixins: [conditionalStylesMixin],
|
||||
mixins: [conditionalStylesMixin, stalenessMixin],
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
props: {
|
||||
childObject: {
|
||||
@ -114,6 +115,10 @@ export default {
|
||||
}
|
||||
},
|
||||
updateView() {
|
||||
this.isStale = false;
|
||||
|
||||
this.triggerUnsubscribeFromStaleness();
|
||||
|
||||
if (this.component) {
|
||||
this.component.$destroy();
|
||||
this.component = undefined;
|
||||
@ -139,6 +144,10 @@ export default {
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer);
|
||||
|
||||
this.subscribeToStaleness(object, (isStale) => {
|
||||
this.updateComponentProp('isStale', isStale);
|
||||
});
|
||||
|
||||
this.component = new Vue({
|
||||
el: viewContainer,
|
||||
components: {
|
||||
@ -169,7 +178,7 @@ export default {
|
||||
this.loading = loaded;
|
||||
}
|
||||
},
|
||||
template: '<div v-if="!isMissing" ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><progress-bar v-show="loading !== false" class="c-telemetry-table__progress-bar" :model="{progressPerc: undefined}" /><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :limit-line-labels="limitLineLabels" :color-palette="colorPalette" :options="options" @plotTickWidth="onTickWidthChange" @lockHighlightPoint="onLockHighlightPointUpdated" @highlights="onHighlightsUpdated" @configLoaded="onConfigLoaded" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
|
||||
template: '<div v-if="!isMissing" ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\', \'is-stale\': isStale}"><progress-bar v-show="loading !== false" class="c-telemetry-table__progress-bar" :model="{progressPerc: undefined}" /><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :limit-line-labels="limitLineLabels" :color-palette="colorPalette" :options="options" @plotTickWidth="onTickWidthChange" @lockHighlightPoint="onLockHighlightPointUpdated" @highlights="onHighlightsUpdated" @configLoaded="onConfigLoaded" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
|
||||
});
|
||||
|
||||
this.setSelection();
|
||||
@ -215,7 +224,8 @@ export default {
|
||||
plotTickWidth: this.plotTickWidth,
|
||||
options: this.options,
|
||||
status: this.status,
|
||||
colorPalette: this.colorPalette
|
||||
colorPalette: this.colorPalette,
|
||||
isStale: this.isStale
|
||||
};
|
||||
},
|
||||
getPlotObject() {
|
||||
|
@ -155,7 +155,7 @@ define([
|
||||
plugins.example.ExampleFaultSource = ExampleFaultSource.default;
|
||||
plugins.example.EventGeneratorPlugin = EventGeneratorPlugin.default;
|
||||
plugins.example.ExampleTags = ExampleTags.default;
|
||||
plugins.example.Generator = () => GeneratorPlugin;
|
||||
plugins.example.Generator = () => GeneratorPlugin.default;
|
||||
|
||||
plugins.UTCTimeSystem = UTCTimeSystem.default;
|
||||
plugins.LocalTimeSystem = LocalTimeSystem;
|
||||
|
@ -28,7 +28,8 @@ define([
|
||||
'./TelemetryTableNameColumn',
|
||||
'./TelemetryTableColumn',
|
||||
'./TelemetryTableUnitColumn',
|
||||
'./TelemetryTableConfiguration'
|
||||
'./TelemetryTableConfiguration',
|
||||
'@/utils/staleness'
|
||||
], function (
|
||||
EventEmitter,
|
||||
_,
|
||||
@ -37,7 +38,8 @@ define([
|
||||
TelemetryTableNameColumn,
|
||||
TelemetryTableColumn,
|
||||
TelemetryTableUnitColumn,
|
||||
TelemetryTableConfiguration
|
||||
TelemetryTableConfiguration,
|
||||
StalenessUtils
|
||||
) {
|
||||
class TelemetryTable extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
@ -56,6 +58,7 @@ define([
|
||||
this.telemetryCollections = {};
|
||||
this.delayedActions = [];
|
||||
this.outstandingRequests = 0;
|
||||
this.stalenessSubscription = {};
|
||||
|
||||
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
||||
this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
|
||||
@ -155,6 +158,19 @@ define([
|
||||
this.telemetryCollections[keyString].on('clear', this.clearData);
|
||||
this.telemetryCollections[keyString].load();
|
||||
|
||||
this.stalenessSubscription[keyString] = {};
|
||||
this.stalenessSubscription[keyString].stalenessUtils = new StalenessUtils.default(this.openmct, telemetryObject);
|
||||
this.openmct.telemetry.isStale(telemetryObject).then(stalenessResponse => {
|
||||
if (stalenessResponse !== undefined) {
|
||||
this.handleStaleness(keyString, stalenessResponse);
|
||||
}
|
||||
});
|
||||
const stalenessSubscription = this.openmct.telemetry.subscribeToStaleness(telemetryObject, (stalenessResponse) => {
|
||||
this.handleStaleness(keyString, stalenessResponse);
|
||||
});
|
||||
|
||||
this.stalenessSubscription[keyString].unsubscribe = stalenessSubscription;
|
||||
|
||||
this.telemetryObjects[keyString] = {
|
||||
telemetryObject,
|
||||
keyString,
|
||||
@ -166,6 +182,15 @@ define([
|
||||
this.emit('object-added', telemetryObject);
|
||||
}
|
||||
|
||||
handleStaleness(keyString, stalenessResponse, skipCheck = false) {
|
||||
if (skipCheck || this.stalenessSubscription[keyString].stalenessUtils.shouldUpdateStaleness(stalenessResponse, keyString)) {
|
||||
this.emit('telemetry-staleness', {
|
||||
keyString,
|
||||
isStale: stalenessResponse.isStale
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTelemetryProcessor(keyString, columnMap, limitEvaluator) {
|
||||
return (telemetry) => {
|
||||
//Check that telemetry object has not been removed since telemetry was requested.
|
||||
@ -255,6 +280,7 @@ define([
|
||||
|
||||
removeTelemetryObject(objectIdentifier) {
|
||||
const keyString = this.openmct.objects.makeKeyString(objectIdentifier);
|
||||
const SKIP_CHECK = true;
|
||||
|
||||
this.configuration.removeColumnsForObject(objectIdentifier, true);
|
||||
this.tableRows.removeRowsByObject(keyString);
|
||||
@ -263,6 +289,10 @@ define([
|
||||
delete this.telemetryObjects[keyString];
|
||||
|
||||
this.emit('object-removed', objectIdentifier);
|
||||
|
||||
this.stalenessSubscription[keyString].unsubscribe();
|
||||
this.stalenessSubscription[keyString].stalenessUtils.destroy();
|
||||
this.handleStaleness(keyString, { isStale: false }, SKIP_CHECK);
|
||||
}
|
||||
|
||||
clearData() {
|
||||
@ -368,6 +398,11 @@ define([
|
||||
this.filterObserver();
|
||||
}
|
||||
|
||||
Object.values(this.stalenessSubscription).forEach(stalenessSubscription => {
|
||||
stalenessSubscription.unsubscribe();
|
||||
stalenessSubscription.stalenessUtils.destroy();
|
||||
});
|
||||
|
||||
if (this.tableComposition !== undefined) {
|
||||
this.tableComposition.off('add', this.addTelemetryObject);
|
||||
this.tableComposition.off('remove', this.removeTelemetryObject);
|
||||
|
@ -22,7 +22,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-table-wrapper"
|
||||
:class="{ 'is-paused': paused }"
|
||||
:class="tableClasses"
|
||||
>
|
||||
<div
|
||||
v-if="enableLegacyToolbar"
|
||||
@ -381,7 +381,8 @@ export default {
|
||||
enableRegexSearch: {},
|
||||
hideHeaders: configuration.hideHeaders,
|
||||
totalNumberOfRows: 0,
|
||||
rowContext: {}
|
||||
rowContext: {},
|
||||
staleObjects: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -416,6 +417,19 @@ export default {
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
tableClasses() {
|
||||
let classes = [];
|
||||
|
||||
if (this.paused) {
|
||||
classes.push('is-paused');
|
||||
}
|
||||
|
||||
if (this.staleObjects.length !== 0) {
|
||||
classes.push('is-stale');
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -488,6 +502,7 @@ export default {
|
||||
this.table.on('refresh', this.clearRowsAndRerender);
|
||||
this.table.on('historical-rows-processed', this.checkForMarkedRows);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
this.table.on('telemetry-staleness', this.handleStaleness);
|
||||
|
||||
this.table.tableRows.on('add', this.rowsAdded);
|
||||
this.table.tableRows.on('remove', this.rowsRemoved);
|
||||
@ -516,6 +531,7 @@ export default {
|
||||
this.table.off('historical-rows-processed', this.checkForMarkedRows);
|
||||
this.table.off('refresh', this.clearRowsAndRerender);
|
||||
this.table.off('outstanding-requests', this.outstandingRequests);
|
||||
this.table.off('telemetry-staleness', this.handleStaleness);
|
||||
|
||||
this.table.tableRows.off('add', this.rowsAdded);
|
||||
this.table.tableRows.off('remove', this.rowsRemoved);
|
||||
@ -726,6 +742,18 @@ export default {
|
||||
outstandingRequests(loading) {
|
||||
this.loading = loading;
|
||||
},
|
||||
handleStaleness({ keyString, isStale }) {
|
||||
const index = this.staleObjects.indexOf(keyString);
|
||||
if (isStale) {
|
||||
if (index === -1) {
|
||||
this.staleObjects.push(keyString);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
this.staleObjects.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
calculateTableSize() {
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
|
@ -305,8 +305,8 @@ $colorIndicatorMenuFg: $colorHeadFg;
|
||||
$colorIndicatorMenuFgHov: pullForward($colorHeadFg, 10%);
|
||||
|
||||
// Staleness
|
||||
$colorTelemFresh: pullForward($colorBodyFg, 20%);
|
||||
$colorTelemStale: pushBack($colorBodyFg, 20%);
|
||||
$colorTelemStale: cyan;
|
||||
$colorTelemStaleFg: #002A2A;
|
||||
$styleTelemStale: italic;
|
||||
|
||||
// Limits
|
||||
|
@ -309,8 +309,8 @@ $colorIndicatorMenuFg: $colorHeadFg;
|
||||
$colorIndicatorMenuFgHov: pullForward($colorHeadFg, 10%);
|
||||
|
||||
// Staleness
|
||||
$colorTelemFresh: pullForward($colorBodyFg, 20%);
|
||||
$colorTelemStale: pushBack($colorBodyFg, 20%);
|
||||
$colorTelemStale: cyan;
|
||||
$colorTelemStaleFg: #002A2A;
|
||||
$styleTelemStale: italic;
|
||||
|
||||
// Limits
|
||||
|
@ -305,8 +305,8 @@ $colorIndicatorMenuFg: $colorHeadFg;
|
||||
$colorIndicatorMenuFgHov: pullForward($colorHeadFg, 10%);
|
||||
|
||||
// Staleness
|
||||
$colorTelemFresh: pullForward($colorBodyFg, 20%);
|
||||
$colorTelemStale: pushBack($colorBodyFg, 20%);
|
||||
$colorTelemStale: #00c9c9;
|
||||
$colorTelemStaleFg: #002A2A;
|
||||
$styleTelemStale: italic;
|
||||
|
||||
// Limits
|
||||
|
@ -164,6 +164,7 @@ $glyph-icon-status-poll-caution: '\e933';
|
||||
$glyph-icon-status-poll-circle-slash: '\e934';
|
||||
$glyph-icon-status-poll-question-mark: '\e935';
|
||||
$glyph-icon-status-poll-edit: '\e936';
|
||||
$glyph-icon-stale: '\e937';
|
||||
$glyph-icon-arrows-right-left: '\ea00';
|
||||
$glyph-icon-arrows-up-down: '\ea01';
|
||||
$glyph-icon-bullet: '\ea02';
|
||||
|
@ -95,6 +95,7 @@
|
||||
.icon-status-poll-circle-slash { @include glyphBefore($glyph-icon-status-poll-circle-slash); }
|
||||
.icon-status-poll-question-mark { @include glyphBefore($glyph-icon-status-poll-question-mark); }
|
||||
.icon-status-poll-edit { @include glyphBefore($glyph-icon-status-poll-edit); }
|
||||
.icon-stale { @include glyphBefore($glyph-icon-stale); }
|
||||
.icon-arrows-right-left { @include glyphBefore($glyph-icon-arrows-right-left); }
|
||||
.icon-arrows-up-down { @include glyphBefore($glyph-icon-arrows-up-down); }
|
||||
.icon-bullet { @include glyphBefore($glyph-icon-bullet); }
|
||||
|
@ -81,6 +81,10 @@ mct-plot {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
|
||||
.c-plot--stacked-container {
|
||||
border: 1px solid transparent;
|
||||
display: flex;
|
||||
@ -89,6 +93,10 @@ mct-plot {
|
||||
min-height: $plotMinH;
|
||||
overflow: hidden;
|
||||
|
||||
.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
|
||||
&[s-selected] {
|
||||
.is-editing & {
|
||||
border: $editMarqueeBorder;
|
||||
@ -596,6 +604,10 @@ mct-plot {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleElement();
|
||||
}
|
||||
|
||||
.plot-series-color-swatch {
|
||||
@include colorSwatch();
|
||||
display: inline-block;
|
||||
@ -649,8 +661,10 @@ mct-plot {
|
||||
// .plot-legend-item is a span of spans.
|
||||
|
||||
.plot-legend-item {
|
||||
border-radius: $smallCr;
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
padding: 1px;
|
||||
|
||||
.plot-series-swatch-and-name,
|
||||
.plot-series-value {
|
||||
|
@ -153,6 +153,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin isStaleGlyph() {
|
||||
content: $glyph-icon-stale;
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
font-style: normal;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@mixin isStaleHolder() {
|
||||
// Applied to objects that frame content, like Display Layout frames, plots, etc.
|
||||
border-radius: 3px;
|
||||
border: 2px solid rgba($colorTelemStale, 0.8) !important;
|
||||
|
||||
&:before {
|
||||
@include isStaleGlyph();
|
||||
color: $colorTelemStale;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 3px;
|
||||
width: 12px;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin isStaleElement() {
|
||||
// Applied directly to values, like LAD Table cells, alphanumerics, plot legend items
|
||||
background: $colorTelemStale !important;
|
||||
color: $colorTelemStaleFg !important;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@mixin isLimit() {
|
||||
&[class*='is-limit'] {
|
||||
&:before {
|
||||
|
@ -90,6 +90,10 @@ div.c-table {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
|
||||
.--width-less-than-600 & {
|
||||
&:not(.is-paused) {
|
||||
.c-table-control-bar {
|
||||
@ -179,12 +183,22 @@ div.c-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: $mainViewPad;
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
}
|
||||
|
||||
.c-lad-table {
|
||||
th, td {
|
||||
width: 33%; // Needed to prevent size jumping as values dynamically update
|
||||
}
|
||||
|
||||
td {
|
||||
&.is-stale {
|
||||
@include isStaleElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************** TABLE AND SUMMARY VIEWS */
|
||||
|
@ -2,7 +2,7 @@
|
||||
"metadata": {
|
||||
"name": "Open MCT Symbols 16px",
|
||||
"lastOpened": 0,
|
||||
"created": 1660771219523
|
||||
"created": 1674103729548
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@ -447,13 +447,21 @@
|
||||
"code": 59702,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 215,
|
||||
"id": 185,
|
||||
"name": "icon-stale",
|
||||
"prevSize": 16,
|
||||
"code": 59703,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 27,
|
||||
"id": 105,
|
||||
"name": "icon-arrows-right-left",
|
||||
"prevSize": 16,
|
||||
"code": 59904,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 26,
|
||||
@ -461,7 +469,7 @@
|
||||
"name": "icon-arrows-up-down",
|
||||
"prevSize": 16,
|
||||
"code": 59905,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 68,
|
||||
@ -469,7 +477,7 @@
|
||||
"name": "icon-bullet",
|
||||
"prevSize": 16,
|
||||
"code": 59906,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 150,
|
||||
@ -477,7 +485,7 @@
|
||||
"prevSize": 16,
|
||||
"code": 59907,
|
||||
"name": "icon-calendar",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 45,
|
||||
@ -485,7 +493,7 @@
|
||||
"name": "icon-chain-links",
|
||||
"prevSize": 16,
|
||||
"code": 59908,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 73,
|
||||
@ -493,7 +501,7 @@
|
||||
"name": "icon-download",
|
||||
"prevSize": 16,
|
||||
"code": 59909,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 39,
|
||||
@ -501,7 +509,7 @@
|
||||
"name": "icon-duplicate",
|
||||
"prevSize": 16,
|
||||
"code": 59910,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 50,
|
||||
@ -509,7 +517,7 @@
|
||||
"name": "icon-folder-new",
|
||||
"prevSize": 16,
|
||||
"code": 59911,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 138,
|
||||
@ -517,7 +525,7 @@
|
||||
"name": "icon-fullscreen-collapse",
|
||||
"prevSize": 16,
|
||||
"code": 59912,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 139,
|
||||
@ -525,7 +533,7 @@
|
||||
"name": "icon-fullscreen-expand",
|
||||
"prevSize": 16,
|
||||
"code": 59913,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 122,
|
||||
@ -533,7 +541,7 @@
|
||||
"name": "icon-layers",
|
||||
"prevSize": 16,
|
||||
"code": 59914,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 151,
|
||||
@ -541,7 +549,7 @@
|
||||
"name": "icon-line-horz",
|
||||
"prevSize": 16,
|
||||
"code": 59915,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 100,
|
||||
@ -549,7 +557,7 @@
|
||||
"name": "icon-magnify",
|
||||
"prevSize": 16,
|
||||
"code": 59916,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 99,
|
||||
@ -557,7 +565,7 @@
|
||||
"name": "icon-magnify-in",
|
||||
"prevSize": 16,
|
||||
"code": 59917,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 101,
|
||||
@ -565,7 +573,7 @@
|
||||
"name": "icon-magnify-out-v2",
|
||||
"prevSize": 16,
|
||||
"code": 59918,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 103,
|
||||
@ -573,7 +581,7 @@
|
||||
"name": "icon-menu",
|
||||
"prevSize": 16,
|
||||
"code": 59919,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 124,
|
||||
@ -581,7 +589,7 @@
|
||||
"name": "icon-move",
|
||||
"prevSize": 16,
|
||||
"code": 59920,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 7,
|
||||
@ -589,7 +597,7 @@
|
||||
"name": "icon-new-window",
|
||||
"prevSize": 16,
|
||||
"code": 59921,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 63,
|
||||
@ -597,7 +605,7 @@
|
||||
"name": "icon-paint-bucket-v2",
|
||||
"prevSize": 16,
|
||||
"code": 59922,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 15,
|
||||
@ -605,7 +613,7 @@
|
||||
"name": "icon-pencil",
|
||||
"prevSize": 16,
|
||||
"code": 59923,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 54,
|
||||
@ -613,7 +621,7 @@
|
||||
"name": "icon-pencil-edit-in-place",
|
||||
"prevSize": 16,
|
||||
"code": 59924,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 40,
|
||||
@ -621,7 +629,7 @@
|
||||
"name": "icon-play",
|
||||
"prevSize": 16,
|
||||
"code": 59925,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 125,
|
||||
@ -629,7 +637,7 @@
|
||||
"name": "icon-pause",
|
||||
"prevSize": 16,
|
||||
"code": 59926,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 119,
|
||||
@ -637,7 +645,7 @@
|
||||
"name": "icon-plot-resource",
|
||||
"prevSize": 16,
|
||||
"code": 59927,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 48,
|
||||
@ -645,7 +653,7 @@
|
||||
"name": "icon-pointer-left",
|
||||
"prevSize": 16,
|
||||
"code": 59928,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 47,
|
||||
@ -653,7 +661,7 @@
|
||||
"name": "icon-pointer-right",
|
||||
"prevSize": 16,
|
||||
"code": 59929,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 85,
|
||||
@ -661,7 +669,7 @@
|
||||
"name": "icon-refresh",
|
||||
"prevSize": 16,
|
||||
"code": 59930,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 55,
|
||||
@ -669,7 +677,7 @@
|
||||
"name": "icon-save",
|
||||
"prevSize": 16,
|
||||
"code": 59931,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 56,
|
||||
@ -677,7 +685,7 @@
|
||||
"name": "icon-save-as",
|
||||
"prevSize": 16,
|
||||
"code": 59932,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 58,
|
||||
@ -685,7 +693,7 @@
|
||||
"name": "icon-sine",
|
||||
"prevSize": 16,
|
||||
"code": 59933,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 113,
|
||||
@ -693,7 +701,7 @@
|
||||
"name": "icon-font",
|
||||
"prevSize": 16,
|
||||
"code": 59934,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 41,
|
||||
@ -701,7 +709,7 @@
|
||||
"name": "icon-thumbs-strip",
|
||||
"prevSize": 16,
|
||||
"code": 59935,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 146,
|
||||
@ -709,7 +717,7 @@
|
||||
"name": "icon-two-parts-both",
|
||||
"prevSize": 16,
|
||||
"code": 59936,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 145,
|
||||
@ -717,7 +725,7 @@
|
||||
"name": "icon-two-parts-one-only",
|
||||
"prevSize": 16,
|
||||
"code": 59937,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 82,
|
||||
@ -725,7 +733,7 @@
|
||||
"name": "icon-resync",
|
||||
"prevSize": 16,
|
||||
"code": 59938,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 86,
|
||||
@ -733,7 +741,7 @@
|
||||
"name": "icon-reset",
|
||||
"prevSize": 16,
|
||||
"code": 59939,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 61,
|
||||
@ -741,7 +749,7 @@
|
||||
"name": "icon-x-in-circle",
|
||||
"prevSize": 16,
|
||||
"code": 59940,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 84,
|
||||
@ -749,7 +757,7 @@
|
||||
"name": "icon-brightness",
|
||||
"prevSize": 16,
|
||||
"code": 59941,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 83,
|
||||
@ -757,7 +765,7 @@
|
||||
"name": "icon-contrast",
|
||||
"prevSize": 16,
|
||||
"code": 59942,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 87,
|
||||
@ -765,7 +773,7 @@
|
||||
"name": "icon-expand",
|
||||
"prevSize": 16,
|
||||
"code": 59943,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 89,
|
||||
@ -773,7 +781,7 @@
|
||||
"name": "icon-list-view",
|
||||
"prevSize": 16,
|
||||
"code": 59944,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 133,
|
||||
@ -781,7 +789,7 @@
|
||||
"name": "icon-grid-snap-to",
|
||||
"prevSize": 16,
|
||||
"code": 59945,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 132,
|
||||
@ -789,7 +797,7 @@
|
||||
"name": "icon-grid-snap-no",
|
||||
"prevSize": 16,
|
||||
"code": 59946,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 94,
|
||||
@ -797,7 +805,7 @@
|
||||
"name": "icon-frame-show",
|
||||
"prevSize": 16,
|
||||
"code": 59947,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 95,
|
||||
@ -805,7 +813,7 @@
|
||||
"name": "icon-frame-hide",
|
||||
"prevSize": 16,
|
||||
"code": 59948,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 97,
|
||||
@ -813,7 +821,7 @@
|
||||
"name": "icon-import",
|
||||
"prevSize": 16,
|
||||
"code": 59949,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 96,
|
||||
@ -821,7 +829,7 @@
|
||||
"name": "icon-export",
|
||||
"prevSize": 16,
|
||||
"code": 59950,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 194,
|
||||
@ -829,7 +837,7 @@
|
||||
"name": "icon-font-size",
|
||||
"prevSize": 16,
|
||||
"code": 59951,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 163,
|
||||
@ -837,7 +845,7 @@
|
||||
"name": "icon-clear-data",
|
||||
"prevSize": 16,
|
||||
"code": 59952,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 173,
|
||||
@ -845,7 +853,7 @@
|
||||
"name": "icon-history",
|
||||
"prevSize": 16,
|
||||
"code": 59953,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 181,
|
||||
@ -853,7 +861,7 @@
|
||||
"name": "icon-arrow-up-to-parent",
|
||||
"prevSize": 16,
|
||||
"code": 59954,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 184,
|
||||
@ -861,7 +869,7 @@
|
||||
"name": "icon-crosshair-in-circle",
|
||||
"prevSize": 16,
|
||||
"code": 59955,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 185,
|
||||
@ -869,7 +877,7 @@
|
||||
"name": "icon-target",
|
||||
"prevSize": 16,
|
||||
"code": 59956,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 187,
|
||||
@ -877,7 +885,7 @@
|
||||
"name": "icon-items-collapse",
|
||||
"prevSize": 16,
|
||||
"code": 59957,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 188,
|
||||
@ -885,7 +893,7 @@
|
||||
"name": "icon-items-expand",
|
||||
"prevSize": 16,
|
||||
"code": 59958,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 190,
|
||||
@ -893,7 +901,7 @@
|
||||
"name": "icon-3-dots",
|
||||
"prevSize": 16,
|
||||
"code": 59959,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 193,
|
||||
@ -901,7 +909,7 @@
|
||||
"name": "icon-grid-on",
|
||||
"prevSize": 16,
|
||||
"code": 59960,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 192,
|
||||
@ -909,7 +917,7 @@
|
||||
"name": "icon-grid-off",
|
||||
"prevSize": 16,
|
||||
"code": 59961,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 191,
|
||||
@ -917,7 +925,7 @@
|
||||
"name": "icon-camera",
|
||||
"prevSize": 16,
|
||||
"code": 59962,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 196,
|
||||
@ -925,7 +933,7 @@
|
||||
"name": "icon-folders-collapse",
|
||||
"prevSize": 16,
|
||||
"code": 59963,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 144,
|
||||
@ -933,7 +941,7 @@
|
||||
"name": "icon-activity",
|
||||
"prevSize": 16,
|
||||
"code": 60160,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 104,
|
||||
@ -941,7 +949,7 @@
|
||||
"name": "icon-activity-mode",
|
||||
"prevSize": 16,
|
||||
"code": 60161,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 137,
|
||||
@ -949,7 +957,7 @@
|
||||
"name": "icon-autoflow-tabular",
|
||||
"prevSize": 16,
|
||||
"code": 60162,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 115,
|
||||
@ -957,7 +965,7 @@
|
||||
"name": "icon-clock",
|
||||
"prevSize": 16,
|
||||
"code": 60163,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 2,
|
||||
@ -965,7 +973,7 @@
|
||||
"name": "icon-database",
|
||||
"prevSize": 16,
|
||||
"code": 60164,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 3,
|
||||
@ -973,7 +981,7 @@
|
||||
"name": "icon-database-query",
|
||||
"prevSize": 16,
|
||||
"code": 60165,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 67,
|
||||
@ -981,7 +989,7 @@
|
||||
"name": "icon-dataset",
|
||||
"prevSize": 16,
|
||||
"code": 60166,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@ -989,7 +997,7 @@
|
||||
"name": "icon-datatable",
|
||||
"prevSize": 16,
|
||||
"code": 60167,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 136,
|
||||
@ -997,7 +1005,7 @@
|
||||
"name": "icon-dictionary",
|
||||
"prevSize": 16,
|
||||
"code": 60168,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 51,
|
||||
@ -1005,7 +1013,7 @@
|
||||
"name": "icon-folder",
|
||||
"prevSize": 16,
|
||||
"code": 60169,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 147,
|
||||
@ -1013,7 +1021,7 @@
|
||||
"name": "icon-image",
|
||||
"prevSize": 16,
|
||||
"code": 60170,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 4,
|
||||
@ -1021,7 +1029,7 @@
|
||||
"name": "icon-layout",
|
||||
"prevSize": 16,
|
||||
"code": 60171,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@ -1029,7 +1037,7 @@
|
||||
"name": "icon-object",
|
||||
"prevSize": 16,
|
||||
"code": 60172,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@ -1037,7 +1045,7 @@
|
||||
"name": "icon-object-unknown",
|
||||
"prevSize": 16,
|
||||
"code": 60173,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 105,
|
||||
@ -1045,7 +1053,7 @@
|
||||
"name": "icon-packet",
|
||||
"prevSize": 16,
|
||||
"code": 60174,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 126,
|
||||
@ -1053,7 +1061,7 @@
|
||||
"name": "icon-page",
|
||||
"prevSize": 16,
|
||||
"code": 60175,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 130,
|
||||
@ -1061,7 +1069,7 @@
|
||||
"name": "icon-plot-overlay",
|
||||
"prevSize": 16,
|
||||
"code": 60176,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
@ -1069,7 +1077,7 @@
|
||||
"name": "icon-plot-stacked",
|
||||
"prevSize": 16,
|
||||
"code": 60177,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 134,
|
||||
@ -1077,7 +1085,7 @@
|
||||
"name": "icon-session",
|
||||
"prevSize": 16,
|
||||
"code": 60178,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 109,
|
||||
@ -1085,7 +1093,7 @@
|
||||
"name": "icon-tabular",
|
||||
"prevSize": 16,
|
||||
"code": 60179,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 107,
|
||||
@ -1093,7 +1101,7 @@
|
||||
"name": "icon-tabular-lad",
|
||||
"prevSize": 16,
|
||||
"code": 60180,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 106,
|
||||
@ -1101,7 +1109,7 @@
|
||||
"name": "icon-tabular-lad-set",
|
||||
"prevSize": 16,
|
||||
"code": 60181,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 70,
|
||||
@ -1109,7 +1117,7 @@
|
||||
"name": "icon-tabular-realtime",
|
||||
"prevSize": 16,
|
||||
"code": 60182,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 60,
|
||||
@ -1117,7 +1125,7 @@
|
||||
"name": "icon-tabular-scrolling",
|
||||
"prevSize": 16,
|
||||
"code": 60183,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 131,
|
||||
@ -1125,7 +1133,7 @@
|
||||
"name": "icon-telemetry",
|
||||
"prevSize": 16,
|
||||
"code": 60184,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 202,
|
||||
@ -1133,7 +1141,7 @@
|
||||
"name": "icon-timeline",
|
||||
"prevSize": 16,
|
||||
"code": 60185,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 81,
|
||||
@ -1141,7 +1149,7 @@
|
||||
"name": "icon-timer",
|
||||
"prevSize": 16,
|
||||
"code": 60186,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 69,
|
||||
@ -1149,7 +1157,7 @@
|
||||
"name": "icon-topic",
|
||||
"prevSize": 16,
|
||||
"code": 60187,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
@ -1157,7 +1165,7 @@
|
||||
"name": "icon-box-with-dashed-lines-v2",
|
||||
"prevSize": 16,
|
||||
"code": 60188,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
@ -1165,7 +1173,7 @@
|
||||
"name": "icon-summary-widget",
|
||||
"prevSize": 16,
|
||||
"code": 60189,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 92,
|
||||
@ -1173,7 +1181,7 @@
|
||||
"name": "icon-notebook",
|
||||
"prevSize": 16,
|
||||
"code": 60190,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 168,
|
||||
@ -1181,7 +1189,7 @@
|
||||
"name": "icon-tabs-view",
|
||||
"prevSize": 16,
|
||||
"code": 60191,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 117,
|
||||
@ -1189,7 +1197,7 @@
|
||||
"name": "icon-flexible-layout",
|
||||
"prevSize": 16,
|
||||
"code": 60192,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 166,
|
||||
@ -1197,7 +1205,7 @@
|
||||
"name": "icon-generator-sine",
|
||||
"prevSize": 16,
|
||||
"code": 60193,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 167,
|
||||
@ -1205,7 +1213,7 @@
|
||||
"name": "icon-generator-event",
|
||||
"prevSize": 16,
|
||||
"code": 60194,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 165,
|
||||
@ -1213,7 +1221,7 @@
|
||||
"name": "icon-gauge-v2",
|
||||
"prevSize": 16,
|
||||
"code": 60195,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 170,
|
||||
@ -1221,7 +1229,7 @@
|
||||
"name": "icon-spectra",
|
||||
"prevSize": 16,
|
||||
"code": 60196,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 171,
|
||||
@ -1229,7 +1237,7 @@
|
||||
"name": "icon-telemetry-spectra",
|
||||
"prevSize": 16,
|
||||
"code": 60197,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 172,
|
||||
@ -1237,7 +1245,7 @@
|
||||
"name": "icon-pushbutton",
|
||||
"prevSize": 16,
|
||||
"code": 60198,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 174,
|
||||
@ -1245,7 +1253,7 @@
|
||||
"name": "icon-conditional",
|
||||
"prevSize": 16,
|
||||
"code": 60199,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 178,
|
||||
@ -1253,7 +1261,7 @@
|
||||
"name": "icon-condition-widget",
|
||||
"prevSize": 16,
|
||||
"code": 60200,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 180,
|
||||
@ -1261,7 +1269,7 @@
|
||||
"name": "icon-alphanumeric",
|
||||
"prevSize": 16,
|
||||
"code": 60201,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 183,
|
||||
@ -1269,7 +1277,7 @@
|
||||
"name": "icon-image-telemetry",
|
||||
"prevSize": 16,
|
||||
"code": 60202,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 198,
|
||||
@ -1277,7 +1285,7 @@
|
||||
"name": "icon-telemetry-aggregate",
|
||||
"prevSize": 16,
|
||||
"code": 60203,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 199,
|
||||
@ -1285,7 +1293,7 @@
|
||||
"name": "icon-bar-graph",
|
||||
"prevSize": 16,
|
||||
"code": 60204,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 200,
|
||||
@ -1293,7 +1301,7 @@
|
||||
"name": "icon-map",
|
||||
"prevSize": 16,
|
||||
"code": 60205,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 203,
|
||||
@ -1301,7 +1309,7 @@
|
||||
"name": "icon-plan",
|
||||
"prevSize": 16,
|
||||
"code": 60206,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 204,
|
||||
@ -1309,14 +1317,6 @@
|
||||
"name": "icon-timelist",
|
||||
"prevSize": 16,
|
||||
"code": 60207,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 214,
|
||||
"id": 184,
|
||||
"name": "icon-notebook-restricted",
|
||||
"prevSize": 16,
|
||||
"code": 60209,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
@ -1326,6 +1326,14 @@
|
||||
"prevSize": 16,
|
||||
"code": 60208,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 214,
|
||||
"id": 184,
|
||||
"name": "icon-notebook-restricted",
|
||||
"prevSize": 16,
|
||||
"code": 60209,
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"id": 0,
|
||||
@ -2327,6 +2335,26 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 186,
|
||||
"paths": [
|
||||
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM681.38 365.14c0.68-20.46-2.22-37.7-8.7-51.68-6.5-13.98-15.7-25.4-27.64-34.28-11.94-8.86-26.1-15.18-42.48-18.94-16.38-3.74-33.78-5.62-52.2-5.62-15.020 0-30.2 1.54-45.54 4.6s-29.16 8.18-41.44 15.36-22.18 16.56-29.68 28.14c-7.52 11.62-11.26 25.94-11.26 42.98s6.66 32.6 19.96 44.52c13.3 11.94 29.34 21.84 48.1 29.68 18.76 7.86 38.020 14 57.82 18.42 19.78 4.44 35.82 8.020 48.1 10.74 28.66 7.52 54.92 16.22 78.8 26.1 23.88 9.9 44.52 22.68 61.92 38.38s30.86 34.8 40.42 57.32c9.54 22.52 14.32 50.16 14.32 82.9 0 43.68-9.040 80.86-27.12 111.56s-41.28 55.62-69.6 74.7c-28.32 19.1-60.22 32.92-95.7 41.44s-70.62 12.8-105.42 12.8c-102.34 0-178.6-20.8-228.74-62.44-50.16-41.6-75.22-107.1-75.22-196.5h152.5c-1.38 25.94 1.7 47.58 9.22 64.98 7.5 17.4 18.42 31.22 32.74 41.44 14.32 10.24 31.38 17.58 51.18 22 19.78 4.44 41.28 6.66 64.48 6.66 16.38 0 32.74-2.040 49.12-6.14s31.22-10.24 44.52-18.42c13.3-8.18 24.22-18.76 32.76-31.72 8.52-12.94 12.8-28.66 12.8-47.080s-5.46-32.24-16.38-43.5c-10.92-11.26-25.080-20.98-42.48-29.16s-37.2-15.36-59.36-21.5c-22.18-6.14-44.52-12.62-67.040-19.44-23.2-6.82-45.72-15-67.54-24.56-21.84-9.54-41.44-21.82-58.84-36.84-17.4-15-31.38-33.42-41.96-55.26-10.58-21.82-15.86-48.44-15.86-79.82 0-40.94 8.52-75.74 25.58-104.4 17.040-28.66 39.22-52.020 66.52-70.1 27.28-18.080 58.16-31.38 92.62-39.92 34.44-8.52 69.42-12.8 104.9-12.8 37.52 0 72.82 4.26 105.92 12.8 33.080 8.54 62.080 22.36 87 41.44 24.9 19.1 44.68 43.5 59.36 73.18 14.66 29.68 22 65.68 22 107.98h-152.5z"
|
||||
],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-stale"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"12552552551": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"paths": [
|
||||
@ -4237,6 +4265,26 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 185,
|
||||
"paths": [
|
||||
"M192 0c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM128 352c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96v0c-53.019 0-96-42.981-96-96v0zM288 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 640c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 320c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM800 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0z"
|
||||
],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-plot-scatter"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"12552552551": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 184,
|
||||
"paths": [
|
||||
@ -4259,26 +4307,6 @@
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 185,
|
||||
"paths": [
|
||||
"M192 0c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM128 352c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96v0c-53.019 0-96-42.981-96-96v0zM288 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 640c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 320c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM800 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0z"
|
||||
],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-plot-scatter"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"12552552551": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"invisible": false,
|
||||
|
@ -62,6 +62,7 @@
|
||||
<glyph unicode="" glyph-name="icon-status-poll-circle-slash" d="M391.18 227.3c35.72-22.98 77.32-35.3 120.82-35.3 59.84 0 116.080 23.3 158.4 65.6 42.3 42.3 65.6 98.56 65.6 158.4 0 43.5-12.32 85.080-35.3 120.82l-309.52-309.52zM512 640c-59.84 0-116.080-23.3-158.4-65.6-42.3-42.3-65.6-98.56-65.6-158.4 0-43.5 12.32-85.080 35.3-120.82l309.52 309.52c-35.72 22.98-77.32 35.3-120.82 35.3zM512 896c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM512 96c-176.74 0-320 143.26-320 320s143.26 320 320 320 320-143.26 320-320-143.26-320-320-320z" />
|
||||
<glyph unicode="" glyph-name="icon-status-poll-question-mark" d="M512 896c-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480s-229.24 480-512 480zM579.020 64h-141.36v136.64h141.36v-136.64zM713.84 462.1c-11.94-17.020-34.9-38.78-68.84-65.24l-33.48-26c-18.24-14.18-30.34-30.74-36.32-49.64-3.78-11.98-5.82-30.58-6.14-55.8h-128.12c1.88 53.26 6.92 90.060 15.080 110.4 8.18 20.34 29.22 43.74 63.16 70.22l34.42 26.94c11.3 8.52 20.42 17.8 27.34 27.9 12.56 17.34 18.86 36.4 18.86 57.2 0 23.94-7 45.78-20.98 65.48-14 19.7-39.54 29.54-76.64 29.54s-62.34-12.14-77.6-36.4c-15.24-24.28-22.88-49.48-22.88-75.64h-136.64c3.78 89.84 35.14 153.5 94.080 191.020 37.18 23.94 82.9 35.94 137.12 35.94 71.22 0 130.42-17.020 177.54-51.060s70.68-84.48 70.68-151.3c0-40.98-10.22-75.5-30.66-103.54z" />
|
||||
<glyph unicode="" glyph-name="icon-status-poll-edit" d="M1000.080 561.36l-336.6-336.76-20.52-6.88-450.96-153.72 160.68 471.52 332.34 332.34c-54.040 18.2-112.28 28.14-173.020 28.14-282.76 0-512-214.9-512-480 0-92.26 27.8-178.44 75.92-251.6l-75.92-292.4 313.5 101.42c61.040-24.1 128.12-37.42 198.5-37.42 282.76 0 512 214.9 512 480 0 50.68-8.4 99.5-23.92 145.36zM408.42 500.76l-2.16-6.3-111.7-327.9 334.12 113.86 4.62 4.68 350.28 350.28c6.8 6.78 14.96 19.1 14.96 38.9 0 34.86-26.82 83.28-69.88 126.38-26.54 26.54-55.9 47.6-82.7 59.34-47.34 20.8-72.020 6.24-82.64-4.36l-354.9-354.88zM470.56 474.58h44v-88h88v-44l-4.7-12.72-139.68-47.54-47.94 47.94 47.6 139.72 12.72 4.6z" />
|
||||
<glyph unicode="" glyph-name="icon-stale" d="M832 896h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM681.38 530.86c0.68 20.46-2.22 37.7-8.7 51.68-6.5 13.98-15.7 25.4-27.64 34.28-11.94 8.86-26.1 15.18-42.48 18.94-16.38 3.74-33.78 5.62-52.2 5.62-15.020 0-30.2-1.54-45.54-4.6s-29.16-8.18-41.44-15.36-22.18-16.56-29.68-28.14c-7.52-11.62-11.26-25.94-11.26-42.98s6.66-32.6 19.96-44.52c13.3-11.94 29.34-21.84 48.1-29.68 18.76-7.86 38.020-14 57.82-18.42 19.78-4.44 35.82-8.020 48.1-10.74 28.66-7.52 54.92-16.22 78.8-26.1 23.88-9.9 44.52-22.68 61.92-38.38s30.86-34.8 40.42-57.32c9.54-22.52 14.32-50.16 14.32-82.9 0-43.68-9.040-80.86-27.12-111.56s-41.28-55.62-69.6-74.7c-28.32-19.1-60.22-32.92-95.7-41.44s-70.62-12.8-105.42-12.8c-102.34 0-178.6 20.8-228.74 62.44-50.16 41.6-75.22 107.1-75.22 196.5h152.5c-1.38-25.94 1.7-47.58 9.22-64.98 7.5-17.4 18.42-31.22 32.74-41.44 14.32-10.24 31.38-17.58 51.18-22 19.78-4.44 41.28-6.66 64.48-6.66 16.38 0 32.74 2.040 49.12 6.14s31.22 10.24 44.52 18.42c13.3 8.18 24.22 18.76 32.76 31.72 8.52 12.94 12.8 28.66 12.8 47.080s-5.46 32.24-16.38 43.5c-10.92 11.26-25.080 20.98-42.48 29.16s-37.2 15.36-59.36 21.5c-22.18 6.14-44.52 12.62-67.040 19.44-23.2 6.82-45.72 15-67.54 24.56-21.84 9.54-41.44 21.82-58.84 36.84-17.4 15-31.38 33.42-41.96 55.26-10.58 21.82-15.86 48.44-15.86 79.82 0 40.94 8.52 75.74 25.58 104.4 17.040 28.66 39.22 52.020 66.52 70.1 27.28 18.080 58.16 31.38 92.62 39.92 34.44 8.52 69.42 12.8 104.9 12.8 37.52 0 72.82-4.26 105.92-12.8 33.080-8.54 62.080-22.36 87-41.44 24.9-19.1 44.68-43.5 59.36-73.18 14.66-29.68 22-65.68 22-107.98h-152.5z" />
|
||||
<glyph unicode="" glyph-name="icon-arrows-right-left" d="M1024 384l-448-512v1024zM448 896l-448-512 448-512z" />
|
||||
<glyph unicode="" glyph-name="icon-arrows-up-down" d="M512 896l512-448h-1024zM0 320l512-448 512 448z" />
|
||||
<glyph unicode="" glyph-name="icon-bullet" d="M832 144c0-44-36-80-80-80h-480c-44 0-80 36-80 80v480c0 44 36 80 80 80h480c44 0 80-36 80-80v-480z" />
|
||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 70 KiB |
Binary file not shown.
Binary file not shown.
@ -14,7 +14,7 @@
|
||||
<div
|
||||
ref="objectViewWrapper"
|
||||
class="c-object-view"
|
||||
:class="objectTypeClass"
|
||||
:class="viewClasses"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
@ -24,6 +24,7 @@ import _ from "lodash";
|
||||
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||
import IndependentTimeConductor from '@/plugins/timeConductor/independent/IndependentTimeConductor.vue';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
const SupportedViewTypes = [
|
||||
'plot-stacked',
|
||||
@ -36,6 +37,7 @@ export default {
|
||||
components: {
|
||||
IndependentTimeConductor
|
||||
},
|
||||
mixins: [stalenessMixin],
|
||||
inject: ["openmct"],
|
||||
props: {
|
||||
showEditView: Boolean,
|
||||
@ -85,8 +87,14 @@ export default {
|
||||
|
||||
return this.domainObject && SupportedViewTypes.includes(viewKey);
|
||||
},
|
||||
objectTypeClass() {
|
||||
return this.domainObject && ('is-object-type-' + this.domainObject.type);
|
||||
viewClasses() {
|
||||
let classes;
|
||||
|
||||
if (this.domainObject) {
|
||||
classes = `is-object-type-${this.domainObject.type} ${this.isStale ? 'is-stale' : ''}`;
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
@ -128,6 +136,7 @@ export default {
|
||||
if (this.domainObject) {
|
||||
//This is to apply styles to subobjects in a layout
|
||||
this.initObjectStyles();
|
||||
this.triggerStalenessSubscribe(this.domainObject);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -161,6 +170,9 @@ export default {
|
||||
this.composition._destroy();
|
||||
}
|
||||
|
||||
this.isStale = false;
|
||||
this.triggerUnsubscribeFromStaleness();
|
||||
|
||||
this.openmct.objectViews.off('clearData', this.clearData);
|
||||
},
|
||||
getStyleReceiver() {
|
||||
@ -192,6 +204,11 @@ export default {
|
||||
this.clear();
|
||||
this.updateView(true);
|
||||
},
|
||||
triggerStalenessSubscribe(object) {
|
||||
if (this.openmct.telemetry.isTelemetryObject(object)) {
|
||||
this.subscribeToStaleness(object);
|
||||
}
|
||||
},
|
||||
updateStyle(styleObj) {
|
||||
let elemToStyle = this.getStyleReceiver();
|
||||
|
||||
@ -306,6 +323,7 @@ export default {
|
||||
|
||||
this.updateView(immediatelySelect);
|
||||
|
||||
this.triggerStalenessSubscribe(this.domainObject);
|
||||
this.initObjectStyles();
|
||||
},
|
||||
initObjectStyles() {
|
||||
|
@ -320,6 +320,10 @@
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
&.is-stale {
|
||||
@include isStaleHolder();
|
||||
}
|
||||
}
|
||||
|
||||
.is-editing {
|
||||
|
68
src/ui/mixins/staleness-mixin.js
Normal file
68
src/ui/mixins/staleness-mixin.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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 StalenessUtils from '@/utils/staleness';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isStale: false
|
||||
};
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.triggerUnsubscribeFromStaleness();
|
||||
},
|
||||
methods: {
|
||||
subscribeToStaleness(domainObject, callback) {
|
||||
if (!this.stalenessUtils) {
|
||||
this.stalenessUtils = new StalenessUtils(this.openmct, domainObject);
|
||||
}
|
||||
|
||||
this.requestStaleness(domainObject);
|
||||
this.unsubscribeFromStaleness = this.openmct.telemetry.subscribeToStaleness(domainObject, (stalenessResponse) => {
|
||||
this.handleStalenessResponse(stalenessResponse, callback);
|
||||
});
|
||||
},
|
||||
async requestStaleness(domainObject) {
|
||||
const stalenessResponse = await this.openmct.telemetry.isStale(domainObject);
|
||||
if (stalenessResponse !== undefined) {
|
||||
this.handleStalenessResponse(stalenessResponse);
|
||||
}
|
||||
},
|
||||
handleStalenessResponse(stalenessResponse, callback) {
|
||||
if (this.stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
|
||||
if (typeof callback === 'function') {
|
||||
callback(stalenessResponse.isStale);
|
||||
} else {
|
||||
this.isStale = stalenessResponse.isStale;
|
||||
}
|
||||
}
|
||||
},
|
||||
triggerUnsubscribeFromStaleness() {
|
||||
if (this.unsubscribeFromStaleness) {
|
||||
this.unsubscribeFromStaleness();
|
||||
delete this.unsubscribeFromStaleness;
|
||||
this.stalenessUtils.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
76
src/utils/staleness.js
Normal file
76
src/utils/staleness.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StalenessUtils {
|
||||
constructor(openmct, domainObject) {
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
this.metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||
this.lastStalenessResponseTime = 0;
|
||||
|
||||
this.setTimeSystem(this.openmct.time.timeSystem());
|
||||
this.watchTimeSystem();
|
||||
}
|
||||
|
||||
shouldUpdateStaleness(stalenessResponse, id) {
|
||||
const stalenessResponseTime = this.parseTime(stalenessResponse);
|
||||
|
||||
if (stalenessResponseTime > this.lastStalenessResponseTime) {
|
||||
this.lastStalenessResponseTime = stalenessResponseTime;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
watchTimeSystem() {
|
||||
this.openmct.time.on('timeSystem', this.setTimeSystem, this);
|
||||
}
|
||||
|
||||
unwatchTimeSystem() {
|
||||
this.openmct.time.off('timeSystem', this.setTimeSystem, this);
|
||||
}
|
||||
|
||||
setTimeSystem(timeSystem) {
|
||||
let metadataValue = { format: timeSystem.key };
|
||||
|
||||
if (this.metadata) {
|
||||
metadataValue = this.metadata.value(timeSystem.key) ?? metadataValue;
|
||||
}
|
||||
|
||||
const valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
|
||||
|
||||
this.parseTime = (stalenessResponse) => {
|
||||
const stalenessDatum = {
|
||||
...stalenessResponse,
|
||||
source: stalenessResponse[timeSystem.key]
|
||||
};
|
||||
|
||||
return valueFormatter.parse(stalenessDatum);
|
||||
};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unwatchTimeSystem();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user