Skip to content

Commit

Permalink
Merge branch 'master' into mct7343-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
unlikelyzero authored Oct 9, 2024
2 parents 12fca7b + 8316142 commit f8baf69
Show file tree
Hide file tree
Showing 14 changed files with 805 additions and 241 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/yamcs-quickstart-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
elif [ "${{ matrix.openmct-version }}" = "stable" ]; then
npm run build:example
fi
- run: npx playwright@1.45.2 install chromium
- run: npx playwright@1.47.2 install chromium
- name: Check that yamcs is available
run: |
docker ps -a
Expand Down
11 changes: 8 additions & 3 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const config = {
yamcsProcessor: "realtime",
yamcsFolder: "myproject",
throttleRate: 1000,
maxBatchSize: 20
// Batch size is specified in characers as there is no performant way of calculating true
// memory usage of a string buffer in real-time.
// String characters can be 8 or 16 bits in JavaScript, depending on the code page used.
// Thus 500,000 characters requires up to 16MB of memory (1,000,000 * 16).
maxBufferSize: 1000000
};
const STATUS_STYLES = {
NO_STATUS: {
Expand Down Expand Up @@ -41,7 +45,9 @@ const STATUS_STYLES = {
const openmct = window.openmct;

(() => {
const THIRTY_MINUTES = 30 * 60 * 1000;
const ONE_SECOND = 1000;
const ONE_MINUTE = ONE_SECOND * 60;
const THIRTY_MINUTES = ONE_MINUTE * 30;

openmct.setAssetPath("/node_modules/openmct/dist");

Expand All @@ -54,7 +60,6 @@ const openmct = window.openmct;
document.addEventListener("DOMContentLoaded", function () {
openmct.start();
});

openmct.install(
openmct.plugins.Conductor({
menuOptions: [
Expand Down
7 changes: 3 additions & 4 deletions src/openmct-yamcs.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import LimitProvider from './providers/limit-provider.js';
import EventLimitProvider from './providers/event-limit-provider.js';
import UserProvider from './providers/user/user-provider.js';

import { faultModelConvertor } from './providers/fault-mgmt-providers/utils.js';
import YamcsFaultProvider from './providers/fault-mgmt-providers/yamcs-fault-provider.js';

import { OBJECT_TYPES } from './const.js';
Expand Down Expand Up @@ -69,16 +68,16 @@ export default function install(
configuration.yamcsInstance,
configuration.yamcsProcessor,
configuration.throttleRate,
configuration.maxBatchSize
configuration.maxBufferSize
);
openmct.telemetry.addProvider(realtimeTelemetryProvider);
realtimeTelemetryProvider.connect();

openmct.faults.addProvider(new YamcsFaultProvider(openmct,
{
faultModelConvertor,
historicalEndpoint: configuration.yamcsHistoricalEndpoint,
yamcsInstance: configuration.yamcsInstance
yamcsInstance: configuration.yamcsInstance,
yamcsProcessor: configuration.yamcsProcessor
}));

const stalenessProvider = new YamcsStalenessProvider(
Expand Down
90 changes: 65 additions & 25 deletions src/providers/fault-mgmt-providers/fault-action-provider.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,100 @@
import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_DEFAULT_SHELVE_DURATION } from './fault-mgmt-constants.js';
import { FAULT_MGMT_ALARMS, FAULT_MGMT_ACTIONS } from './fault-mgmt-constants.js';

export default class FaultActionProvider {
constructor(url, instance, processor = 'realtime') {
constructor(url, instance, processor) {
this.url = url;
this.instance = instance;
this.processor = processor;
}

acknowledgeFault(fault, { comment = '' } = {}) {
const payload = {
comment,
state: 'acknowledged'
comment
};
const options = this._getOptions(payload);
const url = this._getUrl(fault);
const options = this.#getOptions(payload);
const url = this.#getUrl(fault, FAULT_MGMT_ACTIONS.ACKNOWLEDGE);

return this._sendRequest(url, options);
return this.#sendRequest(url, options);
}

shelveFault(fault, { shelved = true, comment = '', shelveDuration = FAULT_MANAGEMENT_DEFAULT_SHELVE_DURATION } = {}) {
let payload = {};
/**
* Shelves or unshelves a fault.
* @param {FaultModel} fault the fault to perform the action on
* @param {Object} options the options to perform the action with
* @param {boolean} options.shelved whether to shelve or unshelve the fault
* @param {string} options.comment the comment to add to the fault
* @param {number} options.shelveDuration the duration to shelve the fault for
* @returns {Promise<Response>} the response from the server
*/
shelveFault(fault, { shelved = true, comment = '', shelveDuration } = {}) {
const payload = {};
const action = shelved ? FAULT_MGMT_ACTIONS.SHELVE : FAULT_MGMT_ACTIONS.UNSHELVE;

if (shelved) {
payload.comment = comment;
payload.shelveDuration = shelveDuration;
payload.state = 'shelved';
} else {
payload.state = 'unshelved';
}

const options = this._getOptions(payload);
let url = this._getUrl(fault);
const options = this.#getOptions(payload);
const url = this.#getUrl(fault, action);

return this._sendRequest(url, options);
return this.#sendRequest(url, options);
}

_getOptions(payload) {
/**
* @typedef {Object} ShelveDuration
* @property {string} name - The name of the shelve duration
* @property {number|null} value - The value of the shelve duration in milliseconds, or null for indefinite
*/

/**
* @returns {ShelveDuration[]} the list of shelve durations
*/
getShelveDurations() {
return [
{
name: '5 Minutes',
value: 300000
},
{
name: '10 Minutes',
value: 600000
},
{
name: '15 Minutes',
value: 900000
},
{
name: 'Indefinite',
value: null
}
];
}

#getOptions(payload) {
return {
body: JSON.stringify(payload),
// credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
method: 'PATCH',
method: 'POST',
mode: 'cors'
};
}

_getUrl(fault) {
let url = `${this.url}api/processors/${this.instance}/${this.processor}/${FAULT_MANAGEMENT_ALARMS}`;
url += `${fault.namespace}/${fault.name}`;
url += `/${fault.seqNum}`;

return url;
/**
* @param {FaultModel} fault the fault to perform the action on
* @param {'acknowledge' | 'shelve' | 'unshelve' | 'clear'} action the action to perform on the fault
* @returns {string} the URL to perform the action on the fault
*/
#getUrl(fault, action) {
return `${this.url}api/processors/${this.instance}/${this.processor}/${FAULT_MGMT_ALARMS}`
+ `${fault.namespace}/${fault.name}/${fault.seqNum}:${action}`;
}

_sendRequest(url, options) {
#sendRequest(url, options) {
return fetch(url, options);
}
}

/** @typedef {import('./utils.js').FaultModel} FaultModel */
12 changes: 9 additions & 3 deletions src/providers/fault-mgmt-providers/fault-mgmt-constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export const FAULT_MANAGEMENT_ALARMS = 'alarms';
export const FAULT_MANAGEMENT_TYPE = 'faultManagement';
export const FAULT_MANAGEMENT_DEFAULT_SHELVE_DURATION = 90000;
export const FAULT_MGMT_ALARMS = 'alarms';
export const FAULT_MGMT_TYPE = 'faultManagement';
export const DEFAULT_SHELVE_DURATION = 90000;
export const FAULT_MGMT_ACTIONS = Object.freeze({
SHELVE: 'shelve',
UNSHELVE: 'unshelve',
ACKNOWLEDGE: 'acknowledge',
CLEAR: 'clear'
});
23 changes: 17 additions & 6 deletions src/providers/fault-mgmt-providers/historical-fault-provider.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import { FAULT_MANAGEMENT_ALARMS, FAULT_MANAGEMENT_TYPE } from './fault-mgmt-constants.js';
import { FAULT_MGMT_ALARMS, FAULT_MGMT_TYPE } from './fault-mgmt-constants.js';
import { convertDataToFaultModel } from './utils.js';

export default class HistoricalFaultProvider {
constructor(faultModelConverter, url, instance, processor = 'realtime') {
this.faultModelConverter = faultModelConverter;
constructor(url, instance, processor) {
this.url = url;
this.instance = instance;
this.processor = processor;
}

/**
* @param {import('openmct').DomainObject} domainObject
* @returns {boolean}
*/
supportsRequest(domainObject) {
return domainObject.type === FAULT_MANAGEMENT_TYPE;
return domainObject.type === FAULT_MGMT_TYPE;
}

/**
* @returns {Promise<FaultModel[]>}
*/
async request() {
let url = `${this.url}api/processors/${this.instance}/${this.processor}/${FAULT_MANAGEMENT_ALARMS}`;
const url = `${this.url}api/processors/${this.instance}/${this.processor}/${FAULT_MGMT_ALARMS}`;

const res = await fetch(url);
const faultsData = await res.json();

return faultsData.alarms?.map(this.faultModelConverter);
return faultsData.alarms?.map(convertDataToFaultModel);
}
}

/**
* @typedef {import('./utils.js').FaultModel} FaultModel
*/
12 changes: 5 additions & 7 deletions src/providers/fault-mgmt-providers/realtime-fault-provider.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { FAULT_MANAGEMENT_TYPE } from './fault-mgmt-constants.js';
import { FAULT_MGMT_TYPE } from './fault-mgmt-constants.js';
import { DATA_TYPES, NAMESPACE, OBJECT_TYPES } from '../../const.js';
import { convertDataToFaultModel } from './utils.js';

export default class RealtimeFaultProvider {
#openmct;
constructor(openmct, faultModelConverter, instance) {
constructor(openmct, instance) {
this.#openmct = openmct;
this.faultModelConverter = faultModelConverter;
this.instance = instance;

this.lastSubscriptionId = 1;
Expand All @@ -30,7 +30,7 @@ export default class RealtimeFaultProvider {
}

supportsSubscribe(domainObject) {
return domainObject.type === FAULT_MANAGEMENT_TYPE;
return domainObject.type === FAULT_MGMT_TYPE;
}

subscribe(domainObject, callback) {
Expand All @@ -53,8 +53,6 @@ export default class RealtimeFaultProvider {
}

handleResponse(type, response, callback) {
const faultData = this.faultModelConverter(response, type);

callback(faultData);
callback(convertDataToFaultModel(response, type));
}
}
59 changes: 44 additions & 15 deletions src/providers/fault-mgmt-providers/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable func-style */
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* as represented by the Administrator of the National Aeronautics and Space
Expand All @@ -19,42 +20,70 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/

import { getValue } from '../../utils.js';

function faultModelConvertor(faultData, type) {
/**
* Converts fault data to a FaultModel.
*
* @param {Object} faultData
* @param {string} [type]
* @returns {FaultModel}
*/
const convertDataToFaultModel = (faultData, type) => {
const parameterDetail = faultData?.parameterDetail;
const currentValueDetail = parameterDetail?.currentValue;
const triggerValueDetail = parameterDetail?.triggerValue;

const currentValue = faultData?.parameterDetail?.currentValue
&& getValue(faultData.parameterDetail.currentValue);
const triggerValue = faultData?.parameterDetail?.triggerValue
&& getValue(faultData.parameterDetail.triggerValue);
const currentValue = currentValueDetail ? getValue(currentValueDetail) : undefined;
const triggerValue = triggerValueDetail ? getValue(triggerValueDetail) : undefined;

return {
type: type || faultData?.type,
fault: {
acknowledged: Boolean(faultData?.acknowledged),
currentValueInfo: {
value: currentValue,
rangeCondition: faultData?.parameterDetail?.currentValue?.rangeCondition,
monitoringResult: faultData?.parameterDetail?.currentValue?.monitoringResult
rangeCondition: currentValueDetail?.rangeCondition,
monitoringResult: currentValueDetail?.monitoringResult
},
id: `id-${faultData?.id?.namespace}-${faultData?.id?.name}`,
name: faultData?.id?.name,
namespace: faultData?.id?.namespace,
seqNum: faultData?.seqNum,
severity: faultData?.severity,
shelved: Boolean(faultData?.shelveInfo),
shortDescription: faultData?.parameterDetail?.parameter?.shortDescription,
shortDescription: parameterDetail?.parameter?.shortDescription,
triggerTime: faultData?.triggerTime,
triggerValueInfo: {
value: triggerValue,
rangeCondition: faultData?.parameterDetail?.triggerValue?.rangeCondition,
monitoringResult: faultData?.parameterDetail?.triggerValue?.monitoringResult
rangeCondition: triggerValueDetail?.rangeCondition,
monitoringResult: triggerValueDetail?.monitoringResult
}
}
};
}

export {
faultModelConvertor
};

export { convertDataToFaultModel };

/**
* @typedef {Object} FaultModel
* @property {string} type
* @property {Object} fault
* @property {boolean} fault.acknowledged
* @property {Object} fault.currentValueInfo
* @property {*} fault.currentValueInfo.value
* @property {string} fault.currentValueInfo.rangeCondition
* @property {string} fault.currentValueInfo.monitoringResult
* @property {string} fault.id
* @property {string} fault.name
* @property {string} fault.namespace
* @property {number} fault.seqNum
* @property {string} fault.severity
* @property {boolean} fault.shelved
* @property {string} fault.shortDescription
* @property {number} fault.triggerTime
* @property {Object} fault.triggerValueInfo
* @property {*} fault.triggerValueInfo.value
* @property {string} fault.triggerValueInfo.rangeCondition
* @property {string} fault.triggerValueInfo.monitoringResult
*/
Loading

0 comments on commit f8baf69

Please sign in to comment.