stacking graphs
This commit is contained in:
parent
0a6a74765d
commit
875ca384a0
@ -19,7 +19,7 @@ module.exports = (server) => ({
|
||||
when: new Date().getTime(),
|
||||
cpu: Math.random() * 100
|
||||
});
|
||||
}, 400);
|
||||
}, 45);
|
||||
|
||||
cdm[id] = {
|
||||
interval,
|
||||
|
@ -2,11 +2,29 @@ const take = require('lodash.take');
|
||||
|
||||
const actions = {
|
||||
'UPDATE_STATS': (state, action) => {
|
||||
const data = [action.payload].concat(state[action.subscription] || []);
|
||||
const data = (state[action.subscription] || {
|
||||
cpu: [],
|
||||
mem: [],
|
||||
disk: []
|
||||
});
|
||||
|
||||
const newData = ['cpu', 'mem', 'disk'].reduce((sum, key) => {
|
||||
const item = {
|
||||
...action.payload.stats[key],
|
||||
when: action.payload.when
|
||||
};
|
||||
|
||||
const prepended = [item].concat(data[key]);
|
||||
|
||||
return {
|
||||
...sum,
|
||||
[key]: take(prepended, state.windowSize)
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state,
|
||||
[action.subscription]: take(data, state.windowSize)
|
||||
[action.subscription]: newData
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -37,7 +55,7 @@ module.exports.subscribe = (id) => (dispatch, getState) => {
|
||||
});
|
||||
|
||||
return dispatch({
|
||||
action: 'SUBSCRIBE',
|
||||
type: 'SUBSCRIBE',
|
||||
payload: p
|
||||
});
|
||||
};
|
||||
@ -58,7 +76,7 @@ module.exports.unsubscribe = (id) => (dispatch, getState) => {
|
||||
});
|
||||
|
||||
return dispatch({
|
||||
action: 'UNSUBSCRIBE',
|
||||
type: 'UNSUBSCRIBE',
|
||||
payload: p
|
||||
});
|
||||
};
|
@ -1,144 +0,0 @@
|
||||
const buildArray = require('build-array');
|
||||
const Chart = require('chart.js');
|
||||
const React = require('react');
|
||||
|
||||
// borderSkipped
|
||||
// patch `.draw` to support `borderSkipped`:
|
||||
// Chart.elements.Rectangle.prototype.draw = function() {
|
||||
// var ctx = this._chart.ctx;
|
||||
// var vm = this._view;
|
||||
//
|
||||
// var halfWidth = vm.width / 2,
|
||||
// leftX = vm.x - halfWidth,
|
||||
// rightX = vm.x + halfWidth,
|
||||
// top = vm.base - (vm.base - vm.y),
|
||||
// halfStroke = vm.borderWidth / 2;
|
||||
//
|
||||
// // Canvas doesn't allow us to stroke inside the width so we can
|
||||
// // adjust the sizes to fit if we're setting a stroke on the line
|
||||
// if (vm.borderWidth) {
|
||||
// leftX += halfStroke;
|
||||
// rightX -= halfStroke;
|
||||
// top += halfStroke;
|
||||
// }
|
||||
//
|
||||
// ctx.beginPath();
|
||||
// ctx.fillStyle = vm.backgroundColor;
|
||||
// ctx.strokeStyle = vm.borderColor;
|
||||
// ctx.lineWidth = vm.borderWidth;
|
||||
//
|
||||
// var borderSkipped = !Array.isArray(vm.borderSkipped)
|
||||
// ? [vm.borderSkipped]
|
||||
// : vm.borderSkipped;
|
||||
//
|
||||
// // Corner points, from bottom-left to bottom-right clockwise
|
||||
// // | 1 2 |
|
||||
// // | 0 3 |
|
||||
// var corners = [
|
||||
// [leftX, vm.base, (borderSkipped.indexOf('bottom') >= 0), 'bottom'],
|
||||
// [leftX, top, (borderSkipped.indexOf('left') >= 0), 'left'],
|
||||
// [rightX, top, (borderSkipped.indexOf('top') >= 0), 'top'],
|
||||
// [rightX, vm.base, (borderSkipped.indexOf('right') >= 0), 'right']
|
||||
// ];
|
||||
//
|
||||
// function cornerAt(index) {
|
||||
// return corners[index % 4];
|
||||
// }
|
||||
//
|
||||
// // Draw rectangle from 'startCorner'
|
||||
// var corner = cornerAt(0);
|
||||
// ctx.moveTo(corner[0], corner[1]);
|
||||
//
|
||||
// for (var i = 1; i < 5; i++) {
|
||||
// corner = cornerAt(i);
|
||||
// ctx.lineTo(corner[0], corner[1]);
|
||||
//
|
||||
// if (!corner[2]) {
|
||||
// ctx.stroke();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// console.log(corners);
|
||||
//
|
||||
// ctx.fill();
|
||||
// };
|
||||
|
||||
module.exports = React.createClass({
|
||||
ref: function(name) {
|
||||
this._refs = this._refs || {};
|
||||
|
||||
return (el) => {
|
||||
this._refs[name] = el;
|
||||
};
|
||||
},
|
||||
fromData: function(data) {
|
||||
return (data || []).map((d) => {
|
||||
return d.cpu;
|
||||
});
|
||||
},
|
||||
componentDidMount: function() {
|
||||
const {
|
||||
data = [],
|
||||
bg,
|
||||
border
|
||||
} = this.props;
|
||||
|
||||
const bars = this.fromData(data);
|
||||
|
||||
this._chart = new Chart(this._refs.component, {
|
||||
type: 'bar',
|
||||
options: {
|
||||
elements: {
|
||||
rectangle: {
|
||||
borderSkipped: ['bottom', 'left', 'right']
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: false
|
||||
}],
|
||||
yAxes: [{
|
||||
display: false
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
data: {
|
||||
labels: buildArray(bars.length).map((v, i) => ''),
|
||||
datasets: [{
|
||||
borderWidth: 1,
|
||||
borderColor: border,
|
||||
backgroundColor: bg,
|
||||
data: bars
|
||||
}]
|
||||
}
|
||||
});
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
const {
|
||||
data = [],
|
||||
bg,
|
||||
border
|
||||
} = this.props;
|
||||
|
||||
const bars = this.fromData(data);
|
||||
|
||||
this._chart.data.labels = buildArray(bars.length).map((v, i) => '');
|
||||
this._chart.data.datasets[0].backgroundColor = bg;
|
||||
this._chart.data.datasets[0].borderColor = border;
|
||||
this._chart.data.datasets[0].data = bars;
|
||||
|
||||
this._chart.update(0);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<canvas
|
||||
ref={this.ref('component')}
|
||||
width='400'
|
||||
height='400'
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
71
spikes/graphs-matrix/chartjs/client/chart/base.js
Normal file
71
spikes/graphs-matrix/chartjs/client/chart/base.js
Normal file
@ -0,0 +1,71 @@
|
||||
const buildArray = require('build-array');
|
||||
const Chart = require('chart.js');
|
||||
const React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
ref: function(name) {
|
||||
this._refs = this._refs || {};
|
||||
|
||||
return (el) => {
|
||||
this._refs[name] = el;
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
const {
|
||||
datasets = [],
|
||||
labels = 0,
|
||||
stacked = false,
|
||||
xAxe = false,
|
||||
yAxe = false,
|
||||
legend = false
|
||||
} = this.props;
|
||||
|
||||
const _labels = !Array.isArray(labels)
|
||||
? buildArray(labels).map((v, i) => '')
|
||||
: labels;
|
||||
|
||||
this._chart = new Chart(this._refs.component, {
|
||||
type: 'bar',
|
||||
stacked: stacked,
|
||||
responsive: true,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: xAxe,
|
||||
stacked: stacked
|
||||
}],
|
||||
yAxes: [{
|
||||
display: yAxe,
|
||||
stacked: stacked
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
display: legend
|
||||
}
|
||||
},
|
||||
data: {
|
||||
labels:
|
||||
datasets: datasets
|
||||
}
|
||||
});
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
const {
|
||||
datasets = [],
|
||||
labels = 0
|
||||
} = this.props;
|
||||
|
||||
this._chart.data.datasets = datasets;
|
||||
this._chart.data.labels = buildArray(labels).map((v, i) => '');
|
||||
this._chart.update(0);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<canvas
|
||||
ref={this.ref('component')}
|
||||
width='400'
|
||||
height='400'
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
31
spikes/graphs-matrix/chartjs/client/chart/cpu.js
Normal file
31
spikes/graphs-matrix/chartjs/client/chart/cpu.js
Normal file
@ -0,0 +1,31 @@
|
||||
const buildArray = require('build-array');
|
||||
const Chart = require('./base');
|
||||
const React = require('react');
|
||||
|
||||
const colors = {
|
||||
user: 'rgb(255, 99, 132)',
|
||||
sys: 'rgb(255, 159, 64)'
|
||||
};
|
||||
|
||||
module.exports = ({
|
||||
data = {},
|
||||
windowSize
|
||||
}) => {
|
||||
const datasets = ['user', 'sys'].map((key) => {
|
||||
return {
|
||||
label: key,
|
||||
backgroundColor: colors[key],
|
||||
data: buildArray(windowSize).map((v, i) => ((data[i] || {})[key] || 0))
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<Chart
|
||||
datasets={datasets}
|
||||
stacked={true}
|
||||
labels={datasets[0].data.length}
|
||||
legend={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
35
spikes/graphs-matrix/chartjs/client/chart/disk.js
Normal file
35
spikes/graphs-matrix/chartjs/client/chart/disk.js
Normal file
@ -0,0 +1,35 @@
|
||||
const pretty = require('prettysize');
|
||||
const buildArray = require('build-array');
|
||||
const Chart = require('./base');
|
||||
const React = require('react');
|
||||
|
||||
const colors = {
|
||||
user: 'rgb(255, 99, 132)',
|
||||
sys: 'rgb(255, 159, 64)'
|
||||
};
|
||||
|
||||
module.exports = ({
|
||||
data = [],
|
||||
windowSize
|
||||
}) => {
|
||||
const datasets = [{
|
||||
label: 'disk',
|
||||
backgroundColor: 'rgb(255, 159, 64)',
|
||||
data: buildArray(windowSize).map((v, i) => {
|
||||
return data[i] ? (data[i].total - data[i].free) : 0;
|
||||
})
|
||||
}];
|
||||
|
||||
const labels = buildArray(windowSize).map((v, i) => {
|
||||
return data[i] ? pretty(datasets[0].data[i]) : '';
|
||||
});
|
||||
|
||||
return (
|
||||
<Chart
|
||||
datasets={datasets}
|
||||
labels={labels}
|
||||
legend={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
8
spikes/graphs-matrix/chartjs/client/chart/index.js
Normal file
8
spikes/graphs-matrix/chartjs/client/chart/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
CPU: require('./cpu'),
|
||||
cpu: require('./cpu'),
|
||||
Mem: require('./mem'),
|
||||
mem: require('./mem'),
|
||||
Disk: require('./disk'),
|
||||
disk: require('./disk')
|
||||
};
|
28
spikes/graphs-matrix/chartjs/client/chart/mem.js
Normal file
28
spikes/graphs-matrix/chartjs/client/chart/mem.js
Normal file
@ -0,0 +1,28 @@
|
||||
const buildArray = require('build-array');
|
||||
const Chart = require('./base');
|
||||
const React = require('react');
|
||||
|
||||
const colors = {
|
||||
user: 'rgb(255, 99, 132)',
|
||||
sys: 'rgb(255, 159, 64)'
|
||||
};
|
||||
|
||||
module.exports = ({
|
||||
data = [],
|
||||
windowSize
|
||||
}) => {
|
||||
const datasets = [{
|
||||
label: 'mem',
|
||||
backgroundColor: 'rgb(255, 99, 132)',
|
||||
data: buildArray(windowSize).map((v, i) => ((data[i] || {}).used || 0))
|
||||
}];
|
||||
|
||||
return (
|
||||
<Chart
|
||||
datasets={datasets}
|
||||
labels={datasets[0].data.length}
|
||||
legend={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -13,8 +13,6 @@ client.connect((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
console.log('connected');
|
||||
});
|
||||
|
||||
const store = Store({
|
||||
|
@ -31,7 +31,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const Graph = connect(
|
||||
const Row = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(React.createClass({
|
||||
@ -43,62 +43,47 @@ const Graph = connect(
|
||||
},
|
||||
render: function() {
|
||||
const {
|
||||
data = [],
|
||||
data = {},
|
||||
windowSize
|
||||
} = this.props;
|
||||
|
||||
const _data = buildArray(windowSize).map((v, i) => {
|
||||
return data[i] ? data[i] : {
|
||||
cpu: 0,
|
||||
when: new Date().getTime()
|
||||
};
|
||||
const charts = Object.keys(data).map((key, i, arr) => {
|
||||
if (!Chart[key]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const chart = React.createElement(Chart[key], {
|
||||
data: data[key],
|
||||
windowSize
|
||||
});
|
||||
|
||||
const median = data.reduce((sum, v) => (sum + v.cpu), 0) / data.length;
|
||||
|
||||
const bg = median > 50
|
||||
? 'rgba(205, 54, 54, 0.3)'
|
||||
: 'rgba(54, 74, 205, 0.3)';
|
||||
|
||||
const border = median > 50
|
||||
? 'rgba(248, 51, 51, 0.5)'
|
||||
: 'rgba(54, 73, 205, 0.5)';
|
||||
|
||||
return (
|
||||
<Chart
|
||||
data={_data}
|
||||
bg={bg}
|
||||
border={border}
|
||||
median={median}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
module.exports = ({
|
||||
x,
|
||||
y
|
||||
}) => {
|
||||
const m = buildArray(y).map((v, i) => {
|
||||
const m = buildArray(x).map((v, y) => {
|
||||
const id = `${i}${y}`;
|
||||
return (
|
||||
<div className={`col-xs-${12/x}`}>
|
||||
<Graph key={id} id={id} />
|
||||
<div key={key} className={`col-xs-${12/arr.length}`}>
|
||||
{chart}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='row'>
|
||||
{m}
|
||||
{charts}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
module.exports = ({
|
||||
rows
|
||||
}) => {
|
||||
const _rows = buildArray(rows).map((v, i) => {
|
||||
return (
|
||||
<Row id={i} key={i} />
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{m}
|
||||
{_rows}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ module.exports = ({
|
||||
return (
|
||||
<AppContainer>
|
||||
<Provider store={store}>
|
||||
<Matrix x={3} y={4} />
|
||||
<Matrix rows={4} />
|
||||
</Provider>
|
||||
</AppContainer>
|
||||
);
|
||||
|
@ -4,6 +4,7 @@
|
||||
"license": "private",
|
||||
"main": "server/index.js",
|
||||
"dependencies": {
|
||||
"async": "^2.1.2",
|
||||
"autoprefixer": "^6.5.1",
|
||||
"babel-eslint": "^7.0.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
@ -19,16 +20,21 @@
|
||||
"chart.js": "^2.3.0",
|
||||
"classnames": "^2.2.5",
|
||||
"component-emitter": "^1.2.1",
|
||||
"cpu-percent": "^2.0.1",
|
||||
"css-loader": "^0.25.0",
|
||||
"d3": "^4.3.0",
|
||||
"diskusage": "^0.1.5",
|
||||
"hapi": "^15.2.0",
|
||||
"hapi-webpack-dev-plugin": "^1.1.4",
|
||||
"inert": "^4.0.2",
|
||||
"lodash.take": "^4.1.1",
|
||||
"metrics-os": "^1.0.1",
|
||||
"nes": "^6.3.1",
|
||||
"pidusage": "^1.1.0",
|
||||
"postcss-loader": "^1.0.0",
|
||||
"postcss-modules-values": "^1.2.2",
|
||||
"postcss-nested": "^1.0.0",
|
||||
"prettysize": "0.0.3",
|
||||
"react": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"react-hot-loader": "^3.0.0-beta.6",
|
||||
|
@ -1,7 +1,39 @@
|
||||
const Emitter = require('component-emitter');
|
||||
const async = require('async');
|
||||
const disk = require('diskusage');
|
||||
const os = require('os');
|
||||
|
||||
const cdm = {};
|
||||
|
||||
const getCPU = (fn) => {
|
||||
return fn(null, {
|
||||
user: os.cpus().reduce((sum, cpu) => sum + cpu.times.user, 0),
|
||||
sys: os.cpus().reduce((sum, cpu) => sum + cpu.times.sys, 0)
|
||||
});
|
||||
};
|
||||
|
||||
const getMem = (fn) => {
|
||||
const free = os.freemem();
|
||||
const total = os.totalmem();
|
||||
const using = total - free;
|
||||
const perc = (using / total) * 100;
|
||||
|
||||
return fn(null, {
|
||||
used: perc
|
||||
});
|
||||
};
|
||||
|
||||
const getDisk = (fn) => {
|
||||
disk.check('/', fn);
|
||||
};
|
||||
|
||||
const getStats = (fn) => {
|
||||
async.parallel({
|
||||
cpu: getCPU,
|
||||
mem: getMem,
|
||||
disk: getDisk
|
||||
}, fn);
|
||||
};
|
||||
|
||||
module.exports = (server) => ({
|
||||
on: (id) => {
|
||||
console.log('on', cdm[id]);
|
||||
@ -10,16 +42,17 @@ module.exports = (server) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let messageId = 0;
|
||||
const interval = setInterval(() => {
|
||||
console.log(`publishing /stats/${id}`);
|
||||
|
||||
getStats((err, stats) => {
|
||||
server.publish(`/stats/${id}`, {
|
||||
when: new Date().getTime(),
|
||||
cpu: Math.random() * 100
|
||||
stats
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
cdm[id] = {
|
||||
interval,
|
||||
|
@ -3,16 +3,6 @@
|
||||
<head>
|
||||
<title>React Boilerplate</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://necolas.github.io/normalize.css/latest/normalize.css" />
|
||||
<link rel="stylesheet" type="text/css" href="https://rawgit.com/epochjs/epoch/master/dist/css/epoch.css" />
|
||||
<style>
|
||||
.epoch.red canvas {
|
||||
fill: rgba(205, 54, 54, 0.3);
|
||||
}
|
||||
|
||||
.epoch.blue canvas {
|
||||
fill: rgba(54, 74, 205, 0.3);
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.container-fluid,
|
||||
.container {
|
||||
|
Loading…
Reference in New Issue
Block a user