Merge pull request #1100 from chaotixkilla/fix-dashboard-network-performance
Fix missing machine performance on machine overview page
This commit is contained in:
commit
045f430766
8 changed files with 75 additions and 55 deletions
|
|
@ -110,11 +110,17 @@ function getMachine (machineId, config) {
|
||||||
else return toMachineObject(r)
|
else return toMachineObject(r)
|
||||||
})
|
})
|
||||||
|
|
||||||
return Promise.all([queryMachine, dbm.machineEvents(), config])
|
return Promise.all([queryMachine, dbm.machineEvents(), config, getNetworkHeartbeatByDevice(machineId), getNetworkPerformanceByDevice(machineId)])
|
||||||
.then(([machine, events, config]) => {
|
.then(([machine, events, config, heartbeat, performance]) => {
|
||||||
const pings = checkPings([machine])
|
const pings = checkPings([machine])
|
||||||
|
const mergedMachine = {
|
||||||
|
...machine,
|
||||||
|
responseTime: _.get('responseTime', heartbeat),
|
||||||
|
packetLoss: _.get('packetLoss', heartbeat),
|
||||||
|
downloadSpeed: _.get('downloadSpeed', performance),
|
||||||
|
}
|
||||||
|
|
||||||
return addName(pings, events, config)(machine)
|
return addName(pings, events, config)(mergedMachine)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +240,20 @@ function getNetworkHeartbeat () {
|
||||||
.then(res => _.map(_.mapKeys(_.camelCase))(res))
|
.then(res => _.map(_.mapKeys(_.camelCase))(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNetworkPerformanceByDevice (deviceId) {
|
||||||
|
const sql = `SELECT device_id, download_speed FROM machine_network_performance WHERE device_id = $1`
|
||||||
|
return db.manyOrNone(sql, [deviceId])
|
||||||
|
.then(res => _.mapKeys(_.camelCase, _.find(it => it.device_id === deviceId, res)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNetworkHeartbeatByDevice (deviceId) {
|
||||||
|
const sql = `SELECT AVG(average_response_time) AS response_time, AVG(average_packet_loss) AS packet_loss, device_id
|
||||||
|
FROM machine_network_heartbeat WHERE device_id = $1
|
||||||
|
GROUP BY device_id`
|
||||||
|
return db.manyOrNone(sql, [deviceId])
|
||||||
|
.then(res => _.mapKeys(_.camelCase, _.find(it => it.device_id === deviceId, res)))
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getMachineName,
|
getMachineName,
|
||||||
getMachines,
|
getMachines,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import React, { memo, useState } from 'react'
|
||||||
|
|
||||||
import { ConfirmDialog } from 'src/components/ConfirmDialog'
|
import { ConfirmDialog } from 'src/components/ConfirmDialog'
|
||||||
import ActionButton from 'src/components/buttons/ActionButton'
|
import ActionButton from 'src/components/buttons/ActionButton'
|
||||||
|
import { H3 } from 'src/components/typography'
|
||||||
import { ReactComponent as EditReversedIcon } from 'src/styling/icons/button/edit/white.svg'
|
import { ReactComponent as EditReversedIcon } from 'src/styling/icons/button/edit/white.svg'
|
||||||
import { ReactComponent as EditIcon } from 'src/styling/icons/button/edit/zodiac.svg'
|
import { ReactComponent as EditIcon } from 'src/styling/icons/button/edit/zodiac.svg'
|
||||||
import { ReactComponent as RebootReversedIcon } from 'src/styling/icons/button/reboot/white.svg'
|
import { ReactComponent as RebootReversedIcon } from 'src/styling/icons/button/reboot/white.svg'
|
||||||
|
|
@ -62,12 +63,6 @@ const getState = machineEventsLazy =>
|
||||||
JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}')
|
JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}')
|
||||||
.state
|
.state
|
||||||
|
|
||||||
const Label = ({ children }) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
return <div className={classes.label}>{children}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const MachineActions = memo(({ machine, onActionSuccess }) => {
|
const MachineActions = memo(({ machine, onActionSuccess }) => {
|
||||||
const [action, setAction] = useState({ command: null })
|
const [action, setAction] = useState({ command: null })
|
||||||
const [preflightOptions, setPreflightOptions] = useState({})
|
const [preflightOptions, setPreflightOptions] = useState({})
|
||||||
|
|
@ -115,7 +110,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Label>Actions</Label>
|
<H3>Actions</H3>
|
||||||
<div className={classes.stack}>
|
<div className={classes.stack}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ import styles from './Cassettes.styles'
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const widthsByNumberOfCassettes = {
|
const widthsByNumberOfCassettes = {
|
||||||
2: { cashbox: 116, cassette: 280, cassetteGraph: 80, editWidth: 174 },
|
2: { cashbox: 203, cassette: 280, cassetteGraph: 80, editWidth: 87 },
|
||||||
3: { cashbox: 106, cassette: 200, cassetteGraph: 60, editWidth: 145 },
|
3: { cashbox: 164, cassette: 200, cassetteGraph: 60, editWidth: 87 },
|
||||||
4: { cashbox: 106, cassette: 164, cassetteGraph: 40, editWidth: 90 }
|
4: { cashbox: 131, cassette: 158, cassetteGraph: 40, editWidth: 87 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const ValidationSchema = Yup.object().shape({
|
const ValidationSchema = Yup.object().shape({
|
||||||
|
|
@ -159,7 +159,7 @@ const CashCassettes = ({ machine, config, refetchData, bills }) => {
|
||||||
elements.push({
|
elements.push({
|
||||||
name: 'edit',
|
name: 'edit',
|
||||||
header: 'Edit',
|
header: 'Edit',
|
||||||
width: 87,
|
width: widthsByNumberOfCassettes[numberOfCassettes].editWidth,
|
||||||
view: () => {
|
view: () => {
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ const getOverridesFields = currency => {
|
||||||
{
|
{
|
||||||
name: 'fixedFee',
|
name: 'fixedFee',
|
||||||
display: 'Fixed fee',
|
display: 'Fixed fee',
|
||||||
width: 144,
|
width: 155,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: currency
|
suffix: currency
|
||||||
|
|
@ -57,7 +57,7 @@ const getOverridesFields = currency => {
|
||||||
{
|
{
|
||||||
name: 'minimumTx',
|
name: 'minimumTx',
|
||||||
display: 'Minimun Tx',
|
display: 'Minimun Tx',
|
||||||
width: 144,
|
width: 155,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: currency
|
suffix: currency
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Label3, P } from 'src/components/typography'
|
import { Label3, P } from 'src/components/typography'
|
||||||
|
import { modelPrettifier } from 'src/utils/machine'
|
||||||
import { formatDate } from 'src/utils/timezones'
|
import { formatDate } from 'src/utils/timezones'
|
||||||
|
|
||||||
import styles from '../Machines.styles'
|
import styles from '../Machines.styles'
|
||||||
|
|
@ -21,7 +22,7 @@ const Details = ({ data, timezone }) => {
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Machine model</Label3>
|
<Label3 className={classes.label3}>Machine model</Label3>
|
||||||
<P>{data.model}</P>
|
<P>{modelPrettifier[data.model]}</P>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Software version</Label3>
|
<Label3 className={classes.label3}>Software version</Label3>
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,21 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { differenceInSeconds } from 'date-fns/fp'
|
import { formatDistance } from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Status } from 'src/components/Status'
|
import { Status } from 'src/components/Status'
|
||||||
import MachineActions from 'src/components/machineActions/MachineActions'
|
import MachineActions from 'src/components/machineActions/MachineActions'
|
||||||
import { H3, Label3, P } from 'src/components/typography'
|
import { H3, Label1, P } from 'src/components/typography'
|
||||||
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard.js'
|
import CopyToClipboard from 'src/pages/Transactions/CopyToClipboard.js'
|
||||||
|
|
||||||
import styles from '../Machines.styles'
|
import styles from '../Machines.styles'
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const makeLastPing = lastPing => {
|
|
||||||
if (!lastPing) return null
|
|
||||||
const secondsAgo = differenceInSeconds(lastPing, new Date())
|
|
||||||
if (secondsAgo < 60) {
|
|
||||||
return `${secondsAgo} ${secondsAgo === 1 ? 'second' : 'seconds'} ago`
|
|
||||||
}
|
|
||||||
if (secondsAgo < 3600) {
|
|
||||||
const minutes = Math.round(secondsAgo / 60)
|
|
||||||
return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`
|
|
||||||
}
|
|
||||||
if (secondsAgo < 3600 * 24) {
|
|
||||||
const hours = Math.round(secondsAgo / 3600)
|
|
||||||
return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`
|
|
||||||
}
|
|
||||||
const days = Math.round(secondsAgo / 3600 / 24)
|
|
||||||
return `${days} ${days === 1 ? 'day' : 'days'} ago`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Overview = ({ data, onActionSuccess }) => {
|
const Overview = ({ data, onActionSuccess }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={classes.contentContainer}>
|
||||||
<div className={classes.row}>
|
<div className={classes.row}>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<H3>{data.name}</H3>
|
<H3>{data.name}</H3>
|
||||||
|
|
@ -41,20 +23,32 @@ const Overview = ({ data, onActionSuccess }) => {
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.row}>
|
<div className={classes.row}>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Status</Label3>
|
<Label1 className={classes.label3}>Status</Label1>
|
||||||
{data && data.statuses ? <Status status={data.statuses[0]} /> : null}
|
{data && data.statuses ? <Status status={data.statuses[0]} /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.row}>
|
<div className={classes.row}>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Last ping</Label3>
|
<Label1 className={classes.label3}>Ping</Label1>
|
||||||
<P>{makeLastPing(data.lastPing)}</P>
|
<P noMargin>
|
||||||
|
{data.responseTime
|
||||||
|
? new BigNumber(data.responseTime).toFixed(3).toString() + ' ms'
|
||||||
|
: 'unavailable'}
|
||||||
|
</P>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className={classes.row}>
|
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Network speed</Label3>
|
<Label1 className={classes.label3}>Last ping</Label1>
|
||||||
<P>
|
<P noMargin>
|
||||||
|
{data.lastPing
|
||||||
|
? formatDistance(new Date(data.lastPing), new Date(), {
|
||||||
|
addSuffix: true
|
||||||
|
})
|
||||||
|
: 'unknown'}
|
||||||
|
</P>
|
||||||
|
</div>
|
||||||
|
<div className={classes.rowItem}>
|
||||||
|
<Label1 className={classes.label3}>Network speed</Label1>
|
||||||
|
<P noMargin>
|
||||||
{data.downloadSpeed
|
{data.downloadSpeed
|
||||||
? new BigNumber(data.downloadSpeed).toFixed(4).toString() +
|
? new BigNumber(data.downloadSpeed).toFixed(4).toString() +
|
||||||
' MB/s'
|
' MB/s'
|
||||||
|
|
@ -62,22 +56,22 @@ const Overview = ({ data, onActionSuccess }) => {
|
||||||
</P>
|
</P>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.row}>
|
|
||||||
<MachineActions
|
|
||||||
machine={data}
|
|
||||||
onActionSuccess={onActionSuccess}></MachineActions>
|
|
||||||
</div>
|
|
||||||
<div className={classes.row}>
|
<div className={classes.row}>
|
||||||
<div className={classes.rowItem}>
|
<div className={classes.rowItem}>
|
||||||
<Label3 className={classes.label3}>Device ID</Label3>
|
<Label1 className={classes.label3}>Device ID</Label1>
|
||||||
<P>
|
<P noMargin>
|
||||||
<CopyToClipboard buttonClassname={classes.copyToClipboard}>
|
<CopyToClipboard buttonClassname={classes.copyToClipboard}>
|
||||||
{data.deviceId}
|
{data.deviceId}
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</P>
|
</P>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div className={classes.row}>
|
||||||
|
<MachineActions
|
||||||
|
machine={data}
|
||||||
|
onActionSuccess={onActionSuccess}></MachineActions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ const styles = {
|
||||||
},
|
},
|
||||||
label3: {
|
label3: {
|
||||||
color: comet,
|
color: comet,
|
||||||
marginTop: 0
|
marginTop: 0,
|
||||||
|
fontSize: 12
|
||||||
},
|
},
|
||||||
row: {
|
row: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
@ -51,6 +52,14 @@ const styles = {
|
||||||
sidebarContainer: {
|
sidebarContainer: {
|
||||||
height: 400,
|
height: 400,
|
||||||
overflowY: 'auto'
|
overflowY: 'auto'
|
||||||
|
},
|
||||||
|
contentContainer: {
|
||||||
|
'& > *': {
|
||||||
|
marginTop: 26
|
||||||
|
},
|
||||||
|
'& > *:first-child': {
|
||||||
|
marginTop: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
const modelPrettifier = {
|
const modelPrettifier = {
|
||||||
douro1: 'Douro',
|
douro1: 'Douro',
|
||||||
sintra: 'Sintra',
|
sintra: 'Sintra',
|
||||||
gaia: 'Gaia'
|
gaia: 'Gaia',
|
||||||
|
tejo: 'Tejo'
|
||||||
}
|
}
|
||||||
|
|
||||||
export { modelPrettifier }
|
export { modelPrettifier }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue