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