cse47122
4 years ago
5 changed files with 2624 additions and 186 deletions
@ -1,96 +1,73 @@ |
|||
const sizeof = require('object-sizeof') |
|||
const FakeDevice = require('./fake-device') |
|||
/* globals describe it */ |
|||
|
|||
module.exports = function FakeDeviceManager ({ |
|||
connection, |
|||
deviceCount = 10, |
|||
spawnInterval = 200, |
|||
metricsInterval = 200, |
|||
sendInterval = 15000, |
|||
measurements, |
|||
onUpdateStats |
|||
}) { |
|||
if (!connection) { |
|||
throw new Error('Missing connection param') |
|||
} |
|||
if (!measurements) { |
|||
throw new Error('Missing measurements param') |
|||
} |
|||
let devices = [] |
|||
const expect = require('chai').expect |
|||
|
|||
let stats = { |
|||
devices: 0, |
|||
measurements: 0, |
|||
writes: 0, |
|||
size: 0, |
|||
errors: 0 |
|||
} |
|||
const FakeDeviceManager = require('../src/fake-device-manager') |
|||
const measurements = { |
|||
usage: {type: 'integer', min: 0, max: 100}, |
|||
voltage: {type: 'integer', min: 0, max: 15}, |
|||
temperature: {type: 'integer', min: -20, max: 60} |
|||
} |
|||
|
|||
/** |
|||
* Return device list |
|||
*/ |
|||
const getDevices = () => { |
|||
return devices |
|||
// create fake influx connection
|
|||
// let connectionWriteData = false
|
|||
const connection = { |
|||
send: (deviceId, data) => { |
|||
return new Promise((resolve, reject) => { |
|||
// connectionWriteData = {deviceId, data}
|
|||
resolve() |
|||
}) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Return stats data |
|||
*/ |
|||
const getStats = () => { |
|||
return stats |
|||
} |
|||
describe('FakeDeviceManager', function () { |
|||
let fakeDeviceManager = null |
|||
|
|||
/** |
|||
* Update stats on device data send event |
|||
*/ |
|||
const onDeviceSendData = (err, data) => { |
|||
if (err) { |
|||
console.log(err) |
|||
stats.errors++ |
|||
return |
|||
} |
|||
stats.measurements += data.length |
|||
data.forEach(chunk => { |
|||
stats.size += sizeof(chunk) |
|||
}) |
|||
stats.writes++ |
|||
} |
|||
it('should throw error if no params are passed', function () { |
|||
expect(FakeDeviceManager).to.throw(Error) |
|||
}) |
|||
|
|||
/** |
|||
* Start device spawn process |
|||
*/ |
|||
const start = () => { |
|||
let __spawnInterval = setInterval(() => { |
|||
stats.devices++ |
|||
devices.push(FakeDevice({ |
|||
deviceId: stats.devices, |
|||
connection, |
|||
measurements, |
|||
onSendData: onDeviceSendData, |
|||
autoStart: true, |
|||
metricsInterval, |
|||
sendInterval |
|||
})) |
|||
// when device limit is reached stop spawn inteval
|
|||
if (stats.devices === deviceCount) { |
|||
clearTimeout(__spawnInterval) |
|||
} |
|||
}, spawnInterval) |
|||
} |
|||
it('shold throw missing connection param', function () { |
|||
expect(() => FakeDeviceManager({})).to.throw('Missing connection param') |
|||
}) |
|||
|
|||
/** |
|||
* Stop all spawned devices |
|||
*/ |
|||
const stop = () => { |
|||
for (let i = 0; i < stats.devices; i++) { |
|||
devices[i].stop() |
|||
} |
|||
} |
|||
it('shold throw missing measurements param', function () { |
|||
expect(() => FakeDeviceManager({ |
|||
connection |
|||
})).to.throw('Missing measurements param') |
|||
}) |
|||
|
|||
return { |
|||
start, |
|||
stop, |
|||
getDevices, |
|||
getStats |
|||
} |
|||
} |
|||
it('should create instance', function () { |
|||
fakeDeviceManager = FakeDeviceManager({ |
|||
connection, |
|||
measurements, |
|||
sendInterval: 500 |
|||
}) |
|||
expect(fakeDeviceManager).to.have.property('start') |
|||
}) |
|||
|
|||
it('should spawn device instances', function (done) { |
|||
fakeDeviceManager.start() |
|||
setTimeout(() => { |
|||
let devices = fakeDeviceManager.getDevices() |
|||
done(!devices.length) |
|||
}, 500) |
|||
}) |
|||
|
|||
it('should collect device stats', function (done) { |
|||
setTimeout(() => { |
|||
let stats = fakeDeviceManager.getStats() |
|||
if (!stats) return done('No data') |
|||
if ( |
|||
stats.devices && |
|||
stats.measurements && |
|||
stats.writes && |
|||
stats.size |
|||
) { |
|||
done() |
|||
} else { |
|||
done('Invalid write data format') |
|||
} |
|||
}, 1000) |
|||
}) |
|||
}) |
|||
|
@ -1,111 +1,81 @@ |
|||
/** |
|||
* Create FakeDevice |
|||
* |
|||
* @param {any} { |
|||
* deviceId, |
|||
* metricsInterval = 100, |
|||
* sendInterval = 15000 |
|||
* } |
|||
* @returns FakeDevice |
|||
*/ |
|||
module.exports = function FakeDevice ({ |
|||
deviceId, |
|||
connection, |
|||
metricsInterval = 100, |
|||
sendInterval = 30000, |
|||
onSendData, |
|||
autoStart = false, |
|||
measurements |
|||
}) { |
|||
if (!deviceId) { |
|||
throw new Error('Missing deviceId param') |
|||
} |
|||
if (!connection) { |
|||
throw new Error('Missing connection param') |
|||
} |
|||
if (!measurements) { |
|||
throw new Error('Missing measurements param') |
|||
} |
|||
/* globals describe it */ |
|||
|
|||
let data = [] |
|||
const expect = require('chai').expect |
|||
|
|||
let __metricsInterval = null |
|||
let __sendInterval = null |
|||
const FakeDevice = require('../src/fake-device') |
|||
const measurements = { |
|||
usage: {type: 'integer', min: 0, max: 100}, |
|||
voltage: {type: 'integer', min: 0, max: 15}, |
|||
temperature: {type: 'integer', min: -20, max: 60} |
|||
} |
|||
|
|||
/** |
|||
* Return data buffer |
|||
*/ |
|||
const getData = () => { |
|||
return data |
|||
// create fake influx connection
|
|||
let connectionWriteData = false |
|||
const connection = { |
|||
send: (deviceId, data) => { |
|||
return new Promise((resolve, reject) => { |
|||
connectionWriteData = {deviceId, data} |
|||
resolve() |
|||
}) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Read data from sensors |
|||
* Will generate fake random data |
|||
*/ |
|||
const readData = () => { |
|||
function getRandomArbitrary (min, max) { |
|||
return Math.random() * (max - min) + min |
|||
} |
|||
describe('FakeDevice', function () { |
|||
let fakeDevice = null |
|||
|
|||
let chunk = {} |
|||
Object.keys(measurements).forEach(measurement => { |
|||
switch (measurements[measurement].type) { |
|||
case 'integer': |
|||
chunk[measurement] = getRandomArbitrary(measurements[measurement].min, measurements[measurement].max) |
|||
break |
|||
default: |
|||
throw new Error('Invalid measurement type') |
|||
} |
|||
}) |
|||
chunk['timestamp'] = new Date() |
|||
it('should throw error if no params are passed', function () { |
|||
expect(FakeDevice).to.throw(Error) |
|||
}) |
|||
|
|||
data.push(chunk) |
|||
} |
|||
it('shold throw missing deviceId param', function () { |
|||
expect(() => FakeDevice({})).to.throw('Missing deviceId param') |
|||
}) |
|||
|
|||
/** |
|||
* Send data to db and clear data buffer |
|||
* Calls onSendData function if set |
|||
*/ |
|||
const send = () => { |
|||
// send data buffer to connection
|
|||
connection.send(deviceId, data) |
|||
.then(() => { |
|||
if (!onSendData) return |
|||
onSendData(null, data) |
|||
// clear internal data buffer on successfull send
|
|||
data = [] |
|||
}) |
|||
.catch(err => { |
|||
if (!onSendData) return |
|||
onSendData(err) |
|||
}) |
|||
} |
|||
it('shold throw missing connection param', function () { |
|||
expect(() => FakeDevice({ |
|||
deviceId: 1 |
|||
})).to.throw('Missing connection param') |
|||
}) |
|||
|
|||
/** |
|||
* Stop collecting data |
|||
*/ |
|||
const stop = () => { |
|||
clearInterval(__metricsInterval) |
|||
clearInterval(__sendInterval) |
|||
} |
|||
it('shold throw missing measurements param', function () { |
|||
expect(() => FakeDevice({ |
|||
deviceId: 1, |
|||
connection |
|||
})).to.throw('Missing measurements param') |
|||
}) |
|||
|
|||
/** |
|||
* Start collecting data |
|||
*/ |
|||
const start = () => { |
|||
__metricsInterval = setInterval(readData, metricsInterval) |
|||
__sendInterval = setInterval(send, sendInterval) |
|||
} |
|||
it('should create instance', function () { |
|||
fakeDevice = FakeDevice({ |
|||
deviceId: 1, |
|||
connection, |
|||
sendInterval: 1000, |
|||
measurements |
|||
}) |
|||
expect(fakeDevice).to.have.property('start') |
|||
}) |
|||
|
|||
if (autoStart) { |
|||
start() |
|||
} |
|||
it('should generate data', function (done) { |
|||
fakeDevice.start() |
|||
setTimeout(() => { |
|||
let data = fakeDevice.getData() |
|||
done(!data.length) |
|||
}, 500) |
|||
}) |
|||
|
|||
return { |
|||
stop, |
|||
start, |
|||
send, |
|||
getData |
|||
} |
|||
} |
|||
it('should send data to influx', function (done) { |
|||
setTimeout(() => { |
|||
if (!connectionWriteData) return done('No data') |
|||
if (!connectionWriteData.deviceId) return done('Invalid write data - missing deviceId') |
|||
if ( |
|||
connectionWriteData.data[0].usage && |
|||
connectionWriteData.data[0].voltage && |
|||
connectionWriteData.data[0].temperature && |
|||
connectionWriteData.data[0].timestamp |
|||
) { |
|||
done() |
|||
} else { |
|||
done('Invalid write data format') |
|||
} |
|||
}, 1000) |
|||
}) |
|||
}) |
|||
|
@ -0,0 +1,23 @@ |
|||
const DeviceManager = require('./src/fake-device-manager') |
|||
const FakeDeviceConnection = require('./src/fake-device-connection')() |
|||
|
|||
const updateStats = require('./src/stats') |
|||
|
|||
console.log('Generating data - send interval: 30s') |
|||
|
|||
let deviceManager = DeviceManager({ |
|||
deviceCount: 10, |
|||
metricsInterval: 30, |
|||
connection: FakeDeviceConnection, |
|||
onUpdateStats: updateStats, |
|||
measurements: { |
|||
temperature: {type: 'integer', min: -10, max: 50}, |
|||
air_humidity: {type: 'integer', min: 30, max: 100}, |
|||
ground_humidity: {type: 'integer', min: 30, max: 100}, |
|||
airforce: {type: 'integer', min: 30, max: 100} |
|||
} |
|||
}) |
|||
|
|||
updateStats(deviceManager) |
|||
|
|||
deviceManager.start() |
File diff suppressed because it is too large
@ -0,0 +1,30 @@ |
|||
{ |
|||
"name": "iot-dummy-data-generator", |
|||
"version": "0.1.0", |
|||
"description": "Spawn dummy IoT devices and generate custom data that can be send to some data store.", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"test": "mocha --watch --reporter spec" |
|||
}, |
|||
"author": "Vedran Jukic", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"influx": "^5.0.3", |
|||
"object-sizeof": "^1.1.1", |
|||
"progress": "^1.1.8" |
|||
}, |
|||
"devDependencies": { |
|||
"eslint": "^3.11.1", |
|||
"eslint-config-standard": "^6.2.1", |
|||
"eslint-plugin-promise": "^3.4.0", |
|||
"eslint-plugin-standard": "^2.0.1", |
|||
"chai": "^3.5.0", |
|||
"deepstream.io": "^2.0.0", |
|||
"deepstream.io-client-js": "^2.0.0", |
|||
"diff": "^3.0.1", |
|||
"jsdoc": "^3.4.3", |
|||
"mocha": "^3.1.2", |
|||
"sinon": "^1.17.6", |
|||
"winston": "^2.3.0" |
|||
} |
|||
} |
Loading…
Reference in new issue