Authored by 周奇琪

1.添加单元测试框架,引入了mock和代码注入

2.加了makefile和travis ci脚本
3.调整index.js的部分写法,修改成ES6的语法格式
4.把依赖babel的方法移到入口index.js
5.把usage.js放到example目录
6.修改package.json引入了babel新版本(5.4.2)
7.为了mock测试支持es6,引入了babel-plugin-rewire插件
... ... @@ -3,4 +3,4 @@ log
.settings/
.idea/
dist/
docs/
docs/
\ No newline at end of file
... ...
language: node_js
node_js:
- 0.12.2
... ...
TESTS = test/run.js
REPORTER = spec
TIMEOUT = 10000
test:
@NODE_ENV=test ./node_modules/mocha/bin/mocha -R $(REPORTER) -t $(TIMEOUT) $(TESTS)
.PHONY: test
... ...
读写器的SDK
------------
title: 读写器的SDK
# 读写器的SDK
# 概要
本SDK主要用于UHF RFID读写器的调用。完全用node来实现Socket的硬件通讯。
已实现接口:
... ... @@ -15,7 +18,8 @@
* 获取固件版本(B4 00 00 00 00)
* 设置盘点时长(B0 00 00 00 00)
类关联图
# 类关联图
```
index + config
... ... @@ -31,8 +35,7 @@ UHF模块指令类 配置指令类 盘点指令类
```
使用方法
==============
# 使用方法
### 环境准备
... ... @@ -52,7 +55,8 @@ cd rfid-sdk
npm install
```
es5调用方法
# es5调用方法
``` javascript
//调用
... ... @@ -67,6 +71,7 @@ require("babel/register");//需要加入Babel的polyfill
var sdk = require('../src/index');//模块入口
```
# 例子
``` javascript
//开始盘点
... ... @@ -98,11 +103,8 @@ sdk.start({
```
# 问题:
## 问题:
1.对于多个返回结果的拆分(已解决)
2.设置定频,没有起作用(待验证)
3.socket连接异常处理
\ No newline at end of file
1. 对于多个返回结果的拆分(已解决)
2. 设置定频,没有起作用(待验证)
3. socket连接异常处理
\ No newline at end of file
... ...
//调用
var sdk = require('../dist/index');
var sdk = require('../index');
//开始盘点
sdk.start({
... ... @@ -24,6 +24,6 @@ sdk.start({
}],
inventoryTime: 400, //设置盘点时长
power: [15, 0, 0, 0] //功率设置
}, function (data) {
}, function(err, data) {
console.log(data);
});
\ No newline at end of file
... ...
require("babel/register"); //es6 polyfill
module.exports = require('./src/index');
\ No newline at end of file
... ...
... ... @@ -3,16 +3,17 @@
"version": "1.0.0",
"description": "rfid uhf sdk",
"main": "index.js",
"repository": "git@git.dev.yoho.cn:web/rfid-sdk.git",
"dependencies": {
"async": "^0.9.0",
"babel": "~5.1.9",
"async": "0.9.0",
"babel": "5.x.x",
"lodash": "3.6.x",
"log4js": "0.6.x",
"pm2": "~0.12.3",
"redis": "^0.12.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "make test"
},
"author": "",
"license": "ISC",
... ... @@ -21,6 +22,11 @@
"gulp-babel": "^5.1.0",
"gulp-doxx": "0.0.4",
"gulp-jshint": "^1.10.0",
"jshint-stylish": "^1.0.2"
"jshint-stylish": "^1.0.2",
"mocha": "*",
"expect.js": "*",
"muk": "*",
"rewire": "*",
"babel-plugin-rewire": "*"
}
}
}
\ No newline at end of file
... ...
... ... @@ -2,54 +2,57 @@
* @fileOverview 调用入口
* @author qiqi.zhou@yoho.cn
*/
//require("babel/register"); //es6 polyfill
/**
* 连接器
* @type {Connector}
*/
var Connector = require('./lib/connector');
import Connector from './lib/connector';
/**
* order config
* @type {Object}
*/
var orders = require('./lib/interface');
import orders from './lib/interface';
/**
* uhf dirctive module
* @type {Uhf}
*/
var Uhf = require('./lib/directive/uhf');
import Uhf from './lib/directive/uhf';
/**
* config dirctive module
* @type {[Config]}
*/
var Config = require('./lib/directive/config');
import Config from './lib/directive/config';
/**
* inventory dirctive module
* @type {Inventory}
*/
var Inventory = require('./lib/directive/inventory');
import Inventory from './lib/directive/inventory';
var _ = require('lodash');
var util = require('./lib/util');
import _ from 'lodash';
var appConfig = require('./config');
import {
locateOrder
}
from './lib/util';
import appConfig from './config';
/**
* 读写器初始化模块实例
* @type {Uhf}
*/
var uhf = new Uhf();
let uhf = new Uhf();
/**
* 配置读写器实例
* @type {Config}
*/
var config = new Config();
let config = new Config();
/**
* 读写器盘点实例
* @type {Inventory}
*/
var inventory = new Inventory();
let inventory = new Inventory();
/**
* 打开读写器
... ... @@ -59,22 +62,25 @@ var inventory = new Inventory();
* @param {Number} port 端口
* @param {Function} callback 打开成功回调
*/
exports.open = function (host, port, callback) {
if (!host || !port) {
return;
export let open = function(host, port, callback) {
if (!host || !port || !callback) {
throw new Error("错误:ip,端口,回调必须要传");
}
//连接一个读写器
var connector = new Connector({
host: host,
port: port
}, function () {
}, function() {
uhf.send(connector, orders.openUhf);
}, receive);
//当打开读写器模块
connector.on('openUhf', function (data) {
if ((data.code === 1 || data.code === 2) && callback) {
callback(connector);
connector.on('openUhf', function(data) {
if (data.code === 1 || data.code === 2) {
callback(null, connector);
} else if (data) {
var error = new Error(data.message);
error.code = data.code;
callback(error);
}
});
};
... ... @@ -86,7 +92,7 @@ exports.open = function (host, port, callback) {
* @param {Connector} connector 连接器
* @param {Array} 传参,依次是4个天线的功率 eg: [15,15,15,0]
*/
exports.setPower = function (connector, param) {
export let setPower = function(connector, param) {
config.send(connector, orders.setPower, param);
};
... ... @@ -97,7 +103,7 @@ exports.setPower = function (connector, param) {
* @param {Connector} connect 连接器
* param {Object} 是否开启灵敏度和灵敏度值 eg:{on:'1',rssi:-60}
*/
exports.setFilter = function (connector, param) {
export let setFilter = function(connector, param) {
config.send(connector, orders.setFilter, param);
};
... ... @@ -108,7 +114,7 @@ exports.setFilter = function (connector, param) {
* @param {Connector} connector 连接器
* @param {Object} param 天线端口和定频的值 eg: {ant: 0, freq: 10 }
*/
exports.setFrequency = function (connector, param) {
export let setFrequency = function(connector, param) {
config.send(connector, orders.setFrequency, param);
};
... ... @@ -119,18 +125,18 @@ exports.setFrequency = function (connector, param) {
* @param {Connector} connect 连接器
* @param {Number} param 时长,单位毫秒 eg: 400
*/
exports.setInventoryTime = function (connector, param) {
export let setInventoryTime = function(connector, param) {
inventory.send(connector, orders.setInventoryTime, param);
};
/**
* 开始盘点
* @method sartInventory
* @exports index/sartInventory
* @method startInventory
* @exports index/startInventory
* @param {Connector} connect 连接器
*/
exports.sartInventory = function (connector) {
inventory.send(connector, orders.sartInventory);
export let startInventory = function(connector) {
inventory.send(connector, orders.startInventory);
};
/**
... ... @@ -139,7 +145,7 @@ exports.sartInventory = function (connector) {
* @exports index/stopInventory
* @param {Connector} connector 连接器
*/
exports.stopInventory = function (connector) {
export let stopInventory = function(connector) {
inventory.send(connector, orders.stopInventory);
};
... ... @@ -172,72 +178,49 @@ exports.stopInventory = function (connector) {
}],
inventoryTime: 400,//设置盘点时长
power: [15, 0, 0, 0]//功率设置
}, function (data) {
}, function (error,data) {
//console.log(data);
});
*/
var isStop = false,
checkInventoryFlag,
checkSpeedFlag;
exports.start = function (options, callback) {
let isStop = false;
export let start = function(options, callback) {
var rets = [];
var countMap = {};
exports.open(options.host, options.port, function (connector) {
if (options.filter) { //开启灵敏度过滤
exports.setFilter(connector, options.filter);
}
if (options.frequencys) { //开启固定频率
_.forIn(options.frequencys, function (val, key) {
exports.setFrequency(connector, val);
});
open(options.host, options.port, function(err, connector) {
if (!options.filter || !options.frequencys || !options.inventoryTime || !options.power) {
let error = new Error('Error:灵敏度,定频,盘点时长,功率必须要传!');
callback(error);
}
//开启灵敏度过滤
setFilter(connector, options.filter);
if (options.inventoryTime) { //设置盘点时长
exports.setInventoryTime(connector, options.inventoryTime);
}
//开启固定频率
_.forIn(options.frequencys, function(val, key) {
setFrequency(connector, val);
});
if (options.power) { //设置功率
connector.on('setInventoryTime', function (data) {
exports.setPower(connector, options.power);
});
//设置盘点时长
setInventoryTime(connector, options.inventoryTime);
}
//设置功率
connector.on('setInventoryTime', function(data) {
setPower(connector, options.power);
});
connector.on('setPower', function (data) {
exports.sartInventory(connector); //开始盘点
//开始盘点
connector.on('setPower', function(data) {
startInventory(connector);
});
connector.on('sartInventory', function (data) {
//console.log(data.code);
connector.on('startInventory', function(data) {
if (data.code !== 2 && !isStop) {
exports.sartInventory(connector);
startInventory(connector);
} else if (data.code === 2) {
rets = procTagInfo(countMap, rets, data.data);
}
callback(rets);
callback(null, rets);
});
checkInventoryFlag = setInterval(function () { //检查是否在架
for (var i = 0; i < rets.length; i++) {
var offTime = 1 * new Date() - rets[i].updateTime;
if (offTime > appConfig.offInventory) {
rets[i].state = 'off';
rets[i].speed = 0;
}
}
}, appConfig.checkInventory);
checkSpeedFlag = setInterval(function () { //检查读取速度(times/sec)
for (var i = 0; i < rets.length; i++) {
if (countMap[rets[i].epc]) {
var prevCount = Number(countMap[rets[i].epc]);
rets[i].speed = prevCount / (appConfig.checkCount / 1000);
countMap[rets[i].epc] = 0;
}
}
}, appConfig.checkCount);
});
};
... ... @@ -245,12 +228,9 @@ exports.start = function (options, callback) {
* 停止间歇盘点
* @method stop
* @exports index/stop
* @param {Connector} connect 连接器
*/
exports.stop = function (connector) {
export let stop = function() {
isStop = true;
clearInterval(checkInventoryFlag);
clearInterval(checkSpeedFlag);
};
/**
... ... @@ -261,7 +241,7 @@ exports.stop = function (connector) {
* @param {Object} data 读取一次的内容
*/
function procTagInfo(countMap, rets, data) {
var isExist = false,
let isExist = false,
now = 1 * new Date();
for (var i = 0; i < rets.length; i++) {
if (rets[i].epc === data.epc) {
... ... @@ -291,26 +271,26 @@ function procTagInfo(countMap, rets, data) {
* @param {Object} data 接收到的数据
*/
function receive(client, data, err) {
var className = null,
let className = null,
ret = null;
if (!data) {
return;
return null;
}
if (err) {
console.log(err);
throw err;
}
className = util.locateOrder(orders, data).val.directive;
className = locateOrder(orders, data).val.directive;
switch (className) {
case 'uhf':
ret = uhf.receive(client, data);
break;
case 'config':
ret = config.receive(client, data);
break;
case 'inventory':
ret = inventory.receive(client, data);
break;
case 'uhf':
ret = uhf.receive(client, data);
break;
case 'config':
ret = config.receive(client, data);
break;
case 'inventory':
ret = inventory.receive(client, data);
break;
}
return ret;
}
\ No newline at end of file
... ...
... ... @@ -36,7 +36,7 @@ class Connector extends events.EventEmitter {
let that = this;
//初始化
client.connect(config.port, config.host, function () {
client.connect(config.port, config.host, function() {
that.connected = true;
log.info(config.host + ':' + config.port + ':读写器连接成功!');
send();
... ... @@ -44,7 +44,7 @@ class Connector extends events.EventEmitter {
//接收数据
client.on('data', function (data) {
client.on('data', function(data) {
let arr = toHex(data),
rets = spliteRet(that.orderMq, arr),
... ... @@ -61,7 +61,7 @@ class Connector extends events.EventEmitter {
});
//异常处理
client.on('error', function (error) {
client.on('error', function(error) {
log.error('错误:' + error.code);
// 读写器连接错误或者突然断开
... ... @@ -71,7 +71,7 @@ class Connector extends events.EventEmitter {
receive(client, null, error);
});
client.on('close', function () {
client.on('close', function() {
log.info('读写器连接断开');
// 触发重连事件
that.emit('reconnect');
... ...
... ... @@ -22,7 +22,7 @@ class Inventory extends Base {
let setInventoryTime = info.setInventoryTime.input;
this.reproc[setInventoryTime] = this.preSetInventoryTime;
this.proc = {
sartInventory: this.start
startInventory: this.start
};
}
... ... @@ -72,7 +72,7 @@ class Inventory extends Base {
retKey = data;
}
ip = client.remoteAddress;
output = info.sartInventory.output;
output = info.startInventory.output;
returnVal = returnProc(retKey, ip, output);
returnVal.data = retData;
... ...
... ... @@ -104,7 +104,7 @@ export default {
'b44ff0': commonMessage.ff0
}
},
sartInventory: {
startInventory: {
directive: 'inventory',
input: 'F100000000',
output: {
... ...
import lib from '../../src';
import expect from 'expect.js';
import muk from 'muk';
import rewire from 'rewire';
import {
ret, orderName, setRet, setOrderName, uhf as FakeUhf
}
from '../mock/fakeUhf';
import FakeConnector from '../mock/fakeConnector';
import FakeConfig from '../mock/fakeConfig';
describe('index.js', function() {
let index;
before(function() {
index = muk('../../src', {
'./lib/connector': FakeConnector,
'./lib/directive/uhf': FakeUhf
});
});
describe('open UHF', function() {
it('open expect return', function() {
expect(function() {
lib.open(null, null, null);
}).to.throwException('错误:ip,端口,回调必须要传');
expect(function() {
lib.open('1.1.1.1', null, null);
}).to.throwException('错误:ip,端口,回调必须要传');
expect(function() {
lib.open('1.1.1.1', '123', null);
}).to.throwException('错误:ip,端口,回调必须要传');
});
it('open expect success', function(done) {
index.open('1.1.1.1', '123', function(err, data) {
expect(data).to.be.a(FakeConnector);
done();
});
});
it('open expect error', function(done) {
let val = {
code: 0,
message: 'fake error'
};
setRet(val);
index.open('1.1.1.1', '123', function(err, data) {
expect(err.message).to.be('fake error');
done();
});
});
});
describe('start inventory', function() {
it('start expect error', function() {
expect(function() {
index.start();
}).to.throwException('错误:ip,端口,回调必须要传');
expect(function() {
index.start({
'port': '123'
});
}).to.throwException('错误:ip,端口,回调必须要传');
});
it('start expect asyn error', function(done) {
index.start({
'port': '123',
'host': '1.1.1.1'
}, function(err, data) {
expect(err.message).to.be('Error:灵敏度,定频,盘点时长,功率必须要传!')
done();
});
});
it('start epxect ok', function(done) {
let val = {
code: 1
};
setRet(val);
let index = rewire("../../src")
index.__set__('setFilter', function() {});
index.__set__('setFrequency', function() {});
index.__set__('setInventoryTime', function(connector, param) {
process.nextTick(function() {
connector.done('setInventoryTime', ret);
});
});
index.__set__('setPower', function(connector, param) {
process.nextTick(function() {
connector.done('setPower', ret)
});
});
index.__set__('startInventory', function(connector, param) {
process.nextTick(function() {
connector.done('startInventory', {
code: 2,
data: {
epc: '123'
}
})
});
});
index.__set__('Connector', FakeConnector);
index.__set__('uhf', new FakeUhf());
index.start({
'port': '123',
'host': '1.1.1.1',
filter: { //设置灵敏度的过滤
'on': '1',
rssi: -60
},
frequencys: [{ //天线的定频
ant: 0,
freq: 10
}, {
ant: 1,
freq: 10
}, {
ant: 2,
freq: 10
}, {
ant: 3,
freq: 10
}],
inventoryTime: 400, //设置盘点时长
power: [15, 0, 0, 0] //功率设置
},
function(err, data) {
expect(data).to.have.length(1);
done();
})
});
});
describe('stop inventory', function() {
it('stop ok', function() {
expect(index.stop).to.be.ok();
});
});
describe('procTagInfo', function() {
it('procTagInfo exit proc', function() {
let proc = rewire("../../src").__get__('procTagInfo');
let countMap = {
'1': 1
};
let rets = [{
epc: '1',
rssi: '2',
updateTime: 1 * new Date(),
state: 'on',
ant: 1
}];
let data = {
epc: '1',
rssi: '3',
ant: 2
}
rets = proc(countMap, rets, data);
expect(rets[0].ant).to.be(2);
expect(rets[0].rssi).to.be('3');
expect(rets[0].epc).to.be('1');
expect(countMap['1']).to.be(2);
expect(rets).to.have.length(1);
});
it('procTagInfo noExist proc', function() {
let proc = rewire("../../src").__get__('procTagInfo');
let rets = proc({}, [], {
epc: '1',
rssi: '3',
ant: 2
});
expect(rets[0].state).to.be('on');
expect(rets[0].speed).to.be(0);
expect(rets).to.have.length(1);
});
});
describe('receive', function() {
it('receive expect uhf and config and inventory ret', function() {
let indexM = rewire("../../src");
let expectClassNames = ['uhf', 'config', 'inventory'];
for (let i = 0; i < expectClassNames.length; i++) {
indexM.__set__('locateOrder', function() {
return {
val: {
directive: expectClassNames[i]
}
}
});
indexM.__set__(expectClassNames[i], {
receive: function() {
return expectClassNames[i];
}
});
let receive = indexM.__get__('receive');
let retData = receive(true, {});
expect(retData).to.be(expectClassNames[i]);
}
});
it('receive param data null expect null', function() {
let indexM = rewire("../../src");
let retData = indexM.__get__('receive')(true, null);
expect(retData).to.be(null);
});
it('receive param err expect error', function() {
let indexM = rewire("../../src");
expect(function() {
indexM.__get__('receive')(true, {}, new Error('mock error'));
}).to.throwException(/mock error/);
});
});
after(function() {
muk.restore();
});
});
\ No newline at end of file
... ...
/**
* @fileOverview 配置
* @module config
* @property {string} log 日志的记录路径
* @property {string} logLevel 日志级别
* @property {number} checkInventory 检查盘点标签的状态
* @property {number} offInventory 离线标签临界点
* @property {number} checkCount 读取数检查
*/
export default {
log: './log',
logLevel: 'ERROR',
checkInventory: 200,
offInventory: 500,
checkCount: 1000
};
\ No newline at end of file
... ...
import events from 'events';
/**
* @class 假的连接器(用于单元测试)
* @extends {EventEmitter}
* @module Connector
* @member {Socket} client
* @member {Array} orderMq
* @description 连接到读写器端
*/
class FakeConnector extends events.EventEmitter {
constructor(config, send, receive) {
super();
this.connected = false;
this.client = {};
let that = this;
//初始化
process.nextTick(function() {
that.connected = true;
send();
});
}
send() {
}
done(order, ret) {
this.emit(order, ret);
};
}
/**
* @exports mock/FakeConnector
*/
export default FakeConnector;
\ No newline at end of file
... ...
/**
* 预期结果
* @type {Object}
*/
export let ret = {
code: 1
};
/**
* 发起的指令名称
* @type {String}
*/
export let orderName = 'openUhf';
export let setRet = function(val) {
ret = val;
}
export let setOrderName = function(val) {
orderName = val;
}
/**
* @class FakeUHF 假的读写器的指令操作上
* @extends {Base}
* @member @see {@link Base}
*/
export default class FakeUhf {
constructor() {}
send(connector, order) {
process.nextTick(function() {
connector.done(orderName, ret);
});
}
}
export let uhf = FakeUhf;
\ No newline at end of file
... ...
/**
* @fileOverview 假的工具方法库
* @module lib/util
* @author qiqi.zhou@yoho.cn
*/
/**
* @mixes 日志记录对象
* @exports util/log
*/
export let log = {
info: function() {},
error: function() {}
};
/**
* @method 无符号转16进制
* @exports util/tenToHex
* @param {Number} number 十进制数
* @return {Number} 16进制数字
*/
export let tenToHex = function(number) {};
/**
* @method 无符号16进制转10进制
* @exports util/hexToTen
* @param {Number} number 十六机制数
* @param {Boolean} minus true表示负数,false表示正数
* @return {Number} 十进制数字
*/
export let hexToTen = function(number, minus) {
};
/**
* @method 转16进制
* @exports util/toHex
* @param {Buff} data 返回数据流
* @return {Array} 转成16进制的数组
*/
export let toHex = function(data) {
};
/**
* @method 处理返回码
* @exports util/returnProc
* @param {Array} data 指令结果
* @param {Array} ip 读写器IP
* @param {Object} output 标准输出的规格对象
* @return {object} 标准输出
*/
export let returnProc = function(data, ip, output) {
};
/**
* @method 返回结果拆分
* @exports util/spliteRet
* @param {Array} mq 发起指令的数组集合
* @param {String} data 得到的指令数据结果集
* @return {Object} 返回拆分结果和拆分依据的指令头
*/
export let spliteRet = function(mq, data) {
};
/**
* @method 定位指令名称
* @exports util/spliteRet
* @param {Array} orders 指令规格数组
* @param {Array} data 返回的结果集 eg. ['f0','2','0','0']
* @return {Object} 返回定位到的指令
*/
export let locateOrder = function(orders, data) {
};
\ No newline at end of file
... ...
require("babel/register")({
plugins: ["babel-plugin-rewire"]
}); //es6 polyfill
require('./lib/index.test');
\ No newline at end of file
... ...