diff options
Diffstat (limited to 'sdnr/wt-odlux/odlux/apps/faultApp/src')
22 files changed, 2053 insertions, 0 deletions
diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts new file mode 100644 index 000000000..7aac8ba35 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ + + +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { clearStuckAlarms } from '../services/faultStatusService'; +import { FaultApplicationBaseAction } from './notificationActions'; + +export class AreStuckAlarmsCleared extends FaultApplicationBaseAction { + constructor(public isBusy: boolean) { + super(); + } +} + + +export const clearStuckAlarmAsyncAction = (dispatch: Dispatch) => async (nodeNames: string[]) => { + dispatch(new AreStuckAlarmsCleared(true)); + const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined; }); + dispatch(new AreStuckAlarmsCleared(false)); + return result; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/notificationActions.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/notificationActions.ts new file mode 100644 index 000000000..584e7cd8b --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/notificationActions.ts @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { Action } from '../../../../framework/src/flux/action'; + +import { FaultAlarmNotification } from '../models/fault'; + +export class FaultApplicationBaseAction extends Action { } + + +export class AddFaultNotificationAction extends FaultApplicationBaseAction { + constructor(public fault:FaultAlarmNotification) { + super(); + } +} + +export class ResetFaultNotificationsAction extends FaultApplicationBaseAction { + +} diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/panelChangeActions.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/panelChangeActions.ts new file mode 100644 index 000000000..fb29e9c1b --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/panelChangeActions.ts @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { Action } from '../../../../framework/src/flux/action'; + +import { PanelId } from '../models/panelId'; + +export class SetPanelAction extends Action { + constructor(public panelId: PanelId) { + super(); + } +} + +export class RememberCurrentPanelAction extends Action { + constructor(public panelId: PanelId) { + super(); + } +} + +export const setPanelAction = (panelId: PanelId) => { + return new SetPanelAction(panelId); +}; + diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/statusActions.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/statusActions.ts new file mode 100644 index 000000000..8b631b96d --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/actions/statusActions.ts @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { getFaultStateFromDatabase } from '../services/faultStatusService'; +import { FaultApplicationBaseAction } from './notificationActions'; + + +export class SetFaultStatusAction extends FaultApplicationBaseAction { + constructor(public criticalFaults: number, public majorFaults: number, public minorFaults: number, public warnings: number, + public isLoadingAlarmStatusChart: boolean, public ConnectedCount: number, public ConnectingCount: number, public DisconnectedCount: number, + public MountedCount: number, public UnableToConnectCount: number, public UndefinedCount: number, public UnmountedCount: number, + public totalCount: number, public isLoadingConnectionStatusChart: boolean) { + super(); + } +} + + +export const refreshFaultStatusAsyncAction = async (dispatch: Dispatch) => { + + // dispatch(new SetFaultStatusAction(0, 0, 0, 0, true, 0, 0, 0, 0, 0, 0, 0, 0, true)); + const result = await getFaultStateFromDatabase().catch(_ => null); + if (result) { + const statusAction = new SetFaultStatusAction( + result.Critical || 0, + result.Major || 0, + result.Minor || 0, + result.Warning || 0, + false, + result.Connected || 0, + result.Connecting || 0, + result.Disconnected || 0, + result.Mounted || 0, + result.UnableToConnect || 0, + result.Undefined || 0, + result.Unmounted || 0, + result.total || 0, + false, + ); + dispatch(statusAction); + return; + } else { + dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false)); + } +}; diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg b/sdnr/wt-odlux/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg new file mode 100644 index 000000000..aabbf4cf4 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg @@ -0,0 +1,19 @@ +<!-- highstreet technologies GmbH colour scheme + Grey #565656 + LBlue #36A9E1 + DBlue #246DA2 + Green #003F2C / #006C4B + Yellw #C8D400 + Red #D81036 +--> + +<svg viewBox="0 0 500 540" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" > + <g transform="matrix(27.7762,0,0,27.7762,-13.8603,-27.7762)"> + + <path fill="#565656" d="M 16.8 15.101 C 15.656 14.242 15 12.929 15 11.5 L 15 8.5 C 15 5.987 13.306 3.862 11 3.208 L 11 2.5 C 11 1.673 10.327 1 9.5 1 C 8.673 1 8 1.673 8 2.5 L 8 3.208 C 5.694 3.863 4 5.987 4 8.5 L 4 11.5 C 4 12.929 3.344 14.241 2.2 15.101 C 2.028 15.23 1.958 15.455 2.026 15.659 C 2.094 15.863 2.285 16.001 2.5 16.001 L 16.499 16.001 C 16.714 16.001 16.905 15.863 16.973 15.659 C 17.041 15.455 16.971 15.23 16.799 15.101 L 16.8 15.101 Z" /> + + <path fill="#D81036" d="m 7.05 17.001 c -0.033 0.164 -0.051 0.331 -0.051 0.5 c 0 1.378 1.122 2.5 2.5 2.5 c 1.378 0 2.5 -1.122 2.5 -2.5 c 0 -0.168 -0.017 -0.336 -0.05 -0.5 l -4.899 0 z" /> + + + </g> +</svg> diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx new file mode 100644 index 000000000..e86b756a7 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx @@ -0,0 +1,138 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ + +import React from 'react'; + +import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { clearStuckAlarmAsyncAction } from '../actions/clearStuckAlarmsAction'; +import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; + +export enum ClearStuckAlarmsDialogMode { + None = 'none', + Show = 'show', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch), + reloadCurrentAlarmsAction: () => dispatcher.dispatch(currentAlarmsReloadAction), +}); + +type clearStuckAlarmsProps = Connect<typeof undefined, typeof mapDispatch> & { + numberDevices: Number; + mode: ClearStuckAlarmsDialogMode; + stuckAlarms: string[]; + onClose: () => void; +}; + +type ClearStuckAlarmsState = { + clearAlarmsSuccessful: boolean; + errormessage: string; + unclearedAlarms: string[]; +}; + +class ClearStuckAlarmsDialogComponent extends React.Component<clearStuckAlarmsProps, ClearStuckAlarmsState> { + constructor(props: clearStuckAlarmsProps) { + super(props); + this.state = { + clearAlarmsSuccessful: true, + errormessage: '', + unclearedAlarms: [], + }; + } + + onClose = (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + this.props.onClose(); + }; + + onRefresh = async (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + const result = await this.props.clearStuckAlarmsAsync(this.props.stuckAlarms); + + if (result && result['devicemanager:output'].nodenames && result['devicemanager:output'].nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared + const undeletedAlarm = this.props.stuckAlarms.filter(item => !result['devicemanager:output'].nodenames.includes(item)); + const error = 'The alarms of the following devices couldn\'t be refreshed: '; + this.setState({ clearAlarmsSuccessful: false, errormessage: error, unclearedAlarms: undeletedAlarm }); + return; + + } else { //show errormessage if no devices were cleared + this.setState({ clearAlarmsSuccessful: false, errormessage: 'Alarms couldn\'t be refreshed.', unclearedAlarms: [] }); + } + + this.props.reloadCurrentAlarmsAction(); + this.props.onClose(); + }; + + onOk = (event: React.MouseEvent) => { + + event.stopPropagation(); + event.preventDefault(); + if (this.state.unclearedAlarms.length > 0) + this.props.reloadCurrentAlarmsAction(); + this.props.onClose(); + }; + + render() { + console.log(this.props.stuckAlarms); + const device = this.props.numberDevices > 1 ? 'devices' : 'device'; + const defaultMessage = 'Are you sure you want to refresh all alarms for ' + this.props.numberDevices + ' ' + device + '?'; + const message = this.state.clearAlarmsSuccessful ? defaultMessage : this.state.errormessage; + + const defaultTitle = 'Refresh Confirmation'; + const title = this.state.clearAlarmsSuccessful ? defaultTitle : 'Refresh Result'; + + return ( + <Dialog open={this.props.mode !== ClearStuckAlarmsDialogMode.None}> + <DialogTitle>{title}</DialogTitle> + <DialogContent> + <DialogContentText> + {message} + </DialogContentText> + { + this.state.unclearedAlarms.map(item => + <DialogContentText> + {item} + </DialogContentText>, + ) + } + </DialogContent> + <DialogActions> + { + this.state.clearAlarmsSuccessful && + <> + <Button color="inherit" onClick={this.onRefresh}>Yes</Button> + <Button color="inherit" onClick={this.onClose}>No</Button> + </> + } + + { + !this.state.clearAlarmsSuccessful && <Button color="inherit" onClick={this.onOk}>Ok</Button> + } + </DialogActions> + </Dialog> + ); + } +} + +const ClearStuckAlarmsDialog = connect(undefined, mapDispatch)(ClearStuckAlarmsDialogComponent); +export default ClearStuckAlarmsDialog; diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx new file mode 100644 index 000000000..a3e32c42c --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx @@ -0,0 +1,473 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; + +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import { Doughnut } from 'react-chartjs-2'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +const styles = () => createStyles({ + pageWidthSettings: { + width: '50%', + float: 'left', + }, +}); + +const scrollbar = { overflow: 'auto', paddingRight: '20px' }; + +let connectionStatusinitialLoad = true; +let connectionStatusinitialStateChanged = false; +let connectionStatusDataLoad: number[] = [0, 0, 0, 0]; +let connectionTotalCount = 0; + +let alarmStatusinitialLoad = true; +let alarmStatusinitialStateChanged = false; +let alarmStatusDataLoad: number[] = [0, 0, 0, 0]; +let alarmTotalCount = 0; + +const mapProps = (state: IApplicationStoreState) => ({ + alarmStatus: state.fault.faultStatus, +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)), +}); + +type HomeComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch> & WithStyles<typeof styles>; + +class DashboardHome extends React.Component<HomeComponentProps> { + constructor(props: HomeComponentProps) { + super(props); + this.state = { + }; + } + + render(): JSX.Element { + + if (!this.props.alarmStatus.isLoadingConnectionStatusChart) { + connectionStatusDataLoad = [ + this.props.alarmStatus.Connected, + this.props.alarmStatus.Connecting, + this.props.alarmStatus.Disconnected, + this.props.alarmStatus.UnableToConnect, + this.props.alarmStatus.Undefined, + ]; + connectionTotalCount = this.props.alarmStatus.Connected + this.props.alarmStatus.Connecting + + this.props.alarmStatus.Disconnected + this.props.alarmStatus.UnableToConnect + this.props.alarmStatus.Undefined; + + } + + if (!this.props.alarmStatus.isLoadingAlarmStatusChart) { + alarmStatusDataLoad = [ + this.props.alarmStatus.critical, + this.props.alarmStatus.major, + this.props.alarmStatus.minor, + this.props.alarmStatus.warning, + ]; + alarmTotalCount = this.props.alarmStatus.critical + this.props.alarmStatus.major + + this.props.alarmStatus.minor + this.props.alarmStatus.warning; + } + + /** Available Network Connection Status chart data */ + const connectionStatusData = { + labels: [ + 'Connected: ' + this.props.alarmStatus.Connected, + 'Connecting: ' + this.props.alarmStatus.Connecting, + 'Disconnected: ' + this.props.alarmStatus.Disconnected, + 'UnableToConnect: ' + this.props.alarmStatus.UnableToConnect, + 'Undefined: ' + this.props.alarmStatus.Undefined, + ], + datasets: [{ + labels: ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect', 'Undefined'], + data: connectionStatusDataLoad, + backgroundColor: [ + 'rgb(0, 153, 51)', + 'rgb(255, 102, 0)', + 'rgb(191, 191, 191)', + 'rgb(191, 191, 191)', + 'rgb(242, 240, 240)', + ], + }], + }; + + + /** No Devices available */ + const connectionStatusUnavailableData = { + labels: ['No Devices available'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(255, 255, 255)', + ], + }], + }; + + /** Loading Connection Status chart */ + const connectionStatusisLoading = { + labels: ['Loading chart...'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(255, 255, 255)', + ], + }], + }; + + /** Loading Alarm Status chart */ + const alarmStatusisLoading = { + labels: ['Loading chart...'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(255, 255, 255)', + ], + }], + }; + + /** Connection status options */ + let labels: String[] = ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect', 'Undefined']; + const connectionStatusOptions = { + tooltips: { + callbacks: { + label: (tooltipItem: any, data: any) => { + let label = + (data.datasets[tooltipItem.datasetIndex].labels && + data.datasets[tooltipItem.datasetIndex].labels[ + tooltipItem.index + ]) || + data.labels[tooltipItem.index] || + ''; + if (label) { + label += ': '; + } + label += + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + + (data.datasets[tooltipItem.datasetIndex].labelSuffix || ''); + + return label; + }, + }, + }, + responsive: true, + maintainAspectRatio: false, + animation: { + duration: 0, + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + }, + onClick: (event: MouseEvent, item: any) => { + if (item[0]) { + let connectionStatus = labels[item[0]._index] + ''; + this.props.navigateToApplication('connect', '/connectionStatus/' + connectionStatus); + } + }, + }; + + /** Connection status unavailable options */ + const connectionStatusUnavailableOptions = { + responsive: true, + maintainAspectRatio: false, + animation: { + duration: 0, + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + tooltip: { + enabled: false, + }, + }, + }; + + /** Add text inside the doughnut chart for Connection Status */ + const connectionStatusPlugins = [{ + beforeDraw: function (chart: any) { + var width = chart.width, + height = chart.height, + ctx = chart.ctx; + ctx.restore(); + var fontSize = (height / 480).toFixed(2); + ctx.font = fontSize + 'em sans-serif'; + ctx.textBaseline = 'top'; + var text = 'Network Connection Status', + textX = Math.round((width - ctx.measureText(text).width) / 2), + textY = height / 2; + ctx.fillText(text, textX, textY); + ctx.save(); + }, + }]; + + /** Alarm status Data */ + const alarmStatusData = { + labels: [ + 'Critical : ' + this.props.alarmStatus.critical, + 'Major : ' + this.props.alarmStatus.major, + 'Minor : ' + this.props.alarmStatus.minor, + 'Warning : ' + this.props.alarmStatus.warning, + ], + datasets: [{ + labels: ['Critical', 'Major', 'Minor', 'Warning'], + data: alarmStatusDataLoad, + backgroundColor: [ + 'rgb(240, 25, 10)', + 'rgb(240, 133, 10)', + 'rgb(240, 240, 10)', + 'rgb(46, 115, 176)', + ], + }], + }; + + /** No Alarm status available */ + const alarmStatusUnavailableData = { + labels: ['No Alarms available'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(0, 153, 51)', + ], + }], + }; + + /** Alarm status Options */ + let alarmLabels: String[] = ['Critical', 'Major', 'Minor', 'Warning']; + const alarmStatusOptions = { + tooltips: { + callbacks: { + label: (tooltipItem: any, data: any) => { + let label = + (data.datasets[tooltipItem.datasetIndex].labels && + data.datasets[tooltipItem.datasetIndex].labels[ + tooltipItem.index + ]) || + data.labels[tooltipItem.index] || + ''; + if (label) { + label += ': '; + } + label += + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + + (data.datasets[tooltipItem.datasetIndex].labelSuffix || ''); + + return label; + }, + }, + }, + responsive: true, + maintainAspectRatio: false, + animation: { + duration: 0, + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + }, + onClick: (event: MouseEvent, item: any) => { + if (item[0]) { + let severity = alarmLabels[item[0]._index] + ''; + this.props.navigateToApplication('fault', '/alarmStatus/' + severity); + } + }, + }; + + /** Alarm status unavailable options */ + const alarmStatusUnavailableOptions = { + responsive: true, + maintainAspectRatio: false, + animation: { + duration: 0, + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + tooltip: { + enabled: false, + }, + }, + }; + /** Add text inside the doughnut chart for Alarm Status */ + const alarmStatusPlugins = [{ + beforeDraw: function (chart: any) { + var width = chart.width, + height = chart.height, + ctx = chart.ctx; + ctx.restore(); + var fontSize = (height / 480).toFixed(2); + ctx.font = fontSize + 'em sans-serif'; + ctx.textBaseline = 'top'; + var text = 'Network Alarm Status', + textX = Math.round((width - ctx.measureText(text).width) / 2), + textY = height / 2; + ctx.fillText(text, textX, textY); + ctx.save(); + }, + }]; + + return ( + <> + <div style={scrollbar} > + <h1 aria-label="welcome-to-odlux">Welcome to ODLUX</h1> + <div style={{ width: '50%', float: 'left' }}> + {this.checkElementsAreLoaded() ? + this.checkConnectionStatus() && connectionTotalCount != 0 ? + <Doughnut + data={connectionStatusData} + type={Doughnut} + width={500} + height={500} + options={connectionStatusOptions} + plugins={connectionStatusPlugins} + /> + : <Doughnut + data={connectionStatusUnavailableData} + type={Doughnut} + width={500} + height={500} + options={connectionStatusUnavailableOptions} + plugins={connectionStatusPlugins} /> + : <Doughnut + data={connectionStatusisLoading} + type={Doughnut} + width={500} + height={500} + options={connectionStatusUnavailableOptions} + plugins={connectionStatusPlugins} + /> + } + </div> + <div style={{ width: '50%', float: 'left' }}> + {this.checkAlarmsAreLoaded() ? + this.checkAlarmStatus() && alarmTotalCount != 0 ? + <Doughnut + data={alarmStatusData} + type={Doughnut} + width={500} + height={500} + options={alarmStatusOptions} + plugins={alarmStatusPlugins} + /> + : <Doughnut + data={alarmStatusUnavailableData} + type={Doughnut} + width={500} + height={500} + options={alarmStatusUnavailableOptions} + plugins={alarmStatusPlugins} + /> + : <Doughnut + data={alarmStatusisLoading} + type={Doughnut} + width={500} + height={500} + options={alarmStatusUnavailableOptions} + plugins={alarmStatusPlugins} + /> + } + </div> + </div> + </> + ); + } + + /** Check if connection status data available */ + public checkConnectionStatus = () => { + let statusCount = this.props.alarmStatus; + if (statusCount.isLoadingConnectionStatusChart) { + return true; + } + if (statusCount.Connected == 0 && statusCount.Connecting == 0 && statusCount.Disconnected == 0 + && statusCount.UnableToConnect == 0 && statusCount.Undefined == 0) { + return false; + } else { + return true; + } + }; + + /** Check if connection status chart data is loaded */ + public checkElementsAreLoaded = () => { + let isLoadingCheck = this.props.alarmStatus; + if (connectionStatusinitialLoad && !isLoadingCheck.isLoadingConnectionStatusChart) { + if (this.checkConnectionStatus()) { + connectionStatusinitialLoad = false; + return true; + } + return false; + } else if (connectionStatusinitialLoad && isLoadingCheck.isLoadingConnectionStatusChart) { + connectionStatusinitialLoad = false; + connectionStatusinitialStateChanged = true; + return !isLoadingCheck.isLoadingConnectionStatusChart; + } else if (connectionStatusinitialStateChanged) { + if (!isLoadingCheck.isLoadingConnectionStatusChart) { + connectionStatusinitialStateChanged = false; + } + return !isLoadingCheck.isLoadingConnectionStatusChart; + } + return true; + }; + + /** Check if alarms data available */ + public checkAlarmStatus = () => { + let alarmCount = this.props.alarmStatus; + if (alarmCount.isLoadingAlarmStatusChart) { + return true; + } + if (alarmCount.critical == 0 && alarmCount.major == 0 && alarmCount.minor == 0 && alarmCount.warning == 0) { + return false; + } else { + return true; + } + }; + + /** Check if alarm status chart data is loaded */ + public checkAlarmsAreLoaded = () => { + let isLoadingCheck = this.props.alarmStatus; + if (alarmStatusinitialLoad && !isLoadingCheck.isLoadingAlarmStatusChart) { + if (this.checkAlarmStatus()) { + alarmStatusinitialLoad = false; + return true; + } + return false; + } else if (alarmStatusinitialLoad && isLoadingCheck.isLoadingAlarmStatusChart) { + alarmStatusinitialLoad = false; + alarmStatusinitialStateChanged = true; + return !isLoadingCheck.isLoadingAlarmStatusChart; + } else if (alarmStatusinitialStateChanged) { + if (!isLoadingCheck.isLoadingAlarmStatusChart) { + alarmStatusinitialStateChanged = false; + } + return !isLoadingCheck.isLoadingAlarmStatusChart; + } + return true; + }; +} + +export default (withRouter(connect(mapProps, mapDispatch)(DashboardHome)));
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/faultStatus.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/faultStatus.tsx new file mode 100644 index 000000000..57374dd77 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/faultStatus.tsx @@ -0,0 +1,106 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import React from 'react'; + +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import Typography from '@mui/material/Typography'; +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; +import Tooltip from '@mui/material/Tooltip'; + +import { connect, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + + +const styles = () => createStyles({ + icon: { + marginLeft: 8, + marginRight: 8, + }, + critical: { + color: 'red', + }, + major: { + color: 'orange', + }, + minor: { + color: '#f7f700', + }, + warning: { + color: '#428bca', + }, +}); + +const mapProps = (state: IApplicationStoreState) => ({ + faultStatus: state.fault.faultStatus, +}); + + +type FaultStatusComponentProps = & WithStyles<typeof styles> & Connect<typeof mapProps>; + +class FaultStatusComponent extends React.Component<FaultStatusComponentProps> { + render(): JSX.Element { + const { classes, faultStatus } = this.props; + + return ( + <> + <Typography variant="body1" color="inherit"> + Alarm Status: + <Tooltip title="Critical Alarms" arrow> + <span aria-label="critical-alarms"> + <FontAwesomeIcon className={`${classes.icon} ${classes.critical}`} icon={faExclamationTriangle} /> + </span> + </Tooltip> + {faultStatus.critical} | + </Typography> + + <Typography variant="body1" color="inherit"> + <Tooltip title="Major Alarms" arrow> + <span aria-label="major-alarms"> + <FontAwesomeIcon className={`${classes.icon} ${classes.major}`} icon={faExclamationTriangle} /> + </span> + </Tooltip> + {faultStatus.major} | + </Typography> + + <Typography variant="body1" color="inherit"> + <Tooltip title="Minor Alarms" arrow> + <span aria-label="minor-alarms"> + <FontAwesomeIcon className={`${classes.icon} ${classes.minor}`} icon={faExclamationTriangle} /> + </span> + </Tooltip> + {faultStatus.minor} | + </Typography> + + <Typography variant="body1" color="inherit"> + <Tooltip title="Warning Alarms" arrow> + <span aria-label="warning-alarms"> + <FontAwesomeIcon className={`${classes.icon} ${classes.warning}`} icon={faExclamationTriangle} /> + </span> + </Tooltip> + {faultStatus.warning} | + </Typography> + </> + ); + } +} + +export const FaultStatus = withStyles(styles)(connect(mapProps)(FaultStatusComponent)); +export default FaultStatus;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx new file mode 100644 index 000000000..59657d8de --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx @@ -0,0 +1,112 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler'; +import { Fault } from '../models/fault'; + +export enum RefreshAlarmLogDialogMode { + None = 'none', + RefreshAlarmLogTable = 'RefreshAlarmLogTable', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + refreshAlarmLog: () => dispatcher.dispatch(alarmLogEntriesReloadAction), +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [RefreshAlarmLogDialogMode.None]: { + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [RefreshAlarmLogDialogMode.RefreshAlarmLogTable]: { + dialogTitle: 'Do you want to refresh the Alarm Log?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + }, +}; + +type RefreshAlarmLogDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: RefreshAlarmLogDialogMode; + onClose: () => void; +}; + +type RefreshAlarmLogDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean }; + +class RefreshAlarmLogDialogComponent extends React.Component<RefreshAlarmLogDialogComponentProps, RefreshAlarmLogDialogComponentState> { + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== RefreshAlarmLogDialogMode.None}> + <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button aria-label="dialog-confirm-button" onClick={() => { + this.onRefresh(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button aria-label="dialog-cancel-button" onClick={() => { + this.onCancel(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private onRefresh = () => { + this.props.refreshAlarmLog(); + this.props.onClose(); + }; + + private onCancel = () => { + this.props.onClose(); + }; +} + +export const RefreshAlarmLogDialog = connect(undefined, mapDispatch)(RefreshAlarmLogDialogComponent); +export default RefreshAlarmLogDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx new file mode 100644 index 000000000..20cd514cd --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx @@ -0,0 +1,113 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; +import { Fault } from '../models/fault'; + +export enum RefreshCurrentAlarmsDialogMode { + None = 'none', + RefreshCurrentAlarmsTable = 'RefreshCurrentAlarmsTable', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + refreshCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction), +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [RefreshCurrentAlarmsDialogMode.None]: { + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable]: { + dialogTitle: 'Do you want to refresh the Current Alarms List?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + }, +}; + +type RefreshCurrentAlarmsDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: RefreshCurrentAlarmsDialogMode; + onClose: () => void; +}; + +type RefreshCurrentAlarmsDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean }; + +class RefreshCurrentAlarmsDialogComponent extends React.Component<RefreshCurrentAlarmsDialogComponentProps, RefreshCurrentAlarmsDialogComponentState> { + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== RefreshCurrentAlarmsDialogMode.None}> + <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button aria-label="dialog-confirm-button" onClick={() => { + this.onRefresh(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button aria-label="dialog-cancel-button" onClick={() => { + this.onCancel(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private onRefresh = () => { + this.props.refreshCurrentAlarms(); + this.props.onClose(); + }; + + private onCancel = () => { + this.props.onClose(); + }; +} + +export const RefreshCurrentAlarmsDialog = connect(undefined, mapDispatch)(RefreshCurrentAlarmsDialogComponent); +export default RefreshCurrentAlarmsDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts new file mode 100644 index 000000000..bdd459669 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { Fault } from '../models/fault'; + +export interface IAlarmLogEntriesState extends IExternalTableState<Fault> { } + +// create eleactic search data fetch handler +const alarmLogEntriesSearchHandler = createSearchDataHandler< Fault>('faultlog'); + +export const { + actionHandler: alarmLogEntriesActionHandler, + createActions: createAlarmLogEntriesActions, + createProperties: createAlarmLogEntriesProperties, + reloadAction: alarmLogEntriesReloadAction, + + // set value action, to change a value +} = createExternal<Fault>(alarmLogEntriesSearchHandler, appState => appState.fault.alarmLogEntries); + diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts new file mode 100644 index 000000000..0d5a8c70d --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ + +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AreStuckAlarmsCleared } from '../actions/clearStuckAlarmsAction'; + +export interface IStuckAlarms { + areAlarmsCleared: boolean; +} + +const initialState: IStuckAlarms = { + areAlarmsCleared: false, +}; + +export const stuckAlarmHandler: IActionHandler<IStuckAlarms> = (state = initialState, action) => { + if (action instanceof AreStuckAlarmsCleared) { + state = { ...state, areAlarmsCleared: action.isBusy }; + } + + return state; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts new file mode 100644 index 000000000..70aa1c27e --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { Fault } from '../models/fault'; + +export interface ICurrentAlarmsState extends IExternalTableState<Fault> { } + +// create eleactic search data fetch handler +const currentAlarmsSearchHandler = createSearchDataHandler<Fault>('faultcurrent'); + +export const { + actionHandler: currentAlarmsActionHandler, + createActions: createCurrentAlarmsActions, + createProperties: createCurrentAlarmsProperties, + reloadAction: currentAlarmsReloadAction, + + // set value action, to change a value +} = createExternal<Fault>(currentAlarmsSearchHandler, appState => appState.fault.currentAlarms); + diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts new file mode 100644 index 000000000..e4a19ae5c --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +// main state handler + +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; +// ** do not remove ** +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { SetPanelAction } from '../actions/panelChangeActions'; +import { PanelId } from '../models/panelId'; +import { alarmLogEntriesActionHandler, IAlarmLogEntriesState } from './alarmLogEntriesHandler'; +import { currentAlarmsActionHandler, ICurrentAlarmsState } from './currentAlarmsHandler'; +import { faultStatusHandler, IFaultStatus } from './faultStatusHandler'; +import { faultNotificationsHandler, IFaultNotifications } from './notificationsHandler'; + +export interface IFaultAppStoreState { + currentAlarms: ICurrentAlarmsState; + faultNotifications: IFaultNotifications; + alarmLogEntries: IAlarmLogEntriesState; + currentOpenPanel: PanelId | null; + faultStatus: IFaultStatus; +} + +const currentOpenPanelHandler: IActionHandler<PanelId | null> = (state = null, action) => { + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; +}; + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + fault: IFaultAppStoreState; + } +} + +const actionHandlers = { + currentAlarms: currentAlarmsActionHandler, + faultNotifications: faultNotificationsHandler, + alarmLogEntries: alarmLogEntriesActionHandler, + currentOpenPanel: currentOpenPanelHandler, + faultStatus: faultStatusHandler, +}; + +export const faultAppRootHandler = combineActionHandler<IFaultAppStoreState>(actionHandlers); +export default faultAppRootHandler; diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts new file mode 100644 index 000000000..21b033e6a --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts @@ -0,0 +1,77 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { SetFaultStatusAction } from '../actions/statusActions'; + +export interface IFaultStatus { + critical: number; + major: number; + minor: number; + warning: number; + isLoadingAlarmStatusChart: boolean; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; + isLoadingConnectionStatusChart: boolean; +} + +const faultStatusInit: IFaultStatus = { + critical: 0, + major: 0, + minor: 0, + warning: 0, + isLoadingAlarmStatusChart: false, + Connected: 0, + Connecting: 0, + Disconnected: 0, + Mounted: 0, + UnableToConnect: 0, + Undefined: 0, + Unmounted: 0, + total: 0, + isLoadingConnectionStatusChart: false, +}; + +export const faultStatusHandler: IActionHandler<IFaultStatus> = (state = faultStatusInit, action) => { + if (action instanceof SetFaultStatusAction) { + state = { + critical: action.criticalFaults, + major: action.majorFaults, + minor: action.minorFaults, + warning: action.warnings, + isLoadingAlarmStatusChart: action.isLoadingAlarmStatusChart, + Connected: action.ConnectedCount, + Connecting: action.ConnectingCount, + Disconnected: action.DisconnectedCount, + Mounted: action.MountedCount, + UnableToConnect: action.UnableToConnectCount, + Undefined: action.UndefinedCount, + Unmounted: action.UnmountedCount, + total: action.totalCount, + isLoadingConnectionStatusChart: action.isLoadingConnectionStatusChart, + }; + } + + return state; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/notificationsHandler.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/notificationsHandler.ts new file mode 100644 index 000000000..3d960bfc4 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/handlers/notificationsHandler.ts @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AddFaultNotificationAction, ResetFaultNotificationsAction } from '../actions/notificationActions'; +import { FaultAlarmNotification } from '../models/fault'; + +export interface IFaultNotifications { + faults: FaultAlarmNotification[]; + since: Date; +} + +const faultNotoficationsInit: IFaultNotifications = { + faults: [], + since: new Date(), +}; + +export const faultNotificationsHandler: IActionHandler<IFaultNotifications> = (state = faultNotoficationsInit, action) => { + if (action instanceof AddFaultNotificationAction) { + state = { + ...state, + faults: [...state.faults, action.fault], + }; + } else if (action instanceof ResetFaultNotificationsAction) { + state = { + ...state, + faults: [], + since: new Date(), + }; + } + + return state; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/index.html b/sdnr/wt-odlux/odlux/apps/faultApp/src/index.html new file mode 100644 index 000000000..c37968074 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <!-- <link rel="stylesheet" href="./vendor.css"> --> + <title>Minimal App</title> +</head> + +<body> + <div id="app"></div> + <script type="text/javascript" src="./require.js"></script> + <script type="text/javascript" src="./config.js"></script> + <script> + // run the application + require(["app", "faultApp", "connectApp" ], function (app, faultApp, connectApp) { + faultApp.register(); + connectApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/models/fault.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/models/fault.ts new file mode 100644 index 000000000..c70253e58 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/models/fault.ts @@ -0,0 +1,97 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +export type Fault = { + id: string; + nodeId: string; + counter: number; + timestamp: string; + objectId: string; + problem: string; + severity: null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed'; + type: string; + sourceType?: string; +}; + +export type FaultAlarmNotification = { + id: string; + timeStamp: string; + nodeName: string; + counter: number; + objectId: string; + problem: string; + severity: string; +}; + +export type FaultAlarmNotificationWS = { + 'node-id': string; + 'data': { + 'counter': number; + 'time-stamp': string; + 'object-id-ref': string; + 'problem': string; + 'severity': null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed'; + }; + 'type': { + 'namespace': string; + 'revision': string; + 'type': string; + }; + 'event-time': string; +}; + +/** + * Fault status return type + */ +export type FaultsReturnType = { + criticals: number; + majors: number; + minors: number; + warnings: number; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; +}; + +export type FaultType = { + Critical: number; + Major: number; + Minor: number; + Warning: number; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; +}; + +export type Faults = { + faults: FaultsReturnType; + 'network-element-connections': FaultsReturnType; +}; + +export type DeletedStuckAlarms = { + nodenames: string[]; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/models/panelId.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/models/panelId.ts new file mode 100644 index 000000000..daebad0e5 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/models/panelId.ts @@ -0,0 +1,18 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +export type PanelId = null | 'CurrentAlarms' | 'AlarmNotifications' | 'AlarmLog';
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/pluginFault.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/pluginFault.tsx new file mode 100644 index 000000000..2ef243c2d --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/pluginFault.tsx @@ -0,0 +1,171 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +// app configuration and main entry point for the app + +import React from 'react'; +import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom'; + +import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; +import applicationManager from '../../../framework/src/services/applicationManager'; +import { IFormatedMessage, subscribe } from '../../../framework/src/services/notificationService'; +import { IApplicationStoreState } from '../../../framework/src/store/applicationStore'; + +import { AddFaultNotificationAction } from './actions/notificationActions'; +import { SetPanelAction } from './actions/panelChangeActions'; +import { refreshFaultStatusAsyncAction, SetFaultStatusAction } from './actions/statusActions'; +import DashboardHome from './components/dashboardHome'; +import { FaultStatus } from './components/faultStatus'; +import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from './handlers/currentAlarmsHandler'; +import { faultAppRootHandler } from './handlers/faultAppRootHandler'; +import { FaultAlarmNotificationWS } from './models/fault'; +import { PanelId } from './models/panelId'; +import { FaultApplication } from './views/faultApplication'; + +const appIcon = require('./assets/icons/faultAppIcon.svg'); // select app icon + +let currentMountId: string | undefined = undefined; +let currentSeverity: string | undefined = undefined; +let refreshInterval: ReturnType<typeof window.setInterval> | null = null; + +const mapProps = (state: IApplicationStoreState) => ({ + currentAlarmsProperties: createCurrentAlarmsProperties(state), +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch, true), + setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)), +}); + +const FaultApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => { + if (currentMountId !== props.match.params.mountId) { + // route parameter has changed + currentMountId = props.match.params.mountId || undefined; + // Hint: This timeout is need, since it is not recommended to change the state while rendering is in progress ! + window.setTimeout(() => { + if (currentMountId) { + props.setCurrentPanel('CurrentAlarms'); + props.currentAlarmsActions.onFilterChanged('nodeId', currentMountId); + if (!props.currentAlarmsProperties.showFilter) { + props.currentAlarmsActions.onToggleFilter(false); + props.currentAlarmsActions.onRefresh(); + } else + props.currentAlarmsActions.onRefresh(); + } + }); + } + return ( + <FaultApplication /> + ); +}); + +const FaultApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ severity?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => { + if (currentSeverity !== props.match.params.severity) { + currentSeverity = props.match.params.severity || undefined; + window.setTimeout(() => { + if (currentSeverity) { + props.setCurrentPanel('CurrentAlarms'); + props.currentAlarmsActions.onFilterChanged('severity', currentSeverity); + if (!props.currentAlarmsProperties.showFilter) { + props.currentAlarmsActions.onToggleFilter(false); + props.currentAlarmsActions.onRefresh(); + } else + props.currentAlarmsActions.onRefresh(); + } + }); + } + return ( + <FaultApplication /> + ); +}); + +const App = withRouter((props: RouteComponentProps) => ( + <Switch> + <Route path={`${props.match.path}/alarmStatus/:severity?`} component={FaultApplicationAlarmStatusRouteAdapter} /> + <Route path={`${props.match.path}/:mountId?`} component={FaultApplicationRouteAdapter} /> + <Redirect to={`${props.match.path}`} /> + </Switch> +)); + +export function register() { + const applicationApi = applicationManager.registerApplication({ + name: 'fault', + icon: appIcon, + rootComponent: App, + rootActionHandler: faultAppRootHandler, + statusBarElement: FaultStatus, + dashbaordElement: DashboardHome, + menuEntry: 'Fault', + }); + + let counter = 0; + // subscribe to the websocket notifications + subscribe<FaultAlarmNotificationWS & IFormatedMessage>('problem-notification', (fault => { + const store = applicationApi && applicationApi.applicationStore; + if (fault && store) { + + store.dispatch(new AddFaultNotificationAction({ + id: String(counter++), + nodeName: fault['node-id'], + counter: +fault.data.counter, + objectId: fault.data['object-id-ref'], + problem: fault.data.problem, + severity: fault.data.severity || '', + timeStamp: fault.data['time-stamp'], + })); + } + })); + + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(currentAlarmsReloadAction); + }); + + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(refreshFaultStatusAsyncAction); + }); + + applicationApi.logoutEvent.addHandler(()=>{ + + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false)); + clearInterval(refreshInterval!); + }); + }); + + function startRefreshInterval() { + const refreshFaultStatus = window.setInterval(() => { + applicationApi.applicationStoreInitialized.then(store => { + + store.dispatch(refreshFaultStatusAsyncAction); + }); + }, 15000); + + return refreshFaultStatus; + } + + applicationApi.loginEvent.addHandler(()=>{ + if (refreshInterval) { + clearInterval(refreshInterval); + } + refreshInterval = startRefreshInterval() as any; + }); + + applicationApi.logoutEvent.addHandler(()=>{ + refreshInterval && window.clearInterval(refreshInterval); + refreshInterval = null; + }); +} diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/services/faultStatusService.ts b/sdnr/wt-odlux/odlux/apps/faultApp/src/services/faultStatusService.ts new file mode 100644 index 000000000..0c7a215f8 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/services/faultStatusService.ts @@ -0,0 +1,69 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import { Result } from '../../../../framework/src/models/elasticSearch'; +import { requestRest } from '../../../../framework/src/services/restService'; + +import { Faults, FaultType } from '../models/fault'; + + +export const getFaultStateFromDatabase = async (): Promise<FaultType | null> => { + const path = 'rests/operations/data-provider:read-status'; + const result = await requestRest<Result<Faults>>(path, { method: 'POST' }); + + let faultType: FaultType = { + Critical: 0, + Major: 0, + Minor: 0, + Warning: 0, + Connected: 0, + Connecting: 0, + Disconnected: 0, + Mounted: 0, + UnableToConnect: 0, + Undefined: 0, + Unmounted: 0, + total: 0, + }; + let faults: Faults[] | null = null; + + if (result && result['data-provider:output'] && result['data-provider:output'].data) { + faults = result['data-provider:output'].data; + faultType = { + Critical: faults[0].faults.criticals, + Major: faults[0].faults.majors, + Minor: faults[0].faults.minors, + Warning: faults[0].faults.warnings, + Connected: faults[0]['network-element-connections'].Connected, + Connecting: faults[0]['network-element-connections'].Connecting, + Disconnected: faults[0]['network-element-connections'].Disconnected, + Mounted: faults[0]['network-element-connections'].Mounted, + UnableToConnect: faults[0]['network-element-connections'].UnableToConnect, + Undefined: faults[0]['network-element-connections'].Undefined, + Unmounted: faults[0]['network-element-connections'].Unmounted, + total: faults[0]['network-element-connections'].total, + }; + } + + return faultType; +}; + +export const clearStuckAlarms = async (nodeNames: string[]) => { + const path = 'rests/operations/devicemanager:clear-current-fault-by-nodename'; + const result = await requestRest<any>(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) }); + return result; +};
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/views/faultApplication.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/views/faultApplication.tsx new file mode 100644 index 000000000..b9f115ed7 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/views/faultApplication.tsx @@ -0,0 +1,247 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * 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. + * ============LICENSE_END========================================================================== + */ +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; + +import Refresh from '@mui/icons-material/Refresh'; +import Sync from '@mui/icons-material/Sync'; +import { AppBar, Tab, Tabs } from '@mui/material'; + +import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { setPanelAction } from '../actions/panelChangeActions'; +import ClearStuckAlarmsDialog, { ClearStuckAlarmsDialogMode } from '../components/clearStuckAlarmsDialog'; +import RefreshAlarmLogDialog, { RefreshAlarmLogDialogMode } from '../components/refreshAlarmLogDialog'; +import RefreshCurrentAlarmsDialog, { RefreshCurrentAlarmsDialogMode } from '../components/refreshCurrentAlarmsDialog'; +import { alarmLogEntriesReloadAction, createAlarmLogEntriesActions, createAlarmLogEntriesProperties } from '../handlers/alarmLogEntriesHandler'; +import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; +import { Fault, FaultAlarmNotification } from '../models/fault'; +import { PanelId } from '../models/panelId'; + +const mapProps = (state: IApplicationStoreState) => ({ + panelId: state.fault.currentOpenPanel, + currentAlarmsProperties: createCurrentAlarmsProperties(state), + faultNotifications: state.fault.faultNotifications, + alarmLogEntriesProperties: createAlarmLogEntriesProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch), + alarmLogEntriesActions: createAlarmLogEntriesActions(dispatcher.dispatch), + reloadCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction), + reloadAlarmLogEntries: () => dispatcher.dispatch(alarmLogEntriesReloadAction), + switchActivePanel: (panelId: PanelId) => { + dispatcher.dispatch(setPanelAction(panelId)); + }, +}); + +type FaultApplicationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>; + +type FaultApplicationState = { + clearAlarmDialogMode: ClearStuckAlarmsDialogMode; + stuckAlarms: string[]; + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode; + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode; +}; + + +const FaultTable = MaterialTable as MaterialTableCtorType<Fault>; +const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType<FaultAlarmNotification>; + +let currentAlarmsInitalSorted = false; +let alarmLogInitialSorted = false; + +class FaultApplicationComponent extends React.Component<FaultApplicationComponentProps, FaultApplicationState> { + constructor(props: FaultApplicationComponentProps) { + super(props); + this.state = { + clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, + stuckAlarms: [], + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None, + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None, + }; + } + + onDialogClose = () => { + this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] }); + }; + + onDialogOpen = () => { + const stuckAlarms = [...new Set(this.props.currentAlarmsProperties.rows.map(item => item.nodeId))]; + this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.Show, stuckAlarms: stuckAlarms }); + }; + + private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => { + this.onToggleTabs(newValue); + }; + + private onToggleTabs = (panelId: PanelId) => { + const nextActivePanel = panelId; + this.props.switchActivePanel(nextActivePanel); + switch (nextActivePanel) { + case 'CurrentAlarms': + if (!currentAlarmsInitalSorted) { + currentAlarmsInitalSorted = true; + this.props.currentAlarmsActions.onHandleExplicitRequestSort('timestamp', 'desc'); + } else { + this.props.reloadCurrentAlarms(); + } + break; + case 'AlarmLog': + if (!alarmLogInitialSorted) { + alarmLogInitialSorted = true; + this.props.alarmLogEntriesActions.onHandleExplicitRequestSort('timestamp', 'desc'); + } else { + this.props.reloadAlarmLogEntries(); + } + break; + case 'AlarmNotifications': + case null: + default: + // nothing to do + break; + } + }; + + + + render(): JSX.Element { + + const clearAlarmsAction = { + icon: Sync, tooltip: 'Clear stuck alarms', ariaLabel:'clear-stuck-alarms', onClick: this.onDialogOpen, + }; + + const refreshCurrentAlarmsAction = { + icon: Refresh, tooltip: 'Refresh Current Alarms List', ariaLabel:'refresh', onClick: () => { + this.setState({ + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable, + }); + }, + }; + + const refreshAlarmLogAction = { + icon: Refresh, tooltip: 'Refresh Alarm log table', ariaLabel:'refresh', onClick: () => { + this.setState({ + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.RefreshAlarmLogTable, + }); + }, + }; + + const areFaultsAvailable = this.props.currentAlarmsProperties.rows && this.props.currentAlarmsProperties.rows.length > 0; + const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentAlarmsAction] : [refreshCurrentAlarmsAction]; + + const { panelId: activePanelId } = this.props; + + return ( + <> + <AppBar enableColorOnDark position="static" > + <Tabs indicatorColor="secondary" textColor="inherit" value={activePanelId} onChange={this.onHandleTabChange} aria-label="fault-tabs"> + <Tab aria-label="current-alarms-list-tab" label="Current Alarms" value="CurrentAlarms" /> + <Tab aria-label="alarm-notifications-list-tab" label={`Alarm Notifications (${this.props.faultNotifications.faults.length})`} value="AlarmNotifications" /> + <Tab aria-label="alarm-log-tab" label="Alarm Log" value="AlarmLog" /> + </Tabs> + </AppBar> + { + activePanelId === 'CurrentAlarms' && + <> + <FaultTable stickyHeader tableId="current-alarms-table" idProperty="id" customActionButtons={customActions} columns={[ + // { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: 'severity', title: 'Severity', type: ColumnType.text, width: '140px' }, + { property: 'timestamp', type: ColumnType.text, title: 'Timestamp' }, + { property: 'nodeId', title: 'Node Name', type: ColumnType.text }, + { property: 'counter', title: 'Count', type: ColumnType.numeric, width: '100px' }, + { property: 'objectId', title: 'Object Id', type: ColumnType.text }, + { property: 'problem', title: 'Alarm Type', type: ColumnType.text }, + ]} {...this.props.currentAlarmsProperties} {...this.props.currentAlarmsActions} /> + <RefreshCurrentAlarmsDialog + mode={this.state.refreshCurrentAlarmsEditorMode} + onClose={this.onCloseRefreshCurrentAlarmsDialog} + /> + </> + } + {activePanelId === 'AlarmNotifications' && + + <FaultAlarmNotificationTable stickyHeader tableId="alarm-notifications-table" idProperty="id" defaultSortColumn='timeStamp' defaultSortOrder='desc' rows={this.props.faultNotifications.faults} asynchronus columns={[ + // { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: 'severity', title: 'Severity', width: '140px', type: ColumnType.text }, + { property: 'timeStamp', title: 'Timestamp', type: ColumnType.text }, + { property: 'nodeName', title: 'Node Name', type: ColumnType.text }, + { property: 'counter', title: 'Count', width: '100px', type: ColumnType.numeric }, + { property: 'objectId', title: 'Object Id', type: ColumnType.text }, + { property: 'problem', title: 'Alarm Type', type: ColumnType.text }, + ]} /> + } + + {activePanelId === 'AlarmLog' && + <> + <FaultTable stickyHeader idProperty={'id'} tableId="alarm-log-table" customActionButtons={[refreshAlarmLogAction]} + columns={[ + // { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: 'severity', title: 'Severity', width: '140px' }, + { property: 'timestamp', title: 'Timestamp' }, + { property: 'nodeId', title: 'Node Name' }, + { property: 'counter', title: 'Count', type: ColumnType.numeric, width: '100px' }, + { property: 'objectId', title: 'Object Id' }, + { property: 'problem', title: 'Alarm Type' }, + { property: 'sourceType', title: 'Source', width: '140px' }, + ]} {...this.props.alarmLogEntriesProperties} {...this.props.alarmLogEntriesActions} /> + <RefreshAlarmLogDialog + mode={this.state.refreshAlarmLogEditorMode} + onClose={this.onCloseRefreshAlarmLogDialog} + /> + </> + + } + { + this.state.clearAlarmDialogMode !== ClearStuckAlarmsDialogMode.None && <ClearStuckAlarmsDialog mode={this.state.clearAlarmDialogMode} numberDevices={this.state.stuckAlarms.length} stuckAlarms={this.state.stuckAlarms} onClose={this.onDialogClose} /> + } + </> + ); + } + + public componentDidMount() { + if (this.props.panelId === null) { //set default tab if none is set + this.onToggleTabs('CurrentAlarms'); + } else { + this.onToggleTabs(this.props.panelId); + } + } + + // private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => { + // return ( + // <FontAwesomeIcon icon={faExclamationTriangle} /> + // ); + // }; + + private onCloseRefreshAlarmLogDialog = () => { + this.setState({ + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None, + }); + }; + + private onCloseRefreshCurrentAlarmsDialog = () => { + this.setState({ + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None, + }); + }; +} + +export const FaultApplication = withRouter(connect(mapProps, mapDisp)(FaultApplicationComponent)); +export default FaultApplication; |