diff --git a/mdot/mDotServer.censis/mDotServer.censis/.vs/HIE/v14/.suo b/mdot/mDotServer.censis/mDotServer.censis/.vs/HIE/v14/.suo index 8c8e1f5..81bd0a6 100644 Binary files a/mdot/mDotServer.censis/mDotServer.censis/.vs/HIE/v14/.suo and b/mdot/mDotServer.censis/mDotServer.censis/.vs/HIE/v14/.suo differ diff --git a/mdot/mDotServer.censis/mDotServer.censis/app.js b/mdot/mDotServer.censis/mDotServer.censis/app.js index d53cc76..d89e5a5 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/app.js +++ b/mdot/mDotServer.censis/mDotServer.censis/app.js @@ -79,7 +79,11 @@ var heartBeat = function() { var app = express(); -app.set('port', process.env.PORT || 4545); +var port = (process.env.VCAP_APP_PORT || 3010); +var host = (process.env.VCAP_APP_HOST || 'localhost'); + + +app.set('port', port); app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(morgan('combined')); @@ -140,8 +144,6 @@ if (isProduction) { heartBeat(); } -var port = (process.env.VCAP_APP_PORT || 3011); -var host = (process.env.VCAP_APP_HOST || 'localhost'); app.get('*', function(req, res) { res.status(404).render('404',{delimiter: '^'}); diff --git a/mdot/mDotServer.censis/mDotServer.censis/app/js/mdot.js b/mdot/mDotServer.censis/mDotServer.censis/app/js/mdot.js index 61d4e50..5c1a420 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/app/js/mdot.js +++ b/mdot/mDotServer.censis/mDotServer.censis/app/js/mdot.js @@ -162,6 +162,8 @@ skipOccupancy = true; + + _(events.data).each(function(i) { var _occupancy = 0; @@ -175,15 +177,20 @@ } } + var recordedOccupancy = this.findOccupancy(i.timestamp, events.occupancy); + tempCollection.add({ - dt: Date.create(i.timestamp).addHours(1), - lux: i.lux, - temp: i.temp, - co2: i.co2, - humid: i.humidity, - noise: i.sound, - occupancy: _occupancy - }); + //Dt: Date.create(i.timestamp).addHours(1), + //dt: new Date(i.timestamp).addHours(1), + dt: i.timestamp, + lux: i.lux, + temp: i.temp, + co2: i.co2, + humid: i.humidity, + noise: i.sound, + occupancy: _occupancy, + recordedOccupancy: recordedOccupancy + }); if (_occupancy !== 0) { const mm = minmaxes[_occupancy.toString()]; @@ -305,16 +312,16 @@ if (this.model.has('device')) { var http = location.protocol; console.log(location); - var slashes = http.concat("//"); + var slashes = http.concat('//'); var host = slashes.concat(window.location.hostname); - if (location.port !== "") { + if (location.port !== '') { host = host + ':' + location.port; } - host = 'https://mdotserver.mybluemix.net'; + host = ['https://mdotserver.mybluemix.net','http://52.211.111.57']; - this.collection.url = host + '/apiv2/mdot/' + this.model.get('device'); + this.collection.url = host[1] + '/apiv2/mdot/' + this.model.get('device'); $('#output').empty(); this.collection.fetch(fetchObj); } else { @@ -361,11 +368,20 @@ doChartV2: function(chartData) { console.time('doChartV2'); var chart = new AmCharts.AmStockChart(); - chart.categoryAxesSettings.minPeriod = '15mm'; + chart.categoryAxesSettings.minPeriod = 'ss'; chart.categoryAxesSettings.minorGridEnabled = true; - chart.theme = 'dark'; + chart.categoryAxesSettings.parseDates = true; + //Chart.categoryAxesSettings.dataDateFormats = "DD-MM-YYYY,JJ:NN:SS"; + //chart.categoryAxesSettings.dateFormats = "DD-MM-YYYY"; + //chart.categoryAxis = new AmCharts.CategoryAxis(); + //chart.categoryAxis.parseDates = true; + //chart.categoryAxis.minPeriod = "hh"; + + chart.theme = 'dark'; + var chartColors = ['#ffff66','#ff9933', '#ff66b3', '#80dfff', '#33ffad', '#ff0063']; //Cs1, cs2, cs3, cs4, cs5, cs6, + // DATASETS ////////////////////////////////////////// // create data sets first var dataSet1 = new AmCharts.DataSet(); @@ -389,11 +405,16 @@ { fromField: 'occupancy', toField: 'occupancy' - }]; + }, + { + fromField: 'recordedOccupancy', + toField: 'recordedOccupancy' + }]; dataSet1.dataProvider = chartData; dataSet1.categoryField = 'date'; + // Set data sets to the chart chart.dataSets = [dataSet1]; @@ -404,20 +425,21 @@ stockPanel1.title = 'Environment'; stockPanel1.percentHeight = 60; - // add value axes + // Add value axes var valueAxis1 = new AmCharts.ValueAxis(); - valueAxis1.axisColor = '#b3b3ff'; - valueAxis1.color = '#b3b3ff'; + valueAxis1.axisColor = chartColors[0]; + valueAxis1.color = chartColors[0]; valueAxis1.offset = 0; valueAxis1.minorGridEnabled = true; valueAxis1.minorTickLength = 8; - + // ValueAxis1.title = '�c'; + // valueAxis1.titleColor = chartColors[0]; stockPanel1.addValueAxis(valueAxis1); var valueAxis2 = new AmCharts.ValueAxis(); valueAxis2.position = 'right'; - valueAxis2.axisColor = '#ff66b3'; - valueAxis2.color = '#ff66b3'; + valueAxis2.axisColor = chartColors[1]; + valueAxis2.color = chartColors[1]; valueAxis2.offset = 0; valueAxis2.minorGridEnabled = true; valueAxis2.minorTickLength = 8; @@ -425,8 +447,8 @@ var valueAxis3 = new AmCharts.ValueAxis(); valueAxis3.position = 'right'; - valueAxis3.axisColor = '#e600e6'; - valueAxis3.color = '#e600e6'; + valueAxis3.axisColor = chartColors[2]; + valueAxis3.color = chartColors[2]; valueAxis3.offset = 50; valueAxis3.minorGridEnabled = true; valueAxis3.minorTickLength = 8; @@ -434,8 +456,8 @@ var valueAxis4 = new AmCharts.ValueAxis(); valueAxis4.position = 'left'; - valueAxis4.axisColor = '#80dfff'; - valueAxis4.color = '#80dfff'; + valueAxis4.axisColor = chartColors[3]; + valueAxis4.color = chartColors[3]; valueAxis4.offset = 50; valueAxis4.minorGridEnabled = true; valueAxis4.minorTickLength = 8; @@ -445,12 +467,19 @@ var graph1 = new AmCharts.StockGraph(); graph1.title = 'CO2'; graph1.valueField = 'co2'; + graph1.type = 'smoothedLine'; graph1.lineThickness = 2; - graph1.lineColor = '#b3b3ff'; + graph1.lineColor = chartColors[0]; + graph1.fillColor = chartColors[0]; + graph1.fillAlphas = 0.1; graph1.useDataSetColors = false; graph1.valueAxis = valueAxis1; stockPanel1.addStockGraph(graph1); + stockPanel1.categoryAxis.parseDates = true; + stockPanel1.categoryAxis.color = '#ffffff'; + + // Create stock legend stockPanel1.stockLegend = new AmCharts.StockLegend(); stockPanel1.stockLegend.color = '#f0f5f5'; @@ -459,8 +488,11 @@ var graph2 = new AmCharts.StockGraph(); graph2.title = 'Humidity'; graph2.valueField = 'humid'; + graph2.type = 'smoothedLine'; graph2.lineThickness = 2; - graph2.lineColor = '#ff66b3'; + graph2.lineColor = chartColors[1]; + graph2.fillColor = chartColors[1]; + graph2.fillAlphas = 0.1; graph2.useDataSetColors = false; graph2.valueAxis = valueAxis2; stockPanel1.addStockGraph(graph2); @@ -468,8 +500,11 @@ var graph3 = new AmCharts.StockGraph(); graph3.title = 'LUX'; graph3.valueField = 'lux'; + graph3.type = 'smoothedLine'; graph3.lineThickness = 2; - graph3.lineColor = '#e600e6'; + graph3.lineColor = chartColors[2]; + graph3.fillColor = chartColors[2]; + graph3.fillAlphas = 0.1; graph3.useDataSetColors = false; graph3.valueAxis = valueAxis3; stockPanel1.addStockGraph(graph3); @@ -477,12 +512,47 @@ var graph4 = new AmCharts.StockGraph(); graph4.title = 'Temperature'; graph4.valueField = 'temp'; + graph4.type = 'smoothedLine'; graph4.lineThickness = 2; - graph4.lineColor = '#80dfff'; + graph4.lineColor = chartColors[3]; + graph4.fillColor = chartColors[3]; + graph4.fillAlphas = 0.1; graph4.useDataSetColors = false; graph4.valueAxis = valueAxis4; stockPanel1.addStockGraph(graph4); + stockPanel1.showCategoryAxis = true; + + stockPanel1.allLabels = [{ + x: 60, + y: 0, + text: 'ppm', + size: 12, + color: chartColors[0] + }, + { + x: 760, + y: 0, + text: '%', + size: 12, + color: chartColors[1] + }, + { + x: 810, + y: 0, + text: 'LUX', + size: 12, + color: chartColors[2] + }, + { + x: 0, + y: 0, + text: 'Celsius', + size: 12, + color: chartColors[3] + }]; + + // Set panels to the chart chart.panels = [stockPanel1]; @@ -490,28 +560,42 @@ var stockPanel2 = new AmCharts.StockPanel(); stockPanel2.showCategoryAxis = false; stockPanel2.title = 'Occupancy'; - stockPanel2.percentHeight = 60; + stockPanel2.percentHeight = 30; + //StockPanel2.height = 20; var valueAxis5 = new AmCharts.ValueAxis(); valueAxis5.position = 'left'; - valueAxis5.axisColor = '#33ffad'; - valueAxis5.color = '#33ffad'; + valueAxis5.axisColor = chartColors[4]; + valueAxis5.color = chartColors[4]; valueAxis5.offset = 0; valueAxis5.minorGridEnabled = true; valueAxis5.minorTickLength = 8; stockPanel2.addValueAxis(valueAxis5); var graph5 = new AmCharts.StockGraph(); - graph5.title = 'Count'; - graph5.type = 'column'; + graph5.title = 'Calculated'; + graph5.type = 'step'; graph5.valueField = 'occupancy'; - graph5.cornerRadiusTop = 1; - graph5.fillAlphas = 1; - graph5.lineColor = '#33ffad'; + //Graph5.cornerRadiusTop = 1; + graph5.fillAlphas = 0.2; + graph5.fillColor = chartColors[4]; + graph5.lineColor = chartColors[4]; graph5.useDataSetColors = false; - stockPanel2.addStockGraph(graph5); + + var graph6 = new AmCharts.StockGraph(); + graph6.title = 'Recorded'; + graph6.type = 'step'; + graph6.valueField = 'recordedOccupancy'; + graph6.cornerRadiusTop = 1; + graph6.fillAlphas = 0.2; + graph6.lineColor = chartColors[5]; + graph6.fillColor = chartColors[5]; + graph6.useDataSetColors = false; + + stockPanel2.addStockGraph(graph6); + // Create stock legend stockPanel2.stockLegend = new AmCharts.StockLegend(); stockPanel2.stockLegend.color = '#f0f5f5'; @@ -564,7 +648,8 @@ lux: i.get('lux'), noise: i.get('noise'), temp: i.get('temp'), - occupancy: i.get('occupancy') + occupancy: i.get('occupancy'), + recordedOccupancy: i.get('recordedOccupancy') }; if (filter.length >= max) { @@ -663,16 +748,7 @@ views.co2Widget = new Widget({model: webSocketModel, id: 'widget-co2', el: $('#widget-co2')}); views.humidityWidget = new Widget({model: webSocketModel, id: 'widget-humidity', el: $('#widget-humidity')}); views.luxWidget = new Widget({model: webSocketModel, id: 'widget-lux', el: $('#widget-lux')}); - - - navigator.geolocation.getCurrentPosition(function(position) { - var latitude = position.coords.latitude; - var longitude = position.coords.longitude; - - views.weather = new Weather({model: new WeatherModel({lat: latitude, long: longitude})}); - }, - function(e) {console.error(e.code + ' / ' + e.message);}); - + views.weather = new Weather({model: new WeatherModel({lat: 55.864237, long: -4.251806})}); diff --git a/mdot/mDotServer.censis/mDotServer.censis/app/js/websocket.js b/mdot/mDotServer.censis/mDotServer.censis/app/js/websocket.js index 480809a..a6472ef 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/app/js/websocket.js +++ b/mdot/mDotServer.censis/mDotServer.censis/app/js/websocket.js @@ -1,15 +1,16 @@ var WEBSOCKET = function(model) { - var wsUrl = 'ws://localhost'; + var wsUrl = ['localhost','mdotserver.mybluemix.net','52.211.111.57']; var wsPort = 3011; - if ('https:' === document.location.protocol) { - wsUrl = 'wss://mdotserver.mybluemix.net/'; - wsPort = ''; - } else { - //wsUrl = 'ws://localhost:3001'; - wsUrl = 'ws://mdotserver.mybluemix.net/'; - wsPort = ''; - } + + if ('https:' === document.location.protocol) { + wsUrl = 'wss://' + wsUrl[2] + '/'; + wsPort = ''; + } else { + //wsUrl = 'ws://localhost:3001'; + wsUrl = 'ws://' + wsUrl[2] + '/'; + wsPort = ''; + } this.socket = null; this.timer = 0; @@ -18,7 +19,6 @@ var WEBSOCKET = function(model) { this.startWebSocket = function() { 'use strict'; - var url = (wsPort === '') ? wsUrl : wsUrl + ':' + wsPort; console.log('Starting socket', url); var wsCtor = window['MozWebSocket'] ? MozWebSocket : WebSocket; diff --git a/mdot/mDotServer.censis/mDotServer.censis/bower.json b/mdot/mDotServer.censis/mDotServer.censis/bower.json index 5bd378f..00775d2 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/bower.json +++ b/mdot/mDotServer.censis/mDotServer.censis/bower.json @@ -1,5 +1,5 @@ { - "name": "sodashserver", + "name": "mDotServer", "description": "Smart Office Dashboard Server", "main": "index.js", "authors": [ @@ -17,7 +17,7 @@ ], "dependencies": { "chroma-js": "^1.1.1", - "jquery": "^2.2.3", + "jquery": "^3.1.1", "mui": "^0.6.8", "sugarjs-date": "^1.5.1", "backbone": "^1.3.3", diff --git a/mdot/mDotServer.censis/mDotServer.censis/gulpfile.js b/mdot/mDotServer.censis/mDotServer.censis/gulpfile.js index dd9255e..2b1d80c 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/gulpfile.js +++ b/mdot/mDotServer.censis/mDotServer.censis/gulpfile.js @@ -57,16 +57,16 @@ gulp.task('appJS', function() { gulp.task('vendor', function() { - return gulp.src(['src/bower_modules/mui/packages/cdn/js/mui.min.js', - 'src/bower_modules/jquery/dist/jquery.min.js', - 'src/bower_modules/base64/base64.min.js', - 'src/bower_modules/underscore/underscore-min.js', - 'src/bower_modules/backbone/backbone-min.js', - 'src/bower_modules/sugarjs-date/sugar-date.min.js', - 'src/bower_modules/notification-js/build/notification.min.js', - 'src/bower_modules/amstock3/amcharts/amcharts.js', - 'src/bower_modules/amstock3/amcharts/serial.js', - 'src/bower_modules/amstock3/amcharts/amstock.js', + return gulp.src(['bower_components/mui/packages/cdn/js/mui.min.js', + 'bower_components/jquery/dist/jquery.min.js', + 'bower_components/base64/base64.min.js', + 'bower_components/underscore/underscore-min.js', + 'bower_components/backbone/backbone-min.js', + 'bower_components/sugarjs-date/sugar-date.min.js', + 'bower_components/notification-js/build/notification.min.js', + 'bower_components/amstock3/amcharts/amcharts.js', + 'bower_components/amstock3/amcharts/serial.js', + 'bower_components/amstock3/amcharts/amstock.js', 'app/lib/themes/dark.js', 'app/lib/dateTime.js']) .pipe(concat('vendor.js')) diff --git a/mdot/mDotServer.censis/mDotServer.censis/process.json b/mdot/mDotServer.censis/mDotServer.censis/process.json index 0626314..8d7eafa 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/process.json +++ b/mdot/mDotServer.censis/mDotServer.censis/process.json @@ -1,20 +1,46 @@ { "apps": [ { - "name": "SODashServer", + "name": "mDotServer", "script": "app.js", - "cwd": "/home/azureuser/live", + "cwd": "/home/ubuntu/live/mDotServer", "watch": true, "instances": 1, + env: { + "NODE_ENV": "development" + }, + env_production : { + "NODE_ENV": "production" + }, "exec_mode": "cluster", "combine_logs": true, - "max_memory_restart": "150M", + "max_memory_restart": "350M", "restart_delay": 5000, "ignore_watch": [ "[\\/\\\\]\\./", "node_modules", - "server/static" + "dist" ] - } + },{ + "name": "mqttArchiver", + "script": "app.js", + "cwd": "/home/ubuntu/live/mqttArchiver", + "watch": true, + "instances": 1, + env: { + "NODE_ENV": "development" + }, + env_production : { + "NODE_ENV": "production" + }, + "exec_mode": "cluster", + "combine_logs": true, + "max_memory_restart": "350M", + "restart_delay": 5000, + "ignore_watch": [ + "[\\/\\\\]\\./", + "node_modules" + ] + } ] } diff --git a/mdot/mDotServer.censis/mDotServer.censis/views/graph-release.ejs b/mdot/mDotServer.censis/mDotServer.censis/views/graph-release.ejs index a162cf2..966dd34 100644 --- a/mdot/mDotServer.censis/mDotServer.censis/views/graph-release.ejs +++ b/mdot/mDotServer.censis/mDotServer.censis/views/graph-release.ejs @@ -48,7 +48,7 @@
Alert logged at ' + dateFormat(now, "dddd, mmmm dS, yyyy, HH:MM:ss") + '
' + contents + ''; + + mailer.sendHTML(email, msg, function(err){ + if(err) { + logger.error(err); + throw err; + } + logger.info('email sent!'); + }); + + + } +}; diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/mqtt/IoTFconnector.js b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/mqtt/IoTFconnector.js new file mode 100644 index 0000000..146f228 --- /dev/null +++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/mqtt/IoTFconnector.js @@ -0,0 +1,130 @@ + + + +var IoTFconnector = function (orgId, api_key, auth_token, $rootScope) { + + + //this.connected = ''; + this.clientId = "a:" + orgId + ":" + Date.now(); + + console.log("clientId: " + this.clientId); + this.hostname = orgId + ".messaging.internetofthings.ibmcloud.com"; + this.client = ''; + + this.initialize = function () { + + client = new Messaging.Client(this.hostname, 8883, this.clientId); + + + client.onMessageArrived = function (msg) { + console.log("Message from :" + msg.destinationName); + }; + + var connectOptions = new Object(); + connectOptions.keepAliveInterval = 3600; + connectOptions.useSSL = true; + connectOptions.userName = api_key; + connectOptions.password = auth_token; + + connectOptions.onSuccess = function () { + IoTFconnector.prototype.clientStatus.connected = true; + // $rootScope.$broadcast("clientStatusUpdated", clientStatus); + console.log("MQTT connected to host: " + client.host + " port : " + client.port + " at " + Date.now()); + + } + + connectOptions.onFailure = function (e) { + console.log("MQTT connection failed at " + Date.now() + "\nerror: " + e.errorCode + " : " + e.errorMessage); + } + + console.log("about to connect to " + client.host); + client.connect(connectOptions); + + //client = new Messaging.Client(this.hostname, 8883, this.clientId); + + //// Initialize the Realtime Graph + ////var rtGraph = new RealtimeGraph(); + //client.onMessageArrived = function(msg) { + // //var topic = msg.destinationName; + + // //var payload = JSON.parse(msg.payloadString); + // ////First message, instantiate the graph + // //if (firstMessage) { + // // $('#chart').empty(); + // // firstMessage = false; + // // rtGraph.displayChart(null, payload); + // //} else { + // // rtGraph.graphData(payload); + // //} + // console.log("Message from :" + msg.destinationName); + //}; + + //client.onConnectionLost = function(e) { + // console.log("Connection Lost at " + Date.now() + " : " + e.errorCode + " : " + e.errorMessage); + // this.connect(connectOptions); + //} + + //var connectOptions = new Object(); + //connectOptions.keepAliveInterval = 3600; + //connectOptions.useSSL = true; + //connectOptions.userName = api_key; + //connectOptions.password = auth_token; + + //connectOptions.onSuccess = function() { + // IoTFconnector.prototype.connected = true; + // console.log("MQTT connected to host: " + client.host + " port : " + client.port + " at " + Date.now()); + + //} + + //connectOptions.onFailure = function(e) { + // console.log("MQTT connection failed at " + Date.now() + "\nerror: " + e.errorCode + " : " + e.errorMessage); + //} + + //console.log("about to connect to " + client.host); + //client.connect(connectOptions); + } + + + this.initialize(); + + //var imageHTML = '
+ * Most applications will create just one Client object and then call its connect() method, + * however applications can create more than one Client object if they wish. + * In this case the combination of host, port and clientId attributes must be different for each Client object. + *
+ * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods + * (even though the underlying protocol exchange might be synchronous in nature). + * This means they signal their completion by calling back to the application, + * via Success or Failure callback functions provided by the application on the method in question. + * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime + * of the script that made the invocation. + *
+ * In contrast there are some callback functions most notably onMessageArrived + * that are defined on the Messaging.Client object. + * These may get called multiple times, and aren't directly related to specific method invocations made by the client. + * + * @name Messaging.Client + * + * @constructor + * Creates a Messaging.Client object that can be used to communicate with a Messaging server. + * + * @param {string} host the address of the messaging server, as a DNS name or dotted decimal IP address. + * @param {number} port the port number in the host to connect to. + * @param {string} clientId the Messaging client identifier, between 1 and 23 characters in length. + * + * @property {string} host read only the server's DNS hostname or dotted decimal IP address. + * @property {number} port read only the server's port. + * @property {string} clientId read only used when connecting to the server. + * @property {function} onConnectionLost called when a connection has been lost, + * after a connect() method has succeeded. + * Establish the call back used when a connection has been lost. The connection may be + * lost because the client initiates a disconnect or because the server or network + * cause the client to be disconnected. The disconnect call back may be called without + * the connectionComplete call back being invoked if, for example the client fails to + * connect. + * A single response object parameter is passed to the onConnectionLost callback containing the following fields: + *
+ * Properties of the connect options are: + * @config {number} [timeout] If the connect has not succeeded within this number of seconds, it is deemed to have failed. + * The default is 30 seconds. + * @config {string} [userName] Authentication username for this connection. + * @config {string} [password] Authentication password for this connection. + * @config {Messaging.Message} [willMessage] sent by the server when the client disconnects abnormally. + * @config {Number} [keepAliveInterval] the server disconnects this client if there is no activity for this + * number of seconds. The default value of 60 seconds is assumed if not set. + * @config {boolean} [cleanSession] if true(default) the client and server persistent state is deleted on successful connect. + * @config {boolean} [useSSL] if present and true, use an SSL Websocket connection. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the connect acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *
+ * @config {number} [qos] the maiximum qos of any publications sent as a result of making this subscription. + * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the subscribe acknowledgement has been received from the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *
+ * @config {object} [invocationContext] passed to the onSuccess callback or onFailure callback. + * @config {function} [onSuccess] called when the unsubscribe acknowledgement has been receive dfrom the server. + * A single response object parameter is passed to the onSuccess callback containing the following fields: + *
+ * Other programming languages, + * Java, + * C. + *
+ * All attributes may be null, which implies the default values. + * + * @name Messaging.Message + * @constructor + * @param {String|ArrayBuffer} payload The message data to be sent. + *
+ * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. + * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. + *
+ * @property {string} destinationName mandatory The name of the destination to which the message is to be sent + * (for messages about to be sent) or the name of the destination from which the message has been received. + * (for messages received by the onMessage function). + *
+ * @property {number} qos The Quality of Service used to deliver the message. + *
+ * @property {Boolean} retained If true, the message is to be retained by the server and delivered + * to both current and future subscriptions. + * If false the server only delivers the message to current subscribers, this is the default for new Messages. + * A received message has the retained boolean set to true if the message was published + * with the retained boolean set to true + * and the subscrption was made after the message has been published. + *
+ * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received.
+ * This is only set on messages received from the server.
+ *
+ */
+ var Message = function (newPayload) {
+ var payload;
+ if ( typeof newPayload === "string"
+ || newPayload instanceof ArrayBuffer
+ || newPayload instanceof Int8Array
+ || newPayload instanceof Uint8Array
+ || newPayload instanceof Int16Array
+ || newPayload instanceof Uint16Array
+ || newPayload instanceof Int32Array
+ || newPayload instanceof Uint32Array
+ || newPayload instanceof Float32Array
+ || newPayload instanceof Float64Array
+ ) {
+ payload = newPayload;
+ } else {
+ throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"]));
+ }
+
+ this._getPayloadString = function () {
+ if (typeof payload === "string")
+ return payload;
+ else
+ return parseUTF8(payload, 0, payload.length);
+ };
+
+ this._getPayloadBytes = function() {
+ if (typeof payload === "string") {
+ var buffer = new ArrayBuffer(UTF8Length(payload));
+ var byteStream = new Uint8Array(buffer);
+ stringToUTF8(payload, byteStream, 0);
+
+ return byteStream;
+ } else {
+ return payload;
+ };
+ };
+
+ var destinationName = undefined;
+ this._getDestinationName = function() { return destinationName; };
+ this._setDestinationName = function(newDestinationName) {
+ if (typeof newDestinationName === "string")
+ destinationName = newDestinationName;
+ else
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"]));
+ };
+
+ var qos = 0;
+ this._getQos = function() { return qos; };
+ this._setQos = function(newQos) {
+ if (newQos === 0 || newQos === 1 || newQos === 2 )
+ qos = newQos;
+ else
+ throw new Error("Invalid argument:"+newQos);
+ };
+
+ var retained = false;
+ this._getRetained = function() { return retained; };
+ this._setRetained = function(newRetained) {
+ if (typeof newRetained === "boolean")
+ retained = newRetained;
+ else
+ throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"]));
+ };
+
+ var duplicate = false;
+ this._getDuplicate = function() { return duplicate; };
+ this._setDuplicate = function(newDuplicate) { duplicate = newDuplicate; };
+ };
+
+ Message.prototype = {
+ get payloadString() { return this._getPayloadString(); },
+ get payloadBytes() { return this._getPayloadBytes(); },
+
+ get destinationName() { return this._getDestinationName(); },
+ set destinationName(newDestinationName) { this._setDestinationName(newDestinationName); },
+
+ get qos() { return this._getQos(); },
+ set qos(newQos) { this._setQos(newQos); },
+
+ get retained() { return this._getRetained(); },
+ set retained(newRetained) { this._setRetained(newRetained); },
+
+ get duplicate() { return this._getDuplicate(); },
+ set duplicate(newDuplicate) { this._setDuplicate(newDuplicate); }
+ };
+
+ // Module contents.
+ return {
+ Client: Client,
+ Message: Message
+ };
+};
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/pusher.js b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/pusher.js
new file mode 100644
index 0000000..c3a0177
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/pusher.js
@@ -0,0 +1,26 @@
+/**
+ *
+ * User: Martin Donnelly
+ * Date: 2016-04-08
+ * Time: 16:35
+ *
+ */
+var Pushover = require('node-pushover'), dateFormat = require('dateformat');
+var push = new Pushover({
+ token: "am7tuw221casnhf7uryx8dhxw6zg1t",
+ user: "BE2vgFxdHJw91lVGMRYvZDDmVa5cCM"
+});
+
+
+var logger = require('log4js').getLogger();
+
+const prefix = process.env.NODE_ENV === 'production' ? 'Production' : 'Dev';
+const title = 'MQTT ' + prefix + ' Archiver Alert';
+module.exports = {
+
+ push : function(contents) {
+ var now = new Date();
+ var msg = 'Alert logged at ' + dateFormat(now, "dddd, mmmm dS, yyyy, HH:MM:ss") + '\n\n' + contents + '';
+ push.send(title, msg);
+ }
+};
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-connector.js b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-connector.js
new file mode 100644
index 0000000..0b2b92b
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-connector.js
@@ -0,0 +1,34 @@
+'uses strict';
+/**
+ *
+ * User: Martin Donnelly
+ * Date: 2016-03-11
+ * Time: 10:22
+ *
+ */
+
+var pgp = require('pg-promise')();
+
+var localCN = {
+ host: 'localhost',
+ port: 5432,
+ database: 'mqttstore',
+ user: 'postgres',
+ password: ''
+};
+
+// ElephantSql settings
+
+var remoteCN = {
+ host: 'jumbo.db.elephantsql.com',
+ port: 5432,
+ database: 'vmlcokon',
+ user: 'vmlcokon',
+ password: 'PQUYLiIW4M6r7SWyZevrES_rRAULYFkp'
+};
+
+const cn = process.env.NODE_ENV === 'production' ? remoteCN : localCN;
+
+
+exports.dbConnection = pgp(cn);
+
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-historian.js b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-historian.js
new file mode 100644
index 0000000..2bd27a6
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-historian.js
@@ -0,0 +1,106 @@
+'use strict';
+var logger = require('log4js').getLogger();
+
+var Sugar = require('sugar/date');
+
+
+
+module.exports = function(db) {
+ var module = {};
+
+
+ module.sqlGetAllRaw = function(type, deviceId) {
+ return new Promise(function(resolve, reject) {
+ db.any('select distinct on (raw.timestamp) * from raw where type=$1 and device=$2 order by timestamp asc;', [type,deviceId])
+ .then(function(d) {
+ return resolve(d);
+ })
+ .catch((err)=> {
+ logger.error(err);
+ return reject(err);
+ });
+ });
+ };
+
+ module.sqlGetRangedRaw = function(params) {
+ return new Promise(function(resolve, reject) {
+ db.any('select distinct on (raw.timestamp) * from raw where type=$1 and device=$2 and timestamp between $3 and $4 order by timestamp asc;', [params.type, params.device, params.startTS, params.endTS])
+ .then(function(d) {
+ return resolve(d);
+ })
+ .catch((err)=> {
+ logger.error(err);
+ return reject(err);
+ });
+
+ });
+
+ };
+
+
+
+ module.doGet = function(params) {
+ var self = this;
+ var _obj = {};
+ var useRange=false;
+ return new Promise(function(resolve, reject) {
+ logger.debug('historian.doGet', params);
+
+
+ _obj.type = params.type;
+ _obj.device = params.device;
+
+ if (params.hasOwnProperty('start') && params.hasOwnProperty('end')) {
+
+ try{
+ _obj.startTS = new Sugar.Date(parseInt(params.start,10)).raw;
+ _obj.endTS = new Sugar.Date(parseInt(params.end,10)).raw;
+ useRange = true;
+ }
+ catch(err)
+ {
+ logger.error(err);
+ useRange = false;
+ }
+
+ if ((_obj.start === null) || (_obj.end === null))
+ {
+ useRange = false;
+ }
+
+ }
+
+ if (useRange) {
+
+ logger.info('Userange:',_obj);
+
+ self.sqlGetRangedRaw(_obj)
+ .then(function(d) {
+ resolve(d);
+ })
+ .catch(function(e) {
+ logger.error(e);
+ reject(e);
+ });
+
+ } else {
+
+ self.sqlGetAllRaw(_obj.type, _obj.device)
+ .then(function(d) {
+ resolve(d);
+ })
+ .catch(function(e) {
+ logger.error(e);
+ reject(e);
+ });
+
+ }
+
+
+ });
+ };
+
+
+ return module;
+};
+
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-save.js b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-save.js
new file mode 100644
index 0000000..a1108de
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/lib/server/db-save.js
@@ -0,0 +1,130 @@
+'use strict';
+
+var atob = require('atob');
+
+module.exports = function(db) {
+ var module = {};
+ module.deviceIds = ['CENSIS-LoRa-1','CENSIS-LoRa-2','CENSIS-LoRa-3','CENSIS-LoRa-4','HIE-mobile-1','HIE-demo','HIE-mobile-2','HIE-smart-campus-1','HIE-smart-campus-2','HIE-smart-campus-3','HIE-smart-campus-4','HIE-smart-campus-5','HIE-smart-campus-6','HIE-smart-campus-7','HIE-mDot-1'];
+
+ module.sqlInsertRawEvent = function(data) {
+ let _data = data;
+
+ return new Promise(function(resolve, reject) {
+ db.func('insert_raw',
+ [_data.timestamp, _data.data.type, _data.data.device, _data.data.event, _data.data])
+ .then(()=> {
+ return resolve('ok');
+ })
+ .catch((err)=> {
+ console.error(err);
+ return reject(err);
+ });
+ });
+ };
+
+ module.sqlInsertDecoded = function(data) {
+ let _data = data;
+
+ return new Promise(function(resolve, reject) {
+ db.func('insert_decoded',
+ [_data.deviceid, _data.timestamp, _data.lux, _data.co2, _data.temp, _data.humidity, _data.sound])
+ .then(()=> {
+ return resolve('ok');
+ })
+ .catch((err)=> {
+ console.error(err);
+ return reject(err);
+ });
+ });
+
+ };
+
+ module.addNewEvent = function(data) {
+ var self = this;
+ return new Promise((resolve, reject) => {
+
+ let _data = {};
+ _data.timestamp = new Date();
+ _data.data = data;
+
+ self.sqlInsertRawEvent(_data)
+ .then((d)=> {
+ //console.log('Postgres returns', d);
+ return resolve({reply: 'raw event inserted'});
+ })
+ .catch((err)=> {
+ console.error(err);
+ return reject(err);
+ });
+ });
+ };
+
+ module.addProcessedEvent = function(data) {
+
+ var self = this;
+ return new Promise((resolve, reject) => {
+
+ let _data = self.rawBreaker(data);
+
+ self.sqlInsertDecoded(_data)
+ .then((d)=> {
+ // console.log('Postgres returns', d);
+ return resolve({reply: 'Processed event inserted'});
+ })
+ .catch((err)=> {
+ console.error(err);
+ return reject(err);
+ });
+
+
+ });
+
+ };
+
+ module.decoder = function(data) {
+ var _obj = {};
+ var _data = atob(data).split('');
+
+ var bytes = _data.map(i => i.charCodeAt());
+
+ _obj.light = parseInt('0x' + ('0' + bytes[0]).substr(-2) + ('0' + bytes[1]).substr(-2));
+ _obj.co2 = parseInt(_data[2] + _data[3] + _data[4] + _data[5] + _data[6], 10);
+ _obj.temp = (parseInt(_data[7] + _data[8] + _data[9] + _data[10] + _data[11], 10) - 1000) / 10;
+ _obj.humid = (parseInt(_data[12] + _data[13] + _data[14] + _data[15] + _data[16], 10) / 10);
+ _obj.noise = parseInt('0x' + ('0' + bytes[17]).substr(-2) + ('0' + bytes[18]).substr(-2));
+ _obj.binData = bytes;
+ console.log(_obj);
+ return _obj;
+ };
+
+ module.rawBreaker = function(data) {
+ var self = this;
+ var workObj = {};
+
+ var device_name = data.topic.split('/')[4];
+ console.log('Device_name', device_name);
+ workObj.deviceid = self.deviceIds.indexOf(device_name);
+
+ if (data.hasOwnProperty('data')) {
+
+ var _data = self.decoder(data.data);
+
+ workObj.lux = _data.light;
+ workObj.co2 = _data.co2;
+ workObj.temp = _data.temp;
+ workObj.humidity = _data.humid;
+ workObj.sound = _data.noise;
+ workObj.timestamp = new Date();
+
+ return workObj;
+ } else {
+ console.error('Data does not have base64 data');
+
+ return null;
+ }
+
+ };
+
+ return module;
+};
+
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/manifest.yml b/mdot/mqttArchiver.censis/mqttArchiver.censis/manifest.yml
new file mode 100644
index 0000000..cb14492
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/manifest.yml
@@ -0,0 +1,8 @@
+applications:
+- path: .
+ memory: 256M
+ instances: 1
+ domain: mybluemix.net
+ name: mqttArchiver
+ host: mqttarchiver
+ disk_quota: 1024M
diff --git a/mdot/mqttArchiver.censis/mqttArchiver.censis/package.json b/mdot/mqttArchiver.censis/mqttArchiver.censis/package.json
new file mode 100644
index 0000000..05cdf1f
--- /dev/null
+++ b/mdot/mqttArchiver.censis/mqttArchiver.censis/package.json
@@ -0,0 +1,89 @@
+{
+ "name": "mqttArchiver",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "private": true,
+ "scripts": {
+ "start": "node app.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": {
+ "atob": "^2.0.3",
+ "basic-authentication": "^1.6.2",
+ "body-parser": "^1.15.2",
+ "cfenv": "1.0.x",
+ "cookie-parser": "^1.4.3",
+ "dateformat": "^1.0.12",
+ "ejs": "^2.5.1",
+ "errorhandler": "^1.4.3",
+ "events": "^1.1.1",
+ "express": "^4.14.0",
+ "express-session": "^1.14.1",
+ "express-session-lw": "^1.0.9",
+ "http": "0.0.0",
+ "http-post": "^0.1.1",
+ "log4js": "^0.6.36",
+ "method-override": "^2.3.6",
+ "morgan": "^1.7.0",
+ "mqtt": "^1.10.0",
+ "mqtt_over_websockets": "0.0.1",
+ "node-pushover": "^0.2.2",
+ "path": "^0.12.7",
+ "pg-promise": "^5.2.7",
+ "queue": "^4.0.0",
+ "request": "^2.72.0",
+ "requestify": "^0.2.3",
+ "routes": "^2.1.0",
+ "sugar": "^2.0.1",
+ "sugar-date": "^2.0.0",
+ "ultrases": "^0.1.3",
+ "websocket": "^1.0.22"
+ },
+ "devDependencies": {
+ "after": "^0.8.1",
+ "apn": "^1.7.8",
+ "apns": "^0.1.0",
+ "basic-authentication": "^1.6.2",
+ "chai": "^3.5.0",
+ "cheerio": "^0.20.0",
+ "clone": "^1.0.2",
+ "del": "^2.2.0",
+ "elapsed": "0.0.7",
+ "gulp": "^3.9.1",
+ "gulp-autoprefixer": "^3.1.0",
+ "gulp-cache": "^0.4.5",
+ "gulp-concat": "^2.6.0",
+ "gulp-cssmin": "^0.1.7",
+ "gulp-cssnano": "^2.1.2",
+ "gulp-debug": "^2.1.2",
+ "gulp-google-webfonts": "0.0.13",
+ "gulp-html-replace": "^1.5.5",
+ "gulp-htmlmin": "^2.0.0",
+ "gulp-inject": "^4.0.0",
+ "gulp-jshint": "^2.0.1",
+ "gulp-jsmin": "^0.1.5",
+ "gulp-livereload": "^3.8.1",
+ "gulp-notify": "^2.2.0",
+ "gulp-rename": "^1.2.2",
+ "gulp-size": "^2.1.0",
+ "gulp-strip-debug": "^1.1.0",
+ "gulp-uglify": "^2.0.0",
+ "jshint": "^2.9.2",
+ "jsonfile": "^2.3.1",
+ "mocha": "^3.0.2",
+ "mqtt-ws": "^0.2.0",
+ "nano": "^6.2.0",
+ "node-cron": "^1.1.1",
+ "require-dir": "^0.3.0",
+ "should": "^10.0.0",
+ "string": "^3.3.1",
+ "superagent": "^2.1.0",
+ "supertest": "^2.0.0"
+ },
+ "author": "Martin Donnelly