Fix broker stats

This commit is contained in:
Thomas Nordquist
2019-07-08 00:19:34 +02:00
parent 77dcbccd5c
commit e3584add7c
3 changed files with 94 additions and 85 deletions

View File

@@ -1,7 +1,8 @@
import * as q from '../../../../backend/src/Model' import * as q from '../../../../backend/src/Model'
import * as React from 'react' import React from 'react'
import Chart from './Chart' import Chart from './Chart'
import { ChartParameters } from '../../reducers/Charts' import { ChartParameters } from '../../reducers/Charts'
import { usePollingToFetchTreeNode } from '../helper/usePollingToFetchTreeNode'
interface Props { interface Props {
tree?: q.Tree<any> tree?: q.Tree<any>
@@ -14,37 +15,6 @@ export function ChartWithTreeNode(props: Props) {
return null return null
} }
const initialTreeNode = tree.findNode(parameters.topic) const treeNode = usePollingToFetchTreeNode(tree, parameters.topic)
const [treeNode, setTreeNode] = React.useState<q.TreeNode<any> | undefined>(initialTreeNode)
usePollingToFetchTreeNode(treeNode, tree, parameters.topic, setTreeNode)
return <Chart treeNode={treeNode} parameters={parameters} /> return <Chart treeNode={treeNode} parameters={parameters} />
} }
/**
* If a node is not available when the plot is shown, keep polling until it has been created
*/
function usePollingToFetchTreeNode(
treeNode: q.TreeNode<any> | undefined,
tree: q.Tree<any>,
path: string,
setTreeNode: React.Dispatch<React.SetStateAction<q.TreeNode<any> | undefined>>
) {
function pollUntilTreeNodeHasBeenFound() {
let intervalTimer: any
if (!treeNode) {
intervalTimer = setInterval(() => {
const node = tree.findNode(path)
if (node) {
setTreeNode(node)
clearInterval(intervalTimer)
}
}, 500)
}
return function cleanup() {
intervalTimer && clearInterval(intervalTimer)
}
}
React.useEffect(pollUntilTreeNodeHasBeenFound, [])
}

View File

