| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- class ReaderClient {
- constructor(options = {}) {
- this.wsUrl = options.wsUrl || "ws://127.0.0.1:6689";
- this.driveType = options.driveType || "D5200";
- this.portName = options.portName || "USB";
- this.hidSN = options.hidSN || "";
- this.driverIp = options.driverIp || "";
- this.driverPort = options.driverPort || "6688";
- // ISO14443A 参数
- this.keyType = options.keyType || "0";
- this.key = options.key || "FFFFFFFFFFFF";
- this.blockAddress = options.blockAddress || "1";
- this.numberOfBlocksRead = options.numberOfBlocksRead || "1";
- // ISO15693 参数
- this.readDataType = options.readDataType || "0";
- this.readSecurityStatus = options.readSecurityStatus || "0";
- this.iso15693BlockAddress = options.iso15693BlockAddress || "0";
- this.iso15693NumberOfBlocks = options.iso15693NumberOfBlocks || "7";
- this.ws = null;
- this.connected = false;
- this.handle = null;
- this.requestId = 1;
- this.pending = new Map();
- }
- async init() {
- if (typeof WebSocket === "undefined") {
- throw new Error("当前浏览器不支持 WebSocket");
- }
- return true;
- }
- async ensureWsConnected() {
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
- return;
- }
- if (this.ws && this.ws.readyState === WebSocket.CONNECTING) {
- await new Promise((resolve, reject) => {
- const timeoutRef = setTimeout(
- () => reject(new Error("读卡器服务连接超时")),
- 5000
- );
- const onOpen = () => {
- clearTimeout(timeoutRef);
- this.ws.removeEventListener("open", onOpen);
- this.ws.removeEventListener("error", onError);
- resolve();
- };
- const onError = () => {
- clearTimeout(timeoutRef);
- this.ws.removeEventListener("open", onOpen);
- this.ws.removeEventListener("error", onError);
- reject(new Error("读卡器服务连接失败"));
- };
- this.ws.addEventListener("open", onOpen);
- this.ws.addEventListener("error", onError);
- });
- return;
- }
- this.ws = new WebSocket(this.wsUrl);
- this.ws.onmessage = (event) => {
- this.handleMessage(event.data);
- };
- this.ws.onclose = () => {
- this.connected = false;
- this.handle = null;
- for (const [, item] of this.pending) {
- clearTimeout(item.timeoutRef);
- item.reject(new Error("读卡器服务连接已断开"));
- }
- this.pending.clear();
- };
- this.ws.onerror = () => {
- // 具体错误在请求超时/close时抛出
- };
- await new Promise((resolve, reject) => {
- const timeoutRef = setTimeout(
- () => reject(new Error("读卡器服务连接超时")),
- 5000
- );
- this.ws.onopen = () => {
- clearTimeout(timeoutRef);
- resolve();
- };
- this.ws.onerror = () => {
- clearTimeout(timeoutRef);
- reject(new Error("读卡器服务连接失败,请先启动厂家读卡器服务程序"));
- };
- });
- }
- handleMessage(rawData) {
- let payload;
- try {
- payload = JSON.parse(rawData);
- } catch {
- for (const [, item] of this.pending) {
- clearTimeout(item.timeoutRef);
- item.resolve(rawData);
- }
- this.pending.clear();
- return;
- }
- const traceId = payload.traceId;
- if (traceId && this.pending.has(traceId)) {
- const item = this.pending.get(traceId);
- this.pending.delete(traceId);
- clearTimeout(item.timeoutRef);
- item.resolve(payload);
- return;
- }
- if (this.pending.size === 1) {
- const firstKey = this.pending.keys().next().value;
- const item = this.pending.get(firstKey);
- this.pending.delete(firstKey);
- clearTimeout(item.timeoutRef);
- item.resolve(payload);
- }
- }
- async sendCommand(command, param = {}, handleOverride, timeoutMs = 6000) {
- await this.ensureWsConnected();
- const traceId = `r${Date.now()}_${this.requestId++}`;
- const message = {
- command,
- param,
- traceId,
- };
- const finalHandle = handleOverride ?? this.handle;
- if (finalHandle !== null && finalHandle !== undefined) {
- message.handle = String(finalHandle);
- }
- const promise = new Promise((resolve, reject) => {
- const timeoutRef = setTimeout(() => {
- this.pending.delete(traceId);
- reject(new Error(`${command} 超时`));
- }, timeoutMs);
- this.pending.set(traceId, { resolve, reject, timeoutRef });
- });
- this.ws.send(JSON.stringify(message));
- return promise;
- }
- getPayloadCode(payload) {
- if (!payload || typeof payload !== "object") {
- return null;
- }
- if (payload.code != null) {
- return Number(payload.code);
- }
- if (payload.status != null) {
- return Number(payload.status);
- }
- if (payload.result?.code != null) {
- return Number(payload.result.code);
- }
- return null;
- }
- getPayloadMessage(payload) {
- if (!payload || typeof payload !== "object") {
- return "";
- }
- return String(
- payload.msg ??
- payload.message ??
- payload.error ??
- payload.result?.msg ??
- payload.result?.message ??
- ""
- );
- }
- extractHandle(payload) {
- if (!payload || typeof payload !== "object") {
- return null;
- }
- const h = payload.handle ?? payload.result?.handle ?? payload.data?.handle;
- if (h === null || h === undefined || h === "") {
- return null;
- }
- return String(h);
- }
- extractTagDataList(payload) {
- if (!payload || typeof payload !== "object") {
- return [];
- }
- if (Array.isArray(payload.tagDataList)) {
- return payload.tagDataList;
- }
- if (Array.isArray(payload.data)) {
- return payload.data;
- }
- if (payload.data && Array.isArray(payload.data.tagDataList)) {
- return payload.data.tagDataList;
- }
- if (payload.result && Array.isArray(payload.result.tagDataList)) {
- return payload.result.tagDataList;
- }
- if (payload.result && Array.isArray(payload.result.data)) {
- return payload.result.data;
- }
- return [];
- }
- parseUidFromTagRow(row) {
- if (!row) {
- return "";
- }
- if (Array.isArray(row)) {
- return String(row[1] || "").trim();
- }
- if (typeof row === "object") {
- if (Array.isArray(row.value)) {
- return String(row.value[1] || "").trim();
- }
- if (Array.isArray(row.data)) {
- return String(row.data[1] || "").trim();
- }
- if (Array.isArray(row.tag)) {
- return String(row.tag[1] || "").trim();
- }
- for (const value of Object.values(row)) {
- if (Array.isArray(value)) {
- return String(value[1] || "").trim();
- }
- }
- }
- return "";
- }
- async selectDevice() {
- await this.init();
- try {
- await this.sendCommand("CloseDriver", {}, -1, 3000);
- } catch {
- // 忽略
- }
- const openResp = await this.sendCommand(
- "OpenDriver",
- {
- driveType: this.driveType,
- portName: this.portName,
- hidSN: this.hidSN,
- driverIp: this.driverIp,
- driverPort: this.driverPort,
- driverId: "",
- userName: "",
- passWord: "",
- },
- undefined,
- 8000
- );
- const code = this.getPayloadCode(openResp);
- if (code != null && code < 0) {
- const msg = this.getPayloadMessage(openResp) || "OpenDriver 失败";
- throw new Error(msg);
- }
- const openedHandle = this.extractHandle(openResp);
- if (!openedHandle) {
- throw new Error("OpenDriver 未返回 handle");
- }
- this.handle = openedHandle;
- this.connected = true;
- return true;
- }
- // ISO14443A 读取 UID
- async readUidISO14443A() {
- await this.init();
- if (!this.connected || !this.handle) {
- throw new Error("读卡器未连接,请先连接读卡器");
- }
- const readResp = await this.sendCommand(
- "ISO14443A_BatchReadMultiBlocks",
- {
- antennaCollection: [1],
- readDataType: "0",
- keyType: this.keyType,
- key: this.key,
- blockAddress: this.blockAddress,
- numberOfBlocksRead: this.numberOfBlocksRead,
- },
- this.handle,
- 6000
- );
- const code = this.getPayloadCode(readResp);
- if (code != null && code < 0) {
- const msg = this.getPayloadMessage(readResp) || "ISO14443A 读卡失败";
- throw new Error(msg);
- }
- const tagDataList = this.extractTagDataList(readResp);
- if (!Array.isArray(tagDataList) || tagDataList.length === 0) {
- throw new Error("未读取到 UID,请确认 ISO14443A 卡片已到读卡位");
- }
- const firstRow = tagDataList[0];
- const uid = this.parseUidFromTagRow(firstRow);
- if (!uid) {
- throw new Error("未读取到 UID,请确认 ISO14443A 卡片已到读卡位");
- }
- return uid.toUpperCase();
- }
- // ISO15693 读取 UID
- async readUidISO15693() {
- await this.init();
- if (!this.connected || !this.handle) {
- throw new Error("读卡器未连接,请先连接读卡器");
- }
- const readResp = await this.sendCommand(
- "ISO15693_BatchReadMultiBlocks",
- {
- antennaCollection: [1],
- readDataType: this.readDataType,
- readSecurityStatus: this.readSecurityStatus,
- blockAddress: this.iso15693BlockAddress,
- numberOfBlocksRead: this.iso15693NumberOfBlocks,
- },
- this.handle,
- 6000
- );
- const code = this.getPayloadCode(readResp);
- if (code != null && code < 0) {
- const msg = this.getPayloadMessage(readResp) || "ISO15693 读卡失败";
- throw new Error(msg);
- }
- const tagDataList = this.extractTagDataList(readResp);
- if (!Array.isArray(tagDataList) || tagDataList.length === 0) {
- throw new Error("未读取到 UID,请确认 ISO15693 标签已到读卡位");
- }
- const firstRow = tagDataList[0];
- const uid = this.parseUidFromTagRow(firstRow);
- if (!uid) {
- throw new Error("未读取到 UID,请确认 ISO15693 标签已到读卡位");
- }
- return uid.toUpperCase();
- }
- // 根据协议类型读取 UID
- async readUid(protocol = "ISO14443A") {
- if (protocol === "ISO15693") {
- return await this.readUidISO15693();
- }
- return await this.readUidISO14443A();
- }
- }
- window.ReaderClient = ReaderClient;
|