import std from './dxStd.js' import dxUart from './dxUart.js' import dxCommon from './dxCommon.js' import log from './dxLogger.js' const dxFingerZaz = {} let options = { id : 'fingerUart', total: 5000, timeout : 500, type : '3', path : '/dev/ttySLB1', baudrate : '115200-8-N-1' } /** * 测试指纹模块命令 * @returns {boolean} 返回true表示测试成功,返回false表示测试失败 */ dxFingerZaz.test = function () { send({prefix: 0x55AA, cmd: 0x0100, data: ''}) let resp = receive({prefix: 0xAA55, cmd: 0x0100}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.test:", resp) return false } } /** * 获取指定编号范围内可注册的首个编号 * @param {number} startId - 起始编号 * @param {number} endId - 结束编号 * @returns {number} 返回空闲的指纹编号,返回false表示获取失败(失败原因可参考ret) */ dxFingerZaz.getEmptyId = function (startId, endId) { if(startId === undefined || startId === null || startId < 1 || startId > options.total){ throw new Error("dxFingerZaz.getEmptyId: 'startId' parameter should be between 1 and " + options.total) } if(endId === undefined || endId === null || endId < 1 || endId > options.total){ throw new Error("dxFingerZaz.getEmptyId: 'endId' parameter should be between 1 and " + options.total) } let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2) send({prefix: 0x55AA, cmd: 0x4500, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4500}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data, 2) } else { log.error("dxFingerZaz.getEmptyId:", resp) return false } } /** * 获取指定编号的模板注册状态 * @param {number} keyId - 指纹编号 * @returns {number} 返回0表示未注册,返回1表示已注册,返回false表示获取失败 */ dxFingerZaz.getStatus = function (keyId) { if (keyId === undefined || keyId === null) { throw new Error("dxFingerZaz.getStatus: 'keyId' parameter should not be null or empty") } send({prefix: 0x55AA, cmd: 0x4600, data: toEndianHexExtended(keyId, 2)}) let resp = receive({prefix: 0xAA55, cmd: 0x4600}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data, 1) } else { log.error("dxFingerZaz.getStatus:", resp) return false } } /** * 采集指纹图像 * @returns {boolean} 返回true表示采集成功,返回false表示采集失败(失败原因可参考ret) */ dxFingerZaz.getImage = function () { send({prefix: 0x55AA, cmd: 0x2000, data: ""}) let resp = receive({prefix: 0xAA55, cmd: 0x2000}) if(resp && resp.ret == 0x00){ return true } else { // log.error("dxFingerZaz.getImage:", resp) return false } } /** * 从暂存在ImageBuffer中的指纹图像产生模板 * @param {number} bufferNum - 指纹模板编号(0-2) * @returns {boolean} 返回true表示生成成功,返回false表示生成失败(失败原因可参考ret) */ dxFingerZaz.generate = function (bufferNum) { if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.generate: 'bufferNum' parameter should be between 0 and 3") } send({prefix: 0x55AA, cmd: 0x6000, data: toEndianHexExtended(bufferNum, 2)}) let resp = receive({prefix: 0xAA55, cmd: 0x6000}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.generate:", resp) return false } } /** * 合并指纹模板 * @param {number} mergeCount - 合并模板数量(2|3) 2:合成 Ram Buffer0 和 Ram Buffer1,3:合成 Ram Buffer0、Ram Buffer1 和 Ram Buffer2 * @param {number} bufferNum - 指纹合并后模板编号(0-2) * @returns {boolean} 返回true表示合并成功,返回false表示合并失败 */ dxFingerZaz.merge = function (mergeCount, bufferNum) { if(mergeCount === undefined || mergeCount === null || mergeCount < 2 || mergeCount > 3){ throw new Error("dxFingerZaz.merge: 'mergeCount' parameter should be between 2 and 3") } if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.merge: 'bufferNum' parameter should be between 0 and 3") } let data = toEndianHexExtended(bufferNum, 2) + toEndianHexExtended(mergeCount, 1) send({prefix: 0x55AA, cmd: 0x6100, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x6100}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.merge:", resp) return false } } /** * 保存指纹模板数据到模块指纹库 * @param {number} keyId - 指纹编号 * @param {number} bufferNum - 指纹模板编号(0-2) * @param {boolean} [overwrite=false] - 是否覆盖已存在的指纹 * @returns {boolean} 返回true表示存储成功,返回false表示存储失败 */ dxFingerZaz.storeChar = function (keyId, bufferNum, overwrite = false) { if(keyId === undefined || keyId === null){ throw new Error("dxFingerZaz.storeChar: 'keyId' parameter should be between 0 and 255") } if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.storeChar: 'bufferNum' parameter should be between 0 and 3") } if (overwrite) { const delRet = dxFingerZaz.delChar(keyId, keyId) if (!delRet) { log.error("dxFingerZaz.storeChar: failed to delete existing fingerprint for keyId", keyId) return false } } let data = toEndianHexExtended(keyId, 2) + toEndianHexExtended(bufferNum, 2) send({prefix: 0x55AA, cmd: 0x4000, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4000}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.storeChar:", resp) return false } } /** * 读取模块中的指纹并暂存在RamBuffer中 * @param {number} keyId - 指纹编号 * @param {number} bufferNum - 指纹模板编号(0-2) * @returns {boolean} 返回true表示存储成功,返回false表示存储失败 */ dxFingerZaz.loadChar = function (keyId, bufferNum) { if(keyId === undefined || keyId === null){ throw new Error("dxFingerZaz.loadChar: 'keyId' parameter should be between 0 and 255") } if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.loadChar: 'bufferNum' parameter should be between 0 and 3") } let data = toEndianHexExtended(keyId, 2) + toEndianHexExtended(bufferNum, 2) send({prefix: 0x55AA, cmd: 0x4100, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4100}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.loadChar:", resp) return false } } /** * 将保存于指定编号的Ram Buffer 中的 Template 上传至HOST * @param {number} bufferNum - 指纹模板编号(0-2) * @returns {string} 返回设备信息,返回false表示获取失败 */ dxFingerZaz.upChar = function (bufferNum) { if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.upChar: 'bufferNum' parameter should be between 0 and 3") } let data = toEndianHexExtended(bufferNum, 2) send({prefix: 0x55AA, cmd: 0x4200, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4200}) if(resp && resp.ret == 0x00){ log.info("resp.data:", fromEndianHexExtended(resp.data, 2)) resp = receive({prefix: 0xA55A, cmd: 0x4200, len: fromEndianHexExtended(resp.data, 2)}) if(resp && resp.ret == 0x00){ return dxCommon.uint8ArrayToHexString(resp.data) } else { log.error("dxFingerZaz.upChar:", resp) return false } } else { log.error("dxFingerZaz.upChar:", resp) return false } } /** * 从HOST下载 Template到模块指定编号的 Ram Buffer * @param {number} bufferNum - 指纹模板编号(0-2) * @param {string} template - 指纹模板数据 * @returns {string} 返回设备信息,返回false表示获取失败 */ dxFingerZaz.downChar = function (bufferNum, template) { if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.downChar: 'bufferNum' parameter should be between 0 and 3") } if(template === undefined || template === null){ throw new Error("dxFingerZaz.downChar: 'template' parameter should not be null or empty") } let data = toEndianHexExtended(bufferNum, 2) + template log.info("template.length:", toEndianHexExtended(data.length / 2, 2)) send({prefix: 0x55AA, cmd: 0x4300, data: toEndianHexExtended(data.length / 2, 2)}) let resp = receive({prefix: 0xAA55, cmd: 0x4300}) if(resp && resp.ret == 0x00){ send({prefix: 0x5AA5, cmd: 0x4300, data: data}) resp = receive({prefix: 0xA55A, cmd: 0x4300}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.downChar:", resp) return false } } else { log.error("dxFingerZaz.downChar:", resp) return false } } /** * 获取指纹模块设备信息 * @returns {string} 返回设备信息,返回false表示获取失败 */ dxFingerZaz.getEnrolledIdList = function () { send({prefix: 0x55AA, cmd: 0x4900, data: ""}) let resp = receive({prefix: 0xAA55, cmd: 0x4900}) if(resp && resp.ret == 0x00){ resp = receive({prefix: 0xA55A, cmd: 0x4900, len: fromEndianHexExtended(resp.data, 2)}) if(resp && resp.ret == 0x00){ return resp.data } else { log.error("dxFingerZaz.getDeviceInfo:", resp) return false } } else { log.error("dxFingerZaz.getDeviceInfo:", resp) return false } } /** * 搜索指纹模板 * @param {number} bufferNum - 指纹模板编号(0-2) * @param {number} startId - 搜索起始编号 * @param {number} endId - 搜索结束编号 * @returns {number} 返回搜索到的指纹编号,返回false表示搜索失败 */ dxFingerZaz.search = function (bufferNum, startId, endId) { if(bufferNum === undefined || bufferNum === null || bufferNum < 0 || bufferNum > 2){ throw new Error("dxFingerZaz.search: 'bufferNum' parameter should be between 0 and 3") } if(startId === undefined || startId === null || startId < 1 || startId > options.total){ throw new Error("dxFingerZaz.search: 'startId' parameter should be between 1 and " + options.total) } if(endId === undefined || endId === null || endId < 1 || endId > options.total){ throw new Error("dxFingerZaz.search: 'endId' parameter should be between 1 and " + options.total) } let data = toEndianHexExtended(bufferNum, 2) + toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2) send({prefix: 0x55AA, cmd: 0x6300, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x6300}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data.slice(0, 2), 2) } else { log.error("dxFingerZaz.search:", resp) return false } } /** * 获取指纹模块参数 * @param {number} paramType - 参数类型(0-4)[0: 设备Id, 1: 安全等级, 2: 重复检查, 3: 波特率, 4: 自动学习] * @returns {number} 返回参数值,返回false表示获取失败 * * 设备Id:[1-255] * 安全等级:[1-5] * 重复校验:[0/1] * 波特率:[1:9600, 2:19200, 3:38400, 4:57600, 5:115200, 6:230400, 7:460800, 8:921600] * 自动学习:[0/1] */ dxFingerZaz.getParam = function (paramType) { if(paramType === undefined || paramType === null || paramType < 0 || paramType > 4){ throw new Error("dxFingerZaz.getParam: 'paramType' parameter should be between 0 and 4") } let data = toEndianHexExtended(paramType, 2) send({prefix: 0x55AA, cmd: 0x0300, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x0300}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data, 4) } else { log.error("dxFingerZaz.getParam:", resp) return false } } /** * 设置指纹模块参数 * @param {number} paramType - 参数类型(0-4)[0: 设备Id, 1: 安全等级, 2: 重复检查, 3: 波特率, 4: 自动学习] * @param {number} paramValue - 参数值 * @returns {boolean} 返回true表示设置成功,返回false表示设置失败 * * 设备Id:[1-255] * 安全等级:[1-5] * 重复校验:[0/1] * 波特率:[1:9600, 2:19200, 3:38400, 4:57600, 5:115200, 6:230400, 7:460800, 8:921600] * 自动学习:[0/1] */ dxFingerZaz.setParam = function (paramType, paramValue) { if(paramType === undefined || paramType === null || paramType < 0 || paramType > 4){ throw new Error("dxFingerZaz.setParam: 'paramType' parameter should be between 0 and 4") } if(paramValue === undefined || paramValue === null || paramValue < 0 || paramValue > 255){ throw new Error("dxFingerZaz.setParam: 'paramValue' parameter should be between 0 and 255") } let data = toEndianHexExtended(paramType, 1) + toEndianHexExtended(paramValue, 4) send({prefix: 0x55AA, cmd: 0x0200, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x0200}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.setParam:", resp) return false } } /** * 获取指纹模块设备信息 * @returns {string} 返回设备信息,返回false表示获取失败 */ dxFingerZaz.getDeviceInfo = function () { send({prefix: 0x55AA, cmd: 0x0400, data: ""}) let resp = receive({prefix: 0xAA55, cmd: 0x0400}) if(resp && resp.ret == 0x00){ resp = receive({prefix: 0xA55A, cmd: 0x0400, len: fromEndianHexExtended(resp.data, 2)}) if(resp && resp.ret == 0x00){ return dxCommon.utf8HexToStr(dxCommon.uint8ArrayToHexString(resp.data.slice(0, resp.length - 3))) } else { log.error("dxFingerZaz.getDeviceInfo:", resp) return false } } else { log.error("dxFingerZaz.getDeviceInfo:", resp) return false } } /** * 指纹检测 * @returns {number} 返回1:有指纹输入,0:无指纹输入,返回false表示检测失败 */ dxFingerZaz.fingerDetect = function () { send({prefix: 0x55AA, cmd: 0x2100, data: ""}) let resp = receive({prefix: 0xAA55, cmd: 0x2100}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data, 1) } else { log.error("dxFingerZaz.fingerDetect:", resp) return false } } /** * 删除指纹模板 * @param {number} startId - 删除起始编号 * @param {number} endId - 删除结束编号 * @returns {boolean} 返回true表示删除成功,返回false表示删除失败 */ dxFingerZaz.delChar = function (startId, endId) { if(startId === undefined || startId === null || startId < 1 || startId > options.total){ throw new Error("dxFingerZaz.delChar: 'startId' parameter should be between 1 and " + options.total) } if(endId === undefined || endId === null || endId < 1 || endId > options.total){ throw new Error("dxFingerZaz.delChar: 'endId' parameter should be between 1 and " + options.total) } let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2) send({prefix: 0x55AA, cmd: 0x4400, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4400}) if(resp && resp.ret == 0x00){ return true } else { log.error("dxFingerZaz.delChar:", resp) return false } } /** * 获取指定编号范围内已注册的指纹总数 * @param {number} startId - 起始编号 * @param {number} endId - 结束编号 * @returns {number} 返回指定编号范围内已注册的指纹总数,返回false表示获取失败 */ dxFingerZaz.getEnrollCount = function (startId, endId) { if(startId === undefined || startId === null || startId < 1 || startId > options.total){ throw new Error("dxFingerZaz.getEnrollCount: 'startId' parameter should be between 1 and " + options.total) } if(endId === undefined || endId === null || endId < 1 || endId > options.total){ throw new Error("dxFingerZaz.getEnrollCount: 'endId' parameter should be between 1 and " + options.total) } let data = toEndianHexExtended(startId, 2) + toEndianHexExtended(endId, 2) send({prefix: 0x55AA, cmd: 0x4800, data: data}) let resp = receive({prefix: 0xAA55, cmd: 0x4800}) if(resp && resp.ret == 0x00){ return fromEndianHexExtended(resp.data, 2) } else { log.error("dxFingerZaz.getEnrollCount:", resp) return false } } /** * 初始化指纹模块 * @param {Object} params * @param {string} params.type - 串口类型(默认 3) * @param {string} params.path - 串口路径(默认 '/dev/ttySLB1') * @param {number} params.baudrate - 波特率(默认 '115200-8-N-1') * @param {number} params.id - id(默认 'fingerUart') * @param {number} params.total - 指纹总数(默认 5000) * @param {number} params.timeout - 超时时间(默认 500) */ dxFingerZaz.init = function (params) { options.id = params.id ? params.id : options.id options.timeout = params.timeout ? params.timeout : options.timeout options.total = params.total ? params.total : options.total options.type = params.type ? params.type : options.type options.path = params.path ? params.path : options.path options.baudrate = params.baudrate ? params.baudrate : options.baudrate dxUart.open(options.type, options.path, options.id) dxUart.ioctl(6, options.baudrate, options.id) } /** * 将数字转换为指定字节数的十六进制字符串 * @param {number} number - 要转换的数字 * @param {number} bytes - 字节数(默认2字节) * @param {boolean} isLittleEndian - 是否小端序(默认true) * @returns {string} 转换后的十六进制字符串 */ function toEndianHexExtended(number, bytes, isLittleEndian = true) { // 计算所需的十六进制位数(每个字节占2位) const hexLength = bytes * 2; let hexStr = number.toString(16).padStart(hexLength, '0'); // 分割为字节数组 const bytesArray = []; for (let i = 0; i < hexLength; i += 2) { bytesArray.push(hexStr.substr(i, 2)); } // 根据字节序反转数组 if (isLittleEndian) bytesArray.reverse(); return bytesArray.join(''); } /** * 将字节数组转换为数字(支持大小端序) * @param {Uint8Array|number[]} bytesArray - 字节数组(如 Uint8Array 或普通数组) * @param {number} bytes - 字节数(必须与实际数据长度一致) * @param {boolean} isLittleEndian - 是否小端序(默认true) * @returns {number} 转换后的数字 */ function fromEndianHexExtended(bytesArray, bytes, isLittleEndian = true) { // 参数校验 if (!bytesArray || bytesArray.length !== bytes) { throw new Error(`Invalid bytes array. Expected length: ${bytes}`); } // 处理小端序:反转字节顺序 const adjustedBytes = isLittleEndian ? Array.from(bytesArray).reverse() : Array.from(bytesArray); // 转换为十六进制字符串 const hexStr = adjustedBytes .map(byte => byte.toString(16).padStart(2, "0")) .join(""); // 转换为数字(支持大整数) return parseInt(hexStr, 16); } /** * 发送数据 * @param {Object} params * @param {number} params.prefix * @param {number} params.cmd * @param {string} params.data */ function send(params) { if (!params || typeof params !== 'object') { throw new Error("Parameters should be an object containing prefix, cmd and data"); } const { prefix, cmd, data } = params; const sid = 0x00; // 默认源地址 0x00 const did = 0x00; // 默认目标地址 0x00 const hexParts = data.match(/[\da-f]{2}/gi) || []; const bytes = new Uint8Array(hexParts.map(h => parseInt(h, 16))); let dataBytes if (prefix === 0x55AA) { dataBytes = new Uint8Array(16); dataBytes.set(bytes.subarray(0, 16), 0); } else if (prefix === 0x5AA5) { dataBytes = new Uint8Array(500); dataBytes.set(bytes.subarray(0, 500), 0); } // 构造基础数据包 const packet = new Uint8Array(8 + dataBytes.length + 2); // 包头(8) + 数据 + 校验(2) const view = new DataView(packet.buffer); // 填充包头 view.setUint16(0, prefix, false); // 大端序 PREFIX (2 bytes) packet[2] = sid; // SID (1 byte) packet[3] = did; // DID (1 byte) view.setUint16(4, cmd, false); // 大端序 CMD (2 bytes) view.setUint16(6, hexParts.length, true); // 大端序 LEN (2 bytes) // 填充数据 packet.set(dataBytes, 8); // 计算校验和 let checksum = 0; for (let i = 0; i < 8 + dataBytes.length; i++) { checksum += packet[i]; } checksum &= 0xFFFF; // 取低 16 位 // 填充校验和(大端序) packet[8 + dataBytes.length] = checksum & 0xFF; packet[8 + dataBytes.length + 1] = (checksum >> 8) & 0xFF; // // TODO 临时日志打印 start // let str = '' // for (let i = 0; i < packet.length; i++) { // str += packet[i].toString(16).padStart(2, '0') + ' ' // } // log.info("send:", str) // // TODO 临时日志打印 end // 发送数据 let ret = dxUart.send(packet.buffer, options.id); if(!ret){ throw new Error("fingerZazUart.send fail") } } /** * 接收数据 * @param {Object} params * @param {number} params.prefix * @param {number} params.cmd * @param {number} params.len * @returns {Object} 返回数据 */ function receive(params) { // 重试5次, 避免读取太快模组未响应 for(let i = 0; i < 5; i++){ const prefix1 = dxUart.receive(1, options.timeout, options.id); if (!prefix1 || (prefix1[0] !== 0xAA && prefix1[0] !== 0xA5)) { std.sleep(50) continue } const prefix2 = dxUart.receive(1, options.timeout, options.id); if (!prefix2 || (prefix2[0] !== 0x55 && prefix2[0] !== 0x5A)) { std.sleep(50) continue } // 定义通用字段 let prefix, sid, did, cmd, len, ret, data, checksum; prefix = (prefix1[0] << 8) | prefix2[0]; const value1 = dxUart.receive(6, options.timeout, options.id); sid = value1[0]; did = value1[1]; cmd = (value1[2] << 8) | value1[3]; len = (value1[5] << 8) | value1[4]; let value2 = dxUart.receive(len, options.timeout, options.id) if(value2 && value2.length === len){ ret = value2[1] << 8 | value2[0]; data = value2.subarray(2, len) } else { throw new Error("fingerZazUart.receive fail: data length mismatch") } let value3 if(prefix1[0] === 0xAA && prefix2[0] === 0x55){ // 校验字在最后 value3 = dxUart.receive((26 - 2 - 8 - len + 2), options.timeout, options.id) checksum = value3[value3.length - 2] | (value3[value3.length - 1] << 8); } else { // 校验字在前面 value3 = dxUart.receive(2, options.timeout, options.id) checksum = value3[0] | (value3[1] << 8); } // 计算校验和(从prefix到data) let sum = prefix1[0]; sum += prefix2[0]; for (let i=0; i < value1.length; i++) sum += value1[i]; for (let i=0; i < value2.length; i++) sum += value2[i]; if ((sum & 0xFFFF) !== checksum) throw "Response Packet Checksum mismatch"; // // TODO 临时日志打印 start // let str = prefix1[0].toString(16).padStart(2, '0') + ' ' + prefix2[0].toString(16).padStart(2, '0') + ' ' // for (let i = 0; i < value1.length; i++) str += value1[i].toString(16).padStart(2, '0') + ' ' // for (let i = 0; i < value2.length; i++) str += value2[i].toString(16).padStart(2, '0') + ' ' // for (let i = 0; i < value3.length; i++) str += value3[i].toString(16).padStart(2, '0') + ' ' // log.info("receive:", str) // // TODO 临时日志打印 end if (params.prefix == prefix && params.cmd == cmd) { return { prefix: prefix, sid, did, command: cmd, length: len, ret: ret, data: Array.from(data), cks: true }; } else { throw new Error("fingerZazUart.receive fail:") } } } export default dxFingerZaz;