@@ -1,13 +1,12 @@
import * as q from '../../../../backend/src/Model' import * as q from '../../../../backend/src/Model'
import * as React from 'react' import React, { useMemo } from 'react'
import { AppState } from '../../reducers' import { AppState } from '../../reducers'
import { Base64Message } from '../../../../backend/src/Model/Base64Message'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { StyleRulesCallback, withStyles, Theme } from '@material-ui/core/styles' import { Theme, withStyles } from '@material-ui/core/styles'
import { TopicViewModel } from '../../model/TopicViewModel' import { TopicViewModel } from '../../model/TopicViewModel'
import { Typography } from '@material-ui/core' import { Typography } from '@material-ui/core'
import { Base64Message } from '../../../../backend/src/Model/Base64Message' import { usePollingToFetchTreeNode } from '../helper/usePollingToFetchTreeNode'
import teal from '@material-ui/core/colors/teal'
const abbreviate = require('number-abbreviate') const abbreviate = require('number-abbreviate')
interface Stats { interface Stats {
@@ -16,10 +15,6 @@ interface Stats {
} }
const styles = (theme: Theme) => ({ const styles = (theme: Theme) => ({
flex: {
display: 'flex',
width: '100%',
},
container: { container: {
width: '100%', width: '100%',
height: '224px', height: '224px',
@@ -34,24 +29,12 @@ interface Props {
tree?: q.Tree<TopicViewModel> tree?: q.Tree<TopicViewModel>
} }
class BrokerStatistics extends React.Component<Props, {}> { function BrokerStatistics(props: Props) {
constructor(props: any) { const { tree, classes } = props
super(props) const sysTopic = usePollingToFetchTreeNode(props.tree, '$SYS')
this.state = {}
}
private renderPair(tree: q.Tree<TopicViewModel>, a: Stats, b: Stats) { return useMemo(() => {
return ( if (!Boolean(sysTopic)) {
<div className={this.props.classes.flex}>
<div style={{ flex: 1 }}>{this.renderStat(tree, a)}</div>
<div style={{ flex: 1 }}>{this.renderStat(tree, b)}</div>
</div>
)
}
public render() {
const { tree, classes } = this.props
if (!tree || !tree.findNode('$SYS/broker/clients/total')) {
return null return null
} }
@@ -94,38 +77,20 @@ class BrokerStatistics extends React.Component<Props, {}> {
}, },
} }
return ( if (!tree) {
<div className={classes.container}>
{this.renderStat(tree, stats.broker)}
{this.renderPair(tree, stats.sent, stats.received)}
{this.renderPair(tree, stats.clients, stats.subscriptions)}
{this.renderPair(tree, stats.sent5m, stats.received5m)}
{this.renderPair(tree, stats.heap, stats.heapMax)}
</div>
)
}
public renderStat(tree: q.Tree<TopicViewModel>, stat: Stats) {
const node = tree.findNode(stat.topic)
if (!node || !node.message) {
return null return null
} }
const str = node.message.value ? Base64Message.toUnicodeString(node.message.value) : ''
let value = node.message && node.message.value ? parseFloat(str) : NaN
value = !isNaN(value) ? abbreviate(value) : str
return ( return (
<div key={stat.title}> <div className={classes.container}>
<Typography> {renderStat(tree, stats.broker)}
<b>{stat.title}</b> {renderPair(tree, stats.sent, stats.received)}
</Typography> {renderPair(tree, stats.clients, stats.subscriptions)}
<Typography style={{ paddingLeft: '8px' }}> {renderPair(tree, stats.sent5m, stats.received5m)}
<i>{value}</i> {renderPair(tree, stats.heap, stats.heapMax)}
</Typography>
</div> </div>
) )
} }, [sysTopic && sysTopic.lastUpdate])
} }
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
@@ -135,3 +100,39 @@ const mapStateToProps = (state: AppState) => {
} }
export default withStyles(styles)(connect(mapStateToProps)(BrokerStatistics)) export default withStyles(styles)(connect(mapStateToProps)(BrokerStatistics))
function renderPair(tree: q.Tree<TopicViewModel>, a: Stats, b: Stats) {
return (
<div
style={{
display: 'flex',
width: '100%',
}}
>
<div style={{ flex: 1 }}>{renderStat(tree, a)}</div>
<div style={{ flex: 1 }}>{renderStat(tree, b)}</div>
</div>
)
}
function renderStat(tree: q.Tree<TopicViewModel>, stat: Stats) {
const node = tree.findNode(stat.topic)
if (!node || !node.message) {
return null
}
const str = node.message.value ? Base64Message.toUnicodeString(node.message.value) : ''
let value = node.message && node.message.value ? parseFloat(str) : NaN
value = !isNaN(value) ? abbreviate(value) : str
return (
<div key={stat.title}>
<Typography>
<b>{stat.title}</b>
</Typography>
<Typography style={{ paddingLeft: '8px' }}>
<i>{value}</i>
</Typography>
</div>
)
}

View File

@@ -0,0 +1,38 @@
import * as q from '../../../../backend/src/Model'
import { useState, useEffect } from 'react'
/**
* If a node is not available when the plot is shown, keep polling until it has been created
*/
export function usePollingToFetchTreeNode(tree: q.Tree<any> | undefined, path: string) {
const [treeNode, setTreeNode] = useState<q.TreeNode<any> | undefined>()
function pollUntilTreeNodeHasBeenFound() {
if (!tree) {
return
}
const initialTreeNode = tree.findNode(path)
if (initialTreeNode) {
setTreeNode(initialTreeNode)
return
}
let intervalTimer: any
if (!treeNode) {
intervalTimer = setInterval(() => {
const node = tree.findNode(path)
if (node) {
setTreeNode(node)
clearInterval(intervalTimer)
}
}, 500)
}
return function cleanup() {
intervalTimer && clearInterval(intervalTimer)
}
}
useEffect(pollUntilTreeNodeHasBeenFound, [tree])
return treeNode
}