身份识别调试完成

This commit is contained in:
yuchenglong
2025-11-21 17:59:46 +08:00
parent e4ec35b1e5
commit 1bfa9fefe0

View File

@@ -3,56 +3,33 @@ const koffi = require("koffi");
const path = require("path");
const iconv = require("iconv-lite");
// 定义结构体
// 定义结构体 (使用原始字节数组,避免对齐问题,手动解析)
const IDCardData = koffi.struct("IDCardData", {
Name: koffi.array("uint8", 32),
Sex: koffi.array("uint8", 6),
Nation: koffi.array("uint8", 20),
Born: koffi.array("uint8", 18),
Address: koffi.array("uint8", 72),
IDCardNo: koffi.array("uint8", 38),
GrantDept: koffi.array("uint8", 32),
UserLifeBegin: koffi.array("uint8", 18),
UserLifeEnd: koffi.array("uint8", 18),
reserved: koffi.array("uint8", 38),
PhotoFileName: koffi.array("uint8", 255),
raw: koffi.array("uint8", 1024), // 分配足够大的空间
});
let lib = null;
let apiCache = null;
let running = false;
// 解码字符串 (尝试 UCS-2/UTF-16LE)
// 解码字符串 (GBK)
function decodeString(buffer) {
// 这里的 buffer 是 uint8 数组,先转为 Buffer
const buf = Buffer.from(buffer);
// 这里的 buffer 是 uint8 数组 Buffer
const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
// 很多读卡器 DLL 返回的是 UCS-2 (UTF-16LE)
// 也有可能是 GBK。我们可以通过检查字节特征来判断或者优先尝试 UCS-2
// UCS-2 的特征是每两个字节一个字符,对于 ASCII 字符,高字节通常是 0x00
// 尝试作为 UTF-16LE 解码
// 找到双 null 结尾 (0x00 0x00)
// 找到第一个 null 字节 (0x00) 的位置
let nullIndex = -1;
for (let i = 0; i < buf.length - 1; i += 2) {
if (buf[i] === 0 && buf[i + 1] === 0) {
for (let i = 0; i < buf.length; i++) {
if (buf[i] === 0) {
nullIndex = i;
break;
}
}
const validBuf16 = nullIndex !== -1 ? buf.slice(0, nullIndex) : buf;
const str16 = iconv.decode(validBuf16, "utf16le").trim();
// 如果解码出来的字符串看起来是乱码(包含很多 )或者为空但 buffer 不为空,可能需要尝试 GBK
// 但通常 Syn_IDCardRead.dll 返回的是 UCS-2
// 让我们先打印一下原始 hex 以便调试
// parentPort.postMessage({ type: 'log', payload: `Hex: ${buf.slice(0, 10).toString('hex')}` });
return str16;
}
// 加载 DLL
const validBuf = nullIndex !== -1 ? buf.slice(0, nullIndex) : buf;
return iconv.decode(validBuf, "gbk").trim();
} // 加载 DLL
function loadDll() {
if (lib) return true;
if (apiCache) return apiCache;
try {
const fs = require("fs");
const dllNames = ["Syn_IDCardRead.dll", "SynIDCardRead.dll"];
@@ -105,7 +82,7 @@ function loadDll() {
};
try {
return {
apiCache = {
Syn_OpenPort: loadFunc("Syn_OpenPort", "_Syn_OpenPort@4", "int", [
"int",
]),
@@ -130,10 +107,8 @@ function loadDll() {
koffi.out(koffi.pointer(IDCardData)),
]),
};
return apiCache;
} catch (e) {
// 如果 lib.stdcall 也不行,尝试回退到 func 但不带 __stdcall (可能不是 stdcall 或者 koffi 版本差异)
// 但根据之前的错误,不带 __stdcall 找不到函数,带了又报类型错误,说明很可能是 stdcall 但字符串解析有问题
// 这里我们坚持用 lib.stdcall 这种显式 API 调用,它比字符串解析更稳定
throw e;
}
} catch (err) {
@@ -200,24 +175,49 @@ async function startListen() {
const data = {}; // koffi 输出对象
const ret = api.Syn_ReadMsg(port, 0, data);
if (ret === 0 && data) {
if (ret === 0 && data && data.raw) {
const rawBuf = Buffer.from(data.raw);
// 调试:打印前 64 字节 Hex
// parentPort.postMessage({
// type: "log",
// payload: `Raw Hex (First 64): ${rawBuf.slice(0, 64).toString("hex")}`,
// });
// 手动切片解析 (修正后的偏移)
// Name: 0-32 (32)
// Sex: 32-36 (4)
// Nation: 36-42 (6)
// Born: 42-60 (18)
// Address: 60-132 (72)
// IDCardNo: 132-170 (38)
// GrantDept: 170-202 (32)
// UserLifeBegin: 202-220 (18)
// UserLifeEnd: 220-238 (18)
// PhotoFileName: 276-531 (255)
const payload = {
name: decodeString(data.Name),
sex: decodeString(data.Sex),
nation: decodeString(data.Nation),
born: decodeString(data.Born),
address: decodeString(data.Address),
id_card_no: decodeString(data.IDCardNo),
grant_dept: decodeString(data.GrantDept),
life_begin: decodeString(data.UserLifeBegin),
life_end: decodeString(data.UserLifeEnd),
photo_path: decodeString(data.PhotoFileName),
name: decodeString(rawBuf.slice(0, 32)),
sex: decodeString(rawBuf.slice(32, 36)),
nation: decodeString(rawBuf.slice(36, 42)),
born: decodeString(rawBuf.slice(42, 60)),
address: decodeString(rawBuf.slice(60, 132)),
id_card_no: decodeString(rawBuf.slice(132, 170)),
grant_dept: decodeString(rawBuf.slice(170, 202)),
life_begin: decodeString(rawBuf.slice(202, 220)),
life_end: decodeString(rawBuf.slice(220, 238)),
photo_path: decodeString(rawBuf.slice(276, 531)),
};
parentPort.postMessage({
type: "log",
payload: `Read IDCard success: ${payload.name} ${payload.id_card_no}`,
});
// 打印完整 payload 以便调试
parentPort.postMessage({
type: "log",
payload: `Full Payload: ${JSON.stringify(payload)}`,
});
parentPort.postMessage({ type: "data", payload });
// 读到卡后暂停一下,避免重复读取太快