Skip to content

Commit ba72624

Browse files
committed
Add another challenge
1 parent bf57bd2 commit ba72624

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+17470
-33
lines changed

challenge10/README.md

Lines changed: 4 additions & 2 deletions

challenge11/README.md

Lines changed: 22 additions & 0 deletions
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

challenge12/README.md

Lines changed: 10 additions & 0 deletions

challenge12/actuator/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM node:6-slim
2+
RUN mkdir -p /app
3+
ADD . /app
4+
RUN cd /app && npm install
5+
CMD node /app/actuator.js

challenge12/actuator/actuator.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use strict';
2+
3+
const Mqtt = require('mqtt');
4+
const Seneca = require('seneca');
5+
6+
const mqtt = Mqtt.connect('mqtt://' + process.env.BROKER_HOST + ':1883');
7+
const seneca = Seneca();
8+
9+
seneca.add({ role: 'actuate', cmd: 'set' }, (args, cb) => {
10+
const payload = JSON.stringify({ 'offset': parseInt(args.offset, 10) });
11+
mqtt.publish('temperature/1/set', new Buffer(payload), { qos: 0, retain: true }, cb);
12+
});
13+
14+
seneca.listen({ port: process.env.ACTUATOR_PORT });

challenge12/actuator/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "actuator",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "actuator.js",
6+
"scripts": {
7+
"start": "node actuator.js"
8+
},
9+
"author": "",
10+
"license": "ISC",
11+
"dependencies": {
12+
"mqtt": "1.x.x",
13+
"seneca": "2.x.x"
14+
}
15+
}

challenge12/broker/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM node:6-slim
2+
RUN mkdir -p /app
3+
ADD . /app
4+
RUN cd /app && npm install
5+
CMD node /app/broker.js

challenge12/broker/broker.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const Mosca = require('mosca');
4+
const Seneca = require('seneca');
5+
6+
7+
const server = new Mosca.Server({});
8+
const seneca = Seneca();
9+
seneca.client({
10+
host: process.env.SERIALIZER_HOST,
11+
port: process.env.SERIALIZER_PORT,
12+
pin: {
13+
role: 'serialize', cmd: 'write'
14+
}
15+
});
16+
17+
server.published = (packet, client, cb) => {
18+
if (!packet.topic.match(/temperature\/[0-9]+\/read/)) {
19+
return cb();
20+
}
21+
22+
const body = parse(packet.payload);
23+
24+
body.role = 'serialize';
25+
body.cmd = 'write';
26+
seneca.act(body, cb);
27+
};
28+
29+
function parse (body) {
30+
try {
31+
return JSON.parse(body);
32+
}
33+
catch (err) {
34+
return null;
35+
}
36+
}

challenge12/broker/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "broker",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "broker.js",
6+
"scripts": {
7+
"start": "node broker.js"
8+
},
9+
"author": "",
10+
"license": "ISC",
11+
"dependencies": {
12+
"mosca": "2.x.x",
13+
"seneca": "2.x.x"
14+
}
15+
}

challenge12/frontend/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM node:6-slim
2+
RUN mkdir -p /app
3+
ADD . /app
4+
RUN cd /app && npm install
5+
CMD node /app/index.js

challenge12/frontend/index.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
const Path = require('path');
4+
const Hapi = require('hapi');
5+
const Inert = require('inert');
6+
const Moment = require('moment');
7+
const Seneca = require('seneca');
8+
const WebStream = require('./webStream');
9+
10+
const seneca = Seneca();
11+
seneca.client({host: process.env.SERIALIZER_HOST, port: process.env.SERIALIZER_PORT, pin: {role: 'serialize', cmd: 'read'}});
12+
seneca.client({host: process.env.ACTUATOR_HOST, port: process.env.ACTUATOR_PORT, pin: {role: 'actuate', cmd: 'set'}});
13+
14+
15+
const serverConfig = {
16+
connections: {
17+
routes: {
18+
files: {
19+
relativeTo: Path.join(__dirname, 'public')
20+
}
21+
}
22+
}
23+
};
24+
25+
const server = new Hapi.Server(serverConfig);
26+
server.connection({ port: process.env.FRONTEND_PORT });
27+
server.register(Inert, () => {
28+
server.route({
29+
method: 'GET',
30+
path: '/set',
31+
handler: (request, reply) => {
32+
seneca.act({
33+
role: 'actuate',
34+
cmd: 'set', offset:
35+
request.query.offset
36+
}, (err) => {
37+
if (err) {
38+
return reply({ result: err });
39+
}
40+
41+
reply({result: 'ok'});
42+
});
43+
}
44+
});
45+
46+
server.route({
47+
method: 'GET',
48+
path: '/{param*}',
49+
handler: {
50+
directory: {
51+
path: '.',
52+
redirectToSlash: true,
53+
index: true
54+
}
55+
}
56+
});
57+
58+
const webStream = WebStream(server.listener);
59+
60+
let lastEmitted = 0;
61+
let i = 0;
62+
setInterval(() => {
63+
seneca.act({
64+
role: 'serialize',
65+
cmd: 'read',
66+
sensorId: '1',
67+
start: Moment().subtract(10, 'minutes').utc().format(),
68+
end: Moment().utc().format()
69+
}, (err, data) => {
70+
let toEmit = [];
71+
72+
data[0].forEach((point) => {
73+
if (Moment(point.time).unix() > lastEmitted) {
74+
lastEmitted = Moment(point.time).unix();
75+
point.time = (new Date(point.time)).getTime();
76+
toEmit.push(point);
77+
}
78+
});
79+
if (toEmit.length) {
80+
console.log('will emit');
81+
console.log(toEmit);
82+
webStream.emit(toEmit);
83+
}
84+
});
85+
}, 1000);
86+
87+
server.start(() => {
88+
console.log(`listening at http://localhost:${server.info.port}`);
89+
});
90+
});

challenge12/frontend/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "api",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"lint": "belly-button",
8+
"start": "node index.js"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"end-of-stream": "1.x.x",
14+
"hapi": "14.x.x",
15+
"inert": "4.x.x",
16+
"moment": "2.x.x",
17+
"seneca": "2.x.x",
18+
"websocket-stream": "3.x.x"
19+
},
20+
"devDependencies": {
21+
"belly-button": "2.0.x"
22+
}
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Charting frontend</title>
5+
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
6+
<link type="text/css" rel="stylesheet" href="/lib/rickshaw.min.css">
7+
<script src="/lib/d3.min.js"></script>
8+
<script src="/lib/jquery.js"></script>
9+
<script src="/lib/lodash.min.js"></script>
10+
<script src="/lib/rickshaw.min.js"></script>
11+
</head>
12+
<body>
13+
<div id='header'>
14+
<h2>Temperature Sensor 1</h2>
15+
</div>
16+
<div id="controls">
17+
<input type='text' id='offset'></input>
18+
<button type='button' id='setOffset'>set</button>
19+
</div>
20+
<div id="chart_container">
21+
<div id="chart"></div>
22+
<div id="timeline"></div>
23+
<div id="preview"></div>
24+
</div>
25+
</body>
26+
<script src="/js/bundle.js"></script>
27+
</html>
28+

0 commit comments

Comments
 (0)