Add connection health indicator
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
|||||||
} from '../../../events'
|
} from '../../../events'
|
||||||
import { AppState } from '../reducers'
|
import { AppState } from '../reducers'
|
||||||
import { Dispatch } from 'redux'
|
import { Dispatch } from 'redux'
|
||||||
import { MqttOptions } from '../../../backend/src/DataSource'
|
import { MqttOptions, DataSourceState } from '../../../backend/src/DataSource'
|
||||||
import { showTree } from './Tree'
|
import { showTree } from './Tree'
|
||||||
import { TopicViewModel } from '../TopicViewModel'
|
import { TopicViewModel } from '../TopicViewModel'
|
||||||
import { showError } from './Global'
|
import { showError } from './Global'
|
||||||
@@ -31,6 +31,27 @@ export const connect = (options: MqttOptions, connectionId: string) => (dispatch
|
|||||||
dispatch(showError(dataSourceState.error))
|
dispatch(showError(dataSourceState.error))
|
||||||
dispatch(disconnect())
|
dispatch(disconnect())
|
||||||
}
|
}
|
||||||
|
dispatch(updateHealth(dataSourceState))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateHealth = (dataSourceState: DataSourceState) => (dispatch: Dispatch<any>, getState: () => AppState) => {
|
||||||
|
let state
|
||||||
|
if (dataSourceState.connecting) {
|
||||||
|
state = 'connecting'
|
||||||
|
} else if (!dataSourceState.connected) {
|
||||||
|
state = 'offline'
|
||||||
|
} else if (dataSourceState.connected) {
|
||||||
|
state = 'online'
|
||||||
|
} else {
|
||||||
|
state = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(state)
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ActionTypes.CONNECTION_SET_HEALTH,
|
||||||
|
health: state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
54
app/src/components/ConnectionHealthIndicator.tsx
Normal file
54
app/src/components/ConnectionHealthIndicator.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import DeviceHubOutlined from '@material-ui/icons/DeviceHubOutlined'
|
||||||
|
import { AppState } from '../reducers'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { ConnectionHealth } from '../reducers/Connection'
|
||||||
|
import { green, orange, red } from '@material-ui/core/colors'
|
||||||
|
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
|
||||||
|
import { Tooltip } from '@material-ui/core';
|
||||||
|
|
||||||
|
const styles: StyleRulesCallback = theme => ({
|
||||||
|
offline: {
|
||||||
|
color: red[700],
|
||||||
|
},
|
||||||
|
online: {
|
||||||
|
color: green[500],
|
||||||
|
},
|
||||||
|
connecting: {
|
||||||
|
color: orange[600],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
classes: any
|
||||||
|
connected: boolean
|
||||||
|
health?: ConnectionHealth
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectionHealthIndicator extends React.Component<Props, {}> {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { classes, health, connected } = this.props
|
||||||
|
if (!health || !connected) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={`Connection health "${health}"`}>
|
||||||
|
<DeviceHubOutlined className={classes[health]} />
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state: AppState) => {
|
||||||
|
return {
|
||||||
|
health: state.connection.health,
|
||||||
|
connected: state.connection.connected || state.connection.connecting,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(withStyles(styles)(ConnectionHealthIndicator))
|
||||||
@@ -14,7 +14,6 @@ import { StyleRulesCallback, Theme, withStyles } from '@material-ui/core/styles'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
CircularProgress,
|
|
||||||
FormControl,
|
FormControl,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Grid,
|
Grid,
|
||||||
@@ -26,6 +25,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
|
import ConnectionHealthIndicator from '../ConnectionHealthIndicator'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
connection: ConnectionOptions
|
connection: ConnectionOptions
|
||||||
@@ -284,7 +284,7 @@ class ConnectionSettings extends React.Component<Props, State> {
|
|||||||
if (this.props.connecting) {
|
if (this.props.connecting) {
|
||||||
return (
|
return (
|
||||||
<Button variant="contained" color="primary" className={classes.button} onClick={actions.disconnect}>
|
<Button variant="contained" color="primary" className={classes.button} onClick={actions.disconnect}>
|
||||||
<CircularProgress size={22} style={{ marginRight: '10px' }} color="secondary" /> Abort
|
<ConnectionHealthIndicator /> Abort
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { connect } from 'react-redux'
|
|||||||
import { connectionActions, settingsActions } from '../actions'
|
import { connectionActions, settingsActions } from '../actions'
|
||||||
import { fade } from '@material-ui/core/styles/colorManipulator'
|
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||||
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
|
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
|
||||||
|
import ConnectionHealthIndicator from './ConnectionHealthIndicator';
|
||||||
|
|
||||||
const styles: StyleRulesCallback = theme => ({
|
const styles: StyleRulesCallback = theme => ({
|
||||||
title: {
|
title: {
|
||||||
@@ -99,6 +100,7 @@ class TitleBar extends React.Component<Props, {}> {
|
|||||||
<Button style={{ margin: 'auto 8px auto auto' }} onClick={actions.connection.disconnect}>
|
<Button style={{ margin: 'auto 8px auto auto' }} onClick={actions.connection.disconnect}>
|
||||||
Disconnect <CloudOff style={{ marginRight: '8px', paddingLeft: '8px' }}/>
|
Disconnect <CloudOff style={{ marginRight: '8px', paddingLeft: '8px' }}/>
|
||||||
</Button>
|
</Button>
|
||||||
|
<ConnectionHealthIndicator />
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as q from '../../../backend/src/Model'
|
|||||||
import { MqttOptions } from '../../../backend/src/DataSource'
|
import { MqttOptions } from '../../../backend/src/DataSource'
|
||||||
import { TopicViewModel } from '../TopicViewModel'
|
import { TopicViewModel } from '../TopicViewModel'
|
||||||
|
|
||||||
|
export type ConnectionHealth = 'offline' | 'online' | 'connecting'
|
||||||
export interface ConnectionState {
|
export interface ConnectionState {
|
||||||
host?: string
|
host?: string
|
||||||
tree?: q.Tree<TopicViewModel>
|
tree?: q.Tree<TopicViewModel>
|
||||||
@@ -12,6 +13,7 @@ export interface ConnectionState {
|
|||||||
error?: string
|
error?: string
|
||||||
connected: boolean
|
connected: boolean
|
||||||
connecting: boolean
|
connecting: boolean
|
||||||
|
health?: ConnectionHealth
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Action = SetConnecting | SetConnected | SetDisconnected | ShowError
|
export type Action = SetConnecting | SetConnected | SetDisconnected | ShowError
|
||||||
@@ -21,6 +23,7 @@ export enum ActionTypes {
|
|||||||
CONNECTION_SET_CONNECTED = 'CONNECTION_SET_CONNECTED',
|
CONNECTION_SET_CONNECTED = 'CONNECTION_SET_CONNECTED',
|
||||||
CONNECTION_SET_DISCONNECTED = 'CONNECTION_SET_DISCONNECTED',
|
CONNECTION_SET_DISCONNECTED = 'CONNECTION_SET_DISCONNECTED',
|
||||||
CONNECTION_SET_SHOW_ERROR = 'CONNECTION_SET_SHOW_ERROR',
|
CONNECTION_SET_SHOW_ERROR = 'CONNECTION_SET_SHOW_ERROR',
|
||||||
|
CONNECTION_SET_HEALTH = 'CONNECTION_SET_HEALTH',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetConnecting {
|
export interface SetConnecting {
|
||||||
@@ -38,6 +41,11 @@ export interface SetDisconnected {
|
|||||||
type: ActionTypes.CONNECTION_SET_DISCONNECTED
|
type: ActionTypes.CONNECTION_SET_DISCONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetHealth {
|
||||||
|
health: ConnectionHealth
|
||||||
|
type: ActionTypes.CONNECTION_SET_DISCONNECTED
|
||||||
|
}
|
||||||
|
|
||||||
export interface ShowError {
|
export interface ShowError {
|
||||||
type: ActionTypes.CONNECTION_SET_SHOW_ERROR
|
type: ActionTypes.CONNECTION_SET_SHOW_ERROR
|
||||||
error?: Error
|
error?: Error
|
||||||
@@ -46,6 +54,7 @@ export interface ShowError {
|
|||||||
const initialState: ConnectionState = {
|
const initialState: ConnectionState = {
|
||||||
connected: false,
|
connected: false,
|
||||||
connecting: false,
|
connecting: false,
|
||||||
|
health: 'disconnected',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const connectionReducer = createReducer(initialState, {
|
export const connectionReducer = createReducer(initialState, {
|
||||||
@@ -53,6 +62,7 @@ export const connectionReducer = createReducer(initialState, {
|
|||||||
CONNECTION_SET_CONNECTED: setConnected,
|
CONNECTION_SET_CONNECTED: setConnected,
|
||||||
CONNECTION_SET_DISCONNECTED: setDisconnected,
|
CONNECTION_SET_DISCONNECTED: setDisconnected,
|
||||||
CONNECTION_SET_SHOW_ERROR: showError,
|
CONNECTION_SET_SHOW_ERROR: showError,
|
||||||
|
CONNECTION_SET_HEALTH: setHealth,
|
||||||
})
|
})
|
||||||
|
|
||||||
function setConnecting(state: ConnectionState, action: SetConnecting): ConnectionState {
|
function setConnecting(state: ConnectionState, action: SetConnecting): ConnectionState {
|
||||||
@@ -64,6 +74,13 @@ function setConnecting(state: ConnectionState, action: SetConnecting): Connectio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setHealth(state: ConnectionState, action: SetHealth): ConnectionState {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
health: action.health,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setConnected(state: ConnectionState, action: SetConnected): ConnectionState {
|
function setConnected(state: ConnectionState, action: SetConnected): ConnectionState {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ export class DataSourceStateMachine {
|
|||||||
|
|
||||||
public setError(error: Error) {
|
public setError(error: Error) {
|
||||||
this.state = {
|
this.state = {
|
||||||
|
...this.state,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
connected: false,
|
// connected: false,
|
||||||
connecting: false,
|
// connecting: false,
|
||||||
}
|
}
|
||||||
this.onUpdate.dispatch(this.state)
|
this.onUpdate.dispatch(this.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConnecting() {
|
public setConnecting() {
|
||||||
this.state = {
|
this.state = {
|
||||||
error: undefined,
|
...this.state,
|
||||||
connected: false,
|
connected: false,
|
||||||
connecting: true,
|
connecting: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ export class MqttSource implements DataSource<MqttOptions> {
|
|||||||
this.stateMachine.setConnected(false)
|
this.stateMachine.setConnected(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
client.on('end', () => {
|
||||||
|
this.stateMachine.setConnected(false)
|
||||||
|
})
|
||||||
|
|
||||||
client.on('reconnect', () => {
|
client.on('reconnect', () => {
|
||||||
this.stateMachine.setConnecting()
|
this.stateMachine.setConnecting()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export class ConnectionManager {
|
|||||||
if (connection) {
|
if (connection) {
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
delete this.connections[hash]
|
delete this.connections[hash]
|
||||||
|
connection.stateMachine.onUpdate.removeAllListeners()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user