Skip to content

Commit

Permalink
Feature/xml support (#18)
Browse files Browse the repository at this point in the history
* XML Measure Support
* XML Command Support
* Run prettier
* Remove irrelevant comment
  • Loading branch information
jason-fox committed Nov 22, 2019
1 parent 02ea8f5 commit ddeed19
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 110 deletions.
9 changes: 1 addition & 8 deletions context-provider/controllers/iot/command/json.js
@@ -1,14 +1,12 @@
// Connect to an IoT Agent and use fallback values if necessary


const IoTDevices = require('../devices');
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';

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


/* global MQTT_CLIENT */

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

function getResult (cmd, status){
function getResult(cmd, status) {
const result = {};
result[cmd] = status;
return JSON.stringify(result);
Expand All @@ -43,7 +41,6 @@ class JSONCommand {
// this will briefly set the bell to on.
// The bell is not a sensor - it will not report state northbound
actuateBell(req, res) {

const command = getJSONCommand(req.body);
const deviceId = 'bell' + req.params.id;

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


if (IoTDevices.notFound(deviceId)) {
return res.status(404).send(getResult(command, NOT_OK));
Expand Down Expand Up @@ -110,7 +106,4 @@ class JSONCommand {
}
}



module.exports = JSONCommand;

5 changes: 0 additions & 5 deletions context-provider/controllers/iot/command/ultralight.js
@@ -1,10 +1,8 @@
// Connect to an IoT Agent and use fallback values if necessary


const IoTDevices = require('../devices');
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';


// A series of constants used by our set of devices
const OK = ' OK';
const NOT_OK = ' NOT OK';
Expand Down Expand Up @@ -117,7 +115,4 @@ class UltralightCommand {
}
}



module.exports = UltralightCommand;

88 changes: 29 additions & 59 deletions context-provider/controllers/iot/command/xml.js
@@ -1,131 +1,101 @@
// Connect to an IoT Agent and use fallback values if necessary


const IoTDevices = require('../devices');
const DEVICE_API_KEY = process.env.DUMMY_DEVICES_API_KEY || '1234';

const debug = require('debug')('tutorial:xml');

const xmlParser = require('xml-parser');

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

const OK = 'success';
const NOT_OK = 'error';

/* global MQTT_CLIENT */

//
// Splits the deviceId from the command sent.
//
function getXMLCommand(string) {
const command = string.split('@');
if (command.length === 1) {
command.push('');
function getResult(status, command, id, info) {
if (info) {
return '<' + status + ' command="' + command + '" device="' + id + '">' + info + '</' + status + '/>';
}
return command[1];
return '<' + status + ' command="' + command + '" device="' + id + '"/>';
}

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

// XML 2.0 is a lightweight text based protocol aimed to constrained
// devices and communications
// where the bandwidth and device memory may be limited resources.
//
// 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:
//
// i (device ID): Device ID (unique for the API Key).
// k (API Key): API Key for the service the device is registered on.
// t (timestamp): Timestamp of the measure. Will override the automatic IoTAgent timestamp (optional).
// d (Data): XML 2.0 payload.
//
// At the moment the API key and timestamp are unused by the simulator.

class XMLCommand {
// The bell will respond to the "ring" command.
// this will briefly set the bell to on.
// The bell is not a sensor - it will not report state northbound
actuateBell(req, res) {


debug(req.body)


const keyValuePairs = req.body.split('|') || [''];
const command = getXMLCommand(keyValuePairs[0]);
const deviceId = 'bell' + req.params.id;
const result = keyValuePairs[0] + '| ' + command;
const data = xmlParser(req.body);
const deviceId = data.root.attributes.device;
const command = data.root.name;

if (IoTDevices.notFound(deviceId)) {
return res.status(404).send(result + NOT_OK);
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
} else if (IoTDevices.isUnknownCommand('bell', command)) {
return res.status(422).send(result + NOT_OK);
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
}

// Update device state
IoTDevices.actuateDevice(deviceId, command);
return res.status(200).send(result + OK);
return res.status(200).send(getResult(OK, command, deviceId));
}

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

if (IoTDevices.notFound(deviceId)) {
return res.status(404).send(result + NOT_OK);
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
} else if (IoTDevices.isUnknownCommand('door', command)) {
return res.status(422).send(result + NOT_OK);
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
}

// Update device state
IoTDevices.actuateDevice(deviceId, command);
return res.status(200).send(result + OK);
return res.status(200).send(getResult(OK, command, deviceId));
}

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

if (IoTDevices.notFound(deviceId)) {
return res.status(404).send(result + NOT_OK);
return res.status(404).send(getResult(NOT_OK, command, deviceId, 'not found'));
} else if (IoTDevices.isUnknownCommand('lamp', command)) {
return res.status(422).send(result + NOT_OK);
return res.status(422).send(getResult(NOT_OK, command, deviceId, 'unknown command'));
}

// Update device state
IoTDevices.actuateDevice(deviceId, command);
return res.status(200).send(result + OK);
return res.status(200).send(getResult(OK, command, deviceId));
}

// cmd topics are consumed by the actuators (bell, lamp and door)
processMqttMessage(topic, message) {
const path = topic.split('/');
if (path.pop() === 'cmd') {
const keyValuePairs = message.split('|') || [''];
const command = getXMLCommand(keyValuePairs[0]);
const deviceId = path.pop();
const result = keyValuePairs[0] + '| ' + command;
const data = xmlParser(message);
const deviceId = data.root.attributes.device;
const command = data.root.name;

if (!IoTDevices.notFound(deviceId)) {
IoTDevices.actuateDevice(deviceId, command);
const topic = '/' + DEVICE_API_KEY + '/' + deviceId + '/cmdexe';
MQTT_CLIENT.publish(topic, result + OK);
MQTT_CLIENT.publish(topic, getResult(OK, command, deviceId));
}
}
}
}



module.exports = XMLCommand;

11 changes: 3 additions & 8 deletions context-provider/controllers/iot/measure/json.js
Expand Up @@ -16,12 +16,9 @@ const IOT_AGENT_URL =
IOT_AGENT_SOUTH_PORT +
IOT_AGENT_DEFAULT_RESOURCE;



/* global SOCKET_IO */
/* global MQTT_CLIENT */


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


function ultralightToJSON(state) {
const keyValuePairs = state.split('|');
const obj = {};
Expand All @@ -51,7 +47,7 @@ function ultralightToJSON(state) {

class JSONMeasure {
constructor(headers) {
this.headers = headers;
this.headers = headers;
this.headers['Content-Type'] = 'application/json';
}

Expand All @@ -62,7 +58,7 @@ class JSONMeasure {
url: IOT_AGENT_URL,
qs: { k: DEVICE_API_KEY, i: deviceId },
headers: this.headers,
body: ultralightToJSON(state)
body: ultralightToJSON(state)
};
const debugText =
'POST ' + IOT_AGENT_URL + '?i=' + options.qs.i + '&k=' + options.qs.k;
Expand All @@ -82,5 +78,4 @@ class JSONMeasure {
}
}

module.exports = JSONMeasure;

module.exports = JSONMeasure;
8 changes: 2 additions & 6 deletions context-provider/controllers/iot/measure/ultralight.js
Expand Up @@ -16,12 +16,9 @@ const IOT_AGENT_URL =
IOT_AGENT_SOUTH_PORT +
IOT_AGENT_DEFAULT_RESOURCE;



/* global SOCKET_IO */
/* global MQTT_CLIENT */


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

class UltralightMeasure {
constructor(headers) {
this.headers = headers;
this.headers = headers;
this.headers['Content-Type'] = 'text/plain';
}

Expand Down Expand Up @@ -72,5 +69,4 @@ class UltralightMeasure {
}
}

module.exports = UltralightMeasure;

module.exports = UltralightMeasure;
57 changes: 33 additions & 24 deletions context-provider/controllers/iot/measure/xml.js
Expand Up @@ -16,53 +16,63 @@ const IOT_AGENT_URL =
IOT_AGENT_SOUTH_PORT +
IOT_AGENT_DEFAULT_RESOURCE;



/* global SOCKET_IO */
/* global MQTT_CLIENT */


// This processor sends ultralight payload northbound to
// This processor sends XML payloads northbound to
// the southport of the IoT Agent and sends measures
// for the motion sensor, door and lamp.

// Ultralight 2.0 is a lightweight text based protocol aimed to constrained
// devices and communications
// where the bandwidth and device memory may be limited resources.
//
// 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:
//
// i (device ID): Device ID (unique for the API Key).
// k (API Key): API Key for the service the device is registered on.
// t (timestamp): Timestamp of the measure. Will override the automatic IoTAgent timestamp (optional).
// d (Data): Ultralight 2.0 payload.
//
// At the moment the API key and timestamp are unused by the simulator.
function ultralightToXML(key, deviceId, state) {
const keyValuePairs = state.split('|');
let payload = '';

payload = payload + '<measure device="' + deviceId + '" key="' + key + '">\n';
for (let i = 0; i < keyValuePairs.length; i = i + 2) {
payload =
payload +
'<' +
keyValuePairs[i] +
' value="' +
keyValuePairs[i + 1] +
'"/>\n';
}
payload = payload + '</measure>';
return payload;
}

class XMLMeasure {
constructor(headers) {
this.headers = headers;
this.headers = headers;
this.headers['Content-Type'] = 'application/xml';
}

// measures sent over HTTP are POST requests with params
sendAsHTTP(deviceId, state) {
const payload = ultralightToXML(DEVICE_API_KEY, deviceId, state);
const options = {
method: 'POST',
url: IOT_AGENT_URL,
qs: { k: DEVICE_API_KEY, i: deviceId },
headers: this.headers,
body: state
body: payload
};
const debugText =
'POST ' + IOT_AGENT_URL + '?i=' + options.qs.i + '&k=' + options.qs.k;
const debugText = 'POST ' + IOT_AGENT_URL;

request(options, error => {
if (error) {
debug(debugText + ' ' + error.code);
}
});
SOCKET_IO.emit('http', debugText + ' ' + state);

SOCKET_IO.emit(
'http',
debugText +
'<br/> ' +
payload
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br/>')
);
}

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

module.exports = XMLMeasure;

module.exports = XMLMeasure;

0 comments on commit ddeed19

Please sign in to comment.