import { connect } from 'react-redux'; import { Controls as PlayerControls, connectPlayer } from 'Player'; import { AreaChart, Area, ComposedChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, CartesianGrid, Label, } from 'recharts'; import { Checkbox } from 'UI'; import { durationFromMsFormatted } from 'App/date'; import { formatBytes } from 'App/utils'; import stl from './performance.css'; import BottomBlock from '../BottomBlock'; import InfoLine from '../BottomBlock/InfoLine'; const CPU_VISUAL_OFFSET = 10; const FPS_COLOR = '#C5E5E7'; const FPS_STROKE_COLOR = "#92C7CA"; const FPS_LOW_COLOR = "pink"; const FPS_VERY_LOW_COLOR = "red"; const CPU_COLOR = "#A8D1DE"; const CPU_STROKE_COLOR = "#69A5B8"; const USED_HEAP_COLOR = '#A9ABDC'; const USED_HEAP_STROKE_COLOR = "#8588CF"; const TOTAL_HEAP_STROKE_COLOR = '#4A4EB7'; const NODES_COUNT_COLOR = "#C6A9DC"; const NODES_COUNT_STROKE_COLOR = "#7360AC"; const HIDDEN_SCREEN_COLOR = "#CCC"; const CURSOR_COLOR = "#394EFF"; const Gradient = ({ color, id }) => ( ); const TOTAL_HEAP = "Allocated Heap"; const USED_HEAP = "JS Heap"; const FPS = "Framerate"; const CPU = "CPU Load"; const NODES_COUNT = "Nodes Сount"; const FPSTooltip = ({ active, payload }) => { if (!active || payload.length < 3) { return null; } if (payload[0].value === null) { return (
{"Page is not active. User switched the tab or hid the window."}
); } let style; if (payload[1].value != null && payload[1].value > 0) { style = { color: FPS_LOW_COLOR }; } if (payload[2].value != null && payload[2].value > 0) { style = { color: FPS_VERY_LOW_COLOR }; } return (
{`${ FPS }: `} { Math.trunc(payload[0].value) }
); }; const CPUTooltip = ({ active, payload }) => { if (!active || payload.length < 1 || payload[0].value === null) { return null; } return (
{`${ CPU }: `} { payload[0].value - CPU_VISUAL_OFFSET } {"%"}
); }; const HeapTooltip = ({ active, payload}) => { if (!active || payload.length < 2) return null; return (

{`${ TOTAL_HEAP }: `} { formatBytes(payload[0].value)}

{`${ USED_HEAP }: `} { formatBytes(payload[1].value)}

); } const NodesCountTooltip = ({ active, payload} ) => { if (!active || payload.length === 0) return null; return (

{`${ NODES_COUNT }: `} { payload[0].value }

); } const TICKS_COUNT = 10; function generateTicks(data: Array): Array { if (data.length === 0) return []; console.log(data, data[0]) const minTime = data[0].time; const maxTime = data[data.length-1].time; const ticks = []; const tickGap = (maxTime - minTime) / (TICKS_COUNT + 1); for (let i = 0; i < TICKS_COUNT; i++) { const tick = tickGap * (i + 1) + minTime; ticks.push(tick); } return ticks; } const LOW_FPS = 30; const VERY_LOW_FPS = 20; const LOW_FPS_MARKER_VALUE = 5; const HIDDEN_SCREEN_MARKER_VALUE = 20; function addFpsMetadata(data) { return data.map((point, i) => { let fpsVeryLowMarker = null; let fpsLowMarker = null; let hiddenScreenMarker = 0; if (point.fps != null) { fpsVeryLowMarker = 0; fpsLowMarker = 0; if (point.fps < VERY_LOW_FPS) { fpsVeryLowMarker = LOW_FPS_MARKER_VALUE; } else if (point.fps < LOW_FPS) { fpsLowMarker = LOW_FPS_MARKER_VALUE; } } if (point.fps == null || (i > 0 && data[i - 1].fps == null) //|| //(i < data.length-1 && data[i + 1].fps == null) ) { hiddenScreenMarker = HIDDEN_SCREEN_MARKER_VALUE; } if (point.cpu != null) { point.cpu += CPU_VISUAL_OFFSET; } return { ...point, fpsLowMarker, fpsVeryLowMarker, hiddenScreenMarker, } }); } @connect(state => ({ userDeviceHeapSize: state.getIn([ "sessions", "current", "userDeviceHeapSize" ]), userDeviceMemorySize: state.getIn([ "sessions", "current", "userDeviceMemorySize" ]), })) export default class Performance extends React.PureComponent { _timeTicks = generateTicks(this.props.performanceChartData) _data = addFpsMetadata(this.props.performanceChartData) // state = { // totalHeap: false, // usedHeap: true, // fps: true, // } // onCheckboxClick = (e, { name, checked }) => this.setState({ [ name ]: checked }) onDotClick = ({ index }) => { const point = this._data[index]; if (!!point) { PlayerControls.jump(point.time); } } onChartClick = (e) => { if (e === null) return; const { activeTooltipIndex } = e; const point = this._data[activeTooltipIndex]; if (!!point) { PlayerControls.jump(point.time); } } render() { const { userDeviceHeapSize, userDeviceMemorySize, connType, connBandwidth, performanceChartTime, avaliability = {}, } = this.props; const { fps, cpu, heap, nodes } = avaliability; const avaliableCount = [ fps, cpu, heap, nodes ].reduce((c, av) => av ? c + 1 : c, 0); const height = avaliableCount === 0 ? "0" : `${100 / avaliableCount}%`; return ( {/* */} = 1000 ? `${ connBandwidth / 1000 } Mbps` : `${ connBandwidth } Kbps` } display={ connBandwidth != null } /> { fps && {/* */} {/* */} {/* */} {/* */} } { cpu && {/* */} ""} domain={[0, 'dataMax']} ticks={this._timeTicks} > } { heap && {/* */} ""} // tick={false} + this._timeTicks to cartesian array domain={[0, 'dataMax']} ticks={this._timeTicks} > max*1.2]} /> } { nodes && {/* */} ""} domain={[0, 'dataMax']} ticks={this._timeTicks} > max*1.2]} /> } ); } } export const ConnectedPerformance = connectPlayer(state => ({ performanceChartTime: state.performanceChartTime, performanceChartData: state.performanceChartData, connType: state.connType, connBandwidth: state.connBandwidth, avaliability: state.performanceAvaliability, }))(Performance);