Add connection health indicator

This commit is contained in:
Thomas Nordquist
2019-02-18 20:47:51 +01:00
parent 4b5d023d19
commit 55fbc642c4
8 changed files with 106 additions and 6 deletions

View File

@@ -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,
}) })
} }

View 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))

View File

@@ -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 />&nbsp;&nbsp;Abort
</Button> </Button>
) )
} }

View File

@@ -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>
) )

View File

@@ -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,

View File

@@ -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,
} }

View File

@@ -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()
}) })

View File

@@ -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()
} }
} }