Browse Source

set up redux store and hook up to websockets

Jordan Schalm 8 years ago
parent
commit
f0f59f3b26

+ 7 - 1
dashboard/js/package.json

@@ -6,8 +6,14 @@
     "react-scripts": "0.8.5"
   },
   "dependencies": {
+    "immutable": "^3.8.1",
+    "moment": "^2.17.1",
     "react": "^15.4.2",
-    "react-dom": "^15.4.2"
+    "react-dom": "^15.4.2",
+    "react-redux": "^5.0.2",
+    "redux": "^3.6.0",
+    "redux-logger": "^2.7.4",
+    "victory": "^0.15.0"
   },
   "scripts": {
     "start": "react-scripts start",

+ 0 - 24
dashboard/js/src/App.css

@@ -1,24 +0,0 @@
-.App {
-  text-align: center;
-}
-
-.App-logo {
-  animation: App-logo-spin infinite 20s linear;
-  height: 80px;
-}
-
-.App-header {
-  background-color: #222;
-  height: 150px;
-  padding: 20px;
-  color: white;
-}
-
-.App-intro {
-  font-size: large;
-}
-
-@keyframes App-logo-spin {
-  from { transform: rotate(0deg); }
-  to { transform: rotate(360deg); }
-}

+ 0 - 21
dashboard/js/src/App.js

@@ -1,21 +0,0 @@
-import React, { Component } from 'react';
-import logo from './logo.svg';
-import './App.css';
-
-class App extends Component {
-  render() {
-    return (
-      <div className="App">
-        <div className="App-header">
-          <img src={logo} className="App-logo" alt="logo" />
-          <h2>Welcome to React</h2>
-        </div>
-        <p className="App-intro">
-          To get started, edit <code>src/App.js</code> and save to reload.
-        </p>
-      </div>
-    );
-  }
-}
-
-export default App;

+ 11 - 0
dashboard/js/src/action-creators.js

@@ -0,0 +1,11 @@
+import ActionTypes from './action-types';
+
+export const tick = payload => ({
+	type: ActionTypes.TICK,
+	payload
+});
+
+export const init = payload => ({
+	type: ActionTypes.INIT,
+	payload
+});

+ 6 - 0
dashboard/js/src/action-types.js

@@ -0,0 +1,6 @@
+import {createActionTypes} from './util';
+
+export default createActionTypes([
+	'INIT',
+	'TICK'
+]);

+ 38 - 0
dashboard/js/src/components/App.js

@@ -0,0 +1,38 @@
+import React, { Component } from 'react';
+import {connect} from 'react-redux';
+import {init, tick} from '../action-creators';
+import LineChart from './LineChart';
+
+const styles = {
+	container: {
+		backgroundSize: 'cover',
+		display: 'flex',
+		flexDirection: 'column'
+	},
+	chartContainer: {
+
+	}
+}
+
+const WS_URL = 'ws://localhost:8080/ws';
+
+class App extends Component {
+	constructor(props) {
+		super();
+		const ws = new WebSocket(WS_URL);
+		ws.onclose = event => console.log(event);
+		ws.onmessage = ({data}) => props.dispatch(tick(data));
+
+		this.state = {ws};
+	}
+
+	render() {
+		return (
+			<div style={styles.container}>
+				<LineChart />
+			</div>
+		);
+	}
+}
+
+export default connect()(App);

+ 0 - 0
dashboard/js/src/App.test.js → dashboard/js/src/components/App.test.js


+ 30 - 0
dashboard/js/src/components/LineChart.js

@@ -0,0 +1,30 @@
+import React, { PropTypes } from 'react';
+import { VictoryChart, VictoryLine } from 'victory';
+import Moment from 'moment';
+
+const LineChart = ({data}) => {
+	return (
+		<VictoryChart
+			height={200}
+			width={800}
+			scale={{x: "time", y: "linear"}}>
+			<VictoryLine data={data} />
+		</VictoryChart>
+	);
+};
+
+LineChart.propTypes = {
+	data: PropTypes.arrayOf(PropTypes.shape({
+		x: PropTypes.instanceOf(Moment),
+		y: PropTypes.number
+	}))
+};
+
+LineChart.defaultProps = {
+	data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(n => ({
+		x: Moment().add(n, 'minutes'),
+		y: n * 100 * Math.random()
+	}))
+};
+
+export default LineChart;

+ 14 - 14
dashboard/js/src/index.js

@@ -1,20 +1,20 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
-import App from './App';
+import App from './components/App';
+import reducer from './reducer';
+import {applyMiddleware, createStore} from 'redux';
+import {Provider} from 'react-redux';
+import logger from 'redux-logger';
 import './index.css';
 
-ReactDOM.render(
-  <App />,
-  document.getElementById('root')
+const store = createStore(
+	reducer,
+	applyMiddleware(logger)
 );
 
-var conn = new WebSocket('ws://localhost:8080/ws');
-conn.onclose = function(event) {
-	console.log(event);
-}
-
-conn.onmessage = function(event) {
-	console.log(JSON.parse(event.data));
-	var point = JSON.parse(event.data);
-	// ram.append(new Date(point.t).getTime(), point.y);
-}
+ReactDOM.render(
+	<Provider store={store}>
+		<App />
+	</Provider>,
+	document.getElementById('root')
+);

File diff suppressed because it is too large
+ 0 - 2
dashboard/js/src/logo.svg


+ 57 - 0
dashboard/js/src/reducer.js

@@ -0,0 +1,57 @@
+import Immutable from 'immutable';
+import ActionTypes from './action-types';
+console.log(ActionTypes);
+const initialState = Immutable.Map({
+	// Keep enough points for 24hrs at 30s resolution
+	maxPoints: 24 * 60 * 2,
+	// List of points for time series charts
+	ram: Immutable.List(),
+	nClients: Immutable.List()
+});
+
+const reducer = (state = initialState, {type, payload}) => {
+	let newState = state;
+
+	switch (type) {
+	// Upon establishing a websocket connection, initiates store with dump
+	// of last N points, up to `maxPoints`
+	// payload = {ram: [{x, y}], nClients: [{x, y}]}
+	case ActionTypes.INIT:
+		newState = state
+			.set('ram', state.get('ram')
+				.push(...payload.ram))
+			.set('nClients', state.get('nClients')
+				.push(...payload.nClients));
+		if (newState.get('ram').count() > state.get('maxPoints')) {
+			newState = newState
+				.set('ram', state.get('ram')
+					.shift())
+				.set('nClients', state.get('nClients')
+					.shift());
+		}
+		return newState;
+
+	// Updates store with a tick from websocket connection, one point for each
+	// chart. Removes oldest point if necessary to make space.
+	// payload = {ram: {x, y}, nClients: {x, y}}
+	case ActionTypes.TICK:
+		newState = state
+			.set('ram', state.get('ram')
+				.push(payload.ram))
+			.set('nClients', state.get('nClients')
+				.push(payload.nClients));
+		if (newState.get('ram').count() > state.get('maxPoints')) {
+			newState = newState
+				.set('ram', state.get('ram')
+					.shift())
+				.set('nClients', state.get('nClients')
+					.shift());
+		}
+		return newState;
+
+	default:
+		return state;
+	}
+}
+
+export default reducer;

+ 9 - 0
dashboard/js/src/util.js

@@ -0,0 +1,9 @@
+// Takes a list of strings representing action types and returns an object
+// mapping each string to itself. For instance: ['a', 'b'] => {a: 'a', b: 'b'}
+export const createActionTypes = (list) => {
+	const types = {};
+	for (let i = 0; i < list.length; i++) {
+		types[list[i]] = list[i];
+	}
+	return types;
+};

+ 152 - 2
dashboard/js/yarn.lock

@@ -1525,6 +1525,72 @@ [email protected], "cssom@>= 0.3.0 < 0.4.0":
   dependencies:
     cssom "0.3.x"
 
+d3-array@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.0.2.tgz#174237bf356a852fadd6af87743d928631de7655"
+
+d3-collection@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.2.tgz#df5acb5400443e9eabe9c1379896c67e52426b39"
+
+d3-color@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.2.tgz#83cb4b3a9474e40795f009d97e97a15649830bbc"
+
+d3-ease@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.2.tgz#b486f8f3ca308ca7be38197d65622b6e30983377"
+
+d3-format@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.0.2.tgz#138618320b4bbeb43b5c0ff30519079fbbd7375e"
+
+d3-interpolate@1, d3-interpolate@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.3.tgz#e119c91b6be4941e581675ca3e1279bb92bd2c9b"
+  dependencies:
+    d3-color "1"
+
+d3-path@1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.3.tgz#60103d0dea9a6cd6ca58de86c6d56724002d3fde"
+
+d3-scale@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.4.tgz#50e28bf6a193b706745528515ed9b3d44205a033"
+  dependencies:
+    d3-array "1"
+    d3-collection "1"
+    d3-color "1"
+    d3-format "1"
+    d3-interpolate "1"
+    d3-time "1"
+    d3-time-format "2"
+
+d3-shape@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.0.4.tgz#145ee100ccbec42f8e3f1996cd05c786f79fe1c6"
+  dependencies:
+    d3-path "1"
+
+d3-time-format@2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.0.3.tgz#3241569b74ddc9c42e0689c0e8a903579fd6280a"
+  dependencies:
+    d3-time "1"
+
+d3-time@1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.4.tgz#2ceba09a76b7450c992a1ded4e10fc6195e69649"
+
+d3-timer@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.4.tgz#adaf7f60c7b54c99b2ffabd28c15a0c108a75321"
+
+d3-voronoi@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.1.tgz#998544dca98ef0e89a6c40c0bac3510d1bc1b8b9"
+
 d@^0.1.1, d@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309"
@@ -1561,6 +1627,10 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
 
[email protected]:
+  version "0.3.4"
+  resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.4.tgz#aac5c39952236abe5f037a2349060ba01b00ae48"
+
 deep-extend@~0.4.0:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
@@ -2403,6 +2473,10 @@ [email protected]:
   version "2.16.3"
   resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
 
+hoist-non-react-statics@^1.0.3:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
+
 home-or-tmp@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -2517,6 +2591,10 @@ ignore@^3.1.5:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435"
 
+immutable@^3.8.1:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2"
+
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -2574,7 +2652,7 @@ interpret@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
 
-invariant@^2.2.0:
+invariant@^2.0.0, invariant@^2.2.0:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
   dependencies:
@@ -3204,6 +3282,10 @@ [email protected], loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.
     json5 "^0.5.0"
     object-assign "^4.0.1"
 
+lodash-es@^4.2.0, lodash-es@^4.2.1:
+  version "4.17.4"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
+
 lodash._arraycopy@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1"
@@ -3297,7 +3379,7 @@ lodash.uniq@^4.3.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.2.0, lodash@^4.3.0:
+"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.12.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
@@ -3448,6 +3530,10 @@ [email protected], "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
   dependencies:
     minimist "0.0.8"
 
+moment@^2.17.1:
+  version "2.17.1"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
+
 [email protected]:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -4231,6 +4317,16 @@ react-dom@^15.4.2:
     loose-envify "^1.1.0"
     object-assign "^4.1.0"
 
+react-redux@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.2.tgz#3d9878f5f71c6fafcd45de1fbb162ea31f389814"
+  dependencies:
+    hoist-non-react-statics "^1.0.3"
+    invariant "^2.0.0"
+    lodash "^4.2.0"
+    lodash-es "^4.2.0"
+    loose-envify "^1.1.0"
+
 [email protected]:
   version "0.8.5"
   resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-0.8.5.tgz#5dc154617be7addef5022c261b96b3e3068418cb"
@@ -4385,6 +4481,21 @@ reduce-function-call@^1.0.1:
   dependencies:
     balanced-match "^0.4.2"
 
+redux-logger@^2.7.4:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.7.4.tgz#891e5d29e7f111d08b5781a237b9965b5858c7f8"
+  dependencies:
+    deep-diff "0.3.4"
+
+redux@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d"
+  dependencies:
+    lodash "^4.2.1"
+    lodash-es "^4.2.1"
+    loose-envify "^1.1.0"
+    symbol-observable "^1.0.2"
+
 regenerate@^1.2.1:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
@@ -4884,6 +4995,10 @@ svgo@^0.7.0:
     sax "~1.2.1"
     whet.extend "~0.9.9"
 
+symbol-observable@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
+
 "symbol-tree@>= 3.1.0 < 4.0.0":
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb"
@@ -5139,6 +5254,41 @@ [email protected]:
   dependencies:
     extsprintf "1.0.2"
 
+victory-chart@^15.0.0:
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/victory-chart/-/victory-chart-15.0.1.tgz#f5b2bb17bf43e81e73fcfb1b24aa717479e0b1f1"
+  dependencies:
+    d3-voronoi "^1.0.0"
+    lodash "^4.12.0"
+    victory-core "^11.0.0"
+
+victory-core@^11.0.0:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/victory-core/-/victory-core-11.0.1.tgz#ecad2ac211765b2d7d04e7cf58307df888137334"
+  dependencies:
+    d3-ease "^1.0.0"
+    d3-interpolate "^1.1.1"
+    d3-scale "^1.0.0"
+    d3-shape "^1.0.0"
+    d3-timer "^1.0.0"
+    lodash "^4.12.0"
+
+victory-pie@^9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/victory-pie/-/victory-pie-9.0.0.tgz#938604e81877ee9d2984c1bb4cf1bdb8db81beb1"
+  dependencies:
+    d3-shape "^1.0.0"
+    lodash "^4.12.0"
+    victory-core "^11.0.0"
+
+victory@^0.15.0:
+  version "0.15.0"
+  resolved "https://registry.yarnpkg.com/victory/-/victory-0.15.0.tgz#b8003492c02a7818fc49326e1d68cb440be6835f"
+  dependencies:
+    victory-chart "^15.0.0"
+    victory-core "^11.0.0"
+    victory-pie "^9.0.0"
+
 [email protected]:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"

Some files were not shown because too many files changed in this diff