diff --git a/Gruntfile.js b/Gruntfile.js index f0ef9327..ef8a91ca 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -14,7 +14,7 @@ * limitations under the License. **/ -// Project configuration for Node-RED-nodes +// Configuration for Node-RED-nodes project module.exports = function(grunt) { grunt.initConfig({ simplemocha: { diff --git a/function/smooth/17-smooth.js b/function/smooth/17-smooth.js index 0d432d65..d26c1749 100644 --- a/function/smooth/17-smooth.js +++ b/function/smooth/17-smooth.js @@ -29,32 +29,34 @@ module.exports = function(RED) { var old = null; this.on('input', function (msg) { - var n = Number(msg.payload); - if (!isNaN(n)) { - if ((node.action === "low") || (node.action === "high")) { - if (old == null) { old = n; } - old = old + (n - old) / node.count; - if (node.action === "low") { msg.payload = old; } - else { msg.payload = n - old; } + if msg.hasOwnProperty("payload")) { + var n = Number(msg.payload); + if (!isNaN(n)) { + if ((node.action === "low") || (node.action === "high")) { + if (old == null) { old = n; } + old = old + (n - old) / node.count; + if (node.action === "low") { msg.payload = old; } + else { msg.payload = n - old; } + } + else { + a.push(n); + if (a.length > node.count) { pop = a.shift(); } + if (node.action === "max") { + msg.payload = Math.max.apply(Math, a); + } + if (node.action === "min") { + msg.payload = Math.min.apply(Math, a); + } + if (node.action === "mean") { + tot = tot + n - pop; + msg.payload = tot / a.length; + } + } + if (node.round) { msg.payload = Math.round(msg.payload); } + node.send(msg); } - else { - a.push(n); - if (a.length > node.count) { pop = a.shift(); } - if (node.action === "max") { - msg.payload = Math.max.apply(Math, a); - } - if (node.action === "min") { - msg.payload = Math.min.apply(Math, a); - } - if (node.action === "mean") { - tot = tot + n - pop; - msg.payload = tot / a.length; - } - } - if (node.round) { msg.payload = Math.round(msg.payload); } - node.send(msg); - } - else { node.log("Not a number: "+msg.payload); } + else { node.log("Not a number: "+msg.payload); } + } // ignore msg with no payload property. }); } RED.nodes.registerType("smooth", SmoothNode); diff --git a/parsers/base64/70-base64.js b/parsers/base64/70-base64.js index d28da040..7a794959 100644 --- a/parsers/base64/70-base64.js +++ b/parsers/base64/70-base64.js @@ -21,27 +21,29 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); var node = this; this.on("input", function(msg) { - if (Buffer.isBuffer(msg.payload)) { - // Take binary buffer and make into a base64 string - msg.payload = msg.payload.toString('base64'); - node.send(msg); - } - else if (typeof msg.payload === "string") { - // Take base64 string and make into binary buffer - var regexp = new RegExp('^[A-Za-z0-9+\/=]*$'); - if ( regexp.test(msg.payload) && (msg.payload.length % 4 === 0) ) { - msg.payload = new Buffer(msg.payload,'base64'); + if (msg.hasOwnProperty("payload")) { + if (Buffer.isBuffer(msg.payload)) { + // Take binary buffer and make into a base64 string + msg.payload = msg.payload.toString('base64'); node.send(msg); } + else if (typeof msg.payload === "string") { + // Take base64 string and make into binary buffer + var regexp = new RegExp('^[A-Za-z0-9+\/=]*$'); + if ( regexp.test(msg.payload) && (msg.payload.length % 4 === 0) ) { + msg.payload = new Buffer(msg.payload,'base64'); + node.send(msg); + } + else { + node.log("Not a Base64 string - maybe we should encode it..."); + msg.payload = (new Buffer(msg.payload,"binary")).toString('base64'); + node.send(msg); + } + } else { - node.log("Not a Base64 string - maybe we should encode it..."); - msg.payload = (new Buffer(msg.payload,"binary")).toString('base64'); - node.send(msg); + node.warn("This node only handles strings or buffers."); } - } - else { - node.warn("This node only handles strings or buffers."); - } + } else { node.warn("No payload found to process"); } }); } RED.nodes.registerType("base64",Base64Node); diff --git a/parsers/msgpack/70-msgpack.js b/parsers/msgpack/70-msgpack.js index e1396447..d8bbf13f 100644 --- a/parsers/msgpack/70-msgpack.js +++ b/parsers/msgpack/70-msgpack.js @@ -22,24 +22,26 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); var node = this; this.on("input", function(msg) { - if (Buffer.isBuffer(msg.payload)) { - var l = msg.payload.length; - try { - msg.payload = msgpack.decode(msg.payload); + if (msg.hasOwnProperty("payload")) { + if (Buffer.isBuffer(msg.payload)) { + var l = msg.payload.length; + try { + msg.payload = msgpack.decode(msg.payload); + node.send(msg); + node.status({text:l +" b->o "+ JSON.stringify(msg.payload).length}); + } + catch (e) { + node.warn("Bad decode: "+e); + node.status({text:"not a msgpack buffer"}); + } + } + else { + var le = JSON.stringify(msg.payload).length; + msg.payload = msgpack.encode(msg.payload); node.send(msg); - node.status({text:l +" b->o "+ JSON.stringify(msg.payload).length}); + node.status({text:le +" o->b "+ msg.payload.length}); } - catch (e) { - node.warn("Bad decode: "+e); - node.status({text:"not a msgpack buffer"}); - } - } - else { - var le = JSON.stringify(msg.payload).length; - msg.payload = msgpack.encode(msg.payload); - node.send(msg); - node.status({text:le +" o->b "+ msg.payload.length}); - } + } else { node.warn("No payload found to process"); }); } RED.nodes.registerType("msgpack",MsgPackNode); diff --git a/test/utility/exif/94-exif_spec.js b/test/utility/exif/94-exif_spec.js index 335cd20d..e910dafe 100644 --- a/test/utility/exif/94-exif_spec.js +++ b/test/utility/exif/94-exif_spec.js @@ -35,40 +35,131 @@ describe('exif node', function() { it('extracts location data from Exif data of JPEG', function(done) { var exif = require('exif'); var ExifImage = exif.ExifImage; - // the jpg file is a single black dot but it was originally a photo taken at IBM Hursley + //console.log(process.cwd()); var data = fs.readFileSync("test/utility/exif/exif_test_image.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor + //var data = fs.readFileSync("exif_test_image.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor + var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, + {id:"helperNode1", type:"helper"}]; - var eD; + helper.load(exifNode, flow, function() { + var exifNode1 = helper.getNode("exifNode1"); + var helperNode1 = helper.getNode("helperNode1"); - new ExifImage({ image : data }, function (error, exifData) { - if (error) { - done(error); - } else { - eD = exifData; - } - - var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - - helper.load(exifNode, flow, function() { - var exifNode1 = helper.getNode("exifNode1"); - var helperNode1 = helper.getNode("helperNode1"); - - helperNode1.on("input", function(msg) { - msg.location.lat.should.equal(50.95624); // this data is stored in the jpg file - msg.location.lon.should.equal(-1.36701); - done(); - }); - - var stub = sinon.stub(ExifImage.prototype, 'loadImage', function(error, callback) { - stub.restore(); - callback(null, eD); - }); - - exifNode1.receive({payload:data}); + helperNode1.on("input", function(msg) { + msg.location.lat.should.equal(50.95624); // this data is stored in the jpg file + msg.location.lon.should.equal(-1.36701); + done(); }); - }); + exifNode1.receive({payload:data}); + }); + }); + + it('should report if no data found', function(done) { + var exif = require('exif'); + var ExifImage = exif.ExifImage; + // the jpg file is a single black dot but it was originally a photo taken at IBM Hursley + //console.log(process.cwd()); + var data = fs.readFileSync("test/utility/exif/exif_test_image2.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor + //var data = fs.readFileSync("exif_test_image2.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor + var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, + {id:"helperNode1", type:"helper"}]; + + helper.load(exifNode, flow, function() { + var exifNode1 = helper.getNode("exifNode1"); + var helperNode1 = helper.getNode("helperNode1"); + + setTimeout(function() { + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "exif"; + }); + logEvents.should.have.length(1); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.toString().should.startWith("The incoming image did not contain Exif GPS"); + done(); + },150); + + exifNode1.receive({payload:data}); + }); + }); + + it('should report if not a jpeg', function(done) { + var exif = require('exif'); + var ExifImage = exif.ExifImage; + // the jpg file is a single black dot but it was originally a photo taken at IBM Hursley + var data = new Buffer("hello"); + var eD; + var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, + {id:"helperNode1", type:"helper"}]; + + helper.load(exifNode, flow, function() { + var exifNode1 = helper.getNode("exifNode1"); + var helperNode1 = helper.getNode("helperNode1"); + + setTimeout(function() { + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "exif"; + }); + logEvents.should.have.length(1); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.toString().should.startWith("Error: The given image is not a JPEG"); + done(); + },150); + + exifNode1.receive({payload:data}); + }); + }); + + it('should report if bad payload', function(done) { + var exif = require('exif'); + var ExifImage = exif.ExifImage; + var data = "hello"; + var eD; + var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, + {id:"helperNode1", type:"helper"}]; + + helper.load(exifNode, flow, function() { + var exifNode1 = helper.getNode("exifNode1"); + var helperNode1 = helper.getNode("helperNode1"); + + setTimeout(function() { + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "exif"; + }); + logEvents.should.have.length(1); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.toString().should.startWith("Invalid payload received, "); + done(); + },150); + + exifNode1.receive({payload:data}); + }); + }); + + it('should report if no payload', function(done) { + var exif = require('exif'); + var ExifImage = exif.ExifImage; + var data = new Buffer("hello"); + var eD; + var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]}, + {id:"helperNode1", type:"helper"}]; + + helper.load(exifNode, flow, function() { + var exifNode1 = helper.getNode("exifNode1"); + var helperNode1 = helper.getNode("helperNode1"); + + setTimeout(function() { + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "exif"; + }); + logEvents.should.have.length(1); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.toString().should.startWith("No payload received, "); + done(); + },150); + + exifNode1.receive({topic:data}); + }); }); }); diff --git a/test/utility/exif/exif_test_image2.jpg b/test/utility/exif/exif_test_image2.jpg new file mode 100644 index 00000000..4e32af46 Binary files /dev/null and b/test/utility/exif/exif_test_image2.jpg differ diff --git a/time/79-suncalc.html b/time/suncalc/79-suncalc.html similarity index 100% rename from time/79-suncalc.html rename to time/suncalc/79-suncalc.html diff --git a/time/79-suncalc.js b/time/suncalc/79-suncalc.js similarity index 100% rename from time/79-suncalc.js rename to time/suncalc/79-suncalc.js diff --git a/time/LICENSE b/time/suncalc/LICENSE similarity index 100% rename from time/LICENSE rename to time/suncalc/LICENSE diff --git a/time/README.md b/time/suncalc/README.md similarity index 100% rename from time/README.md rename to time/suncalc/README.md diff --git a/time/icons/sun.png b/time/suncalc/icons/sun.png similarity index 100% rename from time/icons/sun.png rename to time/suncalc/icons/sun.png diff --git a/time/package.json b/time/suncalc/package.json similarity index 100% rename from time/package.json rename to time/suncalc/package.json diff --git a/utility/exif/94-exif.js b/utility/exif/94-exif.js index c4ab46e4..af8dec0e 100644 --- a/utility/exif/94-exif.js +++ b/utility/exif/94-exif.js @@ -1,5 +1,5 @@ /** - * Copyright 2014 IBM Corp. + * Copyright 2014, 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,15 +24,18 @@ module.exports = function(RED) { return result; } - /*** - * Extracts GPS location information from Exif data. If enough information is - * provided, convert the Exif data into a pair of single floating point number - * latitude/longitude data pairs. Populates msg.location with these. - * Assumes that the msg object will always have exifData available as msg.exif. - * Assume that the GPS data saved into Exif provides a valid value - */ - function addMsgLocationDataFromExifGPSData(msg) { - if(msg.exif.gps) { + function ExifNode(n) { + RED.nodes.createNode(this,n); + var node = this; + + /*** + * Extracts GPS location information from Exif data. If enough information is + * provided, convert the Exif data into a pair of single floating point number + * latitude/longitude data pairs. Populates msg.location with these. + * Assumes that the msg object will always have exifData available as msg.exif. + * Assume that the GPS data saved into Exif provides a valid value + */ + function addMsgLocationDataFromExifGPSData(msg) { var gpsData = msg.exif.gps; // declaring variable purely to make checks more readable if(gpsData.GPSAltitude) { if(!msg.location) { @@ -44,26 +47,22 @@ module.exports = function(RED) { // The data provided in Exif is in degrees, minutes, seconds, this is to be converted into a single floating point degree if(gpsData.GPSLatitude.length === 3) { // OK to convert latitude if(gpsData.GPSLongitude.length === 3) { // OK to convert longitude - var latitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLatitude[0], gpsData.GPSLatitude[1], gpsData.GPSLatitude[2]); - latitude = Math.round(latitude * 100000)/100000; - // (N)orth means positive latitude - // (S)outh means negative latitude - // (E)ast means positive longitude - // (W)est means negative longitude - if(gpsData.GPSLatitudeRef.toString() === 'S' || gpsData.GPSLatitudeRef.toString() === 's') { + var latitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLatitude[0], gpsData.GPSLatitude[1], gpsData.GPSLatitude[2]); + latitude = Math.round(latitude * 100000)/100000; // 5dp is approx 1m resolution... + // (N)orth means positive latitude, (S)outh means negative latitude + if (gpsData.GPSLatitudeRef.toString() === 'S' || gpsData.GPSLatitudeRef.toString() === 's') { latitude = latitude * -1; } var longitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLongitude[0], gpsData.GPSLongitude[1], gpsData.GPSLongitude[2]); - longitude = Math.round(longitude * 100000)/100000; - - if(gpsData.GPSLongitudeRef.toString() === 'W' || gpsData.GPSLongitudeRef.toString() === 'w') { + longitude = Math.round(longitude * 100000)/100000; // 5dp is approx 1m resolution... + // (E)ast means positive longitude, (W)est means negative longitude + if (gpsData.GPSLongitudeRef.toString() === 'W' || gpsData.GPSLongitudeRef.toString() === 'w') { longitude = longitude * -1; } - if(!msg.location) { - msg.location = {}; - } + // Create location property if not exists + if (!msg.location) { msg.location = {}; } msg.location.lat = latitude; msg.location.lon = longitude; return; @@ -76,30 +75,22 @@ module.exports = function(RED) { } else { node.log("The location of this image cannot be determined safely so no location information has been added to the message."); } - } else { - node.log("No location info found in this image."); } - } - function ExifNode(n) { - RED.nodes.createNode(this,n); - var node = this; this.on("input", function(msg) { try { if (msg.payload) { if (Buffer.isBuffer(msg.payload)) { new ExifImage({ image : msg.payload }, function (error, exifData) { if (error) { - //node.error('Failed to extract Exif data from image.'); - //node.log('Failed to extract Exif data from image. '+ error); - node.log(error); + node.log(error.toString()); } else { //msg.payload remains the same buffer - if(exifData) { + if ((exifData) && (exifData.hasOwnProperty("gps")) && (Object.keys(exifData.gps).length !== 0)) { msg.exif = exifData; addMsgLocationDataFromExifGPSData(msg); } else { - node.warn("The incoming buffer did not contain Exif data, nothing to do. "); + node.warn("The incoming image did not contain Exif GPS data, nothing to do. "); } } node.send(msg);