Compare commits
7 Commits
fix-SP
...
de-reactif
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee80c5c14e | ||
|
|
eacbac6aad | ||
|
|
69153fe8f0 | ||
|
|
51196530fd | ||
|
|
fefa46ce7e | ||
|
|
e08ab8ef24 | ||
|
|
7011877e64 |
@@ -1,30 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
// this will be called from the test suite with
|
||||
// await page.addInitScript({ path: path.join(__dirname, 'addInitGauge.js') });
|
||||
// it will install the Gauge since it is not installed by default
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const openmct = window.openmct;
|
||||
openmct.install(openmct.plugins.Gauge());
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "http://localhost:8080",
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "mct-tree-expanded",
|
||||
"value": "[]"
|
||||
},
|
||||
{
|
||||
"name": "tcHistory",
|
||||
"value": "{\"utc\":[{\"start\":1656473493306,\"end\":1656475293306},{\"start\":1655769110258,\"end\":1655770910258},{\"start\":1652301954635,\"end\":1652303754635}]}"
|
||||
},
|
||||
{
|
||||
"name": "mct",
|
||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},{\"key\":\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\",\"namespace\":\"\"},{\"key\":\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1656475294042,\"modified\":1656475294042},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002},\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"43cfb4b1-348c-43c0-a681-c4cf53b5335f\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1655770911020,\"location\":\"mine\",\"persisted\":1655770911020},\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"26739ce0-9a56-466c-91dd-f08bd9bfc9d7\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1656475294040,\"location\":\"mine\",\"persisted\":1656475294040}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,8 @@ const sinon = require('sinon');
|
||||
|
||||
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
|
||||
|
||||
const CUSTOM_NAME = 'CUSTOM_NAME';
|
||||
|
||||
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
|
||||
// Will replace with cy.clock() equivalent
|
||||
test.beforeEach(async ({ context }) => {
|
||||
@@ -52,21 +54,23 @@ test.beforeEach(async ({ context }) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Visual - Default Gauge is correct @addInit', async ({ page }) => {
|
||||
|
||||
await page.addInitScript({ path: path.join(__dirname, '../plugins/gauge', './addInitGauge.js') });
|
||||
test('Visual - Restricted Notebook is visually correct @addInit', async ({ page }) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
await page.addInitScript({ path: path.join(__dirname, '../plugins/notebook', './addRestrictedNotebook.js') });
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
// Click text=CUSTOM_NAME
|
||||
await page.click(`text=${CUSTOM_NAME}`);
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation({waitUntil: 'networkidle'}),
|
||||
page.click('text=OK')
|
||||
]);
|
||||
|
||||
await page.click('text=Gauge');
|
||||
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Gauge object
|
||||
// Take a snapshot of the newly created CUSTOM_NAME notebook
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Gauge');
|
||||
await percySnapshot(page, 'Restricted Notebook with CUSTOM_NAME');
|
||||
|
||||
});
|
||||
|
||||
@@ -211,3 +211,22 @@ test('Visual - Display Layout Icon is correct', async ({ page }) => {
|
||||
await percySnapshot(page, 'Display Layout Create Menu');
|
||||
|
||||
});
|
||||
|
||||
test('Visual - Default Gauge is correct', async ({ page }) => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
await page.click('text=Gauge');
|
||||
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Gauge object
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Gauge');
|
||||
|
||||
});
|
||||
|
||||
|
||||
272
src/MCT.js
272
src/MCT.js
@@ -96,161 +96,166 @@ define([
|
||||
};
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
*/
|
||||
this.selection = new Selection(this);
|
||||
[
|
||||
/**
|
||||
* Tracks current selection state of the application.
|
||||
* @private
|
||||
*/
|
||||
['selection', () => new Selection(this)],
|
||||
|
||||
/**
|
||||
* MCT's time conductor, which may be used to synchronize view contents
|
||||
* for telemetry- or time-based views.
|
||||
* @type {module:openmct.TimeConductor}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name conductor
|
||||
*/
|
||||
this.time = new api.TimeAPI(this);
|
||||
/**
|
||||
* MCT's time conductor, which may be used to synchronize view contents
|
||||
* for telemetry- or time-based views.
|
||||
* @type {module:openmct.TimeConductor}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name conductor
|
||||
*/
|
||||
['time', () => new api.TimeAPI(this)],
|
||||
|
||||
/**
|
||||
* An interface for interacting with the composition of domain objects.
|
||||
* The composition of a domain object is the list of other domain
|
||||
* objects it "contains" (for instance, that should be displayed
|
||||
* beneath it in the tree.)
|
||||
*
|
||||
* `composition` may be called as a function, in which case it acts
|
||||
* as [`composition.get`]{@link module:openmct.CompositionAPI#get}.
|
||||
*
|
||||
* @type {module:openmct.CompositionAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name composition
|
||||
*/
|
||||
this.composition = new api.CompositionAPI(this);
|
||||
/**
|
||||
* An interface for interacting with the composition of domain objects.
|
||||
* The composition of a domain object is the list of other domain
|
||||
* objects it "contains" (for instance, that should be displayed
|
||||
* beneath it in the tree.)
|
||||
*
|
||||
* `composition` may be called as a function, in which case it acts
|
||||
* as [`composition.get`]{@link module:openmct.CompositionAPI#get}.
|
||||
*
|
||||
* @type {module:openmct.CompositionAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name composition
|
||||
*/
|
||||
['composition', () => new api.CompositionAPI(this)],
|
||||
|
||||
/**
|
||||
* Registry for views of domain objects which should appear in the
|
||||
* main viewing area.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objectViews
|
||||
*/
|
||||
this.objectViews = new ViewRegistry();
|
||||
/**
|
||||
* Registry for views of domain objects which should appear in the
|
||||
* main viewing area.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objectViews
|
||||
*/
|
||||
['objectViews', () => new ViewRegistry()],
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in the Inspector area.
|
||||
* These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.InspectorViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name inspectorViews
|
||||
*/
|
||||
this.inspectorViews = new InspectorViewRegistry();
|
||||
/**
|
||||
* Registry for views which should appear in the Inspector area.
|
||||
* These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.InspectorViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name inspectorViews
|
||||
*/
|
||||
['inspectorViews', () => new InspectorViewRegistry()],
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in Edit Properties
|
||||
* dialogs, and similar user interface elements used for
|
||||
* modifying domain objects external to its regular views.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name propertyEditors
|
||||
*/
|
||||
this.propertyEditors = new ViewRegistry();
|
||||
/**
|
||||
* Registry for views which should appear in Edit Properties
|
||||
* dialogs, and similar user interface elements used for
|
||||
* modifying domain objects external to its regular views.
|
||||
*
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name propertyEditors
|
||||
*/
|
||||
['propertyEditors', () => new ViewRegistry()],
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in the status indicator area.
|
||||
* @type {module:openmct.ViewRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name indicators
|
||||
*/
|
||||
this.indicators = new ViewRegistry();
|
||||
/**
|
||||
* Registry for views which should appear in the toolbar area while
|
||||
* editing. These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.ToolbarRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name toolbars
|
||||
*/
|
||||
['toolbars', () => new ToolbarRegistry()],
|
||||
|
||||
/**
|
||||
* Registry for views which should appear in the toolbar area while
|
||||
* editing. These views will be chosen based on the selection state.
|
||||
*
|
||||
* @type {module:openmct.ToolbarRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name toolbars
|
||||
*/
|
||||
this.toolbars = new ToolbarRegistry();
|
||||
/**
|
||||
* Registry for domain object types which may exist within this
|
||||
* instance of Open MCT.
|
||||
*
|
||||
* @type {module:openmct.TypeRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name types
|
||||
*/
|
||||
['types', () => new api.TypeRegistry()],
|
||||
|
||||
/**
|
||||
* Registry for domain object types which may exist within this
|
||||
* instance of Open MCT.
|
||||
*
|
||||
* @type {module:openmct.TypeRegistry}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name types
|
||||
*/
|
||||
this.types = new api.TypeRegistry();
|
||||
/**
|
||||
* An interface for interacting with domain objects and the domain
|
||||
* object hierarchy.
|
||||
*
|
||||
* @type {module:openmct.ObjectAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objects
|
||||
*/
|
||||
['objects', () => new api.ObjectAPI.default(this.types, this)],
|
||||
|
||||
/**
|
||||
* An interface for interacting with domain objects and the domain
|
||||
* object hierarchy.
|
||||
*
|
||||
* @type {module:openmct.ObjectAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name objects
|
||||
*/
|
||||
this.objects = new api.ObjectAPI.default(this.types, this);
|
||||
/**
|
||||
* An interface for retrieving and interpreting telemetry data associated
|
||||
* with a domain object.
|
||||
*
|
||||
* @type {module:openmct.TelemetryAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name telemetry
|
||||
*/
|
||||
['telemetry', () => new api.TelemetryAPI(this)],
|
||||
|
||||
/**
|
||||
* An interface for retrieving and interpreting telemetry data associated
|
||||
* with a domain object.
|
||||
*
|
||||
* @type {module:openmct.TelemetryAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name telemetry
|
||||
*/
|
||||
this.telemetry = new api.TelemetryAPI(this);
|
||||
/**
|
||||
* An interface for creating new indicators and changing them dynamically.
|
||||
*
|
||||
* @type {module:openmct.IndicatorAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name indicators
|
||||
*/
|
||||
['indicators', () => new api.IndicatorAPI(this)],
|
||||
|
||||
/**
|
||||
* An interface for creating new indicators and changing them dynamically.
|
||||
*
|
||||
* @type {module:openmct.IndicatorAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name indicators
|
||||
*/
|
||||
this.indicators = new api.IndicatorAPI(this);
|
||||
/**
|
||||
* MCT's user awareness management, to enable user and
|
||||
* role specific functionality.
|
||||
* @type {module:openmct.UserAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name user
|
||||
*/
|
||||
['user', () => new api.UserAPI(this)],
|
||||
|
||||
/**
|
||||
* MCT's user awareness management, to enable user and
|
||||
* role specific functionality.
|
||||
* @type {module:openmct.UserAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name user
|
||||
*/
|
||||
this.user = new api.UserAPI(this);
|
||||
['notifications', () => new api.NotificationAPI()],
|
||||
|
||||
this.notifications = new api.NotificationAPI();
|
||||
['editor', () => new api.EditorAPI.default(this)],
|
||||
|
||||
this.editor = new api.EditorAPI.default(this);
|
||||
['overlays', () => new OverlayAPI.default()],
|
||||
|
||||
this.overlays = new OverlayAPI.default();
|
||||
['menus', () => new api.MenuAPI(this)],
|
||||
|
||||
this.menus = new api.MenuAPI(this);
|
||||
['actions', () => new api.ActionsAPI(this)],
|
||||
|
||||
this.actions = new api.ActionsAPI(this);
|
||||
['status', () => new api.StatusAPI(this)],
|
||||
|
||||
this.status = new api.StatusAPI(this);
|
||||
['priority', () => api.PriorityAPI],
|
||||
|
||||
this.priority = api.PriorityAPI;
|
||||
['router', () => new ApplicationRouter(this)],
|
||||
|
||||
this.router = new ApplicationRouter(this);
|
||||
this.faults = new api.FaultManagementAPI.default(this);
|
||||
this.forms = new api.FormsAPI.default(this);
|
||||
['faults', () => new api.FaultManagementAPI.default(this)],
|
||||
|
||||
this.branding = BrandingAPI.default;
|
||||
['forms', () => new api.FormsAPI.default(this)],
|
||||
|
||||
/**
|
||||
* MCT's annotation API that enables
|
||||
* human-created comments and categorization linked to data products
|
||||
* @type {module:openmct.AnnotationAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name annotation
|
||||
*/
|
||||
this.annotation = new api.AnnotationAPI(this);
|
||||
['branding', () => BrandingAPI.default],
|
||||
|
||||
/**
|
||||
* MCT's annotation API that enables
|
||||
* human-created comments and categorization linked to data products
|
||||
* @type {module:openmct.AnnotationAPI}
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name annotation
|
||||
*/
|
||||
['annotation', () => new api.AnnotationAPI(this)]
|
||||
].forEach(apiEntry => {
|
||||
const apiName = apiEntry[0];
|
||||
const apiObject = apiEntry[1]();
|
||||
|
||||
Object.defineProperty(this, apiName, {
|
||||
value: apiObject,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
});
|
||||
});
|
||||
|
||||
// Plugins that are installed by default
|
||||
this.install(this.plugins.Plot());
|
||||
@@ -281,6 +286,7 @@ define([
|
||||
this.install(this.plugins.ObjectInterceptors());
|
||||
this.install(this.plugins.DeviceClassifier());
|
||||
this.install(this.plugins.UserIndicator());
|
||||
this.install(this.plugins.Gauge());
|
||||
}
|
||||
|
||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
@@ -49,6 +49,7 @@ export class TelemetryCollection extends EventEmitter {
|
||||
this.pageState = undefined;
|
||||
this.lastBounds = undefined;
|
||||
this.requestAbort = undefined;
|
||||
this.isStrategyLatest = this.options.strategy === 'latest';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,12 +174,14 @@ export class TelemetryCollection extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
let latestBoundedDatum = this.boundedTelemetry[this.boundedTelemetry.length - 1];
|
||||
let data = Array.isArray(telemetryData) ? telemetryData : [telemetryData];
|
||||
let parsedValue;
|
||||
let beforeStartOfBounds;
|
||||
let afterEndOfBounds;
|
||||
let added = [];
|
||||
|
||||
// loop through, sort and dedupe
|
||||
for (let datum of data) {
|
||||
parsedValue = this.parseTime(datum);
|
||||
beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
||||
@@ -218,7 +221,17 @@ export class TelemetryCollection extends EventEmitter {
|
||||
}
|
||||
|
||||
if (added.length) {
|
||||
this.emit('add', added);
|
||||
// if latest strategy is requested, we need to check if the value is the latest unmitted value
|
||||
if (this.isStrategyLatest) {
|
||||
this.boundedTelemetry = [this.boundedTelemetry[this.boundedTelemetry.length - 1]];
|
||||
|
||||
// if true, then this value has yet to be emitted
|
||||
if (this.boundedTelemetry[0] !== latestBoundedDatum) {
|
||||
this.emit('add', this.boundedTelemetry);
|
||||
}
|
||||
} else {
|
||||
this.emit('add', added);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,13 +291,20 @@ export class TelemetryCollection extends EventEmitter {
|
||||
|
||||
if (startChanged) {
|
||||
testDatum[this.timeKey] = bounds.start;
|
||||
// Calculate the new index of the first item within the bounds
|
||||
startIndex = _.sortedIndexBy(
|
||||
this.boundedTelemetry,
|
||||
testDatum,
|
||||
datum => this.parseTime(datum)
|
||||
);
|
||||
discarded = this.boundedTelemetry.splice(0, startIndex);
|
||||
|
||||
// a little more complicated if not latest strategy
|
||||
if (!this.isStrategyLatest) {
|
||||
// Calculate the new index of the first item within the bounds
|
||||
startIndex = _.sortedIndexBy(
|
||||
this.boundedTelemetry,
|
||||
testDatum,
|
||||
datum => this.parseTime(datum)
|
||||
);
|
||||
discarded = this.boundedTelemetry.splice(0, startIndex);
|
||||
} else if (this.parseTime(testDatum) > this.parseTime(this.boundedTelemetry[0])) {
|
||||
discarded = this.boundedTelemetry;
|
||||
this.boundedTelemetry = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (endChanged) {
|
||||
@@ -296,7 +316,6 @@ export class TelemetryCollection extends EventEmitter {
|
||||
datum => this.parseTime(datum)
|
||||
);
|
||||
added = this.futureBuffer.splice(0, endIndex);
|
||||
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
|
||||
}
|
||||
|
||||
if (discarded.length > 0) {
|
||||
@@ -304,6 +323,13 @@ export class TelemetryCollection extends EventEmitter {
|
||||
}
|
||||
|
||||
if (added.length > 0) {
|
||||
if (!this.isStrategyLatest) {
|
||||
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
|
||||
} else {
|
||||
added = [added[added.length - 1]];
|
||||
this.boundedTelemetry = added;
|
||||
}
|
||||
|
||||
this.emit('add', added);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -21,23 +21,25 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fault-mgmt__list-header c-fault-mgmt__list">
|
||||
<div class="c-fault-mgmt__checkbox">
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header c-fault-mgmt__list">
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isSelectAll"
|
||||
@input="selectAll"
|
||||
>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-content">
|
||||
<div class="c-fault-mgmt__list-header-results"> {{ totalFaultsCount }} Results </div>
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-results c-fault-mgmt__list-severity">
|
||||
{{ totalFaultsCount }} Results
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-header-content">
|
||||
<div class="c-fault-mgmt__list-content-right">
|
||||
<div class="c-fault-mgmt__list-header-tripVal c-fault-mgmt__list-trigVal">Trip Value</div>
|
||||
<div class="c-fault-mgmt__list-header-liveVal c-fault-mgmt__list-curVal">Live Value</div>
|
||||
<div class="c-fault-mgmt__list-header-trigTime c-fault-mgmt__list-trigTime">Trigger Time</div>
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-tripVal">Trip Value</div>
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-liveVal">Live Value</div>
|
||||
<div class="c-fault-mgmt-item-header c-fault-mgmt__list-header-trigTime">Trigger Time</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-action-wrapper">
|
||||
<div class=" c-fault-mgmt-item-header c-fault-mgmt__list-header-action-wrapper">
|
||||
<div class="c-fault-mgmt__list-header-sortButton c-fault-mgmt__list-action-button">
|
||||
<SelectField
|
||||
class="c-fault-mgmt-viewButton"
|
||||
|
||||
@@ -26,49 +26,57 @@
|
||||
:class="[
|
||||
{'is-selected': isSelected},
|
||||
{'is-unacknowledged': !fault.acknowledged},
|
||||
{'is-shelved': fault.shelved}
|
||||
{'is-shelved': fault.shelved},
|
||||
{'is-acknowledged': fault.acknowledged}
|
||||
]"
|
||||
>
|
||||
<div class="c-fault-mgmt__checkbox">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isSelected"
|
||||
@input="toggleSelected"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="c-fault-mgmt__list-severity"
|
||||
:title="fault.severity"
|
||||
:class="[
|
||||
'is-severity-' + severity
|
||||
]"
|
||||
>
|
||||
<div class="c-fault-mgmt-item">
|
||||
<div
|
||||
class="c-fault-mgmt__list-severity"
|
||||
:title="fault.severity"
|
||||
:class="[
|
||||
'is-severity-' + severity
|
||||
]"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-content">
|
||||
<div class="c-fault-mgmt__list-pathname">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-content">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-pathname">
|
||||
<div class="c-fault-mgmt__list-path">{{ fault.namespace }}</div>
|
||||
<div class="c-fault-mgmt__list-faultname">{{ fault.name }}</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-content-right">
|
||||
<div
|
||||
class="c-fault-mgmt__list-trigVal"
|
||||
:class="tripValueClassname"
|
||||
title="Trip Value"
|
||||
>{{ fault.triggerValueInfo.value }}</div>
|
||||
<div
|
||||
class="c-fault-mgmt__list-curVal"
|
||||
:class="liveValueClassname"
|
||||
title="Live Value"
|
||||
>
|
||||
{{ fault.currentValueInfo.value }}
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-trigVal">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
:class="tripValueClassname"
|
||||
title="Trip Value"
|
||||
>{{ fault.triggerValueInfo.value }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="c-fault-mgmt__list-trigTime"
|
||||
>{{ fault.triggerTime }}
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-curVal">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
:class="liveValueClassname"
|
||||
title="Live Value"
|
||||
>{{ fault.currentValueInfo.value }}</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-trigTime">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
title="Last Trigger Time"
|
||||
>{{ fault.triggerTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-action-wrapper">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-action-wrapper">
|
||||
<button
|
||||
class="c-fault-mgmt__list-action-button l-browse-bar__actions c-icon-button icon-3-dots"
|
||||
title="Disposition Actions"
|
||||
@@ -77,7 +85,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const RANGE_CONDITION_CLASS = {
|
||||
@@ -149,7 +156,7 @@ export default {
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
cssClass: 'icon-bell',
|
||||
cssClass: 'icon-check',
|
||||
isDisabled: this.fault.acknowledged,
|
||||
name: 'Acknowledge',
|
||||
description: '',
|
||||
|
||||
@@ -35,25 +35,31 @@
|
||||
@shelveSelected="toggleShelveSelected"
|
||||
/>
|
||||
|
||||
<FaultManagementListHeader
|
||||
class="header"
|
||||
:selected-faults="Object.values(selectedFaults)"
|
||||
:total-faults-count="filteredFaultsList.length"
|
||||
@selectAll="selectAll"
|
||||
@sortChanged="sortChanged"
|
||||
/>
|
||||
<div class="c-faults-list-view-header-item-container-wrapper">
|
||||
<div class="c-faults-list-view-header-item-container">
|
||||
<FaultManagementListHeader
|
||||
class="header"
|
||||
:selected-faults="Object.values(selectedFaults)"
|
||||
:total-faults-count="filteredFaultsList.length"
|
||||
@selectAll="selectAll"
|
||||
@sortChanged="sortChanged"
|
||||
/>
|
||||
|
||||
<template v-if="filteredFaultsList.length > 0">
|
||||
<FaultManagementListItem
|
||||
v-for="fault of filteredFaultsList"
|
||||
:key="fault.id"
|
||||
:fault="fault"
|
||||
:is-selected="isSelected(fault)"
|
||||
@toggleSelected="toggleSelected"
|
||||
@acknowledgeSelected="toggleAcknowledgeSelected"
|
||||
@shelveSelected="toggleShelveSelected"
|
||||
/>
|
||||
</template>
|
||||
<div class="c-faults-list-view-item-body">
|
||||
<template v-if="filteredFaultsList.length > 0">
|
||||
<FaultManagementListItem
|
||||
v-for="fault of filteredFaultsList"
|
||||
:key="fault.id"
|
||||
:fault="fault"
|
||||
:is-selected="isSelected(fault)"
|
||||
@toggleSelected="toggleSelected"
|
||||
@acknowledgeSelected="toggleAcknowledgeSelected"
|
||||
@shelveSelected="toggleShelveSelected"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -195,7 +201,7 @@ export default {
|
||||
{
|
||||
key: 'comment',
|
||||
control: 'textarea',
|
||||
name: 'Comment',
|
||||
name: 'Optional comment',
|
||||
pattern: '\\S+',
|
||||
required: false,
|
||||
cssClass: 'l-input-lg',
|
||||
@@ -237,7 +243,7 @@ export default {
|
||||
{
|
||||
key: 'comment',
|
||||
control: 'textarea',
|
||||
name: 'Comment',
|
||||
name: 'Optional comment',
|
||||
pattern: '\\S+',
|
||||
required: false,
|
||||
cssClass: 'l-input-lg',
|
||||
@@ -246,7 +252,7 @@ export default {
|
||||
{
|
||||
key: 'shelveDuration',
|
||||
control: 'select',
|
||||
name: 'Shelve Duration',
|
||||
name: 'Shelve duration',
|
||||
options: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS,
|
||||
required: false,
|
||||
cssClass: 'l-input-lg',
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function FaultManagementPlugin() {
|
||||
name: 'Fault Management',
|
||||
creatable: false,
|
||||
description: 'Fault Management View',
|
||||
cssClass: 'icon-telemetry'
|
||||
cssClass: 'icon-bell'
|
||||
});
|
||||
|
||||
openmct.objectViews.addProvider(new FaultManagementViewProvider(openmct));
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<template>
|
||||
<div class="c-fault-mgmt__toolbar">
|
||||
<button
|
||||
class="c-icon-button icon-bell"
|
||||
class="c-icon-button icon-check"
|
||||
title="Acknowledge selected faults"
|
||||
:disabled="disableAcknowledge"
|
||||
@click="acknowledgeSelected"
|
||||
|
||||
@@ -21,14 +21,13 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fault-mgmt">
|
||||
<FaultManagementListView
|
||||
:faults-list="faultsList"
|
||||
/>
|
||||
</div>
|
||||
<FaultManagementListView
|
||||
:faults-list="faultsList"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import FaultManagementListView from './FaultManagementListView.vue';
|
||||
import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_GLOBAL_ALARMS } from './constants';
|
||||
|
||||
|
||||
@@ -19,216 +19,250 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*********************************************** FAULT PROPERTIES */
|
||||
.is-severity-critical{
|
||||
@include glyphBefore($glyph-icon-alert-triangle);
|
||||
color: $colorStatusError;
|
||||
}
|
||||
|
||||
.is-severity-warning{
|
||||
@include glyphBefore($glyph-icon-alert-rect);
|
||||
color: $colorStatusAlert;
|
||||
}
|
||||
|
||||
.is-severity-watch{
|
||||
@include glyphBefore($glyph-icon-info);
|
||||
color: $colorCommand;
|
||||
}
|
||||
|
||||
.is-unacknowledged{
|
||||
.c-fault-mgmt__list-severity{
|
||||
@include pulse($animName: severityAnim, $dur: 200ms);
|
||||
}
|
||||
}
|
||||
|
||||
.is-selected {
|
||||
background: $colorSelectedBg;
|
||||
}
|
||||
|
||||
.is-shelved{
|
||||
.c-fault-mgmt__list-content{
|
||||
opacity: 50% !important;
|
||||
font-style: italic;
|
||||
}
|
||||
.c-fault-mgmt__list-severity{
|
||||
@include pulse($animName: shelvedAnim, $dur: 0ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$colorFaultItemFg: $colorBodyFg;
|
||||
$colorFaultItemFgEmphasis: $colorBodyFgEm;
|
||||
$colorFaultItemBg: pullForward($colorBodyBg, 5%);
|
||||
|
||||
/*********************************************** SEARCH */
|
||||
.c-fault-mgmt__search-row{
|
||||
.c-fault-mgmt__search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
> * + * {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.c-fault-mgmt-search{
|
||||
.c-fault-mgmt-search {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/*********************************************** TOOLBAR */
|
||||
.c-fault-mgmt__toolbar{
|
||||
display: flex;
|
||||
.c-fault-mgmt__toolbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
> * {
|
||||
font-size: 1.25em;
|
||||
flex: 0 0 auto;
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************** LIST VIEW */
|
||||
.c-faults-list-view {
|
||||
.c-faults-list-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
.c-faults-list-view-header-item-container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: max-content max-content repeat(5,minmax(max-content, 20%)) max-content;
|
||||
grid-row-gap: $interiorMargin;
|
||||
|
||||
/*********************************************** FAULT ITEM */
|
||||
.c-fault-mgmt__list{
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
margin-bottom: 5px;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
margin-left: $interiorMargin;
|
||||
&-wrapper {
|
||||
flex: 1 1 auto;
|
||||
padding-right: $interiorMargin; // Fend of from scrollbar
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&-severity{
|
||||
|
||||
.--width-less-than-600 & {
|
||||
grid-template-columns: max-content max-content 1fr 1fr max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.c-faults-list-view-item-body {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/*********************************************** LIST */
|
||||
.c-fault-mgmt__list {
|
||||
display: contents;
|
||||
color: $colorFaultItemFg;
|
||||
|
||||
&-checkbox{
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
&-severity {
|
||||
font-size: 2em;
|
||||
margin-left: $interiorMarginLg;
|
||||
}
|
||||
|
||||
&-pathname{
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1 auto;
|
||||
|
||||
}
|
||||
&-path{
|
||||
font-size: .75em;
|
||||
}
|
||||
&.is-severity-critical {
|
||||
@include glyphBefore($glyph-icon-alert-triangle);
|
||||
color: $colorStatusError;
|
||||
}
|
||||
|
||||
&-faultname{
|
||||
font-weight: bold;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
&.is-severity-warning {
|
||||
@include glyphBefore($glyph-icon-alert-rect);
|
||||
color: $colorStatusAlert;
|
||||
}
|
||||
|
||||
&-content{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-content-right{
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&-trigVal, &-curVal, &-trigTime{
|
||||
@include ellipsize;
|
||||
border-radius: $controlCr;
|
||||
padding: $interiorMargin;
|
||||
width: 80px;
|
||||
margin-right: $interiorMarginLg;
|
||||
|
||||
}
|
||||
|
||||
&-trigVal {
|
||||
@include isLimit();
|
||||
background: rgba($colorBodyFg, 0.25);
|
||||
}
|
||||
|
||||
&-curVal {
|
||||
@include isLimit();
|
||||
background: rgba($colorBodyFg, 0.25);
|
||||
&-alert{
|
||||
background: $colorWarningHi;
|
||||
&.is-severity-watch {
|
||||
@include glyphBefore($glyph-icon-info);
|
||||
color: $colorCommand;
|
||||
}
|
||||
}
|
||||
|
||||
&-trigTime{
|
||||
width: auto;
|
||||
&-content {
|
||||
display: contents;
|
||||
|
||||
.--width-less-than-600 & {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
&-action-wrapper{
|
||||
display: flex;
|
||||
align-content: right;
|
||||
width: 100px;
|
||||
&-pathname {
|
||||
padding-right: $interiorMarginLg;
|
||||
overflow-wrap: anywhere;
|
||||
min-width: 100px;
|
||||
|
||||
}
|
||||
&-path {
|
||||
font-size: .85em;
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
&-action-button{
|
||||
&-faultname{
|
||||
font-size: 1.3em;
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
&-content-right {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
&-trigTime {
|
||||
grid-column: 6 / span 2;
|
||||
}
|
||||
|
||||
&-action-wrapper {
|
||||
text-align: right;
|
||||
flex: 0 0 auto;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
&-action-button {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
justify-content: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// STATES
|
||||
&.is-unacknowledged {
|
||||
color: $colorFaultItemFgEmphasis;
|
||||
.c-fault-mgmt__list-severity {
|
||||
@include pulse($animName: severityAnim, $dur: 200ms);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-acknowledged,
|
||||
&.is-shelved {
|
||||
.c-fault-mgmt__list-severity {
|
||||
&:before {
|
||||
opacity: 60%;
|
||||
//font-size: 1.5em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
color: $colorFaultItemFgEmphasis;
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
position: absolute;
|
||||
//text-shadow: black 0 0 2px;
|
||||
right: -3px;
|
||||
bottom: -3px;
|
||||
transform-origin: right bottom;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-shelved {
|
||||
.c-fault-mgmt__list-pathname {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-acknowledged .c-fault-mgmt__list-severity:after {
|
||||
content: $glyph-icon-check;
|
||||
}
|
||||
|
||||
&.is-shelved .c-fault-mgmt__list-severity:after {
|
||||
content: $glyph-icon-timer;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************** LIST HEADER */
|
||||
.c-fault-mgmt__list-header{
|
||||
display: flex;
|
||||
background: rgba($colorBodyFg, .23);
|
||||
.c-fault-mgmt__list-header {
|
||||
display: contents;
|
||||
border-radius: $controlCr;
|
||||
align-items: center;
|
||||
|
||||
&-tripVal, &-liveVal, &-trigTime{
|
||||
background: none;
|
||||
* {
|
||||
margin: 0px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
&-trigTime{
|
||||
width: 160px;
|
||||
}
|
||||
&-sortButton{
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
justify-content: right;
|
||||
display: flex;
|
||||
align-content: right;
|
||||
width: 100px;
|
||||
.--width-less-than-600 & {
|
||||
.c-fault-mgmt__list-content-right {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
&-content {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.is-severity-critical{
|
||||
@include glyphBefore($glyph-icon-alert-triangle);
|
||||
color: $colorStatusError;
|
||||
}
|
||||
&-results {
|
||||
grid-column: 2 / span 2;
|
||||
font-size: 1em;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.is-severity-warning{
|
||||
@include glyphBefore($glyph-icon-alert-rect);
|
||||
color: $colorStatusAlert;
|
||||
}
|
||||
&-action-wrapper {
|
||||
grid-column: 7 / span 2;
|
||||
|
||||
.is-severity-watch{
|
||||
@include glyphBefore($glyph-icon-info);
|
||||
color: $colorCommand;
|
||||
}
|
||||
|
||||
.is-unacknowledged{
|
||||
.c-fault-mgmt__list-severity{
|
||||
@include pulse($animName: severityAnim, $dur: 200ms);
|
||||
.--width-less-than-600 & {
|
||||
grid-column: 4 / span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-selected {
|
||||
background: $colorSelectedBg;
|
||||
}
|
||||
/*********************************************** GRID ITEM */
|
||||
.c-fault-mgmt-item {
|
||||
$p: $interiorMargin;
|
||||
padding: $p;
|
||||
background: $colorFaultItemBg;
|
||||
white-space: nowrap;
|
||||
|
||||
.is-shelved{
|
||||
.c-fault-mgmt__list-content{
|
||||
opacity: 60% !important;
|
||||
font-style: italic;
|
||||
&-header {
|
||||
$c: $colorBodyBg;
|
||||
background: $c;
|
||||
border-bottom: 5px solid $c; // Creates illusion of "space" beneath header
|
||||
min-height: 30px; // Needed to align cells
|
||||
padding: $p;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
.c-fault-mgmt__list-severity{
|
||||
@include pulse($animName: shelvedAnim, $dur: 0ms);
|
||||
|
||||
&__value {
|
||||
@include isLimit();
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
padding: $p;
|
||||
border-radius: $controlCr;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.is-selected & {
|
||||
background: $colorSelectedBg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'indicator', 'configuration'],
|
||||
@@ -118,6 +119,9 @@ export default {
|
||||
this.openmct.user.status.off('statusChange', this.fetchStatusSummary);
|
||||
this.openmct.user.status.off('pollQuestionChange', this.setPollQuestion);
|
||||
},
|
||||
created() {
|
||||
this.fetchStatusSummary = _.debounce(this.fetchStatusSummary);
|
||||
},
|
||||
methods: {
|
||||
async fetchCurrentPoll() {
|
||||
const pollQuestion = await this.openmct.user.status.getPollQuestion();
|
||||
|
||||
@@ -199,6 +199,11 @@ class CouchObjectProvider {
|
||||
}
|
||||
|
||||
let response = null;
|
||||
|
||||
if (!this.isObservingObjectChanges()) {
|
||||
this.#observeObjectChanges();
|
||||
}
|
||||
|
||||
try {
|
||||
response = await fetch(this.url + '/' + subPath, fetchOptions);
|
||||
const { status } = response;
|
||||
@@ -471,9 +476,6 @@ class CouchObjectProvider {
|
||||
this.observers[keyString] = this.observers[keyString].filter(observer => observer !== callback);
|
||||
if (this.observers[keyString].length === 0) {
|
||||
delete this.observers[keyString];
|
||||
if (Object.keys(this.observers).length === 0 && this.isObservingObjectChanges()) {
|
||||
this.stopObservingObjectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -498,7 +500,6 @@ class CouchObjectProvider {
|
||||
} else {
|
||||
this.#initiateSharedWorkerFetchChanges(sseURL.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,18 +526,24 @@ class CouchObjectProvider {
|
||||
|
||||
onEventError(error) {
|
||||
console.error('Error on feed', error);
|
||||
if (Object.keys(this.observers).length > 0) {
|
||||
this.#observeObjectChanges();
|
||||
}
|
||||
const { readyState } = error.target;
|
||||
this.#updateIndicatorStatus(readyState);
|
||||
}
|
||||
|
||||
onEventOpen(event) {
|
||||
const { readyState } = event.target;
|
||||
this.#updateIndicatorStatus(readyState);
|
||||
}
|
||||
|
||||
onEventMessage(event) {
|
||||
const { readyState } = event.target;
|
||||
const eventData = JSON.parse(event.data);
|
||||
const identifier = {
|
||||
namespace: this.namespace,
|
||||
key: eventData.id
|
||||
};
|
||||
const keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
this.#updateIndicatorStatus(readyState);
|
||||
let observersForObject = this.observers[keyString];
|
||||
|
||||
if (observersForObject) {
|
||||
@@ -559,17 +566,18 @@ class CouchObjectProvider {
|
||||
|
||||
this.stopObservingObjectChanges = () => {
|
||||
controller.abort();
|
||||
couchEventSource.removeEventListener('message', this.onEventMessage);
|
||||
couchEventSource.removeEventListener('message', this.onEventMessage.bind(this));
|
||||
delete this.stopObservingObjectChanges;
|
||||
};
|
||||
|
||||
console.debug('⇿ Opening CouchDB change feed connection ⇿');
|
||||
|
||||
couchEventSource = new EventSource(url);
|
||||
couchEventSource.onerror = this.onEventError;
|
||||
couchEventSource.onerror = this.onEventError.bind(this);
|
||||
couchEventSource.onopen = this.onEventOpen.bind(this);
|
||||
|
||||
// start listening for events
|
||||
couchEventSource.addEventListener('message', this.onEventMessage);
|
||||
couchEventSource.addEventListener('message', this.onEventMessage.bind(this));
|
||||
|
||||
console.debug('⇿ Opened connection ⇿');
|
||||
}
|
||||
@@ -587,6 +595,31 @@ class CouchObjectProvider {
|
||||
return intermediateResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the indicator status based on the readyState of the EventSource
|
||||
* @private
|
||||
*/
|
||||
#updateIndicatorStatus(readyState) {
|
||||
let message;
|
||||
switch (readyState) {
|
||||
case EventSource.CONNECTING:
|
||||
message = 'pending';
|
||||
break;
|
||||
case EventSource.OPEN:
|
||||
message = 'open';
|
||||
break;
|
||||
case EventSource.CLOSED:
|
||||
message = 'close';
|
||||
break;
|
||||
default:
|
||||
message = 'unknown';
|
||||
break;
|
||||
}
|
||||
|
||||
const indicatorState = this.#messageToIndicatorState(message);
|
||||
this.indicator.setIndicatorToState(indicatorState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -152,7 +152,10 @@ describe('the plugin', () => {
|
||||
mockDomainObject.id = mockDomainObject.identifier.key;
|
||||
|
||||
const fakeUpdateEvent = {
|
||||
data: JSON.stringify(mockDomainObject)
|
||||
data: JSON.stringify(mockDomainObject),
|
||||
target: {
|
||||
readyState: EventSource.CONNECTED
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
@@ -170,7 +173,6 @@ describe('the plugin', () => {
|
||||
expect(provider.create).toHaveBeenCalled();
|
||||
expect(provider.observe).toHaveBeenCalled();
|
||||
expect(provider.isObservingObjectChanges).toHaveBeenCalled();
|
||||
expect(provider.isObservingObjectChanges.calls.mostRecent().returnValue).toBe(true);
|
||||
|
||||
//Set modified timestamp it detects a change and persists the updated model.
|
||||
mockDomainObject.modified = mockDomainObject.persisted + 1;
|
||||
@@ -181,6 +183,7 @@ describe('the plugin', () => {
|
||||
expect(updatedResult).toBeTrue();
|
||||
expect(provider.update).toHaveBeenCalled();
|
||||
expect(provider.fetchChanges).toHaveBeenCalled();
|
||||
expect(provider.isObservingObjectChanges.calls.mostRecent().returnValue).toBe(true);
|
||||
sharedWorkerCallback(fakeUpdateEvent);
|
||||
expect(provider.onEventMessage).toHaveBeenCalled();
|
||||
|
||||
|
||||
@@ -223,15 +223,16 @@ export default {
|
||||
},
|
||||
resizeSoView() {
|
||||
let cW = this.$refs.soView.offsetWidth;
|
||||
let widths = [220, 600];
|
||||
let wClass = '';
|
||||
|
||||
if (cW < 220) {
|
||||
wClass = CSS_WIDTH_LESS_STR + '220';
|
||||
} else if (cW < 600) {
|
||||
wClass = CSS_WIDTH_LESS_STR + '600';
|
||||
for (let width of widths) {
|
||||
if (cW < width) {
|
||||
wClass = wClass.concat(' ', CSS_WIDTH_LESS_STR, width);
|
||||
}
|
||||
}
|
||||
|
||||
this.widthClass = wClass;
|
||||
this.widthClass = wClass.trimStart();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
margin-bottom: $interiorMarginSm;
|
||||
overflow: hidden;
|
||||
padding: 3px;
|
||||
@include smallerControlButtons; // Make button in frame headers a bit smaller
|
||||
|
||||
.c-object-label {
|
||||
font-size: 1.05em;
|
||||
@@ -132,8 +133,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include smallerControlButtons;
|
||||
|
||||
&.has-complex-content {
|
||||
> .c-so-view__view-large { display: block; }
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ const config = require('./webpack.dev');
|
||||
|
||||
const path = require('path');
|
||||
|
||||
config.devtool = false;
|
||||
|
||||
const vueLoaderRule = config.module.rules.find(r => r.use === 'vue-loader');
|
||||
|
||||
vueLoaderRule.use = {
|
||||
|
||||
Reference in New Issue
Block a user