Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Watcher alarms handling #454

Merged
merged 8 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
Version History
===============

v5.19.2
v5.20.0
--------

* Extend thumbnails query `<https://github.com/lsst-ts/LOVE-frontend/pull/455>`_
* Refactor Watcher alarms handling `<https://github.com/lsst-ts/LOVE-frontend/pull/454>`_

v5.19.1
--------
Expand Down
30 changes: 24 additions & 6 deletions love/src/components/Layout/ConfigPanel/ConfigPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import styles from './ConfigPanel.module.css';
import ManagerInterface, { formatTimestamp } from 'Utils';
// import RequeueIcon from 'components/icons/ScriptQueue/RequeueIcon/RequeueIcon';
import ScriptIcon from '../../icons/ScriptIcon/ScriptIcon';
import Button from 'components/GeneralPurpose/Button/Button';

ConfigPanel.propTypes = {
/** Current LOVE configuration */
Expand All @@ -18,21 +19,33 @@ ConfigPanel.propTypes = {

/** Contents of the Config File view Panel, displayed in a modal */
function ConfigPanel({ config, setConfig }) {
const [ownConf, setOwnConf] = useState(config);
const [configList, setConfigList] = useState([]);

useEffect(() => {
ManagerInterface.getConfigFilesList().then((list) => {
setConfigList(list);
});
}, []);

const onConfigSelection = (selection) => {
const id = selection.value;
ManagerInterface.setSelectedConfigFile(id).then((conf) => {
console.log(conf);
setConfig(conf);
ManagerInterface.getConfigFileContent(id).then((conf) => {
setOwnConf(conf);
});
};

const saveConfigSelection = (persist = false) => {
const id = ownConf.id;
if (persist) {
ManagerInterface.setSelectedConfigFile(id).then((conf) => {
setConfig(conf);
});
} else {
setConfig(ownConf);
}
};

const getConfigSelectOption = (conf, index) => {
return {
value: conf.id,
Expand All @@ -51,9 +64,10 @@ function ConfigPanel({ config, setConfig }) {
),
};
};
const configStr = JSON.stringify(config.content, null, 2);

const configStr = JSON.stringify(ownConf.content, null, 2);
const options = configList ? configList.map((conf, index) => getConfigSelectOption(conf, index)) : [];
const defaultOption = getConfigSelectOption(config, 0);
const defaultOption = getConfigSelectOption(ownConf, 0);
return (
<div className={styles.container}>
<div className={styles.title}>LOVE Configuration File</div>
Expand All @@ -65,7 +79,7 @@ function ConfigPanel({ config, setConfig }) {
controlClassName={styles.controlClassName}
options={options}
value={defaultOption}
onChange={(selection) => onConfigSelection(selection)}
onChange={onConfigSelection}
/>
</div>
<AceEditor
Expand All @@ -79,6 +93,10 @@ function ConfigPanel({ config, setConfig }) {
editorProps={{ $blockScrolling: true }}
fontSize={14}
/>
<div className={styles.confirmSection}>
<Button onClick={() => saveConfigSelection(true)}>Select & Save</Button>
<Button onClick={() => saveConfigSelection()}>Select</Button>
</div>
</div>
);
}
Expand Down
7 changes: 7 additions & 0 deletions love/src/components/Layout/ConfigPanel/ConfigPanel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@
width: 1.4em;
height: 1.4em;
}

.confirmSection {
margin-top: 1em;
display: flex;
justify-content: right;
gap: var(--small-padding);
}
2 changes: 2 additions & 0 deletions love/src/components/Layout/Layout.container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
const subscriptions = [
'heartbeat-manager-0-stream',
/* Watcher states */
'event-Watcher-0-alarm',
'event-Watcher-0-stream',
/* Simonyi states */
'event-Scheduler-1-observingMode',
'event-Scheduler-1-observatoryState',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@ const AlarmAudioContainer = ({ ...props }) => {

const mapStateToProps = (state) => {
const alarms = getAllAlarms(state);
const newAlarms = getLastestAlarms(state);
const alarmsConfig = getAlarmConfig(state);
return {
alarms,
newAlarms,
alarmsConfig,
};
};

const mapDispatchToProps = (dispatch) => {
const subscriptions = ['event-Watcher-0-alarm'];
const subscriptions = ['event-Watcher-0-stream', 'event-Watcher-0-alarm'];
return {
subscriptions,
subscribeToStreams: () => {
Expand Down
101 changes: 57 additions & 44 deletions love/src/components/Watcher/AlarmAudio/AlarmAudio.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Howl } from 'howler';
import isEqual from 'lodash/isEqual';

import { severityEnum } from '../../../Config';
import { isAcknowledged, isMuted, isMaxCritical } from '../AlarmUtils';
import { isAcknowledged, isMuted, isMaxCritical, isCritical } from '../AlarmUtils';

import newWarningFile from '../../../sounds/new_warning.mp3';
import newSeriousFile from '../../../sounds/new_serious.mp3';
Expand All @@ -21,8 +21,6 @@ export default class AlarmAudio extends Component {
static propTypes = {
/** List of all the alarms that are displayed */
alarms: PropTypes.array,
/** List of new alarms, that si they are either new or have been changed */
newAlarms: PropTypes.array,
/**
* Contents of the alarms entry of LOVE configuration file.
* It is expected to have a "minSeveritySound" with a values of either "warning", "serious" or "critical"
Expand All @@ -36,7 +34,6 @@ export default class AlarmAudio extends Component {

static defaultProps = {
alarms: [],
newAlarms: [],
alarmsConfig: null,
subscribeToStreams: () => {},
unsubscribeToStreams: () => {},
Expand Down Expand Up @@ -165,60 +162,75 @@ export default class AlarmAudio extends Component {
}

if (
this.props.newAlarms &&
(!isEqual(this.props.newAlarms, prevProps.newAlarms) ||
this.state.minSeveritySound !== prevState.minSeveritySound)
this.props.alarms &&
(!isEqual(this.props.alarms, prevProps.alarms) || this.state.minSeveritySound !== prevState.minSeveritySound)
) {
this.checkAndNotifyAlarms(this.props.newAlarms, prevProps.alarms);
this.checkAndNotifyAlarms(this.props.alarms, prevProps.alarms);
}
};

/**
* Function to check if the alarms have changed and notify the user
*
* If the alarms have changed, then check if the new alarms are more severe than the old alarms
* If they are, then play the appropriate sound
* If they are not, then check if the old alarms were acknowledged
* If they were, then stop the critical sound
* If they were not, then do nothing
*
* @param {*} newAlarms
* @param {*} oldAlarms
*/
checkAndNotifyAlarms = (newAlarms, oldAlarms) => {
let deltaCriticals = 0;
const newHighestAlarm = { severity: severityEnum.ok, type: 'new' };
newAlarms.forEach((newAlarm) => {
if (newAlarm === undefined) return;
const oldAlarm = oldAlarms.find((oldAlarm) => {
return oldAlarm.name.value === newAlarm.name.value;
});

// If alarm was acknowledged stop the critical sound
if (oldAlarm && !isAcknowledged(oldAlarm) && isAcknowledged(newAlarm)) {
this.stopCriticals();
}

// If they are non-acked and non-muted
if (!isAcknowledged(newAlarm) && !isMuted(newAlarm)) {
// If they are new, play the "new" sound
if (!oldAlarm || oldAlarm.maxSeverity.value === severityEnum.ok) {
if (isMaxCritical(newAlarm)) deltaCriticals++;
this.playSound(newAlarm.maxSeverity.value, 'new');
}
// If they have increased, play the "increased" sound
else if (newAlarm.maxSeverity.value > oldAlarm.maxSeverity.value) {
if (newAlarm.maxSeverity.value === severityEnum.critical) deltaCriticals++;
this.playSound(newAlarm.maxSeverity.value, 'increased');
if (newAlarm.severity.value > newHighestAlarm.severity) {
newHighestAlarm.severity = newAlarm.severity.value;
newHighestAlarm.type = 'new';
}
// If they have been unacked, play the "unacked" sound
else if (!isAcknowledged(newAlarm) && isAcknowledged(oldAlarm)) {
if (newAlarm.maxSeverity.value === severityEnum.critical) deltaCriticals++;
this.playSound(newAlarm.maxSeverity.value, 'unacked');

if (oldAlarm) {
if (oldAlarm.severity.value < newAlarm.severity.value) {
// If they are increased, play the "increased" sound
if (newAlarm.severity.value > newHighestAlarm.severity) {
newHighestAlarm.severity = newAlarm.severity.value;
newHighestAlarm.type = 'increased';
}
}

if (!isAcknowledged(newAlarm) && isAcknowledged(oldAlarm)) {
// If they are unacknowledged, play the "unacked" sound
if (newAlarm.severity.value >= newHighestAlarm.severity) {
newHighestAlarm.severity = newAlarm.severity.value;
newHighestAlarm.type = 'unacked';
}
}

if (!isAcknowledged(newAlarm) && !isAcknowledged(oldAlarm)) {
// If they are still critical, play the "still critical" sound
if (isCritical(newAlarm) && isCritical(oldAlarm)) {
newHighestAlarm.severity = newAlarm.severity.value;
newHighestAlarm.type = 'still';
}
}
}
// Else, they should not emit a new sound!
}
// If they are critical and cleared, discount them
if (
oldAlarm &&
isMaxCritical(oldAlarm) &&
!isAcknowledged(oldAlarm) &&
!isMuted(oldAlarm) &&
(isAcknowledged(newAlarm) || isMuted(newAlarm) || !isMaxCritical(newAlarm))
) {
deltaCriticals--;
}
});
this.numCriticals += deltaCriticals;
// Stop the sound if some critical alarm was cleared
if (deltaCriticals < 0) {

if (newHighestAlarm.severity >= this.state.minSeveritySound) {
this.stopCriticals();
// Play the "still sound" if there are still other critical alarms
if (this.numCriticals > 0) {
this.stillCriticalSound.play();
}
this.playSound(newHighestAlarm.severity, newHighestAlarm.type);
}
};

Expand Down Expand Up @@ -263,20 +275,21 @@ export default class AlarmAudio extends Component {
} else if (severity === severityEnum.critical) {
switch (type) {
case 'new': {
this.stopCriticals();
this.newCriticalSound.play();
break;
}
case 'increased': {
this.stopCriticals();
this.increasedCriticalSound.play();
break;
}
case 'unacked': {
this.stopCriticals();
this.unackedCriticalSound.play();
break;
}
case 'still': {
this.stillCriticalSound.play();
break;
}
default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions love/src/components/Watcher/AlarmUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const hasMuteConfig = (alarm) => alarm.mutedSeverity?.value > severityEnu
/** Auxiliary function to define wether an alarms maxSeverity is Critical */
export const isMaxCritical = (alarm) => alarm.maxSeverity?.value === severityEnum.critical;

/** Auxiliary function to define wether an alarms severity is Critical */
export const isCritical = (alarm) => alarm.severity?.value === severityEnum.critical;

/** Auxiliary function to define wether an alarm is active or not */
export const isActive = (alarm) => alarm.severity?.value > severityEnum.ok;

Expand Down
4 changes: 1 addition & 3 deletions love/src/components/Watcher/Watcher.container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ const WatcherContainer = ({ alarms, user, subscribeToStream, unsubscribeToStream
};

const mapStateToProps = (state) => {
// const alarms = mockAlarms;
// const alarms = getAllAlarms(state).concat(mockAlarms);
const alarms = getAllAlarms(state);
const user = getUsername(state);
const taiToUtc = getTaiToUtc(state);
Expand All @@ -57,7 +55,7 @@ const mapStateToProps = (state) => {
};

const mapDispatchToProps = (dispatch) => {
const subscriptions = ['event-Watcher-0-alarm'];
const subscriptions = ['event-Watcher-0-stream', 'event-Watcher-0-alarm'];
return {
subscriptions,
subscribeToStreams: () => {
Expand Down
3 changes: 2 additions & 1 deletion love/src/redux/actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export const REMOVE_CSC_LOG_MESSAGES = 'REMOVE_CSC_LOG_MESSAGES';
export const UPDATE_ERROR_CODE_DATA = 'UPDATE_ERROR_CODE_DATA';
export const REMOVE_CSC_ERROR_CODE_DATA = 'REMOVE_CSC_ERROR_CODE_DATA';

export const RECEIVE_ALARMS = 'RECEIVE_ALARMS';
export const RECEIVE_ALARM = 'RECEIVE_ALARM';
export const RECEIVE_ALL_ALARMS = 'RECEIVE_ALL_ALARMS';

export const RECEIVE_WORKSPACES = 'RECEIVE_WORKSPACES';
export const RECEIVE_WORKSPACES_ERROR = 'RECEIVE_WORKSPACES_ERROR';
Expand Down
15 changes: 11 additions & 4 deletions love/src/redux/actions/alarms.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { RECEIVE_ALARMS } from './actionTypes';
import { RECEIVE_ALARM, RECEIVE_ALL_ALARMS } from './actionTypes';

export const receiveAlarms = (alarms) => {
export const receiveAlarm = (alarm) => {
return {
type: RECEIVE_ALARMS,
alarms,
type: RECEIVE_ALARM,
alarm,
};
};

export const receiveAllAlarms = (alarmsStream) => {
return {
type: RECEIVE_ALL_ALARMS,
alarmsStream,
};
};
5 changes: 3 additions & 2 deletions love/src/redux/actions/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
receiveHeartbeatInfo,
} from './heartbeats';
import { receiveLogMessageData, receiveErrorCodeData } from './summaryData';
import { receiveAlarms } from './alarms';
import { receiveAlarm, receiveAllAlarms } from './alarms';
import { receiveServerTime } from './time';
import { receiveObservingLog } from './observingLogs';
import { getConnectionStatus, getTokenStatus, getToken, getSubscriptions, getSubscription } from '../selectors';
Expand Down Expand Up @@ -250,7 +250,8 @@ export const openWebsocketConnection = () => {
}

if (data.data[0].csc === 'Watcher') {
dispatch(receiveAlarms(stream.alarm));
if (stream.alarm) dispatch(receiveAlarm(stream.alarm[0]));
else if (stream.stream) dispatch(receiveAllAlarms(stream.stream.alarms));
}

if (data.data[0].data.logMessage) {
Expand Down
Loading