@waiting/fingerprint-reader-bp8903
Version:
166 lines (149 loc) • 4.57 kB
JavaScript
/**
* @waiting/fingerprint-reader-bp8903
* 南天 BP8903 集成键盘 指纹采集、验证
*
* @version 2.1.2
* @author waiting
* @license MIT
* @link https://github.com/waitingsong/node-fingerprint-reader-bp8903#readme
*/
import { DTypes } from 'win32-def';
import { info } from '@waiting/log';
import { validateDllFile, normalize } from '@waiting/shared-core';
import { Library } from 'ffi';
import { of, forkJoin } from 'rxjs';
import { tap, retry, mergeMap } from 'rxjs/operators';
/** 初始化参数 */
const initialOpts = {
dllTxt: '',
dllSearchPath: '',
debug: false,
port: 0,
searchAll: false,
};
const dllFuncs = {
ABC_GetFeature: [DTypes.INT, [DTypes.BYTE, DTypes.POINT, DTypes.INT]],
ABC_GetTemplate: [DTypes.INT, [DTypes.BYTE, DTypes.POINT, DTypes.INT]],
ABC_Match: [DTypes.INT, [DTypes.POINT, DTypes.POINT]],
};
function findDeviceList(deviceOpts, apib) {
const arr = [];
if (deviceOpts.port > 0) {
const device = findDevice(deviceOpts.port, deviceOpts, apib);
if (device.openPort > 0) {
arr.push(device);
}
}
else {
throw new Error('deviceOpts.port must be specified')
}
return arr
}
function findDevice(port, deviceOpts, apib) {
const device = {
apib,
deviceOpts,
inUse: false,
openPort: port,
};
return device
}
/** Sampling fingerprint once, return base64 */
function readOnce(device, bufLen = 1024) {
device.deviceOpts.debug && info('staring read once...');
const buf = Buffer.alloc(bufLen);
const code = device.apib.ABC_GetFeature(device.openPort, buf, bufLen);
const ret = code === 1
? buf
: Buffer.alloc(0);
if (device.deviceOpts.debug) {
info(`Fingerprint readOnce code: ${code}. (1:succeed, 0/others:failed)`);
}
return ret
}
/** Sampling fingerprint 3times, return base64 */
function readThrice(device, bufLen = 1024) {
device.deviceOpts.debug && info('staring read thrice...');
const buf = Buffer.alloc(bufLen);
const code = device.apib.ABC_GetTemplate(device.openPort, buf, bufLen);
const ret = code === 1
? buf
: Buffer.alloc(0);
if (device.deviceOpts.debug) {
info(`Fingerprint readThice code: ${code}. (1:succeed, 0/others:failed)`);
}
return ret
}
function compareFP(device, fp1, fp2) {
return new Promise(resolve => {
// const code = device.apib.ABC_Match(fp1, fp2)
device.apib.ABC_Match.async(fp1, fp2, (err, code) => {
resolve(code === 1 ? true : false);
return code
});
})
}
async function init(options) {
const deviceOpts = parseDeviceOpts(options);
const { debug } = deviceOpts;
if (debug) {
info(deviceOpts);
}
await validateDllFile(deviceOpts.dllTxt);
const apib = Library(deviceOpts.dllTxt, dllFuncs);
const devices = findDeviceList(deviceOpts, apib);
if (devices && devices.length) {
return devices
}
else {
throw new Error('未找到读卡设备')
}
}
/**
* Sample fingprint
*
* mode (default: strict):
* - simple: read once
* - strict: read 3 times
*/
function sampleFP(device, mode = 'strict') {
const buf = mode === 'simple' ? readOnce(device) : readThrice(device);
const ret = parseResultBuffer(buf);
return Promise.resolve(ret)
}
function verifyFP(device, fp) {
const fp1$ = of(readOnce(device)).pipe(tap(buf => {
if (!buf.byteLength) {
throw new Error('Sampling result empty. will retry once')
}
}), retry(1), tap(buf => {
if (!buf.byteLength) {
throw new Error('Sampling result empty')
}
}));
const fp2$ = of(Buffer.from(fp)).pipe(tap(buf => {
if (!buf.byteLength) {
throw new Error('Input fingerprint key being validated is invalid')
}
}));
const ret$ = forkJoin(fp1$, fp2$).pipe(mergeMap(([fp1, fp2]) => compareFP(device, fp1, fp2)));
return ret$.toPromise()
}
function parseDeviceOpts(options) {
const deviceOpts = Object.assign({}, initialOpts, options);
if (!options.dllTxt) {
throw new Error('params dllTxt undefined or blank')
}
else {
deviceOpts.dllTxt = normalize(deviceOpts.dllTxt);
}
return deviceOpts
}
/** convert sampling result to base64 */
function parseResultBuffer(buf) {
const ret = buf.byteLength
? buf.toString().replace(/\0+$/, '')
: '';
return ret
}
export { initialOpts, init, sampleFP, verifyFP };