zklib-ts 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ztcp.js ADDED
@@ -0,0 +1,800 @@
1
+ import { Socket } from 'net';
2
+ import { COMMANDS, Constants, REQUEST_DATA, DISCOVERED_CMD } from './helper/command';
3
+ import { ZkError } from './exceptions/handler';
4
+ import { authKey, checkNotEventTCP, decodeTCPHeader, createTCPHeader, removeTcpHeader, exportErrorMessage, decodeRTEvent, splitTcpPackets } from './helper/utils';
5
+ import { UserService } from './services/user.service';
6
+ import { TransactionService } from './services/transaction.service';
7
+ import { OptionsService } from './services/options.service';
8
+
9
+ class ZTCP {
10
+ /**
11
+ * @param_ip ip address of device
12
+ * @param_port port number of device
13
+ * @param_timeout connection timout
14
+ * @param_comm_key communication key of device (if the case)
15
+ * @return Zkteco TCP socket connection instance
16
+ */
17
+ ip;
18
+ port;
19
+ timeout;
20
+ sessionId = 0;
21
+ replyId = 0;
22
+ socket;
23
+ comm_key;
24
+ user_count = 0;
25
+ fp_count = 0;
26
+ pwd_count = 0;
27
+ oplog_count = 0;
28
+ attlog_count = 0;
29
+ fp_cap = 0;
30
+ user_cap = 0;
31
+ attlog_cap = 0;
32
+ fp_av = 0;
33
+ user_av = 0;
34
+ attlog_av = 0;
35
+ face_count = 0;
36
+ face_cap = 0;
37
+ userPacketSize = 72;
38
+ verbose = false;
39
+ packetNumber = 0;
40
+ replyData = Buffer.from([]);
41
+ _optionsService;
42
+ _transactionService;
43
+ _userService;
44
+ constructor(ip, port, timeout, comm_key, verbose) {
45
+ this.ip = ip;
46
+ this.port = port;
47
+ this.timeout = timeout ? timeout : 10000;
48
+ this.replyId = 0;
49
+ this.comm_key = comm_key;
50
+ this.verbose = verbose;
51
+ this._optionsService = new OptionsService(this);
52
+ this._userService = new UserService(this);
53
+ this._transactionService = new TransactionService(this);
54
+ }
55
+ createSocket(cbError, cbClose) {
56
+ return new Promise((resolve, reject) => {
57
+ this.socket = new Socket();
58
+ // Handle socket error
59
+ this.socket.once("error", (err) => {
60
+ this.socket = undefined; // Ensure socket reference is cleared
61
+ reject(err);
62
+ if (typeof cbError === "function")
63
+ cbError(err);
64
+ });
65
+ // Handle successful connection
66
+ this.socket.once("connect", () => {
67
+ resolve(this.socket);
68
+ });
69
+ // Handle socket closure
70
+ this.socket.once("close", () => {
71
+ this.socket = undefined; // Ensure socket reference is cleared
72
+ if (typeof cbClose === "function")
73
+ cbClose("tcp");
74
+ });
75
+ // Set socket timeout if provided
76
+ if (this.timeout) {
77
+ this.socket.setTimeout(this.timeout);
78
+ }
79
+ // Initiate connection
80
+ this.socket.connect(this.port, this.ip);
81
+ });
82
+ }
83
+ async connect() {
84
+ try {
85
+ let reply = await this.executeCmd(COMMANDS.CMD_CONNECT, "");
86
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
87
+ return true;
88
+ }
89
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_UNAUTH) {
90
+ const hashedCommkey = authKey(this.comm_key, this.sessionId);
91
+ reply = await this.executeCmd(COMMANDS.CMD_AUTH, hashedCommkey);
92
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
93
+ return true;
94
+ }
95
+ else {
96
+ throw new Error("error de authenticacion");
97
+ }
98
+ }
99
+ else {
100
+ // No reply received; throw an error
101
+ throw new Error("NO_REPLY_ON_CMD_CONNECT");
102
+ }
103
+ }
104
+ catch (err) {
105
+ // Log the error for debugging, if necessary
106
+ console.error("Failed to connect:", err);
107
+ // Re-throw the error for handling by the caller
108
+ throw err;
109
+ }
110
+ }
111
+ async closeSocket() {
112
+ return new Promise((resolve, reject) => {
113
+ // If no socket is present, resolve immediately
114
+ if (!this.socket) {
115
+ return resolve(true);
116
+ }
117
+ // Clean up listeners to avoid potential memory leaks or duplicate handling
118
+ this.socket.removeAllListeners("data");
119
+ // Set a timeout to handle cases where socket.end might not resolve
120
+ const timer = setTimeout(() => {
121
+ this.socket?.destroy(); // Forcibly close the socket if not closed properly
122
+ resolve(true); // Resolve even if the socket was not closed properly
123
+ }, 2000);
124
+ // Close the socket and clear the timeout upon successful completion
125
+ this.socket.end(() => {
126
+ clearTimeout(timer);
127
+ resolve(true); // Resolve once the socket has ended
128
+ });
129
+ // Handle socket errors during closing
130
+ this.socket.once("error", (err) => {
131
+ clearTimeout(timer);
132
+ reject(err); // Reject the promise with the error
133
+ });
134
+ });
135
+ }
136
+ writeMessage(msg, connect, cb) {
137
+ return new Promise((resolve, reject) => {
138
+ // Check if the socket is initialized
139
+ if (!this.socket) {
140
+ return reject(new Error("Socket is not initialized"));
141
+ }
142
+ // Define a variable for the timeout reference
143
+ const timer = setTimeout(() => {
144
+ // Check if the socket is still valid before trying to remove the listener
145
+ cleanUp();
146
+ reject(new Error("TIMEOUT_ON_WRITING_MESSAGE")); // Reject the promise on timeout
147
+ }, connect ? 3000 : this.timeout);
148
+ // Handle incoming data
149
+ const onData = (data) => {
150
+ // Check if the socket is still valid before trying to remove the listener
151
+ cleanUp(); // Clear the timeout once data is received
152
+ resolve(cb(data)); // Resolve the promise with the received data
153
+ };
154
+ const cleanUp = () => {
155
+ if (timer)
156
+ clearTimeout(timer);
157
+ if (this.socket) {
158
+ this.socket.removeListener("data", onData); // Remove the data event listener
159
+ }
160
+ };
161
+ // Attach the data event listener
162
+ this.socket.on("data", onData);
163
+ // Attempt to write the message to the socket
164
+ this.socket.write(msg, undefined, (err) => {
165
+ if (err) {
166
+ cleanUp();
167
+ reject(err); // Reject the promise with the write error
168
+ }
169
+ });
170
+ });
171
+ }
172
+ async requestData(msg) {
173
+ try {
174
+ return await new Promise((resolve, reject) => {
175
+ let timer = null;
176
+ let replyBuffer = Buffer.from([]);
177
+ // Internal callback to handle data reception
178
+ const internalCallback = (data_1) => {
179
+ if (this.socket) {
180
+ this.socket.removeListener("data", handleOnData); // Clean up listener
181
+ }
182
+ if (timer)
183
+ clearTimeout(timer); // Clear the timeout
184
+ resolve(data_1); // Resolve the promise with the data
185
+ };
186
+ const onTimeOut = () => setTimeout(() => {
187
+ if (this.socket) {
188
+ this.socket.removeListener("data", handleOnData); // Clean up listener on timeout
189
+ }
190
+ reject(new Error("TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA")); // Reject on timeout
191
+ }, this.timeout);
192
+ // Handle incoming data
193
+ const handleOnData = (data_3) => {
194
+ replyBuffer = Buffer.concat([replyBuffer, data_3]); // Accumulate data
195
+ // Check if the data is a valid TCP event
196
+ if (checkNotEventTCP(data_3))
197
+ return;
198
+ // Decode the TCP header
199
+ const header = decodeTCPHeader(replyBuffer.subarray(0, 16));
200
+ if (this.verbose) {
201
+ console.log("response command: ", header.commandId, COMMANDS[header.commandId], "replyid: ", header.replyId);
202
+ }
203
+ // Handle based on command ID
204
+ if (header.commandId === COMMANDS.CMD_DATA) {
205
+ // Set a timeout to handle delayed responses
206
+ timer = setTimeout(() => {
207
+ internalCallback(replyBuffer); // Resolve with accumulated buffer
208
+ }, 1000);
209
+ }
210
+ else {
211
+ // Set a timeout to handle errors
212
+ timer = onTimeOut();
213
+ // Extract packet length and handle accordingly
214
+ const packetLength = data_3.readUIntLE(4, 2);
215
+ if (packetLength > 8) {
216
+ internalCallback(data_3); // Resolve immediately if sufficient data
217
+ }
218
+ }
219
+ };
220
+ // Ensure the socket is valid before attaching the listener
221
+ if (this.socket) {
222
+ this.socket.on("data", handleOnData);
223
+ // Write the message to the socket
224
+ this.socket.write(msg, undefined, (err) => {
225
+ if (err) {
226
+ if (this.socket) {
227
+ this.socket.removeListener("data", handleOnData); // Clean up listener on error
228
+ }
229
+ return reject(err); // Reject the promise with the error
230
+ }
231
+ // Set a timeout to handle cases where no response is received
232
+ timer = onTimeOut();
233
+ });
234
+ }
235
+ else {
236
+ reject(new Error("SOCKET_NOT_INITIALIZED")); // Reject if socket is not initialized
237
+ }
238
+ });
239
+ }
240
+ catch (err_1) {
241
+ console.error("Promise Rejected:", err_1); // Log the rejection reason
242
+ throw err_1; // Re-throw the error to be handled by the caller
243
+ }
244
+ }
245
+ /**
246
+ *
247
+ * @param {*} command
248
+ * @param {*} data
249
+ *
250
+ *
251
+ * reject error when command fail and resolve data when success
252
+ */
253
+ async executeCmd(command, data) {
254
+ // Reset sessionId and replyId for connection commands
255
+ if (command === COMMANDS.CMD_CONNECT) {
256
+ this.sessionId = 0;
257
+ this.replyId = 0;
258
+ }
259
+ else {
260
+ this.replyId++;
261
+ }
262
+ const currentReply = this.replyId;
263
+ const buf = createTCPHeader(command, this.sessionId, this.replyId, data);
264
+ const callback = (responseData) => {
265
+ const packets = splitTcpPackets(responseData);
266
+ for (const packet of packets) {
267
+ const headers = decodeTCPHeader(packet);
268
+ if (this.verbose) {
269
+ const JOIN_CMD = { ...COMMANDS, ...DISCOVERED_CMD };
270
+ console.debug("request command:", COMMANDS[command], "\nresponse command: ", JOIN_CMD[headers.commandId], "replyid: ", headers.replyId);
271
+ }
272
+ if (+headers.replyId === currentReply + 1) {
273
+ return packet;
274
+ }
275
+ continue;
276
+ }
277
+ };
278
+ return new Promise((Resolve, Reject) => {
279
+ // Write the message to the socket and wait for a response
280
+ this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT, callback)
281
+ .then((reply) => {
282
+ // Remove TCP header from the response
283
+ let rReply;
284
+ try {
285
+ rReply = removeTcpHeader(reply);
286
+ }
287
+ catch (e) {
288
+ console.log("reply", reply);
289
+ }
290
+ // Update sessionId for connection command responses
291
+ if (command === COMMANDS.CMD_CONNECT &&
292
+ rReply &&
293
+ rReply.length >= 6) {
294
+ // Assuming sessionId is located at offset 4 and is 2 bytes long
295
+ this.sessionId = rReply.readUInt16LE(4);
296
+ }
297
+ Resolve(rReply);
298
+ })
299
+ .catch((err) => {
300
+ // Log or handle the error if necessary
301
+ console.error("Error executing command:", err);
302
+ Reject(err); // Re-throw the error for handling by the caller
303
+ });
304
+ });
305
+ }
306
+ async sendChunkRequest(start, size) {
307
+ this.replyId++;
308
+ const reqData = Buffer.alloc(8);
309
+ reqData.writeUInt32LE(start, 0);
310
+ reqData.writeUInt32LE(size, 4);
311
+ const buf = createTCPHeader(COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
312
+ try {
313
+ const promise = new Promise((resolve, reject) => {
314
+ this.socket?.write(buf, undefined, (err) => {
315
+ if (err) {
316
+ console.error(`[TCP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
317
+ reject(err); // Reject the promise if there is an error
318
+ }
319
+ else {
320
+ resolve(true); // Resolve the promise if the write operation succeeds
321
+ }
322
+ });
323
+ });
324
+ await promise;
325
+ }
326
+ catch (err) {
327
+ // Handle or log the error as needed
328
+ console.error(`[TCP][SEND_CHUNK_REQUEST] Exception: ${err.message}`);
329
+ throw err; // Re-throw the error for handling by the caller
330
+ }
331
+ }
332
+ /**
333
+ *
334
+ * @param {Buffer} reqData - indicate the type of data that need to receive ( user or attLog)
335
+ * @param {Function} cb - callback is triggered when receiving packets
336
+ *
337
+ * readWithBuffer will reject error if it'wrong when starting request data
338
+ * readWithBuffer will return { data: replyData , err: Error } when receiving requested data
339
+ */
340
+ readWithBuffer(reqData, cb) {
341
+ return new Promise(async (resolve, reject) => {
342
+ this.replyId++;
343
+ const buf = createTCPHeader(COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
344
+ let reply;
345
+ try {
346
+ reply = await this.requestData(buf);
347
+ }
348
+ catch (err) {
349
+ reject(err);
350
+ }
351
+ const header = decodeTCPHeader(reply?.subarray(0, 16));
352
+ switch (header.commandId) {
353
+ case COMMANDS.CMD_DATA: {
354
+ resolve({ data: reply.subarray(16), mode: 8 });
355
+ break;
356
+ }
357
+ case COMMANDS.CMD_ACK_OK:
358
+ case COMMANDS.CMD_PREPARE_DATA: {
359
+ // this case show that data is prepared => send command to get these data
360
+ // reply variable includes information about the size of following data
361
+ const recvData = reply.subarray(16);
362
+ const size = recvData.readUIntLE(1, 4);
363
+ // We need to split the data to many chunks to receive , because it's to large
364
+ // After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
365
+ const remain = size % Constants.MAX_CHUNK;
366
+ const numberChunks = Math.round(size - remain) / Constants.MAX_CHUNK;
367
+ this.packetNumber = numberChunks + (remain > 0 ? 1 : 0);
368
+ //let replyData = Buffer.from([])
369
+ let totalBuffer = Buffer.from([]);
370
+ let realTotalBuffer = Buffer.from([]);
371
+ let timer = setTimeout(() => {
372
+ internalCallback(this.replyData, new Error("TIMEOUT WHEN RECEIVING PACKET"));
373
+ }, this.timeout);
374
+ const internalCallback = (replyData, err = null) => {
375
+ this.socket && this.socket.removeAllListeners("data");
376
+ timer && clearTimeout(timer);
377
+ resolve({ data: replyData, err });
378
+ };
379
+ this.socket?.once("close", () => {
380
+ internalCallback(this.replyData, new Error("Socket is disconnected unexpectedly"));
381
+ });
382
+ for (let i = 0; i <= numberChunks; i++) {
383
+ const data = await new Promise((resolve2, reject2) => {
384
+ try {
385
+ this.sendChunkRequest(i * Constants.MAX_CHUNK, i === numberChunks ? remain : Constants.MAX_CHUNK);
386
+ this.socket?.on("data", (reply) => {
387
+ clearTimeout(timer);
388
+ timer = setTimeout(() => {
389
+ internalCallback(this.replyData, new Error(`TIME OUT !! ${this.packetNumber} PACKETS REMAIN !`));
390
+ }, this.timeout);
391
+ if (this.verbose && reply.length >= 8) {
392
+ const headers = decodeTCPHeader(reply);
393
+ if (COMMANDS[headers.commandId]) {
394
+ switch (headers.commandId) {
395
+ case COMMANDS.CMD_ACK_OK:
396
+ case COMMANDS.CMD_DATA:
397
+ this.verbose &&
398
+ console.log("CMD received: ", COMMANDS[headers.commandId]);
399
+ break;
400
+ case COMMANDS.CMD_PREPARE_DATA:
401
+ this.verbose &&
402
+ console.log("CMD received: ", COMMANDS[headers.commandId]);
403
+ this.verbose &&
404
+ console.log(`recieve chunk: prepare data size is ${headers.payloadSize}`);
405
+ break;
406
+ default:
407
+ break;
408
+ }
409
+ }
410
+ }
411
+ totalBuffer = Buffer.concat([totalBuffer, reply]);
412
+ const packetLength = totalBuffer.readUIntLE(4, 2);
413
+ if (totalBuffer.length >= 8 + packetLength) {
414
+ realTotalBuffer = Buffer.concat([
415
+ realTotalBuffer,
416
+ totalBuffer.subarray(16, 8 + packetLength),
417
+ ]);
418
+ totalBuffer = totalBuffer.subarray(8 + packetLength);
419
+ if ((this.packetNumber > 1 &&
420
+ realTotalBuffer.length === Constants.MAX_CHUNK + 8) ||
421
+ (this.packetNumber === 1 &&
422
+ realTotalBuffer.length === remain + 8)) {
423
+ this.packetNumber--;
424
+ cb && cb(realTotalBuffer.length, size);
425
+ resolve2(realTotalBuffer.subarray(8));
426
+ totalBuffer = Buffer.from([]);
427
+ realTotalBuffer = Buffer.from([]);
428
+ }
429
+ }
430
+ });
431
+ }
432
+ catch (e) {
433
+ reject2(e);
434
+ }
435
+ });
436
+ this.replyData = Buffer.concat([
437
+ this.replyData,
438
+ data,
439
+ ]);
440
+ this.socket?.removeAllListeners("data");
441
+ if (this.packetNumber <= 0) {
442
+ resolve({ data: this.replyData });
443
+ }
444
+ }
445
+ break;
446
+ }
447
+ default: {
448
+ reject(new Error("ERROR_IN_UNHANDLE_CMD " + exportErrorMessage(header.commandId)));
449
+ }
450
+ }
451
+ });
452
+ }
453
+ /**
454
+ *
455
+ * @param {*} callbackInProcess
456
+ * reject error when starting request data
457
+ * return { data: records, err: Error } when receiving requested data
458
+ */
459
+ async freeData() {
460
+ try {
461
+ const resp = await this.executeCmd(COMMANDS.CMD_FREE_DATA, "");
462
+ return !!resp;
463
+ }
464
+ catch (err) {
465
+ console.error("Error freeing data:", err);
466
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
467
+ }
468
+ }
469
+ async disableDevice() {
470
+ try {
471
+ const resp = await this.executeCmd(COMMANDS.CMD_DISABLEDEVICE, REQUEST_DATA.DISABLE_DEVICE);
472
+ return !!resp;
473
+ }
474
+ catch (err) {
475
+ console.error("Error disabling device:", err);
476
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
477
+ }
478
+ }
479
+ async enableDevice() {
480
+ try {
481
+ const resp = await this.executeCmd(COMMANDS.CMD_ENABLEDEVICE, "");
482
+ return !!resp;
483
+ }
484
+ catch (err) {
485
+ console.error("Error enabling device:", err);
486
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
487
+ }
488
+ }
489
+ async disconnect() {
490
+ try {
491
+ // Attempt to execute the disconnect command
492
+ await this.executeCmd(COMMANDS.CMD_EXIT, "");
493
+ }
494
+ catch (err) {
495
+ // Log any errors encountered during command execution
496
+ console.error("Error during disconnection:", err);
497
+ // Optionally, add more handling or recovery logic here
498
+ }
499
+ // Attempt to close the socket and return the result
500
+ try {
501
+ await this.closeSocket();
502
+ }
503
+ catch (err) {
504
+ // Log any errors encountered while closing the socket
505
+ console.error("Error during socket closure:", err);
506
+ // Optionally, rethrow or handle the error if necessary
507
+ throw err; // Re-throwing to propagate the error
508
+ }
509
+ }
510
+ async getInfo() {
511
+ try {
512
+ // Execute the command to retrieve free sizes from the device
513
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
514
+ // Parse the response data to extract and return relevant information
515
+ return {
516
+ userCounts: data.readUIntLE(24, 4), // Number of users
517
+ logCounts: data.readUIntLE(40, 4), // Number of logs
518
+ logCapacity: data.readUIntLE(72, 4), // Capacity of logs in bytes
519
+ };
520
+ }
521
+ catch (err) {
522
+ // Log the error for debugging purposes
523
+ console.error("Error getting device info:", err);
524
+ // Re-throw the error to allow upstream error handling
525
+ throw err;
526
+ }
527
+ }
528
+ async getSizes() {
529
+ try {
530
+ // Execute the command to retrieve free sizes from the device
531
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
532
+ // Parse the response data to extract and return relevant information
533
+ const buf = data.slice(8); // remove header
534
+ this.user_count = buf.readUIntLE(16, 4);
535
+ this.fp_count = buf.readUIntLE(24, 4);
536
+ this.pwd_count = buf.readUIntLE(52, 4);
537
+ this.oplog_count = buf.readUIntLE(40, 4);
538
+ this.attlog_count = buf.readUIntLE(32, 4);
539
+ this.fp_cap = buf.readUIntLE(56, 4);
540
+ this.user_cap = buf.readUIntLE(60, 4);
541
+ this.attlog_cap = buf.readUIntLE(64, 4);
542
+ this.fp_av = buf.readUIntLE(68, 4);
543
+ this.user_av = buf.readUIntLE(72, 4);
544
+ this.attlog_av = buf.readUIntLE(76, 4);
545
+ this.face_count = buf.readUIntLE(80, 4);
546
+ this.face_cap = buf.readUIntLE(88, 4);
547
+ return {
548
+ userCounts: this.user_count, // Number of users
549
+ logCounts: this.attlog_count, // Number of logs
550
+ fingerCount: this.fp_count,
551
+ adminCount: this.pwd_count,
552
+ opLogCount: this.oplog_count,
553
+ logCapacity: this.attlog_cap, // Capacity of logs in bytes
554
+ fingerCapacity: this.fp_cap,
555
+ userCapacity: this.user_cap,
556
+ attLogCapacity: this.attlog_cap,
557
+ fingerAvailable: this.fp_av,
558
+ userAvailable: this.user_av,
559
+ attLogAvailable: this.attlog_av,
560
+ faceCount: this.face_count,
561
+ faceCapacity: this.face_cap,
562
+ };
563
+ }
564
+ catch (err) {
565
+ // Log the error for debugging purposes
566
+ console.error("Error getting device info:", err);
567
+ // Re-throw the error to allow upstream error handling
568
+ throw err;
569
+ }
570
+ }
571
+ #listeners = new Map();
572
+ async getAttendanceSize() {
573
+ try {
574
+ // Execute command to get free sizes
575
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
576
+ // Parse and return the attendance size
577
+ return data.readUIntLE(40, 4); // Assuming data at offset 40 represents the attendance size
578
+ }
579
+ catch (err) {
580
+ // Log error details for debugging
581
+ console.error("Error getting attendance size:", err);
582
+ // Re-throw the error to be handled by the caller
583
+ throw err;
584
+ }
585
+ }
586
+ // Clears the attendance logs on the device
587
+ async clearAttendanceLog() {
588
+ return await this._transactionService.clearAttendanceLog();
589
+ }
590
+ /**
591
+ * Clears all data on the device
592
+ * @value 1 Attendance records
593
+ * @value 2 Fingerprint templates
594
+ * @value 3 None
595
+ * @value 4 Operation records
596
+ * @value 5 User information
597
+ * @default 0 Delete all
598
+ */
599
+ async clearData(value) {
600
+ try {
601
+ // Execute the command to clear all data
602
+ await this.disableDevice();
603
+ if (!value)
604
+ value = 3;
605
+ const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
606
+ await this.refreshData();
607
+ await this.enableDevice();
608
+ return !!buf;
609
+ }
610
+ catch (err) {
611
+ // Log the error for debugging purposes
612
+ console.error("Error clearing data:", err);
613
+ // Re-throw the error to be handled by the caller
614
+ throw err;
615
+ }
616
+ }
617
+ async getRealTimeLogs(cb = (realTimeLog) => { }) {
618
+ this.replyId++; // Increment the reply ID for this request
619
+ try {
620
+ // Create a buffer with the command header to request real-time logs
621
+ const buf = createTCPHeader(COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, Buffer.from([0x01, 0x00, 0x00, 0x00]));
622
+ // Send the request to the device
623
+ this.socket?.write(buf, undefined, (err) => {
624
+ if (err) {
625
+ // Log and reject the promise if there is an error writing to the socket
626
+ console.error("Error sending real-time logs request:", err);
627
+ throw err;
628
+ }
629
+ });
630
+ // Ensure data listeners are added only once
631
+ if (this.socket?.listenerCount("data") === 0) {
632
+ console.log("entraaa");
633
+ this.socket.on("data", (data) => {
634
+ // Check if the data is an event and not just a regular response
635
+ if (checkNotEventTCP(data)) {
636
+ // Process the data if it is of the expected length
637
+ if (data.length > 16) {
638
+ // Decode and pass the log to the callback
639
+ cb(decodeRTEvent(data));
640
+ }
641
+ }
642
+ });
643
+ }
644
+ }
645
+ catch (err) {
646
+ // Handle errors and reject the promise
647
+ console.error("Error getting real-time logs:", err);
648
+ throw err;
649
+ }
650
+ }
651
+ /**
652
+ * Get all Finger objects
653
+ * @returns {Record<string, Finger[]>}
654
+ */
655
+ async getTemplates(callbackInProcess = () => { }) {
656
+ return await this._userService.getTemplates(callbackInProcess);
657
+ }
658
+ /**
659
+ * Return size
660
+ * @param packet
661
+ */
662
+ testTcpTop(packet) {
663
+ // Check if packet is too small
664
+ if (packet.length <= 8)
665
+ return 0;
666
+ // Extract header values using little-endian format
667
+ const headerValue1 = packet.readUInt16LE(0);
668
+ const headerValue2 = packet.readUInt16LE(2);
669
+ const size = packet.readUInt32LE(4);
670
+ // Check if magic numbers match
671
+ if (headerValue1 === Constants.MACHINE_PREPARE_DATA_1 &&
672
+ headerValue2 === Constants.MACHINE_PREPARE_DATA_2) {
673
+ return size;
674
+ }
675
+ return 0;
676
+ }
677
+ async refreshData() {
678
+ try {
679
+ const reply = await this.executeCmd(COMMANDS.CMD_REFRESHDATA, "");
680
+ return !!reply;
681
+ }
682
+ catch (err) {
683
+ console.error("Error getting user templates: ", err);
684
+ throw err;
685
+ }
686
+ }
687
+ async sendWithBuffer(buffer) {
688
+ const MAX_CHUNK = 1024;
689
+ const size = buffer.length;
690
+ await this.freeData();
691
+ const commandString = Buffer.alloc(4); // 'I' is 4 bytes
692
+ commandString.writeUInt32LE(size, 0);
693
+ try {
694
+ const cmdResponse = await this.executeCmd(COMMANDS.CMD_PREPARE_DATA, commandString);
695
+ // responds with 2000 = CMD_ACK_OK
696
+ if (!cmdResponse) {
697
+ throw new Error("Can't prepare data");
698
+ }
699
+ }
700
+ catch (e) {
701
+ console.error(e);
702
+ }
703
+ const remain = size % MAX_CHUNK;
704
+ const packets = Math.floor((size - remain) / MAX_CHUNK);
705
+ let start = 0;
706
+ try {
707
+ for (let i = 0; i < packets; i++) {
708
+ const resp = await this.sendChunk(buffer.slice(start, start + MAX_CHUNK));
709
+ if (resp) {
710
+ start += MAX_CHUNK;
711
+ if (i == packets - 1 && remain) {
712
+ const lastPacket = await this.sendChunk(buffer.slice(start, start + remain));
713
+ return lastPacket;
714
+ }
715
+ }
716
+ }
717
+ }
718
+ catch (e) {
719
+ console.error(e);
720
+ }
721
+ }
722
+ async sendChunk(commandString) {
723
+ try {
724
+ return await new Promise((resolve, reject) => {
725
+ resolve(this.executeCmd(COMMANDS.CMD_DATA, commandString));
726
+ });
727
+ }
728
+ catch (e) {
729
+ throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
730
+ }
731
+ }
732
+ async readSocket(length, cb = null) {
733
+ let replyBufer = Buffer.from([]);
734
+ const totalPackets = 0;
735
+ return new Promise((resolve, reject) => {
736
+ let timer = setTimeout(() => {
737
+ internalCallback(replyBufer, new Error("TIMEOUT WHEN RECEIVING PACKET"));
738
+ }, this.timeout);
739
+ const internalCallback = (replyData, err = null) => {
740
+ this.socket && this.socket.removeListener("data", onDataEnroll);
741
+ timer && clearTimeout(timer);
742
+ resolve({ data: replyData, err: err });
743
+ };
744
+ function onDataEnroll(data) {
745
+ clearTimeout(timer);
746
+ timer = setTimeout(() => {
747
+ internalCallback(replyBufer, new Error(`TIME OUT !! ${totalPackets} PACKETS REMAIN !`));
748
+ }, this.timeout);
749
+ replyBufer = Buffer.concat([replyBufer, data], replyBufer.length + data.length);
750
+ if (data.length == length) {
751
+ internalCallback(data);
752
+ }
753
+ }
754
+ this.socket.once("close", () => {
755
+ internalCallback(replyBufer, new Error("Socket is disconnected unexpectedly"));
756
+ });
757
+ this.socket.on("data", onDataEnroll);
758
+ }).catch((err) => {
759
+ console.error("Promise Rejected:", err); // Log the rejection reason
760
+ throw err; // Re-throw the error to be handled by the caller
761
+ });
762
+ }
763
+ /**
764
+ * Register events
765
+ * @param {number} flags - Event flags
766
+ * @returns {Promise<void>}
767
+ * @throws {ZKErrorResponse} If registration fails
768
+ */
769
+ async regEvent(flags) {
770
+ try {
771
+ const commandString = Buffer.alloc(4); // 'I' format is 4 bytes
772
+ commandString.writeUInt32LE(flags, 0); // Little-endian unsigned int
773
+ const cmdResponse = await this.executeCmd(COMMANDS.CMD_REG_EVENT, commandString);
774
+ if (this.verbose)
775
+ console.log("regEvent: ", cmdResponse.readUInt16LE(0));
776
+ }
777
+ catch (e) {
778
+ throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
779
+ }
780
+ }
781
+ async cancelCapture() {
782
+ try {
783
+ const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, "");
784
+ return !!reply;
785
+ }
786
+ catch (e) {
787
+ throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
788
+ }
789
+ }
790
+ async restartDevice() {
791
+ try {
792
+ await this.executeCmd(COMMANDS.CMD_RESTART, "");
793
+ }
794
+ catch (e) {
795
+ throw new ZkError(e, COMMANDS.CMD_RESTART, this.ip);
796
+ }
797
+ }
798
+ }
799
+
800
+ export { ZTCP };