Skip to content

Commit ddeed19

Browse files
authored
Feature/xml support (#18)
* XML Measure Support * XML Command Support * Run prettier * Remove irrelevant comment
1 parent 02ea8f5 commit ddeed19

File tree

8 files changed

+71
-110
lines changed

8 files changed

+71
-110
lines changed

context-provider/controllers/iot/command/json.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
// Connect to an IoT Agent and use fallback values if necessary
22

3-
43
const IoTDevices = require('../devices');
54
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';
65

76
// A series of constants used by our set of devices
87
const OK = 'OK';
98
const NOT_OK = 'NOT OK';
109

11-
1210
/* global MQTT_CLIENT */
1311

1412
//
@@ -19,7 +17,7 @@ function getJSONCommand(string) {
1917
return Object.keys(obj)[0];
2018
}
2119

22-
function getResult (cmd, status){
20+
function getResult(cmd, status) {
2321
const result = {};
2422
result[cmd] = status;
2523
return JSON.stringify(result);
@@ -43,7 +41,6 @@ class JSONCommand {
4341
// this will briefly set the bell to on.
4442
// The bell is not a sensor - it will not report state northbound
4543
actuateBell(req, res) {
46-
4744
const command = getJSONCommand(req.body);
4845
const deviceId = 'bell' + req.params.id;
4946

@@ -64,7 +61,6 @@ class JSONCommand {
6461
actuateDoor(req, res) {
6562
const command = getJSONCommand(req.body);
6663
const deviceId = 'door' + req.params.id;
67-
6864

6965
if (IoTDevices.notFound(deviceId)) {
7066
return res.status(404).send(getResult(command, NOT_OK));
@@ -110,7 +106,4 @@ class JSONCommand {
110106
}
111107
}
112108

113-
114-
115109
module.exports = JSONCommand;
116-

context-provider/controllers/iot/command/ultralight.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Connect to an IoT Agent and use fallback values if necessary
22

3-
43
const IoTDevices = require('../devices');
54
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';
65

7-
86
// A series of constants used by our set of devices
97
const OK = ' OK';
108
const NOT_OK = ' NOT OK';
@@ -117,7 +115,4 @@ class UltralightCommand {
117115
}
118116
}
119117

120-
121-
122118
module.exports = UltralightCommand;
123-
Lines changed: 29 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,101 @@
11
// Connect to an IoT Agent and use fallback values if necessary
22

3-
43
const IoTDevices = require('../devices');
54
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';
6-
7-
const debug = require('debug')('tutorial:xml');
8-
5+
const xmlParser = require('xml-parser');
96

107
// A series of constants used by our set of devices
11-
const OK = ' OK';
12-
const NOT_OK = ' NOT OK';
13-
8+
const OK = 'success';
9+
const NOT_OK = 'error';
1410

1511
/* global MQTT_CLIENT */
1612

1713
//
1814
// Splits the deviceId from the command sent.
1915
//
20-
function getXMLCommand(string) {
21-
const command = string.split('@');
22-
if (command.length === 1) {
23-
command.push('');
16+
function getResult(status, command, id, info) {
17+
if (info) {
18+
return '<' + status + ' command="' + command + '" device="' + id + '">' + info + '</' + status + '/>';
2419
}
25-
return command[1];
20+
return '<' + status + ' command="' + command + '" device="' + id + '"/>';
2621
}
2722

2823
// This processor sends XML payload northbound to
2924
// the southport of the IoT Agent and sends measures
3025
// for the motion sensor, door and lamp.
3126

32-
// XML 2.0 is a lightweight text based protocol aimed to constrained
33-
// devices and communications
34-
// where the bandwidth and device memory may be limited resources.
35-
//
36-
// A device can report new measures to the IoT Platform using an HTTP GET request to the /iot/d path with the following query parameters:
37-
//
38-
// i (device ID): Device ID (unique for the API Key).
39-
// k (API Key): API Key for the service the device is registered on.
40-
// t (timestamp): Timestamp of the measure. Will override the automatic IoTAgent timestamp (optional).
41-
// d (Data): XML 2.0 payload.
42-
//
43-
// At the moment the API key and timestamp are unused by the simulator.
44-
4527
class XMLCommand {
4628
// The bell will respond to the "ring" command.
4729
// this will briefly set the bell to on.
4830
// The bell is not a sensor - it will not report state northbound
4931
actuateBell(req, res) {
50-
51-
52-
debug(req.body)
53-
54-
55-
const keyValuePairs = req.body.split('|') || [''];
56-
const command = getXMLCommand(keyValuePairs[0]);
57-
const deviceId = 'bell' + req.params.id;
58-
const result = keyValuePairs[0] + '| ' + command;
32+
const data = xmlParser(req.body);
33+
const deviceId = data.root.attributes.device;
34+
const command = data.root.name;
5935

6036
if (IoTDevices.notFound(deviceId)) {
61-
return res.status(404).send(result + NOT_OK);
37+
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
6238
} else if (IoTDevices.isUnknownCommand('bell', command)) {
63-
return res.status(422).send(result + NOT_OK);
39+
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
6440
}
6541

6642
// Update device state
6743
IoTDevices.actuateDevice(deviceId, command);
68-
return res.status(200).send(result + OK);
44+
return res.status(200).send(getResult(OK, command, deviceId));
6945
}
7046

7147
// The door responds to "open", "close", "lock" and "unlock" commands
7248
// Each command alters the state of the door. When the door is unlocked
7349
// it can be opened and shut by external events.
7450
actuateDoor(req, res) {
75-
const keyValuePairs = req.body.split('|') || [''];
76-
const command = getXMLCommand(keyValuePairs[0]);
77-
const deviceId = 'door' + req.params.id;
78-
const result = keyValuePairs[0] + '| ' + command;
51+
const data = xmlParser(req.body);
52+
const deviceId = data.root.attributes.device;
53+
const command = data.root.name;
7954

8055
if (IoTDevices.notFound(deviceId)) {
81-
return res.status(404).send(result + NOT_OK);
56+
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
8257
} else if (IoTDevices.isUnknownCommand('door', command)) {
83-
return res.status(422).send(result + NOT_OK);
58+
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
8459
}
8560

8661
// Update device state
8762
IoTDevices.actuateDevice(deviceId, command);
88-
return res.status(200).send(result + OK);
63+
return res.status(200).send(getResult(OK, command, deviceId));
8964
}
9065

9166
// The lamp can be "on" or "off" - it also registers luminosity.
9267
// It will slowly dim as time passes (provided no movement is detected)
9368
actuateLamp(req, res) {
94-
const keyValuePairs = req.body.split('|') || [''];
95-
const command = getXMLCommand(keyValuePairs[0]);
96-
const deviceId = 'lamp' + req.params.id;
97-
const result = keyValuePairs[0] + '| ' + command;
69+
const data = xmlParser(req.body);
70+
const deviceId = data.root.attributes.device;
71+
const command = data.root.name;
9872

9973
if (IoTDevices.notFound(deviceId)) {
100-
return res.status(404).send(result + NOT_OK);
74+
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
10175
} else if (IoTDevices.isUnknownCommand('lamp', command)) {
102-
return res.status(422).send(result + NOT_OK);
76+
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
10377
}
10478

10579
// Update device state
10680
IoTDevices.actuateDevice(deviceId, command);
107-
return res.status(200).send(result + OK);
81+
return res.status(200).send(getResult(OK, command, deviceId));
10882
}
10983

11084
// cmd topics are consumed by the actuators (bell, lamp and door)
11185
processMqttMessage(topic, message) {
11286
const path = topic.split('/');
11387
if (path.pop() === 'cmd') {
114-
const keyValuePairs = message.split('|') || [''];
115-
const command = getXMLCommand(keyValuePairs[0]);
116-
const deviceId = path.pop();
117-
const result = keyValuePairs[0] + '| ' + command;
88+
const data = xmlParser(message);
89+
const deviceId = data.root.attributes.device;
90+
const command = data.root.name;
11891

11992
if (!IoTDevices.notFound(deviceId)) {
12093
IoTDevices.actuateDevice(deviceId, command);
12194
const topic = '/' + DEVICE_API_KEY + '/' + deviceId + '/cmdexe';
122-
MQTT_CLIENT.publish(topic, result + OK);
95+
MQTT_CLIENT.publish(topic, getResult(OK, command, deviceId));
12396
}
12497
}
12598
}
12699
}
127100

128-
129-
130101
module.exports = XMLCommand;
131-

context-provider/controllers/iot/measure/json.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@ const IOT_AGENT_URL =
1616
IOT_AGENT_SOUTH_PORT +
1717
IOT_AGENT_DEFAULT_RESOURCE;
1818

19-
20-
2119
/* global SOCKET_IO */
2220
/* global MQTT_CLIENT */
2321

24-
2522
// This processor sends ultralight payload northbound to
2623
// the southport of the IoT Agent and sends measures
2724
// for the motion sensor, door and lamp.
@@ -39,7 +36,6 @@ const IOT_AGENT_URL =
3936
//
4037
// At the moment the API key and timestamp are unused by the simulator.
4138

42-
4339
function ultralightToJSON(state) {
4440
const keyValuePairs = state.split('|');
4541
const obj = {};
@@ -51,7 +47,7 @@ function ultralightToJSON(state) {
5147

5248
class JSONMeasure {
5349
constructor(headers) {
54-
this.headers = headers;
50+
this.headers = headers;
5551
this.headers['Content-Type'] = 'application/json';
5652
}
5753

@@ -62,7 +58,7 @@ class JSONMeasure {
6258
url: IOT_AGENT_URL,
6359
qs: { k: DEVICE_API_KEY, i: deviceId },
6460
headers: this.headers,
65-
body: ultralightToJSON(state)
61+
body: ultralightToJSON(state)
6662
};
6763
const debugText =
6864
'POST ' + IOT_AGENT_URL + '?i=' + options.qs.i + '&k=' + options.qs.k;
@@ -82,5 +78,4 @@ class JSONMeasure {
8278
}
8379
}
8480

85-
module.exports = JSONMeasure;
86-
81+
module.exports = JSONMeasure;

context-provider/controllers/iot/measure/ultralight.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@ const IOT_AGENT_URL =
1616
IOT_AGENT_SOUTH_PORT +
1717
IOT_AGENT_DEFAULT_RESOURCE;
1818

19-
20-
2119
/* global SOCKET_IO */
2220
/* global MQTT_CLIENT */
2321

24-
2522
// This processor sends ultralight payload northbound to
2623
// the southport of the IoT Agent and sends measures
2724
// for the motion sensor, door and lamp.
@@ -41,7 +38,7 @@ const IOT_AGENT_URL =
4138

4239
class UltralightMeasure {
4340
constructor(headers) {
44-
this.headers = headers;
41+
this.headers = headers;
4542
this.headers['Content-Type'] = 'text/plain';
4643
}
4744

@@ -72,5 +69,4 @@ class UltralightMeasure {
7269
}
7370
}
7471

75-
module.exports = UltralightMeasure;
76-
72+
module.exports = UltralightMeasure;

context-provider/controllers/iot/measure/xml.js

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,63 @@ const IOT_AGENT_URL =
1616
IOT_AGENT_SOUTH_PORT +
1717
IOT_AGENT_DEFAULT_RESOURCE;
1818

19-
20-
2119
/* global SOCKET_IO */
2220
/* global MQTT_CLIENT */
2321

24-
25-
// This processor sends ultralight payload northbound to
22+
// This processor sends XML payloads northbound to
2623
// the southport of the IoT Agent and sends measures
2724
// for the motion sensor, door and lamp.
2825

29-
// Ultralight 2.0 is a lightweight text based protocol aimed to constrained
30-
// devices and communications
31-
// where the bandwidth and device memory may be limited resources.
32-
//
33-
// A device can report new measures to the IoT Platform using an HTTP GET request to the /iot/d path with the following query parameters:
34-
//
35-
// i (device ID): Device ID (unique for the API Key).
36-
// k (API Key): API Key for the service the device is registered on.
37-
// t (timestamp): Timestamp of the measure. Will override the automatic IoTAgent timestamp (optional).
38-
// d (Data): Ultralight 2.0 payload.
39-
//
40-
// At the moment the API key and timestamp are unused by the simulator.
26+
function ultralightToXML(key, deviceId, state) {
27+
const keyValuePairs = state.split('|');
28+
let payload = '';
29+
30+
payload = payload + '<measure device="' + deviceId + '" key="' + key + '">\n';
31+
for (let i = 0; i < keyValuePairs.length; i = i + 2) {
32+
payload =
33+
payload +
34+
'<' +
35+
keyValuePairs[i] +
36+
' value="' +
37+
keyValuePairs[i + 1] +
38+
'"/>\n';
39+
}
40+
payload = payload + '</measure>';
41+
return payload;
42+
}
4143

4244
class XMLMeasure {
4345
constructor(headers) {
44-
this.headers = headers;
46+
this.headers = headers;
4547
this.headers['Content-Type'] = 'application/xml';
4648
}
4749

4850
// measures sent over HTTP are POST requests with params
4951
sendAsHTTP(deviceId, state) {
52+
const payload = ultralightToXML(DEVICE_API_KEY, deviceId, state);
5053
const options = {
5154
method: 'POST',
5255
url: IOT_AGENT_URL,
53-
qs: { k: DEVICE_API_KEY, i: deviceId },
5456
headers: this.headers,
55-
body: state
57+
body: payload
5658
};
57-
const debugText =
58-
'POST ' + IOT_AGENT_URL + '?i=' + options.qs.i + '&k=' + options.qs.k;
59+
const debugText = 'POST ' + IOT_AGENT_URL;
5960

6061
request(options, error => {
6162
if (error) {
6263
debug(debugText + ' ' + error.code);
6364
}
6465
});
65-
SOCKET_IO.emit('http', debugText + ' ' + state);
66+
67+
SOCKET_IO.emit(
68+
'http',
69+
debugText +
70+
'<br/> ' +
71+
payload
72+
.replace(/</g, '&lt;')
73+
.replace(/>/g, '&gt;')
74+
.replace(/\n/g, '<br/>')
75+
);
6676
}
6777

6878
// measures sent over MQTT are posted as topics (motion sensor, lamp and door)
@@ -72,5 +82,4 @@ class XMLMeasure {
7282
}
7383
}
7484

75-
module.exports = XMLMeasure;
76-
85+
module.exports = XMLMeasure;

0 commit comments

Comments
 (0)