@waiting/idcard-reader-bp8903
Version:
232 lines (207 loc) • 7.84 kB
JavaScript
/**
* @waiting/idcard-reader-bp8903
* 南天 BP8903 二代机具读卡
*
* @version 2.3.2
* @author waiting
* @license MIT
* @link https://github.com/waitingsong/node-idcard-reader-bp8903#readme
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
var sharedCore = require('@waiting/shared-core');
var win32Def = require('win32-def');
var idcardReaderBase = require('@waiting/idcard-reader-base');
var log = require('@waiting/log');
var ffi = require('ffi');
var rxjs = require('rxjs');
var operators = require('rxjs/operators');
var iconv = require('iconv-lite');
const config = {
appDir: '',
tmpDir: sharedCore.join(sharedCore.tmpdir(), 'idcard-reader'),
};
const dllFuncs = {
OpenCom: [win32Def.DTypes.INT32, [win32Def.DTypes.INT32, win32Def.DTypes.POINT, win32Def.DTypes.INT32, win32Def.DTypes.INT32]],
CloseCom: [win32Def.DTypes.INT32, []],
IDCard_GetInformation: [win32Def.DTypes.INT32, [win32Def.DTypes.INT32, win32Def.DTypes.INT32, win32Def.DTypes.POINT, win32Def.DTypes.INT32, win32Def.DTypes.POINT, win32Def.DTypes.POINT, win32Def.DTypes.POINT]],
};
function connectDevice(device, port) {
if (device && device.inUse) {
device.deviceOpts.debug && log.info('Cautiton: connectDevice() device in use');
return 0
}
const openRet = device.apib.OpenCom(port, Buffer.from(''), 9600, 1);
device.deviceOpts.debug && log.info(`open com ret: ${openRet}`);
return openRet === 0 ? port : 0
}
function disconnectDevice(device) {
const ret = device.apib.CloseCom();
device.deviceOpts.debug && log.info(`disconnectDevice at port: ${device.openPort}, ret: ${ret} `);
device.inUse = false;
return true
}
function findDeviceList(deviceOpts, compositeOpts, apib) {
const arr = [];
if (deviceOpts.port > 0) {
const device = findDevice(deviceOpts.port, deviceOpts, compositeOpts, apib);
if (device.openPort > 0) {
arr.push(device);
}
}
else {
// 检测串口. bp8903 为串口接口
for (let i = 1; i <= 16; i++) {
const device = findDevice(i, deviceOpts, compositeOpts, apib);
if (device.openPort > 0) {
arr.push(device);
if (!deviceOpts.searchAll) {
break
}
}
}
}
return arr
}
function findDevice(openPort, deviceOpts, compositeOpts, apib) {
const device = {
apib,
deviceOpts,
compositeOpts,
inUse: false,
openPort: 0,
};
const port = connectDevice(device, openPort);
if (port > 0) {
device.inUse = true;
device.openPort = port;
deviceOpts.debug && log.info(`Found device at serial/usb port: ${port}`);
disconnectDevice(device);
}
return device
}
/** 读取二代证基础信息 */
function readAll(device) {
const path = sharedCore.dirname(device.deviceOpts.dllTxt);
process.env.PATH = `${process.env.PATH};${path}`;
const buf = Buffer.alloc(10240);
const srcDir = path.replace(/\\/g, '/') + '/';
const targetPath = sharedCore.normalize(device.deviceOpts.imgSaveDir + '/').replace(/\\/g, '/');
if (device.deviceOpts.debug) {
log.info('starting reading readCard ret');
log.info('IDCard_GetInformation() src path:' + srcDir);
log.info('IDCard_GetInformation() target path:' + targetPath);
}
const code = device.apib.IDCard_GetInformation(device.openPort, 9600, Buffer.from(''), 3, buf, Buffer.from(srcDir), Buffer.from(targetPath));
const ret = iconv.decode(buf, 'gbk');
if (device.deviceOpts.debug) {
log.info(`readDataBase code: ${code}`);
log.info(`readDataBase bufLen: ${buf.byteLength}`);
log.info(`readDataBase ret: ${ret}`);
// info(buf.slice(80))
}
return ret.replace(/\0/g, '')
}
async function init(options) {
const deviceOpts = idcardReaderBase.parseDeviceOpts(options);
const compositeOpts = idcardReaderBase.parseCompositeOpts(options);
// 先清空 解决头像图片生成可能失败问题
// compositeOpts.fontHwxhei = ''
// compositeOpts.fontOcrb = ''
// compositeOpts.fontSimhei = ''
const { debug } = deviceOpts;
if (debug) {
log.info(compositeOpts);
log.info(deviceOpts);
}
await idcardReaderBase.validateDllFile(deviceOpts.dllTxt);
// 允许 未指定照片解码dll
if (deviceOpts.dllImage && compositeOpts.useComposite && !await sharedCore.isFileExists(deviceOpts.dllImage)) {
throw new Error('File not exists: ' + deviceOpts.dllImage)
}
await idcardReaderBase.testWrite(deviceOpts.imgSaveDir);
const apib = ffi.Library(deviceOpts.dllTxt, dllFuncs);
const devices = findDeviceList(deviceOpts, compositeOpts, apib);
if (devices && devices.length) {
return devices
}
else {
throw new Error('未找到读卡设备')
}
}
/** Read card data */
function read(device) {
if (device.openPort) {
try {
disconnectDevice(device);
}
catch (ex) {
throw ex
}
const text$ = rxjs.of(readAll(device)).pipe(operators.timeout(2000));
const iddata$ = text$.pipe(operators.retry(device.deviceOpts.findCardRetryTimes), operators.map(text => {
const base = pickFields(text);
const imagePath = device.compositeOpts.useComposite
? genAvatarPath(device.deviceOpts.imgSaveDir, base.idc)
: '';
const ret = Object.assign({}, idcardReaderBase.initialIDData, { base,
imagePath });
return ret
}));
const ret$ = iddata$.pipe(operators.delay(device.compositeOpts.useComposite ? 300 : 0), operators.tap(() => {
disconnectDevice(device);
}), operators.mergeMap(data => {
return sharedCore.fileExists(data.imagePath).pipe(operators.map(path => {
data.imagePath = path;
return data
}))
}), operators.mergeMap(data => {
return !device.compositeOpts.useComposite || !data.imagePath
? rxjs.of(data)
: idcardReaderBase.composite(data.imagePath, data.base, device.compositeOpts).pipe(operators.map(imgPath => {
data.compositePath = imgPath;
return data
}))
}), operators.timeout(20000), operators.catchError((err) => {
disconnectDevice(device);
throw err
}));
return ret$.toPromise()
}
else {
throw new Error('设备端口未指定')
}
}
/** Pick fields from origin text */
function pickFields(text) {
const base = Object.assign({}, idcardReaderBase.initialDataBase);
if (!text || !text.length) {
return base
}
/** 姓名|身份证号|性别|民族|出生年月|住址|签发机关|起始日期|截至日期 */
const arr = text.split('|');
if (arr.length === 9) {
base.name = arr[0].trim();
base.idc = arr[1];
base.genderName = arr[2];
base.nationName = arr[3];
base.birth = arr[4];
base.address = arr[5].trim();
base.regorg = arr[6].trim();
base.startdate = arr[7];
base.enddate = arr[8];
}
return idcardReaderBase.formatBaseData(base)
}
function genAvatarPath(imgSaveDir, idc) {
const path = sharedCore.join(imgSaveDir, `${idc}.bmp`);
return path
}
// base directory of this module
config.appDir = __dirname + '/..';
exports.initialCompositeOpts = idcardReaderBase.initialCompositeOpts;
exports.initialOpts = idcardReaderBase.initialOpts;
exports.nationMap = idcardReaderBase.nationMap;
exports.init = init;
exports.read = read;
exports.genAvatarPath = genAvatarPath;