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/index.cjs.js CHANGED
@@ -1,9 +1,3500 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const ztcp_1 = require("./ztcp");
5
- const zudp_1 = require("./zudp");
6
- const handler_1 = require("./exceptions/handler");
3
+ var net = require('net');
4
+ var fs = require('fs');
5
+ var dgram = require('node:dgram');
6
+
7
+ function _interopNamespaceDefault(e) {
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+
24
+ var dgram__namespace = /*#__PURE__*/_interopNamespaceDefault(dgram);
25
+
26
+ var COMMANDS;
27
+ (function (COMMANDS) {
28
+ COMMANDS[COMMANDS["CMD_ACK_DATA"] = 2002] = "CMD_ACK_DATA";
29
+ /** There was an error when processing the request.*/
30
+ COMMANDS[COMMANDS["CMD_ACK_ERROR"] = 2001] = "CMD_ACK_ERROR";
31
+ COMMANDS[COMMANDS["CMD_ACK_ERROR_CMD"] = 65533] = "CMD_ACK_ERROR_CMD";
32
+ COMMANDS[COMMANDS["CMD_ACK_ERROR_DATA"] = 65531] = "CMD_ACK_ERROR_DATA";
33
+ COMMANDS[COMMANDS["CMD_ACK_ERROR_INIT"] = 65532] = "CMD_ACK_ERROR_INIT";
34
+ /** [0xD0, 0x07] The request was processed sucessfully. */
35
+ COMMANDS[COMMANDS["CMD_ACK_OK"] = 2000] = "CMD_ACK_OK";
36
+ COMMANDS[COMMANDS["CMD_ACK_REPEAT"] = 2004] = "CMD_ACK_REPEAT";
37
+ COMMANDS[COMMANDS["CMD_ACK_RETRY"] = 2003] = "CMD_ACK_RETRY";
38
+ /** [0xD5, 0x07] Connection not authorized. */
39
+ COMMANDS[COMMANDS["CMD_ACK_UNAUTH"] = 2005] = "CMD_ACK_UNAUTH";
40
+ /** Received unknown command. */
41
+ COMMANDS[COMMANDS["CMD_ACK_UNKNOWN"] = 65535] = "CMD_ACK_UNKNOWN";
42
+ /** Request attendance log. */
43
+ COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
44
+ /** [0x4E, 0x04] Request to begin session using commkey. */
45
+ COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
46
+ /** [0x3e, 0x00] Disable normal authentication of users. */
47
+ COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
48
+ /** Capture fingerprint picture. */
49
+ COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
50
+ /** Capture the entire image. */
51
+ COMMANDS[COMMANDS["CMD_CAPTUREIMAGE"] = 1012] = "CMD_CAPTUREIMAGE";
52
+ /** Change transmission speed. */
53
+ COMMANDS[COMMANDS["CMD_CHANGE_SPEED"] = 1101] = "CMD_CHANGE_SPEED";
54
+ /** [0x77, 0x00] Get checksum of machine's buffer. */
55
+ COMMANDS[COMMANDS["CMD_CHECKSUM_BUFFER"] = 119] = "CMD_CHECKSUM_BUFFER";
56
+ /** Restore access control to default. */
57
+ COMMANDS[COMMANDS["CMD_CLEAR_ACC"] = 32] = "CMD_CLEAR_ACC";
58
+ /** Clears admins privileges. */
59
+ COMMANDS[COMMANDS["CMD_CLEAR_ADMIN"] = 20] = "CMD_CLEAR_ADMIN";
60
+ /** Delete attendance record. */
61
+ COMMANDS[COMMANDS["CMD_CLEAR_ATTLOG"] = 15] = "CMD_CLEAR_ATTLOG";
62
+ /** Delete data. */
63
+ COMMANDS[COMMANDS["CMD_CLEAR_DATA"] = 14] = "CMD_CLEAR_DATA";
64
+ /** Clear screen captions. */
65
+ COMMANDS[COMMANDS["CMD_CLEAR_LCD"] = 67] = "CMD_CLEAR_LCD";
66
+ /** Delete operations log. */
67
+ COMMANDS[COMMANDS["CMD_CLEAR_OPLOG"] = 33] = "CMD_CLEAR_OPLOG";
68
+ /** [0xE8, 0x03] Begin connection. */
69
+ COMMANDS[COMMANDS["CMD_CONNECT"] = 1000] = "CMD_CONNECT";
70
+ /** [0xDD, 0x05] Data packet. */
71
+ COMMANDS[COMMANDS["CMD_DATA"] = 1501] = "CMD_DATA";
72
+ /** Indicates that it is ready to receive data. */
73
+ COMMANDS[COMMANDS["CMD_DATA_RDY"] = 1504] = "CMD_DATA_RDY";
74
+ /** Read/Write a large data set. */
75
+ COMMANDS[COMMANDS["CMD_DATA_WRRQ"] = 1503] = "CMD_DATA_WRRQ";
76
+ /** Read saved data. */
77
+ COMMANDS[COMMANDS["CMD_DB_RRQ"] = 7] = "CMD_DB_RRQ";
78
+ /** Deletes fingerprint template. */
79
+ COMMANDS[COMMANDS["CMD_DEL_FPTMP"] = 134] = "CMD_DEL_FPTMP";
80
+ /** Delete short message. */
81
+ COMMANDS[COMMANDS["CMD_DELETE_SMS"] = 72] = "CMD_DELETE_SMS";
82
+ /** Delete user short message. */
83
+ COMMANDS[COMMANDS["CMD_DELETE_UDATA"] = 74] = "CMD_DELETE_UDATA";
84
+ /** Delete user. */
85
+ COMMANDS[COMMANDS["CMD_DELETE_USER"] = 18] = "CMD_DELETE_USER";
86
+ /** Delete user fingerprint template. */
87
+ COMMANDS[COMMANDS["CMD_DELETE_USERTEMP"] = 19] = "CMD_DELETE_USERTEMP";
88
+ /** Disables fingerprint, rfid reader and keyboard. */
89
+ COMMANDS[COMMANDS["CMD_DISABLEDEVICE"] = 1003] = "CMD_DISABLEDEVICE";
90
+ /** Get door state. */
91
+ COMMANDS[COMMANDS["CMD_DOORSTATE_RRQ"] = 75] = "CMD_DOORSTATE_RRQ";
92
+ /** Clear Mifare card. */
93
+ COMMANDS[COMMANDS["CMD_EMPTY_MIFARE"] = 78] = "CMD_EMPTY_MIFARE";
94
+ /** Enables the ":" in screen clock. */
95
+ COMMANDS[COMMANDS["CMD_ENABLE_CLOCK"] = 57] = "CMD_ENABLE_CLOCK";
96
+ /** Change machine state to "normal work". */
97
+ COMMANDS[COMMANDS["CMD_ENABLEDEVICE"] = 1002] = "CMD_ENABLEDEVICE";
98
+ /** [0xE9, 0x03] Disconnect. */
99
+ COMMANDS[COMMANDS["CMD_EXIT"] = 1001] = "CMD_EXIT";
100
+ /** [0xDE, 0x05] Release buffer used for data transmission. */
101
+ COMMANDS[COMMANDS["CMD_FREE_DATA"] = 1502] = "CMD_FREE_DATA";
102
+ /** Request machine status (remaining space). */
103
+ COMMANDS[COMMANDS["CMD_GET_FREE_SIZES"] = 50] = "CMD_GET_FREE_SIZES";
104
+ /** Request max size for users id. */
105
+ COMMANDS[COMMANDS["CMD_GET_PINWIDTH"] = 69] = "CMD_GET_PINWIDTH";
106
+ /** Request machine time. */
107
+ COMMANDS[COMMANDS["CMD_GET_TIME"] = 201] = "CMD_GET_TIME";
108
+ COMMANDS[COMMANDS["CMD_GET_USERTEMP"] = 88] = "CMD_GET_USERTEMP";
109
+ /** Request the firmware edition. */
110
+ COMMANDS[COMMANDS["CMD_GET_VERSION"] = 1100] = "CMD_GET_VERSION";
111
+ /** Get group timezone. */
112
+ COMMANDS[COMMANDS["CMD_GRPTZ_RRQ"] = 25] = "CMD_GRPTZ_RRQ";
113
+ /** Set group timezone. */
114
+ COMMANDS[COMMANDS["CMD_GRPTZ_WRQ"] = 26] = "CMD_GRPTZ_WRQ";
115
+ /** Read operations log. */
116
+ COMMANDS[COMMANDS["CMD_OPLOG_RRQ"] = 34] = "CMD_OPLOG_RRQ";
117
+ /** Read configuration value of the machine. */
118
+ COMMANDS[COMMANDS["CMD_OPTIONS_RRQ"] = 11] = "CMD_OPTIONS_RRQ";
119
+ /** Change configuration value of the machine. */
120
+ COMMANDS[COMMANDS["CMD_OPTIONS_WRQ"] = 12] = "CMD_OPTIONS_WRQ";
121
+ /** Shut-down machine. */
122
+ COMMANDS[COMMANDS["CMD_POWEROFF"] = 1005] = "CMD_POWEROFF";
123
+ /** [0xDC, 0x05] Prepare for data transmission. */
124
+ COMMANDS[COMMANDS["CMD_PREPARE_DATA"] = 1500] = "CMD_PREPARE_DATA";
125
+ /** [0xF5, 0x03] Refresh the machine stored data. */
126
+ COMMANDS[COMMANDS["CMD_REFRESHDATA"] = 1013] = "CMD_REFRESHDATA";
127
+ /** Refresh the configuration parameters. */
128
+ COMMANDS[COMMANDS["CMD_REFRESHOPTION"] = 1014] = "CMD_REFRESHOPTION";
129
+ /** Realtime events. */
130
+ COMMANDS[COMMANDS["CMD_REG_EVENT"] = 500] = "CMD_REG_EVENT";
131
+ /** Restart machine. */
132
+ COMMANDS[COMMANDS["CMD_RESTART"] = 1004] = "CMD_RESTART";
133
+ /** Change machine state to "awaken". */
134
+ COMMANDS[COMMANDS["CMD_RESUME"] = 1007] = "CMD_RESUME";
135
+ /** Set machine time. */
136
+ COMMANDS[COMMANDS["CMD_SET_TIME"] = 202] = "CMD_SET_TIME";
137
+ /** Change machine state to "idle". */
138
+ COMMANDS[COMMANDS["CMD_SLEEP"] = 1006] = "CMD_SLEEP";
139
+ /** Download short message. */
140
+ COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
141
+ /** Upload short message. */
142
+ COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
143
+ /** [0x3d, 0x00] Start enroll procedure. */
144
+ COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
145
+ /** [0x3c, 0x00] Set the machine to authentication state. */
146
+ COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
147
+ /** Query state. */
148
+ COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
149
+ /** Test if fingerprint exists. */
150
+ COMMANDS[COMMANDS["CMD_TEST_TEMP"] = 1011] = "CMD_TEST_TEMP";
151
+ /** Test voice. */
152
+ COMMANDS[COMMANDS["CMD_TESTVOICE"] = 1017] = "CMD_TESTVOICE";
153
+ /** [0x77, 0x00] Transfer fp template from buffer. */
154
+ COMMANDS[COMMANDS["CMD_TMP_WRITE"] = 87] = "CMD_TMP_WRITE";
155
+ /** Get device timezones. */
156
+ COMMANDS[COMMANDS["CMD_TZ_RRQ"] = 27] = "CMD_TZ_RRQ";
157
+ /** Set device timezones. */
158
+ COMMANDS[COMMANDS["CMD_TZ_WRQ"] = 28] = "CMD_TZ_WRQ";
159
+ /** Set user short message. */
160
+ COMMANDS[COMMANDS["CMD_UDATA_WRQ"] = 73] = "CMD_UDATA_WRQ";
161
+ /** Get group combination to unlock. */
162
+ COMMANDS[COMMANDS["CMD_ULG_RRQ"] = 29] = "CMD_ULG_RRQ";
163
+ /** Set group combination to unlock. */
164
+ COMMANDS[COMMANDS["CMD_ULG_WRQ"] = 30] = "CMD_ULG_WRQ";
165
+ /** Unlock door for a specified amount of time. */
166
+ COMMANDS[COMMANDS["CMD_UNLOCK"] = 31] = "CMD_UNLOCK";
167
+ /** Upload user data. */
168
+ COMMANDS[COMMANDS["CMD_USER_WRQ"] = 8] = "CMD_USER_WRQ";
169
+ /** Read user group. */
170
+ COMMANDS[COMMANDS["CMD_USERGRP_RRQ"] = 21] = "CMD_USERGRP_RRQ";
171
+ /** Set user group. */
172
+ COMMANDS[COMMANDS["CMD_USERGRP_WRQ"] = 22] = "CMD_USERGRP_WRQ";
173
+ /** [0x09, 0x00] Read user fingerprint template. */
174
+ COMMANDS[COMMANDS["CMD_USERTEMP_RRQ"] = 9] = "CMD_USERTEMP_RRQ";
175
+ /** Upload user fingerprint template. */
176
+ COMMANDS[COMMANDS["CMD_USERTEMP_WRQ"] = 10] = "CMD_USERTEMP_WRQ";
177
+ /** Get user timezones. */
178
+ COMMANDS[COMMANDS["CMD_USERTZ_RRQ"] = 23] = "CMD_USERTZ_RRQ";
179
+ /** Set the user timezones. */
180
+ COMMANDS[COMMANDS["CMD_USERTZ_WRQ"] = 24] = "CMD_USERTZ_WRQ";
181
+ /** Read verification style of a given user. */
182
+ COMMANDS[COMMANDS["CMD_VERIFY_RRQ"] = 80] = "CMD_VERIFY_RRQ";
183
+ /** Change verification style of a given user. */
184
+ COMMANDS[COMMANDS["CMD_VERIFY_WRQ"] = 79] = "CMD_VERIFY_WRQ";
185
+ /** Prints chars to the device screen. */
186
+ COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
187
+ /** Write data to Mifare card. */
188
+ COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
189
+ })(COMMANDS || (COMMANDS = {}));
190
+ var RTEvent;
191
+ (function (RTEvent) {
192
+ /** Triggered alarm. */
193
+ RTEvent[RTEvent["EF_ALARM"] = 512] = "EF_ALARM";
194
+ /** Attendance entry. */
195
+ RTEvent[RTEvent["EF_ATTLOG"] = 1] = "EF_ATTLOG";
196
+ /** Pressed keyboard key. */
197
+ RTEvent[RTEvent["EF_BUTTON"] = 16] = "EF_BUTTON";
198
+ /** Upload user data. */
199
+ RTEvent[RTEvent["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
200
+ /** Enrolled user. */
201
+ RTEvent[RTEvent["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
202
+ /** Pressed finger. */
203
+ RTEvent[RTEvent["EF_FINGER"] = 2] = "EF_FINGER";
204
+ /** Fingerprint score in enroll procedure. */
205
+ RTEvent[RTEvent["EF_FPFTR"] = 256] = "EF_FPFTR";
206
+ /** Restore access control to default. */
207
+ RTEvent[RTEvent["EF_UNLOCK"] = 32] = "EF_UNLOCK";
208
+ /** Registered user placed finger. */
209
+ RTEvent[RTEvent["EF_VERIFY"] = 128] = "EF_VERIFY";
210
+ })(RTEvent || (RTEvent = {}));
211
+ var DISCOVERED_CMD;
212
+ (function (DISCOVERED_CMD) {
213
+ /** [0x7f, 0x13] */
214
+ DISCOVERED_CMD[DISCOVERED_CMD["UNKNOWN"] = 4991] = "UNKNOWN";
215
+ /** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
216
+ DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
217
+ /** [0x87, 0x13] i guess is an error reply code, is returned when attempint to read options ~isABCPinEnable and ~T9FunOn */
218
+ DISCOVERED_CMD[DISCOVERED_CMD["UNKNOWN_OR_NOT_SUPPORTED"] = 4999] = "UNKNOWN_OR_NOT_SUPPORTED";
219
+ })(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
220
+ var Constants;
221
+ (function (Constants) {
222
+ Constants[Constants["USHRT_MAX"] = 65535] = "USHRT_MAX";
223
+ Constants[Constants["MAX_CHUNK"] = 65472] = "MAX_CHUNK";
224
+ Constants[Constants["MACHINE_PREPARE_DATA_1"] = 20560] = "MACHINE_PREPARE_DATA_1";
225
+ Constants[Constants["MACHINE_PREPARE_DATA_2"] = 32130] = "MACHINE_PREPARE_DATA_2";
226
+ })(Constants || (Constants = {}));
227
+ const REQUEST_DATA = {
228
+ START_TAG: Buffer.from([0x50, 0x50, 0x82, 0x7d]),
229
+ DISABLE_DEVICE: Buffer.from([0, 0, 0, 0]),
230
+ GET_REAL_TIME_EVENT: Buffer.from([0x01, 0x00, 0x00, 0x00]),
231
+ GET_ATTENDANCE_LOGS: Buffer.from([0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
232
+ GET_USERS: Buffer.from([0x01, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
233
+ GET_TEMPLATES: Buffer.from([0x01, 0x07, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
234
+ };
235
+
236
+ /**
237
+ * Error types for device communication
238
+ */
239
+ const ERROR_TYPES = {
240
+ ECONNRESET: 'ECONNRESET',
241
+ ECONNREFUSED: 'ECONNREFUSED'};
242
+ /**
243
+ * Custom error class for device communication errors
244
+ */
245
+ class ZkError {
246
+ err;
247
+ ip;
248
+ command;
249
+ /**
250
+ * Creates a new ZkError instance
251
+ * @param err The error object
252
+ * @param command The command that caused the error
253
+ * @param ip The IP address of the device
254
+ */
255
+ constructor(err, command, ip) {
256
+ this.err = err;
257
+ this.ip = ip;
258
+ this.command = command;
259
+ }
260
+ /**
261
+ * Gets a user-friendly error message
262
+ * @returns A formatted error message
263
+ */
264
+ toast() {
265
+ if (this.err.code === ERROR_TYPES.ECONNRESET) {
266
+ return 'Another device is connecting to the device so the connection is interrupted';
267
+ }
268
+ else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
269
+ return 'IP of the device is refused';
270
+ }
271
+ return this.err.message;
272
+ }
273
+ /**
274
+ * Gets detailed error information
275
+ * @returns An object containing error details
276
+ */
277
+ getError() {
278
+ return {
279
+ err: {
280
+ message: this.err.message,
281
+ code: this.err.code
282
+ },
283
+ ip: this.ip,
284
+ command: this.command
285
+ };
286
+ }
287
+ }
288
+
289
+ const parseCurrentTime = () => {
290
+ const currentTime = new Date();
291
+ return {
292
+ year: currentTime.getFullYear(),
293
+ month: currentTime.getMonth() + 1,
294
+ day: currentTime.getDate(),
295
+ hour: currentTime.getHours(),
296
+ second: currentTime.getSeconds()
297
+ };
298
+ };
299
+ const log = (text) => {
300
+ const currentTime = parseCurrentTime();
301
+ const fileName = `${currentTime.day}`.padStart(2, '0') +
302
+ `${currentTime.month}`.padStart(2, '0') +
303
+ `${currentTime.year}.err.log`;
304
+ const logMessage = `\n [${currentTime.hour}:${currentTime.second}] ${text}`;
305
+ fs.appendFile(fileName, logMessage, () => { });
306
+ };
307
+
308
+ /**
309
+ * Represents a User as is from ZkDevice and contain methods
310
+ * */
311
+ class User {
312
+ uid;
313
+ name;
314
+ privilege;
315
+ password;
316
+ group_id;
317
+ user_id;
318
+ card;
319
+ /**
320
+ * Creates a new User instance
321
+ * @param uid User ID
322
+ * @param name User name
323
+ * @param privilege Privilege level
324
+ * @param password User password (default: "")
325
+ * @param group_id Group ID (default: "")
326
+ * @param user_id Alternate user ID (default: "")
327
+ * @param card Card number (default: 0)
328
+ */
329
+ constructor(uid, name, privilege, password = '', group_id = '', user_id = '', card = 0) {
330
+ this.uid = uid;
331
+ this.name = name;
332
+ this.privilege = privilege;
333
+ this.password = password;
334
+ this.group_id = group_id;
335
+ this.user_id = user_id;
336
+ this.card = card;
337
+ }
338
+ ensureEncoding(string) {
339
+ try {
340
+ return decodeURIComponent(string);
341
+ }
342
+ catch (e) {
343
+ return unescape(string);
344
+ }
345
+ }
346
+ repack29() {
347
+ // Pack format: <BHB5s8sIxBhI (total 29 bytes)
348
+ const buf = Buffer.alloc(29);
349
+ let offset = 0;
350
+ buf.writeUInt8(2, offset);
351
+ offset += 1;
352
+ buf.writeUInt16LE(this.uid, offset);
353
+ offset += 2;
354
+ buf.writeUInt8(this.privilege, offset);
355
+ offset += 1;
356
+ const passwordBuf = Buffer.from(this.ensureEncoding(this.password));
357
+ passwordBuf.copy(buf, offset, 0, 5);
358
+ offset += 5;
359
+ const nameBuf = Buffer.from(this.ensureEncoding(this.name));
360
+ nameBuf.copy(buf, offset, 0, 8);
361
+ offset += 8;
362
+ buf.writeUInt32LE(this.card, offset);
363
+ offset += 4;
364
+ offset += 1; // padding byte
365
+ buf.writeUInt8(0, offset);
366
+ offset += 1;
367
+ buf.writeUInt32LE(parseInt(this.user_id) || 0, offset);
368
+ return buf;
369
+ }
370
+ repack73() {
371
+ // Pack format: <BHB8s24sIB7sx24s (total 73 bytes)
372
+ const buf = Buffer.alloc(73);
373
+ let offset = 0;
374
+ buf.writeUInt8(2, offset);
375
+ offset += 1;
376
+ buf.writeUInt16LE(this.uid, offset);
377
+ offset += 2;
378
+ buf.writeUInt8(this.privilege, offset);
379
+ offset += 1;
380
+ const passwordBuf = Buffer.from(this.ensureEncoding(this.password));
381
+ passwordBuf.copy(buf, offset, 0, 8);
382
+ offset += 8;
383
+ const nameBuf = Buffer.from(this.ensureEncoding(this.name));
384
+ nameBuf.copy(buf, offset, 0, 24);
385
+ offset += 24;
386
+ buf.writeUInt32LE(this.card, offset);
387
+ offset += 4;
388
+ buf.writeUInt8(1, offset);
389
+ offset += 1;
390
+ const groupBuf = Buffer.from(this.ensureEncoding(String(this.group_id)));
391
+ groupBuf.copy(buf, offset, 0, 7);
392
+ offset += 8;
393
+ const userIdBuf = Buffer.from(this.ensureEncoding(String(this.user_id)));
394
+ userIdBuf.copy(buf, offset, 0, 24);
395
+ return buf;
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Represents an Attendance Records
401
+ */
402
+ class Attendance {
403
+ /** Internal serial number for the user */
404
+ sn;
405
+ /** User ID/Pin stored as a string */
406
+ user_id;
407
+ /** Verification type */
408
+ type;
409
+ /** Time of the attendance event */
410
+ record_time;
411
+ /** Verify state */
412
+ state;
413
+ ip;
414
+ constructor(sn, user_id, type, record_time, state) {
415
+ this.sn = sn;
416
+ this.user_id = user_id;
417
+ this.type = type || undefined;
418
+ this.record_time = record_time;
419
+ this.state = state || undefined;
420
+ }
421
+ }
422
+
423
+ const parseHexToTime = (hex) => {
424
+ const time = {
425
+ year: hex.readUIntLE(0, 1),
426
+ month: hex.readUIntLE(1, 1),
427
+ date: hex.readUIntLE(2, 1),
428
+ hour: hex.readUIntLE(3, 1),
429
+ minute: hex.readUIntLE(4, 1),
430
+ second: hex.readUIntLE(5, 1),
431
+ };
432
+ return new Date(2000 + time.year, time.month - 1, time.date, time.hour, time.minute, time.second);
433
+ };
434
+ const createChkSum = (buf) => {
435
+ let chksum = 0;
436
+ for (let i = 0; i < buf.length; i += 2) {
437
+ if (i === buf.length - 1) {
438
+ chksum += buf[i];
439
+ }
440
+ else {
441
+ chksum += buf.readUInt16LE(i);
442
+ }
443
+ chksum %= Constants.USHRT_MAX;
444
+ }
445
+ chksum = Constants.USHRT_MAX - chksum - 1;
446
+ return chksum;
447
+ };
448
+ const createUDPHeader = (command, sessionId, replyId, data) => {
449
+ const dataBuffer = Buffer.from(data);
450
+ const buf = Buffer.alloc(8 + dataBuffer.length);
451
+ buf.writeUInt16LE(command, 0);
452
+ buf.writeUInt16LE(0, 2);
453
+ buf.writeUInt16LE(sessionId, 4);
454
+ buf.writeUInt16LE(replyId, 6);
455
+ dataBuffer.copy(buf, 8);
456
+ const chksum2 = createChkSum(buf);
457
+ buf.writeUInt16LE(chksum2, 2);
458
+ replyId = (replyId + 1) % Constants.USHRT_MAX;
459
+ buf.writeUInt16LE(replyId, 6);
460
+ return buf;
461
+ };
462
+ const createTCPHeader = (command, sessionId, replyId, data) => {
463
+ const buf = createUDPHeader(command, sessionId, replyId, data);
464
+ const prefixBuf = Buffer.from([
465
+ 0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00,
466
+ ]);
467
+ prefixBuf.writeUInt16LE(buf.length, 4);
468
+ return Buffer.concat([prefixBuf, buf]);
469
+ };
470
+ const removeTcpHeader = (buf) => {
471
+ if (buf.length < 8) {
472
+ return buf;
473
+ }
474
+ if (buf.compare(Buffer.from([0x50, 0x50, 0x82, 0x7d]), 0, 4, 0, 4) !== 0) {
475
+ return buf;
476
+ }
477
+ return buf.slice(8);
478
+ };
479
+ const parseTimeToDate = (time) => {
480
+ const second = time % 60;
481
+ time = (time - second) / 60;
482
+ const minute = time % 60;
483
+ time = (time - minute) / 60;
484
+ const hour = time % 24;
485
+ time = (time - hour) / 24;
486
+ const day = (time % 31) + 1;
487
+ time = (time - (day - 1)) / 31;
488
+ const month = time % 12;
489
+ time = (time - month) / 12;
490
+ const year = time + 2000;
491
+ return new Date(Date.UTC(year, month, day, hour, minute, second));
492
+ };
493
+ const decodeUserData28 = (userData) => {
494
+ return {
495
+ uid: userData.readUIntLE(0, 2),
496
+ privilege: userData.readUIntLE(2, 1),
497
+ name: userData
498
+ .slice(8, 8 + 8)
499
+ .toString("ascii")
500
+ .split("\0")
501
+ .shift() || "",
502
+ user_id: userData.readUIntLE(24, 4).toString(),
503
+ };
504
+ };
505
+ const decodeUserData72 = (userData) => {
506
+ return new User(userData.readUIntLE(0, 2), userData.slice(11).toString("ascii").split("\0").shift() || "", userData.readUIntLE(2, 1), userData
507
+ .subarray(3, 3 + 8)
508
+ .toString("ascii")
509
+ .split("\0")
510
+ .shift() || "", userData.readUIntLE(39, 1), userData
511
+ .slice(48, 48 + 9)
512
+ .toString("ascii")
513
+ .split("\0")
514
+ .shift() || "", userData.readUIntLE(35, 4));
515
+ };
516
+ const decodeRecordData40 = (recordData) => {
517
+ return new Attendance(recordData.readUIntLE(0, 2), recordData
518
+ .slice(2, 2 + 9)
519
+ .toString("ascii")
520
+ .split("\0")
521
+ .shift() || "", recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
522
+ };
523
+ const decodeRecordData16 = (recordData) => {
524
+ return {
525
+ user_id: recordData.readUIntLE(0, 2).toString(),
526
+ record_time: parseTimeToDate(recordData.readUInt32LE(4)),
527
+ };
528
+ };
529
+ const decodeRecordRealTimeLog18 = (recordData) => {
530
+ const user_id = recordData.readUIntLE(8, 1).toString();
531
+ const record_time = parseHexToTime(recordData.subarray(12, 18));
532
+ return { user_id, record_time };
533
+ };
534
+ const decodeUDPHeader = (header) => {
535
+ return {
536
+ commandId: header.readUIntLE(0, 2),
537
+ checkSum: header.readUIntLE(2, 2),
538
+ sessionId: header.readUIntLE(4, 2),
539
+ replyId: header.readUIntLE(6, 2),
540
+ };
541
+ };
542
+ const decodeTCPHeader = (header) => {
543
+ const recvData = header.subarray(8);
544
+ const payloadSize = header.readUIntLE(4, 2);
545
+ return {
546
+ commandId: recvData.readUIntLE(0, 2),
547
+ checkSum: recvData.readUIntLE(2, 2),
548
+ sessionId: recvData.readUIntLE(4, 2),
549
+ replyId: recvData.readUIntLE(6, 2),
550
+ payloadSize,
551
+ };
552
+ };
553
+ const exportErrorMessage = (commandValue) => {
554
+ const keys = Object.keys(COMMANDS);
555
+ for (const key of keys) {
556
+ if (COMMANDS[key] === commandValue) {
557
+ return key.toString();
558
+ }
559
+ }
560
+ return "AN UNKNOWN ERROR";
561
+ };
562
+ const isRTEvent = (event) => Object.values(RTEvent).includes(event);
563
+ const checkNotEventTCP = (data) => {
564
+ try {
565
+ const cleanedData = removeTcpHeader(data);
566
+ const commandId = cleanedData.readUIntLE(0, 2);
567
+ const event = cleanedData.readUIntLE(4, 2);
568
+ return isRTEvent(event) && commandId === COMMANDS.CMD_REG_EVENT;
569
+ }
570
+ catch (err) {
571
+ log(`[228] : ${err.toString()} ,${data.toString("hex")} `);
572
+ return false;
573
+ }
574
+ };
575
+ const checkNotEventUDP = (data) => {
576
+ const { commandId } = decodeUDPHeader(data.subarray(0, 8));
577
+ return commandId === COMMANDS.CMD_REG_EVENT;
578
+ };
579
+ const decodeRTEvent = (data) => {
580
+ const header = decodeTCPHeader(data);
581
+ console.log(header);
582
+ const event = header.sessionId;
583
+ const recvData = data.subarray(16);
584
+ const build = {
585
+ alarm4bytes: function (data) {
586
+ return {
587
+ alarm_type: data.readUintLE(0, 4),
588
+ alarm_cause: data.readUintLE(4, 2),
589
+ user_uid: data.readUintLE(6, 2),
590
+ match_type: data.readUintLE(8, 4),
591
+ };
592
+ },
593
+ enrolledFinger: function (data) {
594
+ return {
595
+ result: data.readUIntLE(0, 2),
596
+ size: data.readUintLE(2, 2),
597
+ pin: data.subarray(4, 4 + 9).toString("ascii"),
598
+ fid: data.readUIntLE(1, 13),
599
+ };
600
+ },
601
+ fingerScore: function (data) {
602
+ return { score: data.readUintLE(0, 1) };
603
+ },
604
+ };
605
+ switch (event) {
606
+ case RTEvent.EF_FINGER:
607
+ return { event };
608
+ case RTEvent.EF_FPFTR:
609
+ return { event, payload: build.fingerScore(recvData) };
610
+ default:
611
+ console.log("utils linea 284");
612
+ break;
613
+ }
614
+ };
615
+ const makeKey = (key, sessionId) => {
616
+ let k = 0;
617
+ for (let i = 0; i < 32; i++) {
618
+ if ((key & (1 << i)) !== 0) {
619
+ k = (k << 1) | 1;
620
+ }
621
+ else {
622
+ k = k << 1;
623
+ }
624
+ }
625
+ k += sessionId;
626
+ let hex = k.toString(16).padStart(8, "0");
627
+ const response = new Uint8Array(4);
628
+ let index = 3;
629
+ while (hex.length > 0) {
630
+ response[index] = parseInt(hex.substring(0, 2), 16);
631
+ index--;
632
+ hex = hex.substring(2);
633
+ }
634
+ response[0] ^= "Z".charCodeAt(0);
635
+ response[1] ^= "K".charCodeAt(0);
636
+ response[2] ^= "S".charCodeAt(0);
637
+ response[3] ^= "O".charCodeAt(0);
638
+ let finalKey = response[0] +
639
+ (response[1] << 8) +
640
+ (response[2] << 16) +
641
+ (response[3] << 24);
642
+ const swp = finalKey >>> 16;
643
+ finalKey = (finalKey << 16) | swp;
644
+ return finalKey >>> 0;
645
+ };
646
+ const authKey = (comKey, sessionId) => {
647
+ const k = makeKey(comKey, sessionId) >>> 0;
648
+ const rand = Math.floor(Math.random() * 256);
649
+ let hex = k.toString(16).padStart(8, "0");
650
+ const response = new Uint8Array(4);
651
+ let index = 3;
652
+ while (index >= 0) {
653
+ response[index] = parseInt(hex.substring(0, 2), 16);
654
+ index--;
655
+ hex = hex.substring(2);
656
+ }
657
+ response[0] ^= rand;
658
+ response[1] ^= rand;
659
+ response[2] = rand;
660
+ response[3] ^= rand;
661
+ return Array.from(response);
662
+ };
663
+ /** Some Tcp packets receibed by the socket client can be merged in one single data event by the OS, and we need to split to handle correctly */
664
+ const splitTcpPackets = (data) => {
665
+ const packets = [];
666
+ let unProcessed = data;
667
+ while (unProcessed.length > 0) {
668
+ const headers = decodeTCPHeader(unProcessed);
669
+ packets.push(unProcessed.subarray(0, headers.payloadSize + 8));
670
+ unProcessed = unProcessed.subarray(headers.payloadSize + 8);
671
+ }
672
+ return packets;
673
+ };
674
+
675
+ /**
676
+ * Represents a fingerprint template with associated metadata
677
+ */
678
+ class Finger {
679
+ uid;
680
+ fid;
681
+ /** Flag indicating 0 = invalid | 1 = valid | 3 = duress. if is not initilizaed, default is 1 = valid */
682
+ valid;
683
+ template;
684
+ size;
685
+ mark;
686
+ /**
687
+ * Creates a new Finger instance
688
+ * @param uid User internal reference
689
+ * @param fid Finger ID (value >= 0 && value <= 9)
690
+ * @param valid Flag indicating 0 = invalid | 1 = valid | 3 = duress
691
+ * @param template Fingerprint template data buffer
692
+ */
693
+ constructor(uid, fid, template, valid) {
694
+ this.uid = Number(uid);
695
+ this.fid = Number(fid);
696
+ this.valid = valid ? Number(valid) : 1;
697
+ this.template = template;
698
+ this.size = template.length;
699
+ // Create mark showing first and last 8 bytes as hex
700
+ const start = Uint8Array.prototype.slice.call(template, 0, 8).toString('hex');
701
+ const end = Uint8Array.prototype.slice.call(template, -8).toString('hex');
702
+ this.mark = `${start}...${end}`;
703
+ }
704
+ /**
705
+ * Packs the fingerprint data with metadata into a Buffer
706
+ * @returns Buffer containing packed fingerprint data
707
+ */
708
+ repack() {
709
+ // pack("HHbb%is" % (self.size), self.size+6, self.uid, self.fid, self.valid, self.template)
710
+ const buf = Buffer.alloc(6 + this.size); // HHbb = 6 bytes + template size
711
+ let offset = 0;
712
+ buf.writeUInt16LE(this.size + 6, offset);
713
+ offset += 2;
714
+ buf.writeUInt16LE(this.uid, offset);
715
+ offset += 2;
716
+ buf.writeUInt8(this.fid, offset);
717
+ offset += 1;
718
+ buf.writeUInt8(this.valid, offset);
719
+ offset += 1;
720
+ this.template.copy(buf, offset);
721
+ return buf;
722
+ }
723
+ /**
724
+ * Packs only the fingerprint template data into a Buffer
725
+ * @returns Buffer containing just the template data
726
+ */
727
+ repackOnly() {
728
+ // pack("H%is" % (self.size), self.size, self.template)
729
+ const buf = Buffer.alloc(2 + this.size); // H = 2 bytes + template size
730
+ buf.writeUInt16LE(this.size, 0);
731
+ this.template.copy(buf, 2);
732
+ return buf;
733
+ }
734
+ /**
735
+ * Compares this fingerprint with another for equality
736
+ * @param other Another Finger instance to compare with
737
+ * @returns true if all properties and template data match
738
+ */
739
+ equals(other) {
740
+ if (!(other instanceof Finger))
741
+ return false;
742
+ return this.uid === other.uid &&
743
+ this.fid === other.fid &&
744
+ this.valid === other.valid &&
745
+ this.template.equals(other.template);
746
+ }
747
+ }
748
+
749
+ class UserService {
750
+ _zkTcp;
751
+ _users;
752
+ constructor(zkTcp) {
753
+ this._zkTcp = zkTcp;
754
+ }
755
+ async getUserByUserId(user_id) {
756
+ if (!this._users) {
757
+ await this.getUsers();
758
+ }
759
+ if (this._users.has(String(user_id))) {
760
+ return this._users.get(String(user_id));
761
+ }
762
+ else
763
+ throw new Error("user_id not exists");
764
+ }
765
+ async getUsers() {
766
+ try {
767
+ // Free any existing buffer data to prepare for a new request
768
+ if (this._users) {
769
+ return { data: Array.from(this._users.values()) };
770
+ }
771
+ else {
772
+ this._users = new Map([]);
773
+ }
774
+ if (this._zkTcp.socket) {
775
+ await this._zkTcp.freeData();
776
+ }
777
+ await this._zkTcp.disableDevice();
778
+ const users = await new Promise((resolve, reject) => {
779
+ // Request user data
780
+ this._zkTcp
781
+ .readWithBuffer(REQUEST_DATA.GET_USERS)
782
+ .then(async (data) => {
783
+ // Ensure data.data is a valid buffer
784
+ if (!data.data || !(data.data instanceof Buffer)) {
785
+ reject(new Error("Invalid data received"));
786
+ return;
787
+ }
788
+ let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
789
+ const users = [];
790
+ // Constants for user data processing
791
+ const USER_PACKET_SIZE = 72;
792
+ // Process each user packet
793
+ while (userData.length >= USER_PACKET_SIZE) {
794
+ // Decode user data and add to the users array
795
+ const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
796
+ users.push(user);
797
+ this._users.set(user.user_id, user);
798
+ userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
799
+ }
800
+ resolve(users);
801
+ })
802
+ .catch(reject);
803
+ });
804
+ // Free buffer data after receiving the data
805
+ if (this._zkTcp.socket && users) {
806
+ await this._zkTcp.enableDevice();
807
+ await this._zkTcp.freeData();
808
+ }
809
+ // Return the list of users
810
+ return { data: users };
811
+ }
812
+ catch (err) {
813
+ // Log the error for debugging
814
+ console.error("Error getting users:", err);
815
+ // Re-throw the error to be handled by the caller
816
+ throw err;
817
+ }
818
+ }
819
+ async setUser(user_id, name, password, role = 0, cardno = 0) {
820
+ try {
821
+ // Validate input parameters
822
+ if (user_id.length > 9 ||
823
+ name.length > 24 ||
824
+ password.length > 8 ||
825
+ typeof role !== "number" ||
826
+ cardno.toString().length > 10) {
827
+ throw new Error("Invalid input parameters");
828
+ }
829
+ // Allocate and initialize the buffer
830
+ const commandBuffer = Buffer.alloc(72);
831
+ // Fill the buffer with user data
832
+ commandBuffer.writeUInt16LE(0, 0); // uid will be set in the device
833
+ commandBuffer.writeUInt16LE(role, 2);
834
+ commandBuffer.write(password.padEnd(8, "\0"), 3, 8); // Ensure password is 8 bytes
835
+ commandBuffer.write(name.padEnd(24, "\0"), 11, 24); // Ensure name is 24 bytes
836
+ commandBuffer.writeUInt16LE(cardno, 35);
837
+ commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
838
+ commandBuffer.write(user_id.padEnd(9, "\0"), 48, 9); // Ensure userid is 9 bytes
839
+ // Send the command and return the result
840
+ const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
841
+ return !!created;
842
+ }
843
+ catch (err) {
844
+ // Log error details for debugging
845
+ console.error("Error setting user:", err);
846
+ // Re-throw error for upstream handling
847
+ throw err;
848
+ }
849
+ }
850
+ async DeleteUser(user_id) {
851
+ try {
852
+ const user = await this.getUserByUserId(user_id);
853
+ // Allocate and initialize the buffer
854
+ const commandBuffer = Buffer.alloc(72);
855
+ // Write UID to the buffer
856
+ commandBuffer.writeUInt16LE(user.uid, 0);
857
+ // Send the delete command and return the result
858
+ const deleted = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
859
+ return !!deleted;
860
+ }
861
+ catch (err) {
862
+ // Log error details for debugging
863
+ console.error("Error deleting user:", err);
864
+ // Re-throw error for upstream handling
865
+ throw err;
866
+ }
867
+ }
868
+ async getTemplates(cb) {
869
+ const templates = [];
870
+ try {
871
+ await this._zkTcp.disableDevice();
872
+ if (this._zkTcp.socket) {
873
+ await this._zkTcp.freeData();
874
+ }
875
+ if (!this._zkTcp.fp_count) {
876
+ await this._zkTcp.getSizes();
877
+ }
878
+ if (this._zkTcp.fp_count == 0)
879
+ return { data: [] };
880
+ const resp = (await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES));
881
+ let templateData = resp.data.subarray(4);
882
+ let totalSize = resp.data.readUIntLE(0, 4);
883
+ while (totalSize) {
884
+ const buf = templateData.subarray(0, 6);
885
+ const size = buf.readUIntLE(0, 2);
886
+ const uid = buf.readUIntLE(2, 2);
887
+ const fid = buf.readUIntLE(4, 1);
888
+ const valid = buf.readUIntLE(5, 1);
889
+ // Force-copy bytes so we don't retain the entire big backing buffer
890
+ const tplBytes = Buffer.from(templateData.subarray(6, size));
891
+ templates.push(new Finger(uid, fid, tplBytes, valid));
892
+ templateData = templateData.subarray(size);
893
+ totalSize -= size;
894
+ }
895
+ if (cb)
896
+ cb(templates);
897
+ return { data: templates };
898
+ }
899
+ catch (err) {
900
+ this._zkTcp.verbose && console.log("Error getting templates", err);
901
+ throw err;
902
+ }
903
+ finally {
904
+ await this._zkTcp.freeData();
905
+ await this._zkTcp.enableDevice();
906
+ }
907
+ }
908
+ async DownloadFp(user_id, fid) {
909
+ try {
910
+ const user = (await this.getUserByUserId(String(user_id)));
911
+ if (0 > fid || fid > 9)
912
+ throw new Error("fid must be between 0 and 9");
913
+ // Allocate and initialize the buffer
914
+ const data = Buffer.alloc(3);
915
+ // Fill the buffer with user data
916
+ data.writeUInt16LE(user.uid, 0);
917
+ data.writeUIntLE(fid, 2, 1);
918
+ this._zkTcp.replyId++;
919
+ const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
920
+ let fingerSize = 0;
921
+ let fingerTemplate = Buffer.from([]);
922
+ const template = await new Promise((resolve, reject) => {
923
+ let timeout;
924
+ const cleanup = () => {
925
+ if (this._zkTcp.socket) {
926
+ this._zkTcp.socket.removeListener("data", receiveData);
927
+ }
928
+ if (timeout)
929
+ clearTimeout(timeout);
930
+ };
931
+ let timer = () => setTimeout(() => {
932
+ cleanup();
933
+ reject(new Error("Time Out, Could not retrieve data"));
934
+ }, this._zkTcp.timeout);
935
+ const receiveData = (data) => {
936
+ timeout = timer();
937
+ if (data.length === 0)
938
+ return;
939
+ try {
940
+ if (data.length === 0)
941
+ return;
942
+ const headers = decodeTCPHeader(data);
943
+ switch (headers.commandId) {
944
+ case DISCOVERED_CMD.FID_NOT_FOUND:
945
+ throw new Error("Could not retrieve data. maybe finger id not exists?");
946
+ case COMMANDS.CMD_PREPARE_DATA:
947
+ fingerSize = data.readUIntLE(16, 2);
948
+ break;
949
+ case COMMANDS.CMD_DATA:
950
+ // A single 'data' event might contain multiple TCP packets combined by the OS
951
+ // in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
952
+ // so It's important to split data received for remove CMD_ACK_OK headers
953
+ fingerTemplate = Buffer.concat([
954
+ fingerTemplate,
955
+ data.subarray(16, fingerSize + 10),
956
+ ]);
957
+ // @ts-ignore
958
+ resolve(fingerTemplate);
959
+ break;
960
+ case COMMANDS.CMD_ACK_OK:
961
+ cleanup();
962
+ // @ts-ignore
963
+ resolve(fingerTemplate);
964
+ return;
965
+ default:
966
+ // If it's not a recognized command but has data, it might be raw template data
967
+ if (headers.commandId > 2000 && headers.commandId < 3000) {
968
+ // Likely another ACK or system msg
969
+ }
970
+ else {
971
+ fingerTemplate = Buffer.concat([fingerTemplate, data]);
972
+ }
973
+ break;
974
+ }
975
+ clearTimeout(timeout);
976
+ }
977
+ catch (e) {
978
+ cleanup();
979
+ reject(e);
980
+ }
981
+ };
982
+ if (this._zkTcp.socket) {
983
+ this._zkTcp.socket.on("data", receiveData);
984
+ this._zkTcp.socket.write(packet, undefined, (err) => {
985
+ if (err) {
986
+ cleanup();
987
+ reject(err);
988
+ }
989
+ });
990
+ }
991
+ else {
992
+ reject(new Error("Socket not initialized"));
993
+ }
994
+ });
995
+ return new Finger(user.uid, fid, template);
996
+ }
997
+ catch (err) {
998
+ throw err;
999
+ }
1000
+ finally {
1001
+ await this._zkTcp.freeData();
1002
+ }
1003
+ }
1004
+ /**
1005
+ *
1006
+ * @param user_id {string} user
1007
+ * @param fingers {Finger[]} array of finger templates instances
1008
+ * */
1009
+ async saveTemplates(user_id, fingers = []) {
1010
+ if (fingers.length > 9 || fingers.length == 0)
1011
+ throw new Error("maximum finger length is 10 and can't be empty");
1012
+ try {
1013
+ await this._zkTcp.disableDevice();
1014
+ // check users exists
1015
+ const user = await this.getUserByUserId(user_id);
1016
+ let fpack = Buffer.alloc(0);
1017
+ let table = Buffer.alloc(0);
1018
+ const fnum = 0x10;
1019
+ let tstart = 0;
1020
+ for (const finger of fingers) {
1021
+ const tfp = finger.repackOnly();
1022
+ const tableEntry = Buffer.alloc(11); // b=1, H=2, b=1, I=4 => 1+2+1+4=8? Wait, bHbI is 1+2+1+4=8 bytes
1023
+ tableEntry.writeInt8(2, 0);
1024
+ tableEntry.writeUInt16LE(user.uid, 1);
1025
+ tableEntry.writeInt8(fnum + finger.fid, 3);
1026
+ tableEntry.writeUInt32LE(tstart, 4);
1027
+ table = Buffer.concat([table, tableEntry]);
1028
+ tstart += tfp.length;
1029
+ fpack = Buffer.concat([fpack, tfp]);
1030
+ }
1031
+ let upack;
1032
+ if (this._zkTcp.userPacketSize === 28) {
1033
+ upack = user.repack29();
1034
+ }
1035
+ else {
1036
+ upack = user.repack73();
1037
+ }
1038
+ const head = Buffer.alloc(12); // III = 3*4 bytes
1039
+ head.writeUInt32LE(upack.length, 0);
1040
+ head.writeUInt32LE(table.length, 4);
1041
+ head.writeUInt32LE(fpack.length, 8);
1042
+ const packet = Buffer.concat([head, upack, table, fpack]);
1043
+ await this._zkTcp.sendWithBuffer(packet);
1044
+ const command = 110;
1045
+ const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
1046
+ commandString.writeUInt32LE(12, 0);
1047
+ commandString.writeUInt16LE(0, 4);
1048
+ commandString.writeUInt16LE(8, 6);
1049
+ await this._zkTcp.executeCmd(command, commandString);
1050
+ if (this._zkTcp.verbose)
1051
+ console.log("finally bulk save user templates: \n", "templates saved successfully");
1052
+ }
1053
+ catch (error) {
1054
+ throw error;
1055
+ }
1056
+ finally {
1057
+ await this._zkTcp.refreshData();
1058
+ await this._zkTcp.enableDevice();
1059
+ }
1060
+ }
1061
+ async deleteFinger(user_id, fid) {
1062
+ try {
1063
+ const user = (await this.getUserByUserId(user_id));
1064
+ const buf = Buffer.alloc(4);
1065
+ buf.writeUInt16LE(user_id ? user.uid : 0, 0);
1066
+ buf.writeUint16LE(fid ? fid : 0, 2);
1067
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
1068
+ return !!reply;
1069
+ }
1070
+ catch (error) {
1071
+ throw new Error("Can't save utemp");
1072
+ }
1073
+ finally {
1074
+ await this._zkTcp.refreshData();
1075
+ }
1076
+ }
1077
+ async enrollInfo(user_id, tempId) {
1078
+ try {
1079
+ let timer;
1080
+ const setTimeoutTimer = (cb) => {
1081
+ if (timer)
1082
+ clearTimeout(timer);
1083
+ return setTimeout(() => {
1084
+ cb(new Error("[ENROLL_INFO] time out"));
1085
+ }, 1000 * 20);
1086
+ };
1087
+ const cleanUp = () => {
1088
+ if (timer)
1089
+ clearTimeout(timer);
1090
+ };
1091
+ return await new Promise((resolve, reject) => {
1092
+ const handleRtEvent = (rtEvent) => {
1093
+ switch (rtEvent.event) {
1094
+ case RTEvent.EF_FPFTR:
1095
+ timer = setTimeoutTimer(reject);
1096
+ break;
1097
+ case RTEvent.EF_FINGER:
1098
+ console.log(rtEvent);
1099
+ break;
1100
+ case RTEvent.EF_ENROLLFINGER:
1101
+ cleanUp();
1102
+ resolve(rtEvent);
1103
+ break;
1104
+ default:
1105
+ console.log(rtEvent);
1106
+ break;
1107
+ }
1108
+ };
1109
+ // Start enroll process
1110
+ this.getUserByUserId(user_id)
1111
+ .then(async (user) => {
1112
+ /** First check if Finger index already exists, and if so, it must be deleted */
1113
+ try {
1114
+ const exists = await this.DownloadFp(user_id, tempId);
1115
+ console.log("exists: ", exists);
1116
+ if (exists) {
1117
+ this._zkTcp.verbose &&
1118
+ console.debug("Deleting Finger index before start enroll");
1119
+ await this.deleteFinger(user_id, tempId);
1120
+ }
1121
+ }
1122
+ catch (e) {
1123
+ this._zkTcp.verbose &&
1124
+ console.debug("Finger index is empty, skipping delete");
1125
+ }
1126
+ const userBuf = Buffer.alloc(24);
1127
+ userBuf.write(user_id, 0, 24, "ascii");
1128
+ const commandString = Buffer.concat([
1129
+ userBuf,
1130
+ Buffer.from([tempId, 1]),
1131
+ ]);
1132
+ await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString); // #5
1133
+ this._zkTcp.timeout = 60000; // 60 seconds timeout
1134
+ await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, ""); // #17
1135
+ timer = setTimeoutTimer(reject);
1136
+ void (await this._zkTcp.getRealTimeLogs(handleRtEvent)); // #9
1137
+ })
1138
+ .catch(reject);
1139
+ });
1140
+ }
1141
+ catch (error) {
1142
+ throw error;
1143
+ }
1144
+ finally {
1145
+ //await this._zkTcp.cancelCapture();
1146
+ //await this.verify(user_id);
1147
+ }
1148
+ }
1149
+ async verify(user_id) {
1150
+ try {
1151
+ const user = await this.getUserByUserId(user_id);
1152
+ const command_string = Buffer.alloc(4);
1153
+ command_string.writeUInt32LE(user.uid, 0);
1154
+ const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
1155
+ if (this._zkTcp.verbose)
1156
+ console.log(reply.readUInt16LE(0));
1157
+ return !!reply;
1158
+ }
1159
+ catch (error) {
1160
+ console.error(error);
1161
+ throw error;
1162
+ }
1163
+ }
1164
+ /**
1165
+ * Upload a single fingerprint for a given user id
1166
+ * @param user_id {string} user id for customer
1167
+ * @param fingerTemplate {string} finger template in base64 string
1168
+ * @param fid {number} finger id is a number between 0 and 9
1169
+ * @param fp_valid {number} finger flag. e.g., valid=1, duress=3
1170
+ */
1171
+ async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
1172
+ try {
1173
+ const user = this._users.get(user_id);
1174
+ await this._zkTcp.disableDevice();
1175
+ const prep_struct = Buffer.alloc(4);
1176
+ const fingerBuffer = Buffer.from(fingerTemplate, "base64");
1177
+ const fp_size = fingerBuffer.length;
1178
+ prep_struct.writeUInt16LE(fp_size, 0);
1179
+ const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
1180
+ if (initPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
1181
+ throw new Error(`received unexpected command: ${initPacket.readUInt16LE(0)}`);
1182
+ }
1183
+ const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
1184
+ if (fpPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
1185
+ throw new Error(`received unexpected command: ${fpPacket.readUInt16LE(0)}`);
1186
+ }
1187
+ const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, "");
1188
+ if (cheksumPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
1189
+ throw new Error(`received unexpected command: ${cheksumPacket.readUInt16LE(0)}`);
1190
+ }
1191
+ const tmp_wreq = Buffer.alloc(6);
1192
+ tmp_wreq.writeUInt16LE(user.uid, 0);
1193
+ tmp_wreq.writeUIntLE(fid, 2, 1);
1194
+ tmp_wreq.writeUIntLE(fp_valid, 3, 1);
1195
+ tmp_wreq.writeUInt16LE(fp_size, 4);
1196
+ const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
1197
+ if (tmp_wreqPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
1198
+ throw new Error(`received unexpected command: ${tmp_wreqPacket.readUInt16LE(0)}`);
1199
+ }
1200
+ const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, "");
1201
+ if (freeData.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
1202
+ throw new Error(`received unexpected command: ${freeData.readUInt16LE(0)}`);
1203
+ }
1204
+ return true;
1205
+ }
1206
+ catch (err) {
1207
+ throw err;
1208
+ }
1209
+ finally {
1210
+ await this._zkTcp.refreshData();
1211
+ await this._zkTcp.enableDevice();
1212
+ }
1213
+ }
1214
+ }
1215
+
1216
+ class TransactionService {
1217
+ _zkTcp;
1218
+ constructor(zkTcp) {
1219
+ this._zkTcp = zkTcp;
1220
+ }
1221
+ async getAttendances(callbackInProcess = () => { }) {
1222
+ try {
1223
+ // Free any existing buffer data to prepare for a new request
1224
+ if (this._zkTcp.socket) {
1225
+ await this._zkTcp.freeData();
1226
+ }
1227
+ // Request attendance logs and handle chunked data
1228
+ const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
1229
+ // Free buffer data after receiving the attendance logs
1230
+ if (this._zkTcp.socket) {
1231
+ await this._zkTcp.freeData();
1232
+ }
1233
+ // Constants for record processing
1234
+ const RECORD_PACKET_SIZE = 40;
1235
+ // Ensure data.data is a valid buffer
1236
+ if (!data.data || !(data.data instanceof Buffer)) {
1237
+ throw new Error('Invalid data received');
1238
+ }
1239
+ // Process the record data
1240
+ let recordData = data.data.subarray(4); // Skip header
1241
+ const records = [];
1242
+ // Process each attendance record
1243
+ while (recordData.length >= RECORD_PACKET_SIZE) {
1244
+ const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
1245
+ records.push({ ...record, ip: this._zkTcp.ip }); // Add IP address to each record
1246
+ recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
1247
+ }
1248
+ // Return the list of attendance records
1249
+ return { data: records };
1250
+ }
1251
+ catch (err) {
1252
+ // Log and re-throw the error
1253
+ console.error('Error getting attendance records:', err);
1254
+ throw err; // Re-throw the error for handling by the caller
1255
+ }
1256
+ }
1257
+ // Clears the attendance logs on the device
1258
+ async clearAttendanceLog() {
1259
+ try {
1260
+ // Execute the command to clear attendance logs
1261
+ await this._zkTcp.disableDevice();
1262
+ const buf = await this._zkTcp.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
1263
+ await this._zkTcp.refreshData();
1264
+ await this._zkTcp.enableDevice();
1265
+ return !!buf;
1266
+ }
1267
+ catch (err) {
1268
+ // Log the error for debugging purposes
1269
+ console.error('Error clearing attendance log:', err);
1270
+ // Re-throw the error to be handled by the caller
1271
+ throw err;
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ /**
1277
+ *
1278
+ * @param {number} time
1279
+ */
1280
+ const decode = time => {
1281
+ const second = time % 60;
1282
+ time = (time - second) / 60;
1283
+ const minute = time % 60;
1284
+ time = (time - minute) / 60;
1285
+ const hour = time % 24;
1286
+ time = (time - hour) / 24;
1287
+ const day = time % 31 + 1;
1288
+ time = (time - (day - 1)) / 31;
1289
+ const month = time % 12;
1290
+ time = (time - month) / 12;
1291
+ const year = time + 2000;
1292
+ return new Date(year, month, day, hour, minute, second);
1293
+ };
1294
+ /**
1295
+ *
1296
+ * @param {Date} date
1297
+ */
1298
+ const encode = date => {
1299
+ return (((date.getFullYear() % 100) * 12 * 31 + date.getMonth() * 31 + date.getDate() - 1) * (24 * 60 * 60) +
1300
+ (date.getHours() * 60 + date.getMinutes()) * 60 +
1301
+ date.getSeconds());
1302
+ };
1303
+ var timeParser = { encode, decode };
1304
+
1305
+ /**
1306
+ * SDK Parameters Enum
1307
+ *
1308
+ * Generated from SDK parameter table with the following structure:
1309
+ * - Keys: Parameter names in uppercase without ~ prefix
1310
+ * - Values: Original parameter names
1311
+ * - JSDoc: Includes description, permissions, and notes
1312
+ */
1313
+ var SdkParameter;
1314
+ (function (SdkParameter) {
1315
+ /**
1316
+ * Device ID.
1317
+ * Permissions: RW
1318
+ * Notes: Value ranges from 1 to 254.
1319
+ */
1320
+ SdkParameter["DEVICE_ID"] = "DeviceID";
1321
+ /**
1322
+ * Language.
1323
+ * Permissions: RW
1324
+ * Notes: For english it is 97.
1325
+ */
1326
+ SdkParameter["NEW_LNG"] = "NewLng";
1327
+ /**
1328
+ * The machine will enter standby state or power off, after this time elapses.
1329
+ * Permissions: RW
1330
+ * Notes: Given in minutes.
1331
+ */
1332
+ SdkParameter["IDLE_MINUTE"] = "IdleMinute";
1333
+ /**
1334
+ * Lock control time.
1335
+ * Permissions: RW
1336
+ * Notes: Given in seconds.
1337
+ */
1338
+ SdkParameter["LOCK_ON"] = "LockOn";
1339
+ /**
1340
+ * Attendance record quantity alarm.
1341
+ * Permissions: RW
1342
+ * Notes:
1343
+ */
1344
+ SdkParameter["ALARM_ATT_LOG"] = "AlarmAttLog";
1345
+ /**
1346
+ * Operation record quantity alarm.
1347
+ * Permissions: RW
1348
+ * Notes:
1349
+ */
1350
+ SdkParameter["ALARM_OP_LOG"] = "AlarmOpLog";
1351
+ /**
1352
+ * Minimun time to record the same attendance state.
1353
+ * Permissions: RW
1354
+ * Notes: Units are unknown.
1355
+ */
1356
+ SdkParameter["ALARM_RE_REC"] = "AlarmReRec";
1357
+ /**
1358
+ * Baud rate for RS232/485.
1359
+ * Permissions: RW
1360
+ * Notes: Valid values are 1200, 2400, 4800, 9600, 19200, 38400 57600, 115200.
1361
+ */
1362
+ SdkParameter["RS232_BAUD_RATE"] = "RS232BaudRate";
1363
+ /**
1364
+ * Enable flag for network functions.
1365
+ * Permissions: RW
1366
+ * Notes:
1367
+ */
1368
+ SdkParameter["NETWORK_ON"] = "NetworkOn";
1369
+ /**
1370
+ * Enable flag for RS232.
1371
+ * Permissions: RW
1372
+ * Notes:
1373
+ */
1374
+ SdkParameter["RS232_ON"] = "RS232On";
1375
+ /**
1376
+ * Enable flag for RS485.
1377
+ * Permissions: RW
1378
+ * Notes:
1379
+ */
1380
+ SdkParameter["RS485_ON"] = "RS485On";
1381
+ /**
1382
+ * Enable announcements(voice).
1383
+ * Permissions: RW
1384
+ * Notes:
1385
+ */
1386
+ SdkParameter["VOICE_ON"] = "VoiceOn";
1387
+ /**
1388
+ * Perform high-speed comparison.
1389
+ * Permissions: RW
1390
+ * Notes: Value codification is unknown.
1391
+ */
1392
+ SdkParameter["MSPEED"] = "MSpeed";
1393
+ /**
1394
+ * Idle mode.
1395
+ * Permissions: RW
1396
+ * Notes: 87 indicates shutdown and 88 indicates hibernation.
1397
+ */
1398
+ SdkParameter["IDLE_POWER"] = "IdlePower";
1399
+ /**
1400
+ * Automatic shutdown time.
1401
+ * Permissions: RW
1402
+ * Notes: Value 255 indicates the machine to not shutdown automatically.
1403
+ */
1404
+ SdkParameter["AUTO_POWER_OFF"] = "AutoPowerOff";
1405
+ /**
1406
+ * Automatic startup time.
1407
+ * Permissions: RW
1408
+ * Notes: Value 255 indicates the machine to not startup automatically.
1409
+ */
1410
+ SdkParameter["AUTO_POWER_ON"] = "AutoPowerOn";
1411
+ /**
1412
+ * Automatic hibernation time.
1413
+ * Permissions: RW
1414
+ * Notes: Value 255 indicates the machine to not suspend automatically.
1415
+ */
1416
+ SdkParameter["AUTO_POWER_SUSPEND"] = "AutoPowerSuspend";
1417
+ /**
1418
+ * Alarm 1 time.
1419
+ * Permissions: RW
1420
+ * Notes: Value 65535 disables the alarm(t).
1421
+ */
1422
+ SdkParameter["AUTO_ALARM1"] = "AutoAlarm1";
1423
+ /**
1424
+ * 1:N comparison threshold.
1425
+ * Permissions: RW
1426
+ * Notes: Integer.
1427
+ */
1428
+ SdkParameter["MTHRESHOLD"] = "MThreshold";
1429
+ /**
1430
+ * Registration threshold.
1431
+ * Permissions: RW
1432
+ * Notes: Integer.
1433
+ */
1434
+ SdkParameter["ETHRESHOLD"] = "EThreshold";
1435
+ /**
1436
+ * 1:1 comparison threshold.
1437
+ * Permissions: RW
1438
+ * Notes: Integer.
1439
+ */
1440
+ SdkParameter["VTHRESHOLD"] = "VThreshold";
1441
+ /**
1442
+ * Display matching score during verification.
1443
+ * Permissions: RW
1444
+ * Notes: Bool.
1445
+ */
1446
+ SdkParameter["SHOW_SCORE"] = "ShowScore";
1447
+ /**
1448
+ * Number of people that may unlock the door at the same time.
1449
+ * Permissions: RW
1450
+ * Notes: Integer.
1451
+ */
1452
+ SdkParameter["UNLOCK_PERSON"] = "UnlockPerson";
1453
+ /**
1454
+ * Verify only the card number.
1455
+ * Permissions: RW
1456
+ * Notes: Bool.
1457
+ */
1458
+ SdkParameter["ONLY_PIN_CARD"] = "OnlyPINCard";
1459
+ /**
1460
+ * Network speed.
1461
+ * Permissions: RW
1462
+ * Notes: Value correspondence: 1=100M-H, 4=10M-F, 5=100M-F, 8=Auto, others=10M-H.
1463
+ */
1464
+ SdkParameter["HI_SPEED_NET"] = "HiSpeedNet";
1465
+ /**
1466
+ * Accept only registered cards.
1467
+ * Permissions: RW
1468
+ * Notes: Bool.
1469
+ */
1470
+ SdkParameter["MUST_ENROLL"] = "MustEnroll";
1471
+ /**
1472
+ * Timeout to return to the initial state.
1473
+ * Permissions: RW
1474
+ * Notes: Given in seconds.
1475
+ */
1476
+ SdkParameter["TO_STATE"] = "TOState";
1477
+ /**
1478
+ * Timeout to return to the initial state if there are no inputs after entering PIN.
1479
+ * Permissions: RW
1480
+ * Notes: Given in seconds.
1481
+ */
1482
+ SdkParameter["TO_STATE_PIN"] = "TOState";
1483
+ /**
1484
+ * Timeout to return to the initial state if there are no inputs after entering menu.
1485
+ * Permissions: RW
1486
+ * Notes: Given in seconds.
1487
+ */
1488
+ SdkParameter["TO_MENU"] = "TOMenu";
1489
+ /**
1490
+ * Time format.
1491
+ * Permissions: NA
1492
+ * Notes: Value codification is unknown.
1493
+ */
1494
+ SdkParameter["DT_FMT"] = "DtFmt";
1495
+ /**
1496
+ * Flag for mandatory 1:1 comparison.
1497
+ * Permissions: RW
1498
+ * Notes: Bool.
1499
+ */
1500
+ SdkParameter["MUST_1TO1"] = "Must1To1";
1501
+ /**
1502
+ * Alarm 2 time.
1503
+ * Permissions: RW
1504
+ * Notes: Value 65535 disables the alarm(t).
1505
+ */
1506
+ SdkParameter["AUTO_ALARM2"] = "AutoAlarm2";
1507
+ /**
1508
+ * Alarm 3 time.
1509
+ * Permissions: RW
1510
+ * Notes: Value 65535 disables the alarm(t).
1511
+ */
1512
+ SdkParameter["AUTO_ALARM3"] = "AutoAlarm3";
1513
+ /**
1514
+ * Alarm 4 time.
1515
+ * Permissions: RW
1516
+ * Notes: Value 65535 disables the alarm(t).
1517
+ */
1518
+ SdkParameter["AUTO_ALARM4"] = "AutoAlarm4";
1519
+ /**
1520
+ * Alarm 5 time.
1521
+ * Permissions: RW
1522
+ * Notes: Value 65535 disables the alarm(t).
1523
+ */
1524
+ SdkParameter["AUTO_ALARM5"] = "AutoAlarm5";
1525
+ /**
1526
+ * Alarm 6 time.
1527
+ * Permissions: RW
1528
+ * Notes: Value 65535 disables the alarm(t).
1529
+ */
1530
+ SdkParameter["AUTO_ALARM6"] = "AutoAlarm6";
1531
+ /**
1532
+ * Automatic status changing times.
1533
+ * Permissions: ?
1534
+ * Notes: -1 value indicates that the status will not change automatically.
1535
+ */
1536
+ SdkParameter["AS1"] = "AS1";
1537
+ /**
1538
+ * Automatic status changing times.
1539
+ * Permissions: ?
1540
+ * Notes: -1 value indicates that the status will not change automatically.
1541
+ */
1542
+ SdkParameter["AS2"] = "AS2";
1543
+ /**
1544
+ * Automatic status changing times.
1545
+ * Permissions: ?
1546
+ * Notes: -1 value indicates that the status will not change automatically.
1547
+ */
1548
+ SdkParameter["AS3"] = "AS3";
1549
+ /**
1550
+ * Automatic status changing times.
1551
+ * Permissions: ?
1552
+ * Notes: -1 value indicates that the status will not change automatically.
1553
+ */
1554
+ SdkParameter["AS4"] = "AS4";
1555
+ /**
1556
+ * Automatic status changing times.
1557
+ * Permissions: ?
1558
+ * Notes: -1 value indicates that the status will not change automatically.
1559
+ */
1560
+ SdkParameter["AS5"] = "AS5";
1561
+ /**
1562
+ * Automatic status changing times.
1563
+ * Permissions: ?
1564
+ * Notes: -1 value indicates that the status will not change automatically.
1565
+ */
1566
+ SdkParameter["AS6"] = "AS6";
1567
+ /**
1568
+ * Automatic status changing times.
1569
+ * Permissions: ?
1570
+ * Notes: -1 value indicates that the status will not change automatically.
1571
+ */
1572
+ SdkParameter["AS7"] = "AS7";
1573
+ /**
1574
+ * Automatic status changing times.
1575
+ * Permissions: ?
1576
+ * Notes: -1 value indicates that the status will not change automatically.
1577
+ */
1578
+ SdkParameter["AS8"] = "AS8";
1579
+ /**
1580
+ * Automatic status changing times.
1581
+ * Permissions: ?
1582
+ * Notes: -1 value indicates that the status will not change automatically.
1583
+ */
1584
+ SdkParameter["AS9"] = "AS9";
1585
+ /**
1586
+ * Automatic status changing times.
1587
+ * Permissions: ?
1588
+ * Notes: -1 value indicates that the status will not change automatically.
1589
+ */
1590
+ SdkParameter["AS10"] = "AS10";
1591
+ /**
1592
+ * Automatic status changing times.
1593
+ * Permissions: ?
1594
+ * Notes: -1 value indicates that the status will not change automatically.
1595
+ */
1596
+ SdkParameter["AS11"] = "AS11";
1597
+ /**
1598
+ * Automatic status changing times.
1599
+ * Permissions: ?
1600
+ * Notes: -1 value indicates that the status will not change automatically.
1601
+ */
1602
+ SdkParameter["AS12"] = "AS12";
1603
+ /**
1604
+ * Automatic status changing times.
1605
+ * Permissions: ?
1606
+ * Notes: -1 value indicates that the status will not change automatically.
1607
+ */
1608
+ SdkParameter["AS13"] = "AS13";
1609
+ /**
1610
+ * Automatic status changing times.
1611
+ * Permissions: ?
1612
+ * Notes: -1 value indicates that the status will not change automatically.
1613
+ */
1614
+ SdkParameter["AS14"] = "AS14";
1615
+ /**
1616
+ * Automatic status changing times.
1617
+ * Permissions: ?
1618
+ * Notes: -1 value indicates that the status will not change automatically.
1619
+ */
1620
+ SdkParameter["AS15"] = "AS15";
1621
+ /**
1622
+ * Automatic status changing times.
1623
+ * Permissions: ?
1624
+ * Notes: -1 value indicates that the status will not change automatically.
1625
+ */
1626
+ SdkParameter["AS16"] = "AS16";
1627
+ /**
1628
+ * Wiegand failure ID.
1629
+ * Permissions: ?
1630
+ * Notes:
1631
+ */
1632
+ SdkParameter["WG_FAILED_ID"] = "WGFailedID";
1633
+ /**
1634
+ * Wiegand duress ID.
1635
+ * Permissions: ?
1636
+ * Notes:
1637
+ */
1638
+ SdkParameter["WG_DURESS_ID"] = "WGDuressID";
1639
+ /**
1640
+ * Wiegand zone bit.
1641
+ * Permissions: ?
1642
+ * Notes:
1643
+ */
1644
+ SdkParameter["WG_SITE_CODE"] = "WGSiteCode";
1645
+ /**
1646
+ * Pulse width of Wiegand outputs.
1647
+ * Permissions: ?
1648
+ * Notes:
1649
+ */
1650
+ SdkParameter["WG_PULSE_WIDTH"] = "WGPulseWidth";
1651
+ /**
1652
+ * Pulse interval for Wiegand outputs.
1653
+ * Permissions: ?
1654
+ * Notes:
1655
+ */
1656
+ SdkParameter["WG_PULSE_INTERVAL"] = "WGPulseInterval";
1657
+ /**
1658
+ * ID of the start sector on the Mifare card where fingerprints are stored.
1659
+ * Permissions: ?
1660
+ * Notes:
1661
+ */
1662
+ SdkParameter["RF_START"] = "RFSStart";
1663
+ /**
1664
+ * Total number of sectors on the Mifare card where fingerprints are stored.
1665
+ * Permissions: ?
1666
+ * Notes:
1667
+ */
1668
+ SdkParameter["RF_LEN"] = "RFSLen";
1669
+ /**
1670
+ * Number of fingerprints stored on the Mifare card.
1671
+ * Permissions: ?
1672
+ * Notes:
1673
+ */
1674
+ SdkParameter["RF_FPC"] = "RFFPC";
1675
+ /**
1676
+ * Forbidden.
1677
+ * Permissions: NA
1678
+ * Notes:
1679
+ */
1680
+ SdkParameter["FORBIDDEN"] = "Forbidden";
1681
+ /**
1682
+ * Wheter to display the attendance status.
1683
+ * Permissions: RW
1684
+ * Notes:
1685
+ */
1686
+ SdkParameter["SHOW_STATE"] = "~ShowState";
1687
+ /**
1688
+ * TCP Port.
1689
+ * Permissions: ?
1690
+ * Notes:
1691
+ */
1692
+ SdkParameter["TCP_PORT"] = "TCPPort";
1693
+ /**
1694
+ * UDP Port.
1695
+ * Permissions: ?
1696
+ * Notes:
1697
+ */
1698
+ SdkParameter["UDP_PORT"] = "UDPPort";
1699
+ /**
1700
+ * Fingerprint algorithm version.
1701
+ * Permissions: R
1702
+ * Notes:
1703
+ */
1704
+ SdkParameter["ZK_FP_VERSION"] = "~ZKFPVersion";
1705
+ /**
1706
+ * Face algorithm version.
1707
+ * Permissions: R
1708
+ * Notes:
1709
+ */
1710
+ SdkParameter["ZK_FACE_VERSION"] = "~ZKFaceVersion";
1711
+ /**
1712
+ * Finger vein version.
1713
+ * Permissions: R
1714
+ * Notes:
1715
+ */
1716
+ SdkParameter["ZK_FV_VERSION"] = "~ZKFVVersion";
1717
+ /**
1718
+ * Face function.
1719
+ * Permissions: R
1720
+ * Notes:
1721
+ */
1722
+ SdkParameter["FACE_FUN_ON"] = "~FaceFunOn";
1723
+ /**
1724
+ * User id max length.
1725
+ * Permissions: R
1726
+ * Notes:
1727
+ */
1728
+ SdkParameter["PIN2_WIDTH"] = "~PIN2Width";
1729
+ /**
1730
+ * Does the user id support chars.
1731
+ * Permissions: R
1732
+ * Notes:
1733
+ */
1734
+ SdkParameter["IS_SUPPORT_ABC_PIN"] = "IsSupportABCPin";
1735
+ /**
1736
+ * ?
1737
+ * Permissions: ?
1738
+ * Notes:
1739
+ */
1740
+ SdkParameter["IME_FUN_ON"] = "IMEFunOn";
1741
+ /**
1742
+ * ?
1743
+ * Permissions: ?
1744
+ * Notes:
1745
+ */
1746
+ SdkParameter["IS_SUPPORT_ALARM_EXT"] = "IsSupportAlarmExt";
1747
+ /**
1748
+ * ?
1749
+ * Permissions: ?
1750
+ * Notes:
1751
+ */
1752
+ SdkParameter["DCTZ"] = "~DCTZ";
1753
+ /**
1754
+ * ?
1755
+ * Permissions: ?
1756
+ * Notes:
1757
+ */
1758
+ SdkParameter["DOTZ"] = "~DOTZ";
1759
+ /**
1760
+ * -
1761
+ * Permissions: ?
1762
+ * Notes:
1763
+ */
1764
+ SdkParameter["EXTEND_OP_LOG"] = "ExtendOPLog";
1765
+ /**
1766
+ * Bool
1767
+ * Permissions: RW
1768
+ * Notes:
1769
+ */
1770
+ SdkParameter["WORK_CODE"] = "WorkCode";
1771
+ /**
1772
+ * Integer
1773
+ * Permissions: RW
1774
+ * Notes:
1775
+ */
1776
+ SdkParameter["LANGUAGE"] = "Language";
1777
+ /**
1778
+ * -
1779
+ * Permissions: ?
1780
+ * Notes:
1781
+ */
1782
+ SdkParameter["BIOMETRIC_TYPE"] = "BiometricType";
1783
+ /**
1784
+ * Bool
1785
+ * Permissions: RW
1786
+ * Notes:
1787
+ */
1788
+ SdkParameter["FINGER_FUN_ON"] = "FingerFunOn";
1789
+ /**
1790
+ * Bool
1791
+ * Permissions: ?
1792
+ * Notes:
1793
+ */
1794
+ SdkParameter["IS_ONLY_RF_MACHINE"] = "~IsOnlyRFMachine";
1795
+ /**
1796
+ * Vendor name
1797
+ * Permissions: ?
1798
+ * Notes:
1799
+ */
1800
+ SdkParameter["OEM_VENDOR"] = "~OEMVendor";
1801
+ /**
1802
+ * Device name
1803
+ * Permissions: ?
1804
+ * Notes:
1805
+ */
1806
+ SdkParameter["DEVICE_NAME"] = "~DeviceName";
1807
+ /**
1808
+ * MAC address
1809
+ * Permissions: ?
1810
+ * Notes:
1811
+ */
1812
+ SdkParameter["MAC"] = "MAC";
1813
+ /**
1814
+ * Serial number
1815
+ * Permissions: ?
1816
+ * Notes:
1817
+ */
1818
+ SdkParameter["SERIAL_NUMBER"] = "~SerialNumber";
1819
+ /**
1820
+ * Date string
1821
+ * Permissions: ?
1822
+ * Notes:
1823
+ */
1824
+ SdkParameter["PRODUCT_TIME"] = "~ProductTime";
1825
+ /**
1826
+ * Bool
1827
+ * Permissions: ?
1828
+ * Notes:
1829
+ */
1830
+ SdkParameter["IS_ABC_PIN_ENABLE"] = "~IsABCPinEnable";
1831
+ /**
1832
+ * Bool
1833
+ * Permissions: ?
1834
+ * Notes:
1835
+ */
1836
+ SdkParameter["T9_FUN_ON"] = "~T9FunOn";
1837
+ /**
1838
+ * Date Time
1839
+ * Permissions: RW
1840
+ */
1841
+ SdkParameter["DATETIME"] = "DateTime";
1842
+ /**
1843
+ *
1844
+ */
1845
+ SdkParameter["OS"] = "~OS";
1846
+ })(SdkParameter || (SdkParameter = {}));
1847
+
1848
+ class OptionsService {
1849
+ _zkTcp;
1850
+ constructor(zkTcp) {
1851
+ this._zkTcp = zkTcp;
1852
+ }
1853
+ /** Ask if the device doesn't support alphanumeric symbols for user id values. */
1854
+ async isAbcPinEnable() {
1855
+ const keyword = "~IsABCPinEnable";
1856
+ try {
1857
+ // Execute the command to get the PIN information
1858
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1859
+ // Extract and format the PIN information from the response data
1860
+ return data
1861
+ .slice(8) // Skip the first 8 bytes (header)
1862
+ .toString("ascii") // Convert buffer to ASCII string
1863
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1864
+ .replace(/\u0000/g, ""); // Remove null characters 0x00
1865
+ }
1866
+ catch (err) {
1867
+ // Log the error for debugging
1868
+ console.error("Error getting PIN:", err);
1869
+ // Re-throw the error to be handled by the caller
1870
+ throw err;
1871
+ }
1872
+ }
1873
+ /** Ask if the device doesn't support alphanumeric symbols for user id values. */
1874
+ async isT9FunOn() {
1875
+ const keyword = "~T9FunOn";
1876
+ try {
1877
+ // Execute the command to get the PIN information
1878
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1879
+ // Extract and format the PIN information from the response data
1880
+ return data
1881
+ .slice(8) // Skip the first 8 bytes (header)
1882
+ .toString("ascii") // Convert buffer to ASCII string
1883
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1884
+ .replace(/\u0000/g, ""); // Remove null characters 0x00
1885
+ }
1886
+ catch (err) {
1887
+ // Log the error for debugging
1888
+ console.error("Error getting PIN:", err);
1889
+ // Re-throw the error to be handled by the caller
1890
+ throw err;
1891
+ }
1892
+ }
1893
+ async getDeviceId() {
1894
+ const keyword = SdkParameter.DEVICE_ID;
1895
+ try {
1896
+ let result;
1897
+ let retry = true;
1898
+ while (retry) {
1899
+ // Execute the command to get the device name
1900
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1901
+ // Extract and format the device name from the response data
1902
+ result = data
1903
+ .slice(8) // Skip the first 8 bytes (header)
1904
+ .toString("ascii") // Convert buffer to ASCII string
1905
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1906
+ .replace(/\u0000/g, ""); // Remove null characters
1907
+ retry = result.includes("=");
1908
+ }
1909
+ return result;
1910
+ }
1911
+ catch (err) {
1912
+ // Log the error for debugging
1913
+ console.error("Error getting vendor:", err);
1914
+ // Re-throw the error for higher-level handling
1915
+ throw err;
1916
+ }
1917
+ }
1918
+ /**
1919
+ * Change Device ID
1920
+ * @param id a number between 1 and 254
1921
+ */
1922
+ async setDeviceId(id) {
1923
+ if (id < 1 || id > 254)
1924
+ throw new Error("Device ID must be a number between 1 and 254");
1925
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_WRQ, `DeviceID=${id}\x00`);
1926
+ return data.readUInt16LE(0) === COMMANDS.CMD_ACK_OK;
1927
+ }
1928
+ async getVendor() {
1929
+ const keyword = "~OEMVendor";
1930
+ try {
1931
+ let result;
1932
+ let retry = true;
1933
+ while (retry) {
1934
+ // Execute the command to get the device name
1935
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1936
+ // Extract and format the device name from the response data
1937
+ result = data
1938
+ .slice(8) // Skip the first 8 bytes (header)
1939
+ .toString("ascii") // Convert buffer to ASCII string
1940
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1941
+ .replace(/\u0000/g, ""); // Remove null characters
1942
+ retry = result.includes("=");
1943
+ }
1944
+ return result;
1945
+ }
1946
+ catch (err) {
1947
+ // Log the error for debugging
1948
+ console.error("Error getting vendor:", err);
1949
+ // Re-throw the error for higher-level handling
1950
+ throw err;
1951
+ }
1952
+ }
1953
+ async getProductTime() {
1954
+ const keyword = "~ProductTime";
1955
+ try {
1956
+ // Execute the command to get serial number
1957
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1958
+ // Extract and format the serial number from the response data
1959
+ const ProductTime = data
1960
+ .slice(8) // Skip the first 8 bytes (header)
1961
+ .toString("ascii") // Convert buffer to string
1962
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1963
+ .replace(/\u0000/g, ""); // Remove null characters
1964
+ return new Date(ProductTime);
1965
+ }
1966
+ catch (err) {
1967
+ // Log the error for debugging
1968
+ console.error("Error getting Product Time:", err);
1969
+ // Re-throw the error for higher-level handling
1970
+ throw err;
1971
+ }
1972
+ }
1973
+ async getMacAddress() {
1974
+ const keyword = "MAC";
1975
+ try {
1976
+ // Execute the command to get serial number
1977
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
1978
+ // Extract and format the serial number from the response data
1979
+ const macAddr = data
1980
+ .slice(8) // Skip the first 8 bytes (header)
1981
+ .toString("ascii") // Convert buffer to string
1982
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
1983
+ .replace(/\u0000/g, ""); // Remove null characters
1984
+ return macAddr;
1985
+ }
1986
+ catch (err) {
1987
+ // Log the error for debugging
1988
+ console.error("Error getting MAC address:", err);
1989
+ // Re-throw the error for higher-level handling
1990
+ throw err;
1991
+ }
1992
+ }
1993
+ async getNetworkParams() {
1994
+ try {
1995
+ const params = {
1996
+ IPAddress: "",
1997
+ NetMask: "",
1998
+ GATEIPAddress: "",
1999
+ TCPPort: "",
2000
+ };
2001
+ const keywords = Object.keys(params);
2002
+ for await (const keyword of keywords) {
2003
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2004
+ params[keyword] = data
2005
+ .slice(8)
2006
+ .toString("utf-8")
2007
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2008
+ .replace(/\u0000/g, "")
2009
+ .replace("=", "."); // Replace equal simbol to dot, due to sometimes there are parsing errors
2010
+ }
2011
+ return params;
2012
+ }
2013
+ catch (err) {
2014
+ console.error("Error getting Network Params: ", err);
2015
+ throw err;
2016
+ }
2017
+ }
2018
+ async getSerialNumber() {
2019
+ const keyword = "~SerialNumber";
2020
+ let serialNumber = "";
2021
+ let count = 10;
2022
+ try {
2023
+ // Execute the command to get serial number
2024
+ /**
2025
+ * @dev implemented a counter and a while loop because sometimes serial number parses wrong for some reason
2026
+ * */
2027
+ while (serialNumber.length !== 13 && count > 0) {
2028
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2029
+ // Extract and format the serial number from the response data
2030
+ const SN = data
2031
+ .slice(8) // Skip the first 8 bytes (header)
2032
+ .toString("utf-8") // Convert buffer to string
2033
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2034
+ .replace("=", "") // Remove sometines last number is a character equal to = or unknow character
2035
+ .replace(/\u0000/g, ""); // Remove null characters
2036
+ count--;
2037
+ serialNumber = SN;
2038
+ }
2039
+ return serialNumber;
2040
+ }
2041
+ catch (err) {
2042
+ // Log the error for debugging
2043
+ console.error("Error getting serial number:", err);
2044
+ // Re-throw the error for higher-level handling
2045
+ throw err;
2046
+ }
2047
+ }
2048
+ /**
2049
+ * get Zkteko Template version
2050
+ * @returns number
2051
+ */
2052
+ async getDeviceVersion() {
2053
+ const keyword = "~ZKFPVersion";
2054
+ try {
2055
+ // Execute the command to get device version
2056
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2057
+ // Extract and format the device version from the response data
2058
+ // Remove null characters
2059
+ return data
2060
+ .slice(8) // Skip the first 8 bytes (header)
2061
+ .toString("ascii") // Convert buffer to ASCII string
2062
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2063
+ .replace(/\u0000/g, "");
2064
+ }
2065
+ catch (err) {
2066
+ // Log the error for debugging
2067
+ console.error("Error getting device version:", err);
2068
+ // Re-throw the error for higher-level handling
2069
+ throw err;
2070
+ }
2071
+ }
2072
+ /**
2073
+ * get Device/Model Name
2074
+ * @returns
2075
+ */
2076
+ async getDeviceName() {
2077
+ const keyword = "~DeviceName";
2078
+ try {
2079
+ let result;
2080
+ let retry = true;
2081
+ while (retry) {
2082
+ // Execute the command to get the device name
2083
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2084
+ // Extract and format the device name from the response data
2085
+ result = data
2086
+ .slice(8) // Skip the first 8 bytes (header)
2087
+ .toString("ascii") // Convert buffer to ASCII string
2088
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2089
+ .replace(/\u0000/g, ""); // Remove null characters
2090
+ retry = result.includes("=");
2091
+ }
2092
+ return result;
2093
+ }
2094
+ catch (err) {
2095
+ // Log the error for debugging
2096
+ console.error("Error getting device name:", err);
2097
+ // Re-throw the error for higher-level handling
2098
+ throw err;
2099
+ }
2100
+ }
2101
+ async getPlatform() {
2102
+ const keyword = "~Platform";
2103
+ try {
2104
+ let result;
2105
+ let retry = true;
2106
+ while (retry) {
2107
+ // Execute the command to get the device name
2108
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2109
+ // Extract and format the device name from the response data
2110
+ result = data
2111
+ .slice(8) // Skip the first 8 bytes (header)
2112
+ .toString("ascii") // Convert buffer to ASCII string
2113
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2114
+ .replace(/\u0000/g, ""); // Remove null characters
2115
+ retry = result.includes("=");
2116
+ }
2117
+ return result;
2118
+ }
2119
+ catch (err) {
2120
+ // Log the error for debugging
2121
+ console.error("Error getting platform information:", err);
2122
+ // Re-throw the error for higher-level handling
2123
+ throw err;
2124
+ }
2125
+ }
2126
+ async getOS() {
2127
+ const keyword = "~OS";
2128
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2129
+ return data
2130
+ .slice(8) // Skip the first 8 bytes (header)
2131
+ .toString("ascii") // Convert buffer to ASCII string
2132
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2133
+ .replace(/\u0000/g, "");
2134
+ }
2135
+ async getWorkCode() {
2136
+ const keyword = "WorkCode";
2137
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2138
+ // Extract and format the WorkCode information from the response data
2139
+ // Remove null characters
2140
+ return data
2141
+ .slice(8) // Skip the first 8 bytes (header)
2142
+ .toString("ascii") // Convert buffer to ASCII string
2143
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2144
+ .replace(/\u0000/g, "");
2145
+ }
2146
+ /**
2147
+ * get User ID max length
2148
+ * @returns
2149
+ */
2150
+ async getPIN() {
2151
+ const keyword = "~PIN2Width";
2152
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2153
+ return data
2154
+ .slice(8) // Skip the first 8 bytes (header)
2155
+ .toString("ascii") // Convert buffer to ASCII string
2156
+ .replace(`${keyword}=`, "") // Remove the keyword prefix
2157
+ .replace(/\u0000/g, "");
2158
+ }
2159
+ async getFaceOn() {
2160
+ const keyword = "FaceFunOn";
2161
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2162
+ const status = data
2163
+ .slice(8) // Skip the first 8 bytes (header)
2164
+ .toString("ascii") // Convert buffer to ASCII string
2165
+ .replace(`${keyword}=`, ""); // Remove the keyword prefix
2166
+ // Determine and return the face function status
2167
+ return status.includes("0") ? "No" : "Yes";
2168
+ }
2169
+ async getSSR() {
2170
+ const keyword = "~SSR";
2171
+ const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
2172
+ return data
2173
+ .slice(8) // Skip the first 8 bytes (header)
2174
+ .toString("ascii") // Convert buffer to ASCII string
2175
+ .replace(`${keyword}=`, "");
2176
+ }
2177
+ async getFirmware() {
2178
+ try {
2179
+ // Execute the command to get firmware information
2180
+ const data = await this._zkTcp.executeCmd(1100, "");
2181
+ // Extract and return the firmware version from the response data
2182
+ return data.slice(8).toString("ascii"); // Skip the first 8 bytes (header) and convert to ASCII string
2183
+ }
2184
+ catch (err) {
2185
+ // Log the error for debugging
2186
+ console.error("Error getting firmware version:", err);
2187
+ // Re-throw the error to be handled by the caller
2188
+ throw err;
2189
+ }
2190
+ }
2191
+ async getTime() {
2192
+ try {
2193
+ // Execute the command to get the current time
2194
+ const response = await this._zkTcp.executeCmd(COMMANDS.CMD_GET_TIME, "");
2195
+ // Check if the response is valid
2196
+ if (!response || response.length < 12) {
2197
+ throw new Error("Invalid response received for time command");
2198
+ }
2199
+ // Extract and decode the time value from the response
2200
+ const timeValue = response.readUInt32LE(8); // Read 4 bytes starting at offset 8
2201
+ const time = timeParser.decode(timeValue); // Parse and return the decoded time
2202
+ return time;
2203
+ }
2204
+ catch (err) {
2205
+ // Log the error for debugging
2206
+ console.error("Error getting time:", err);
2207
+ // Re-throw the error for the caller to handle
2208
+ throw err;
2209
+ }
2210
+ }
2211
+ async setTime(tm) {
2212
+ try {
2213
+ // Validate the input time
2214
+ if (!(tm instanceof Date) && typeof tm !== "number") {
2215
+ throw new TypeError("Invalid time parameter. Must be a Date object or a timestamp.");
2216
+ }
2217
+ // Convert the input time to a Date object if it's not already
2218
+ const date = tm instanceof Date ? tm : new Date(tm);
2219
+ // Encode the time into the required format
2220
+ const encodedTime = timeParser.encode(date);
2221
+ // Create a buffer and write the encoded time
2222
+ const commandString = Buffer.alloc(32);
2223
+ commandString.writeUInt32LE(encodedTime, 0);
2224
+ // Send the command to set the time
2225
+ const time = await this._zkTcp.executeCmd(COMMANDS.CMD_SET_TIME, commandString);
2226
+ return !!time;
2227
+ }
2228
+ catch (err) {
2229
+ // Log the error for debugging
2230
+ console.error("Error setting time:", err);
2231
+ // Re-throw the error for the caller to handle
2232
+ throw err;
2233
+ }
2234
+ }
2235
+ async voiceTest() {
2236
+ try {
2237
+ // Define the command data for the voice test
2238
+ const commandData = Buffer.from("\x00\x00", "binary");
2239
+ await this._zkTcp.executeCmd(COMMANDS.CMD_TESTVOICE, commandData);
2240
+ // Execute the command and return the result
2241
+ }
2242
+ catch (err) {
2243
+ // Log the error for debugging purposes
2244
+ console.error("Error executing voice test:", err);
2245
+ // Re-throw the error to be handled by the caller
2246
+ throw err;
2247
+ }
2248
+ }
2249
+ }
2250
+
2251
+ class ZTCP {
2252
+ /**
2253
+ * @param_ip ip address of device
2254
+ * @param_port port number of device
2255
+ * @param_timeout connection timout
2256
+ * @param_comm_key communication key of device (if the case)
2257
+ * @return Zkteco TCP socket connection instance
2258
+ */
2259
+ ip;
2260
+ port;
2261
+ timeout;
2262
+ sessionId = 0;
2263
+ replyId = 0;
2264
+ socket;
2265
+ comm_key;
2266
+ user_count = 0;
2267
+ fp_count = 0;
2268
+ pwd_count = 0;
2269
+ oplog_count = 0;
2270
+ attlog_count = 0;
2271
+ fp_cap = 0;
2272
+ user_cap = 0;
2273
+ attlog_cap = 0;
2274
+ fp_av = 0;
2275
+ user_av = 0;
2276
+ attlog_av = 0;
2277
+ face_count = 0;
2278
+ face_cap = 0;
2279
+ userPacketSize = 72;
2280
+ verbose = false;
2281
+ packetNumber = 0;
2282
+ replyData = Buffer.from([]);
2283
+ _optionsService;
2284
+ _transactionService;
2285
+ _userService;
2286
+ constructor(ip, port, timeout, comm_key, verbose) {
2287
+ this.ip = ip;
2288
+ this.port = port;
2289
+ this.timeout = timeout ? timeout : 10000;
2290
+ this.replyId = 0;
2291
+ this.comm_key = comm_key;
2292
+ this.verbose = verbose;
2293
+ this._optionsService = new OptionsService(this);
2294
+ this._userService = new UserService(this);
2295
+ this._transactionService = new TransactionService(this);
2296
+ }
2297
+ createSocket(cbError, cbClose) {
2298
+ return new Promise((resolve, reject) => {
2299
+ this.socket = new net.Socket();
2300
+ // Handle socket error
2301
+ this.socket.once("error", (err) => {
2302
+ this.socket = undefined; // Ensure socket reference is cleared
2303
+ reject(err);
2304
+ if (typeof cbError === "function")
2305
+ cbError(err);
2306
+ });
2307
+ // Handle successful connection
2308
+ this.socket.once("connect", () => {
2309
+ resolve(this.socket);
2310
+ });
2311
+ // Handle socket closure
2312
+ this.socket.once("close", () => {
2313
+ this.socket = undefined; // Ensure socket reference is cleared
2314
+ if (typeof cbClose === "function")
2315
+ cbClose("tcp");
2316
+ });
2317
+ // Set socket timeout if provided
2318
+ if (this.timeout) {
2319
+ this.socket.setTimeout(this.timeout);
2320
+ }
2321
+ // Initiate connection
2322
+ this.socket.connect(this.port, this.ip);
2323
+ });
2324
+ }
2325
+ async connect() {
2326
+ try {
2327
+ let reply = await this.executeCmd(COMMANDS.CMD_CONNECT, "");
2328
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
2329
+ return true;
2330
+ }
2331
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_UNAUTH) {
2332
+ const hashedCommkey = authKey(this.comm_key, this.sessionId);
2333
+ reply = await this.executeCmd(COMMANDS.CMD_AUTH, hashedCommkey);
2334
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
2335
+ return true;
2336
+ }
2337
+ else {
2338
+ throw new Error("error de authenticacion");
2339
+ }
2340
+ }
2341
+ else {
2342
+ // No reply received; throw an error
2343
+ throw new Error("NO_REPLY_ON_CMD_CONNECT");
2344
+ }
2345
+ }
2346
+ catch (err) {
2347
+ // Log the error for debugging, if necessary
2348
+ console.error("Failed to connect:", err);
2349
+ // Re-throw the error for handling by the caller
2350
+ throw err;
2351
+ }
2352
+ }
2353
+ async closeSocket() {
2354
+ return new Promise((resolve, reject) => {
2355
+ // If no socket is present, resolve immediately
2356
+ if (!this.socket) {
2357
+ return resolve(true);
2358
+ }
2359
+ // Clean up listeners to avoid potential memory leaks or duplicate handling
2360
+ this.socket.removeAllListeners("data");
2361
+ // Set a timeout to handle cases where socket.end might not resolve
2362
+ const timer = setTimeout(() => {
2363
+ this.socket?.destroy(); // Forcibly close the socket if not closed properly
2364
+ resolve(true); // Resolve even if the socket was not closed properly
2365
+ }, 2000);
2366
+ // Close the socket and clear the timeout upon successful completion
2367
+ this.socket.end(() => {
2368
+ clearTimeout(timer);
2369
+ resolve(true); // Resolve once the socket has ended
2370
+ });
2371
+ // Handle socket errors during closing
2372
+ this.socket.once("error", (err) => {
2373
+ clearTimeout(timer);
2374
+ reject(err); // Reject the promise with the error
2375
+ });
2376
+ });
2377
+ }
2378
+ writeMessage(msg, connect, cb) {
2379
+ return new Promise((resolve, reject) => {
2380
+ // Check if the socket is initialized
2381
+ if (!this.socket) {
2382
+ return reject(new Error("Socket is not initialized"));
2383
+ }
2384
+ // Define a variable for the timeout reference
2385
+ const timer = setTimeout(() => {
2386
+ // Check if the socket is still valid before trying to remove the listener
2387
+ cleanUp();
2388
+ reject(new Error("TIMEOUT_ON_WRITING_MESSAGE")); // Reject the promise on timeout
2389
+ }, connect ? 3000 : this.timeout);
2390
+ // Handle incoming data
2391
+ const onData = (data) => {
2392
+ // Check if the socket is still valid before trying to remove the listener
2393
+ cleanUp(); // Clear the timeout once data is received
2394
+ resolve(cb(data)); // Resolve the promise with the received data
2395
+ };
2396
+ const cleanUp = () => {
2397
+ if (timer)
2398
+ clearTimeout(timer);
2399
+ if (this.socket) {
2400
+ this.socket.removeListener("data", onData); // Remove the data event listener
2401
+ }
2402
+ };
2403
+ // Attach the data event listener
2404
+ this.socket.on("data", onData);
2405
+ // Attempt to write the message to the socket
2406
+ this.socket.write(msg, undefined, (err) => {
2407
+ if (err) {
2408
+ cleanUp();
2409
+ reject(err); // Reject the promise with the write error
2410
+ }
2411
+ });
2412
+ });
2413
+ }
2414
+ async requestData(msg) {
2415
+ try {
2416
+ return await new Promise((resolve, reject) => {
2417
+ let timer = null;
2418
+ let replyBuffer = Buffer.from([]);
2419
+ // Internal callback to handle data reception
2420
+ const internalCallback = (data_1) => {
2421
+ if (this.socket) {
2422
+ this.socket.removeListener("data", handleOnData); // Clean up listener
2423
+ }
2424
+ if (timer)
2425
+ clearTimeout(timer); // Clear the timeout
2426
+ resolve(data_1); // Resolve the promise with the data
2427
+ };
2428
+ const onTimeOut = () => setTimeout(() => {
2429
+ if (this.socket) {
2430
+ this.socket.removeListener("data", handleOnData); // Clean up listener on timeout
2431
+ }
2432
+ reject(new Error("TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA")); // Reject on timeout
2433
+ }, this.timeout);
2434
+ // Handle incoming data
2435
+ const handleOnData = (data_3) => {
2436
+ replyBuffer = Buffer.concat([replyBuffer, data_3]); // Accumulate data
2437
+ // Check if the data is a valid TCP event
2438
+ if (checkNotEventTCP(data_3))
2439
+ return;
2440
+ // Decode the TCP header
2441
+ const header = decodeTCPHeader(replyBuffer.subarray(0, 16));
2442
+ if (this.verbose) {
2443
+ console.log("response command: ", header.commandId, COMMANDS[header.commandId], "replyid: ", header.replyId);
2444
+ }
2445
+ // Handle based on command ID
2446
+ if (header.commandId === COMMANDS.CMD_DATA) {
2447
+ // Set a timeout to handle delayed responses
2448
+ timer = setTimeout(() => {
2449
+ internalCallback(replyBuffer); // Resolve with accumulated buffer
2450
+ }, 1000);
2451
+ }
2452
+ else {
2453
+ // Set a timeout to handle errors
2454
+ timer = onTimeOut();
2455
+ // Extract packet length and handle accordingly
2456
+ const packetLength = data_3.readUIntLE(4, 2);
2457
+ if (packetLength > 8) {
2458
+ internalCallback(data_3); // Resolve immediately if sufficient data
2459
+ }
2460
+ }
2461
+ };
2462
+ // Ensure the socket is valid before attaching the listener
2463
+ if (this.socket) {
2464
+ this.socket.on("data", handleOnData);
2465
+ // Write the message to the socket
2466
+ this.socket.write(msg, undefined, (err) => {
2467
+ if (err) {
2468
+ if (this.socket) {
2469
+ this.socket.removeListener("data", handleOnData); // Clean up listener on error
2470
+ }
2471
+ return reject(err); // Reject the promise with the error
2472
+ }
2473
+ // Set a timeout to handle cases where no response is received
2474
+ timer = onTimeOut();
2475
+ });
2476
+ }
2477
+ else {
2478
+ reject(new Error("SOCKET_NOT_INITIALIZED")); // Reject if socket is not initialized
2479
+ }
2480
+ });
2481
+ }
2482
+ catch (err_1) {
2483
+ console.error("Promise Rejected:", err_1); // Log the rejection reason
2484
+ throw err_1; // Re-throw the error to be handled by the caller
2485
+ }
2486
+ }
2487
+ /**
2488
+ *
2489
+ * @param {*} command
2490
+ * @param {*} data
2491
+ *
2492
+ *
2493
+ * reject error when command fail and resolve data when success
2494
+ */
2495
+ async executeCmd(command, data) {
2496
+ // Reset sessionId and replyId for connection commands
2497
+ if (command === COMMANDS.CMD_CONNECT) {
2498
+ this.sessionId = 0;
2499
+ this.replyId = 0;
2500
+ }
2501
+ else {
2502
+ this.replyId++;
2503
+ }
2504
+ const currentReply = this.replyId;
2505
+ const buf = createTCPHeader(command, this.sessionId, this.replyId, data);
2506
+ const callback = (responseData) => {
2507
+ const packets = splitTcpPackets(responseData);
2508
+ for (const packet of packets) {
2509
+ const headers = decodeTCPHeader(packet);
2510
+ if (this.verbose) {
2511
+ const JOIN_CMD = { ...COMMANDS, ...DISCOVERED_CMD };
2512
+ console.debug("request command:", COMMANDS[command], "\nresponse command: ", JOIN_CMD[headers.commandId], "replyid: ", headers.replyId);
2513
+ }
2514
+ if (+headers.replyId === currentReply + 1) {
2515
+ return packet;
2516
+ }
2517
+ continue;
2518
+ }
2519
+ };
2520
+ return new Promise((Resolve, Reject) => {
2521
+ // Write the message to the socket and wait for a response
2522
+ this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT, callback)
2523
+ .then((reply) => {
2524
+ // Remove TCP header from the response
2525
+ let rReply;
2526
+ try {
2527
+ rReply = removeTcpHeader(reply);
2528
+ }
2529
+ catch (e) {
2530
+ console.log("reply", reply);
2531
+ }
2532
+ // Update sessionId for connection command responses
2533
+ if (command === COMMANDS.CMD_CONNECT &&
2534
+ rReply &&
2535
+ rReply.length >= 6) {
2536
+ // Assuming sessionId is located at offset 4 and is 2 bytes long
2537
+ this.sessionId = rReply.readUInt16LE(4);
2538
+ }
2539
+ Resolve(rReply);
2540
+ })
2541
+ .catch((err) => {
2542
+ // Log or handle the error if necessary
2543
+ console.error("Error executing command:", err);
2544
+ Reject(err); // Re-throw the error for handling by the caller
2545
+ });
2546
+ });
2547
+ }
2548
+ async sendChunkRequest(start, size) {
2549
+ this.replyId++;
2550
+ const reqData = Buffer.alloc(8);
2551
+ reqData.writeUInt32LE(start, 0);
2552
+ reqData.writeUInt32LE(size, 4);
2553
+ const buf = createTCPHeader(COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
2554
+ try {
2555
+ const promise = new Promise((resolve, reject) => {
2556
+ this.socket?.write(buf, undefined, (err) => {
2557
+ if (err) {
2558
+ console.error(`[TCP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
2559
+ reject(err); // Reject the promise if there is an error
2560
+ }
2561
+ else {
2562
+ resolve(true); // Resolve the promise if the write operation succeeds
2563
+ }
2564
+ });
2565
+ });
2566
+ await promise;
2567
+ }
2568
+ catch (err) {
2569
+ // Handle or log the error as needed
2570
+ console.error(`[TCP][SEND_CHUNK_REQUEST] Exception: ${err.message}`);
2571
+ throw err; // Re-throw the error for handling by the caller
2572
+ }
2573
+ }
2574
+ /**
2575
+ *
2576
+ * @param {Buffer} reqData - indicate the type of data that need to receive ( user or attLog)
2577
+ * @param {Function} cb - callback is triggered when receiving packets
2578
+ *
2579
+ * readWithBuffer will reject error if it'wrong when starting request data
2580
+ * readWithBuffer will return { data: replyData , err: Error } when receiving requested data
2581
+ */
2582
+ readWithBuffer(reqData, cb) {
2583
+ return new Promise(async (resolve, reject) => {
2584
+ this.replyId++;
2585
+ const buf = createTCPHeader(COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
2586
+ let reply;
2587
+ try {
2588
+ reply = await this.requestData(buf);
2589
+ }
2590
+ catch (err) {
2591
+ reject(err);
2592
+ }
2593
+ const header = decodeTCPHeader(reply?.subarray(0, 16));
2594
+ switch (header.commandId) {
2595
+ case COMMANDS.CMD_DATA: {
2596
+ resolve({ data: reply.subarray(16), mode: 8 });
2597
+ break;
2598
+ }
2599
+ case COMMANDS.CMD_ACK_OK:
2600
+ case COMMANDS.CMD_PREPARE_DATA: {
2601
+ // this case show that data is prepared => send command to get these data
2602
+ // reply variable includes information about the size of following data
2603
+ const recvData = reply.subarray(16);
2604
+ const size = recvData.readUIntLE(1, 4);
2605
+ // We need to split the data to many chunks to receive , because it's to large
2606
+ // After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
2607
+ const remain = size % Constants.MAX_CHUNK;
2608
+ const numberChunks = Math.round(size - remain) / Constants.MAX_CHUNK;
2609
+ this.packetNumber = numberChunks + (remain > 0 ? 1 : 0);
2610
+ //let replyData = Buffer.from([])
2611
+ let totalBuffer = Buffer.from([]);
2612
+ let realTotalBuffer = Buffer.from([]);
2613
+ let timer = setTimeout(() => {
2614
+ internalCallback(this.replyData, new Error("TIMEOUT WHEN RECEIVING PACKET"));
2615
+ }, this.timeout);
2616
+ const internalCallback = (replyData, err = null) => {
2617
+ this.socket && this.socket.removeAllListeners("data");
2618
+ timer && clearTimeout(timer);
2619
+ resolve({ data: replyData, err });
2620
+ };
2621
+ this.socket?.once("close", () => {
2622
+ internalCallback(this.replyData, new Error("Socket is disconnected unexpectedly"));
2623
+ });
2624
+ for (let i = 0; i <= numberChunks; i++) {
2625
+ const data = await new Promise((resolve2, reject2) => {
2626
+ try {
2627
+ this.sendChunkRequest(i * Constants.MAX_CHUNK, i === numberChunks ? remain : Constants.MAX_CHUNK);
2628
+ this.socket?.on("data", (reply) => {
2629
+ clearTimeout(timer);
2630
+ timer = setTimeout(() => {
2631
+ internalCallback(this.replyData, new Error(`TIME OUT !! ${this.packetNumber} PACKETS REMAIN !`));
2632
+ }, this.timeout);
2633
+ if (this.verbose && reply.length >= 8) {
2634
+ const headers = decodeTCPHeader(reply);
2635
+ if (COMMANDS[headers.commandId]) {
2636
+ switch (headers.commandId) {
2637
+ case COMMANDS.CMD_ACK_OK:
2638
+ case COMMANDS.CMD_DATA:
2639
+ this.verbose &&
2640
+ console.log("CMD received: ", COMMANDS[headers.commandId]);
2641
+ break;
2642
+ case COMMANDS.CMD_PREPARE_DATA:
2643
+ this.verbose &&
2644
+ console.log("CMD received: ", COMMANDS[headers.commandId]);
2645
+ this.verbose &&
2646
+ console.log(`recieve chunk: prepare data size is ${headers.payloadSize}`);
2647
+ break;
2648
+ default:
2649
+ break;
2650
+ }
2651
+ }
2652
+ }
2653
+ totalBuffer = Buffer.concat([totalBuffer, reply]);
2654
+ const packetLength = totalBuffer.readUIntLE(4, 2);
2655
+ if (totalBuffer.length >= 8 + packetLength) {
2656
+ realTotalBuffer = Buffer.concat([
2657
+ realTotalBuffer,
2658
+ totalBuffer.subarray(16, 8 + packetLength),
2659
+ ]);
2660
+ totalBuffer = totalBuffer.subarray(8 + packetLength);
2661
+ if ((this.packetNumber > 1 &&
2662
+ realTotalBuffer.length === Constants.MAX_CHUNK + 8) ||
2663
+ (this.packetNumber === 1 &&
2664
+ realTotalBuffer.length === remain + 8)) {
2665
+ this.packetNumber--;
2666
+ cb && cb(realTotalBuffer.length, size);
2667
+ resolve2(realTotalBuffer.subarray(8));
2668
+ totalBuffer = Buffer.from([]);
2669
+ realTotalBuffer = Buffer.from([]);
2670
+ }
2671
+ }
2672
+ });
2673
+ }
2674
+ catch (e) {
2675
+ reject2(e);
2676
+ }
2677
+ });
2678
+ this.replyData = Buffer.concat([
2679
+ this.replyData,
2680
+ data,
2681
+ ]);
2682
+ this.socket?.removeAllListeners("data");
2683
+ if (this.packetNumber <= 0) {
2684
+ resolve({ data: this.replyData });
2685
+ }
2686
+ }
2687
+ break;
2688
+ }
2689
+ default: {
2690
+ reject(new Error("ERROR_IN_UNHANDLE_CMD " + exportErrorMessage(header.commandId)));
2691
+ }
2692
+ }
2693
+ });
2694
+ }
2695
+ /**
2696
+ *
2697
+ * @param {*} callbackInProcess
2698
+ * reject error when starting request data
2699
+ * return { data: records, err: Error } when receiving requested data
2700
+ */
2701
+ async freeData() {
2702
+ try {
2703
+ const resp = await this.executeCmd(COMMANDS.CMD_FREE_DATA, "");
2704
+ return !!resp;
2705
+ }
2706
+ catch (err) {
2707
+ console.error("Error freeing data:", err);
2708
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
2709
+ }
2710
+ }
2711
+ async disableDevice() {
2712
+ try {
2713
+ const resp = await this.executeCmd(COMMANDS.CMD_DISABLEDEVICE, REQUEST_DATA.DISABLE_DEVICE);
2714
+ return !!resp;
2715
+ }
2716
+ catch (err) {
2717
+ console.error("Error disabling device:", err);
2718
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
2719
+ }
2720
+ }
2721
+ async enableDevice() {
2722
+ try {
2723
+ const resp = await this.executeCmd(COMMANDS.CMD_ENABLEDEVICE, "");
2724
+ return !!resp;
2725
+ }
2726
+ catch (err) {
2727
+ console.error("Error enabling device:", err);
2728
+ throw err; // Optionally, re-throw the error if you need to handle it upstream
2729
+ }
2730
+ }
2731
+ async disconnect() {
2732
+ try {
2733
+ // Attempt to execute the disconnect command
2734
+ await this.executeCmd(COMMANDS.CMD_EXIT, "");
2735
+ }
2736
+ catch (err) {
2737
+ // Log any errors encountered during command execution
2738
+ console.error("Error during disconnection:", err);
2739
+ // Optionally, add more handling or recovery logic here
2740
+ }
2741
+ // Attempt to close the socket and return the result
2742
+ try {
2743
+ await this.closeSocket();
2744
+ }
2745
+ catch (err) {
2746
+ // Log any errors encountered while closing the socket
2747
+ console.error("Error during socket closure:", err);
2748
+ // Optionally, rethrow or handle the error if necessary
2749
+ throw err; // Re-throwing to propagate the error
2750
+ }
2751
+ }
2752
+ async getInfo() {
2753
+ try {
2754
+ // Execute the command to retrieve free sizes from the device
2755
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
2756
+ // Parse the response data to extract and return relevant information
2757
+ return {
2758
+ userCounts: data.readUIntLE(24, 4), // Number of users
2759
+ logCounts: data.readUIntLE(40, 4), // Number of logs
2760
+ logCapacity: data.readUIntLE(72, 4), // Capacity of logs in bytes
2761
+ };
2762
+ }
2763
+ catch (err) {
2764
+ // Log the error for debugging purposes
2765
+ console.error("Error getting device info:", err);
2766
+ // Re-throw the error to allow upstream error handling
2767
+ throw err;
2768
+ }
2769
+ }
2770
+ async getSizes() {
2771
+ try {
2772
+ // Execute the command to retrieve free sizes from the device
2773
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
2774
+ // Parse the response data to extract and return relevant information
2775
+ const buf = data.slice(8); // remove header
2776
+ this.user_count = buf.readUIntLE(16, 4);
2777
+ this.fp_count = buf.readUIntLE(24, 4);
2778
+ this.pwd_count = buf.readUIntLE(52, 4);
2779
+ this.oplog_count = buf.readUIntLE(40, 4);
2780
+ this.attlog_count = buf.readUIntLE(32, 4);
2781
+ this.fp_cap = buf.readUIntLE(56, 4);
2782
+ this.user_cap = buf.readUIntLE(60, 4);
2783
+ this.attlog_cap = buf.readUIntLE(64, 4);
2784
+ this.fp_av = buf.readUIntLE(68, 4);
2785
+ this.user_av = buf.readUIntLE(72, 4);
2786
+ this.attlog_av = buf.readUIntLE(76, 4);
2787
+ this.face_count = buf.readUIntLE(80, 4);
2788
+ this.face_cap = buf.readUIntLE(88, 4);
2789
+ return {
2790
+ userCounts: this.user_count, // Number of users
2791
+ logCounts: this.attlog_count, // Number of logs
2792
+ fingerCount: this.fp_count,
2793
+ adminCount: this.pwd_count,
2794
+ opLogCount: this.oplog_count,
2795
+ logCapacity: this.attlog_cap, // Capacity of logs in bytes
2796
+ fingerCapacity: this.fp_cap,
2797
+ userCapacity: this.user_cap,
2798
+ attLogCapacity: this.attlog_cap,
2799
+ fingerAvailable: this.fp_av,
2800
+ userAvailable: this.user_av,
2801
+ attLogAvailable: this.attlog_av,
2802
+ faceCount: this.face_count,
2803
+ faceCapacity: this.face_cap,
2804
+ };
2805
+ }
2806
+ catch (err) {
2807
+ // Log the error for debugging purposes
2808
+ console.error("Error getting device info:", err);
2809
+ // Re-throw the error to allow upstream error handling
2810
+ throw err;
2811
+ }
2812
+ }
2813
+ #listeners = new Map();
2814
+ async getAttendanceSize() {
2815
+ try {
2816
+ // Execute command to get free sizes
2817
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
2818
+ // Parse and return the attendance size
2819
+ return data.readUIntLE(40, 4); // Assuming data at offset 40 represents the attendance size
2820
+ }
2821
+ catch (err) {
2822
+ // Log error details for debugging
2823
+ console.error("Error getting attendance size:", err);
2824
+ // Re-throw the error to be handled by the caller
2825
+ throw err;
2826
+ }
2827
+ }
2828
+ // Clears the attendance logs on the device
2829
+ async clearAttendanceLog() {
2830
+ return await this._transactionService.clearAttendanceLog();
2831
+ }
2832
+ /**
2833
+ * Clears all data on the device
2834
+ * @value 1 Attendance records
2835
+ * @value 2 Fingerprint templates
2836
+ * @value 3 None
2837
+ * @value 4 Operation records
2838
+ * @value 5 User information
2839
+ * @default 0 Delete all
2840
+ */
2841
+ async clearData(value) {
2842
+ try {
2843
+ // Execute the command to clear all data
2844
+ await this.disableDevice();
2845
+ if (!value)
2846
+ value = 3;
2847
+ const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
2848
+ await this.refreshData();
2849
+ await this.enableDevice();
2850
+ return !!buf;
2851
+ }
2852
+ catch (err) {
2853
+ // Log the error for debugging purposes
2854
+ console.error("Error clearing data:", err);
2855
+ // Re-throw the error to be handled by the caller
2856
+ throw err;
2857
+ }
2858
+ }
2859
+ async getRealTimeLogs(cb = (realTimeLog) => { }) {
2860
+ this.replyId++; // Increment the reply ID for this request
2861
+ try {
2862
+ // Create a buffer with the command header to request real-time logs
2863
+ const buf = createTCPHeader(COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, Buffer.from([0x01, 0x00, 0x00, 0x00]));
2864
+ // Send the request to the device
2865
+ this.socket?.write(buf, undefined, (err) => {
2866
+ if (err) {
2867
+ // Log and reject the promise if there is an error writing to the socket
2868
+ console.error("Error sending real-time logs request:", err);
2869
+ throw err;
2870
+ }
2871
+ });
2872
+ // Ensure data listeners are added only once
2873
+ if (this.socket?.listenerCount("data") === 0) {
2874
+ console.log("entraaa");
2875
+ this.socket.on("data", (data) => {
2876
+ // Check if the data is an event and not just a regular response
2877
+ if (checkNotEventTCP(data)) {
2878
+ // Process the data if it is of the expected length
2879
+ if (data.length > 16) {
2880
+ // Decode and pass the log to the callback
2881
+ cb(decodeRTEvent(data));
2882
+ }
2883
+ }
2884
+ });
2885
+ }
2886
+ }
2887
+ catch (err) {
2888
+ // Handle errors and reject the promise
2889
+ console.error("Error getting real-time logs:", err);
2890
+ throw err;
2891
+ }
2892
+ }
2893
+ /**
2894
+ * Get all Finger objects
2895
+ * @returns {Record<string, Finger[]>}
2896
+ */
2897
+ async getTemplates(callbackInProcess = () => { }) {
2898
+ return await this._userService.getTemplates(callbackInProcess);
2899
+ }
2900
+ /**
2901
+ * Return size
2902
+ * @param packet
2903
+ */
2904
+ testTcpTop(packet) {
2905
+ // Check if packet is too small
2906
+ if (packet.length <= 8)
2907
+ return 0;
2908
+ // Extract header values using little-endian format
2909
+ const headerValue1 = packet.readUInt16LE(0);
2910
+ const headerValue2 = packet.readUInt16LE(2);
2911
+ const size = packet.readUInt32LE(4);
2912
+ // Check if magic numbers match
2913
+ if (headerValue1 === Constants.MACHINE_PREPARE_DATA_1 &&
2914
+ headerValue2 === Constants.MACHINE_PREPARE_DATA_2) {
2915
+ return size;
2916
+ }
2917
+ return 0;
2918
+ }
2919
+ async refreshData() {
2920
+ try {
2921
+ const reply = await this.executeCmd(COMMANDS.CMD_REFRESHDATA, "");
2922
+ return !!reply;
2923
+ }
2924
+ catch (err) {
2925
+ console.error("Error getting user templates: ", err);
2926
+ throw err;
2927
+ }
2928
+ }
2929
+ async sendWithBuffer(buffer) {
2930
+ const MAX_CHUNK = 1024;
2931
+ const size = buffer.length;
2932
+ await this.freeData();
2933
+ const commandString = Buffer.alloc(4); // 'I' is 4 bytes
2934
+ commandString.writeUInt32LE(size, 0);
2935
+ try {
2936
+ const cmdResponse = await this.executeCmd(COMMANDS.CMD_PREPARE_DATA, commandString);
2937
+ // responds with 2000 = CMD_ACK_OK
2938
+ if (!cmdResponse) {
2939
+ throw new Error("Can't prepare data");
2940
+ }
2941
+ }
2942
+ catch (e) {
2943
+ console.error(e);
2944
+ }
2945
+ const remain = size % MAX_CHUNK;
2946
+ const packets = Math.floor((size - remain) / MAX_CHUNK);
2947
+ let start = 0;
2948
+ try {
2949
+ for (let i = 0; i < packets; i++) {
2950
+ const resp = await this.sendChunk(buffer.slice(start, start + MAX_CHUNK));
2951
+ if (resp) {
2952
+ start += MAX_CHUNK;
2953
+ if (i == packets - 1 && remain) {
2954
+ const lastPacket = await this.sendChunk(buffer.slice(start, start + remain));
2955
+ return lastPacket;
2956
+ }
2957
+ }
2958
+ }
2959
+ }
2960
+ catch (e) {
2961
+ console.error(e);
2962
+ }
2963
+ }
2964
+ async sendChunk(commandString) {
2965
+ try {
2966
+ return await new Promise((resolve, reject) => {
2967
+ resolve(this.executeCmd(COMMANDS.CMD_DATA, commandString));
2968
+ });
2969
+ }
2970
+ catch (e) {
2971
+ throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
2972
+ }
2973
+ }
2974
+ async readSocket(length, cb = null) {
2975
+ let replyBufer = Buffer.from([]);
2976
+ const totalPackets = 0;
2977
+ return new Promise((resolve, reject) => {
2978
+ let timer = setTimeout(() => {
2979
+ internalCallback(replyBufer, new Error("TIMEOUT WHEN RECEIVING PACKET"));
2980
+ }, this.timeout);
2981
+ const internalCallback = (replyData, err = null) => {
2982
+ this.socket && this.socket.removeListener("data", onDataEnroll);
2983
+ timer && clearTimeout(timer);
2984
+ resolve({ data: replyData, err: err });
2985
+ };
2986
+ function onDataEnroll(data) {
2987
+ clearTimeout(timer);
2988
+ timer = setTimeout(() => {
2989
+ internalCallback(replyBufer, new Error(`TIME OUT !! ${totalPackets} PACKETS REMAIN !`));
2990
+ }, this.timeout);
2991
+ replyBufer = Buffer.concat([replyBufer, data], replyBufer.length + data.length);
2992
+ if (data.length == length) {
2993
+ internalCallback(data);
2994
+ }
2995
+ }
2996
+ this.socket.once("close", () => {
2997
+ internalCallback(replyBufer, new Error("Socket is disconnected unexpectedly"));
2998
+ });
2999
+ this.socket.on("data", onDataEnroll);
3000
+ }).catch((err) => {
3001
+ console.error("Promise Rejected:", err); // Log the rejection reason
3002
+ throw err; // Re-throw the error to be handled by the caller
3003
+ });
3004
+ }
3005
+ /**
3006
+ * Register events
3007
+ * @param {number} flags - Event flags
3008
+ * @returns {Promise<void>}
3009
+ * @throws {ZKErrorResponse} If registration fails
3010
+ */
3011
+ async regEvent(flags) {
3012
+ try {
3013
+ const commandString = Buffer.alloc(4); // 'I' format is 4 bytes
3014
+ commandString.writeUInt32LE(flags, 0); // Little-endian unsigned int
3015
+ const cmdResponse = await this.executeCmd(COMMANDS.CMD_REG_EVENT, commandString);
3016
+ if (this.verbose)
3017
+ console.log("regEvent: ", cmdResponse.readUInt16LE(0));
3018
+ }
3019
+ catch (e) {
3020
+ throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
3021
+ }
3022
+ }
3023
+ async cancelCapture() {
3024
+ try {
3025
+ const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, "");
3026
+ return !!reply;
3027
+ }
3028
+ catch (e) {
3029
+ throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
3030
+ }
3031
+ }
3032
+ async restartDevice() {
3033
+ try {
3034
+ await this.executeCmd(COMMANDS.CMD_RESTART, "");
3035
+ }
3036
+ catch (e) {
3037
+ throw new ZkError(e, COMMANDS.CMD_RESTART, this.ip);
3038
+ }
3039
+ }
3040
+ }
3041
+
3042
+ class ZUDP {
3043
+ ip;
3044
+ port;
3045
+ timeout;
3046
+ socket;
3047
+ sessionId;
3048
+ replyId;
3049
+ inport;
3050
+ comm_key;
3051
+ constructor(ip, port, timeout, inport, comm_key = 0) {
3052
+ this.ip = ip;
3053
+ this.port = port;
3054
+ this.timeout = timeout;
3055
+ this.socket = null;
3056
+ this.sessionId = null;
3057
+ this.replyId = 0;
3058
+ this.inport = inport;
3059
+ this.comm_key = comm_key;
3060
+ }
3061
+ createSocket(cbError, cbClose) {
3062
+ return new Promise((resolve, reject) => {
3063
+ this.socket = dgram__namespace.createSocket('udp4');
3064
+ this.socket.setMaxListeners(Infinity);
3065
+ this.socket.once('error', (err) => {
3066
+ this.socket = null;
3067
+ reject(err);
3068
+ if (cbError)
3069
+ cbError(err);
3070
+ });
3071
+ this.socket.once('close', () => {
3072
+ this.socket = null;
3073
+ if (cbClose)
3074
+ cbClose('udp');
3075
+ });
3076
+ this.socket.once('listening', () => {
3077
+ resolve(this.socket);
3078
+ });
3079
+ try {
3080
+ this.socket.bind(this.inport);
3081
+ }
3082
+ catch (err) {
3083
+ this.socket = null;
3084
+ reject(err);
3085
+ if (cbError)
3086
+ cbError(err);
3087
+ }
3088
+ });
3089
+ }
3090
+ async connect() {
3091
+ try {
3092
+ let reply = await this.executeCmd(COMMANDS.CMD_CONNECT, '');
3093
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
3094
+ return true;
3095
+ }
3096
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_UNAUTH) {
3097
+ const hashedCommkey = authKey(this.comm_key, this.sessionId);
3098
+ reply = await this.executeCmd(COMMANDS.CMD_AUTH, hashedCommkey);
3099
+ if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
3100
+ return true;
3101
+ }
3102
+ else {
3103
+ throw new Error('Authentication error');
3104
+ }
3105
+ }
3106
+ else {
3107
+ throw new Error('NO_REPLY_ON_CMD_CONNECT');
3108
+ }
3109
+ }
3110
+ catch (err) {
3111
+ console.error('Error in connect method:', err);
3112
+ throw err;
3113
+ }
3114
+ }
3115
+ async closeSocket() {
3116
+ return new Promise((resolve, reject) => {
3117
+ if (!this.socket) {
3118
+ resolve(true);
3119
+ return;
3120
+ }
3121
+ const timeout = 2000;
3122
+ const timer = setTimeout(() => {
3123
+ console.warn('Socket close timeout');
3124
+ resolve(true);
3125
+ }, timeout);
3126
+ this.socket.removeAllListeners('message');
3127
+ // @ts-ignore
3128
+ this.socket.close((err) => {
3129
+ clearTimeout(timer);
3130
+ if (err) {
3131
+ console.error('Error closing socket:', err);
3132
+ reject(err);
3133
+ }
3134
+ else {
3135
+ resolve(true);
3136
+ }
3137
+ this.socket = null;
3138
+ });
3139
+ });
3140
+ }
3141
+ writeMessage(msg, connect) {
3142
+ return new Promise((resolve, reject) => {
3143
+ if (!this.socket) {
3144
+ reject(new Error('Socket not initialized'));
3145
+ return;
3146
+ }
3147
+ let sendTimeoutId;
3148
+ const onMessage = (data) => {
3149
+ clearTimeout(sendTimeoutId);
3150
+ this.socket.removeListener('message', onMessage);
3151
+ resolve(data);
3152
+ };
3153
+ this.socket.once('message', onMessage);
3154
+ this.socket.send(msg, 0, msg.length, this.port, this.ip, (err) => {
3155
+ if (err) {
3156
+ this.socket.removeListener('message', onMessage);
3157
+ reject(err);
3158
+ return;
3159
+ }
3160
+ if (this.timeout) {
3161
+ sendTimeoutId = setTimeout(() => {
3162
+ this.socket.removeListener('message', onMessage);
3163
+ reject(new Error('TIMEOUT_ON_WRITING_MESSAGE'));
3164
+ }, connect ? 2000 : this.timeout);
3165
+ }
3166
+ });
3167
+ });
3168
+ }
3169
+ requestData(msg) {
3170
+ return new Promise((resolve, reject) => {
3171
+ if (!this.socket) {
3172
+ reject(new Error('Socket not initialized'));
3173
+ return;
3174
+ }
3175
+ let sendTimeoutId;
3176
+ let responseTimeoutId;
3177
+ const handleOnData = (data) => {
3178
+ if (checkNotEventUDP(data))
3179
+ return;
3180
+ clearTimeout(sendTimeoutId);
3181
+ clearTimeout(responseTimeoutId);
3182
+ this.socket.removeListener('message', handleOnData);
3183
+ resolve(data);
3184
+ };
3185
+ const onReceiveTimeout = () => {
3186
+ this.socket.removeListener('message', handleOnData);
3187
+ reject(new Error('TIMEOUT_ON_RECEIVING_REQUEST_DATA'));
3188
+ };
3189
+ this.socket.on('message', handleOnData);
3190
+ this.socket.send(msg, 0, msg.length, this.port, this.ip, (err) => {
3191
+ if (err) {
3192
+ this.socket.removeListener('message', handleOnData);
3193
+ reject(err);
3194
+ return;
3195
+ }
3196
+ responseTimeoutId = setTimeout(onReceiveTimeout, this.timeout);
3197
+ });
3198
+ sendTimeoutId = setTimeout(() => {
3199
+ this.socket.removeListener('message', handleOnData);
3200
+ reject(new Error('TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA'));
3201
+ }, this.timeout);
3202
+ });
3203
+ }
3204
+ async executeCmd(command, data) {
3205
+ try {
3206
+ if (command === COMMANDS.CMD_CONNECT) {
3207
+ this.sessionId = 0;
3208
+ this.replyId = 0;
3209
+ }
3210
+ else {
3211
+ this.replyId++;
3212
+ }
3213
+ const buf = createUDPHeader(command, this.sessionId, this.replyId, data);
3214
+ const reply = await this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT);
3215
+ if (reply && reply.length > 0) {
3216
+ if (command === COMMANDS.CMD_CONNECT) {
3217
+ this.sessionId = reply.readUInt16LE(4);
3218
+ }
3219
+ }
3220
+ return reply;
3221
+ }
3222
+ catch (err) {
3223
+ console.error(`Error executing command ${command}:`, err);
3224
+ throw err;
3225
+ }
3226
+ }
3227
+ async sendChunkRequest(start, size) {
3228
+ this.replyId++;
3229
+ const reqData = Buffer.alloc(8);
3230
+ reqData.writeUInt32LE(start, 0);
3231
+ reqData.writeUInt32LE(size, 4);
3232
+ const buf = createUDPHeader(COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
3233
+ try {
3234
+ await new Promise((resolve, reject) => {
3235
+ this.socket.send(buf, 0, buf.length, this.port, this.ip, (err) => {
3236
+ if (err) {
3237
+ log(`[UDP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
3238
+ reject(err);
3239
+ }
3240
+ else {
3241
+ resolve();
3242
+ }
3243
+ });
3244
+ });
3245
+ }
3246
+ catch (error) {
3247
+ log(`[UDP][SEND_CHUNK_REQUEST] Exception: ${error.message}`);
3248
+ throw error;
3249
+ }
3250
+ }
3251
+ async readWithBuffer(reqData, cb = null) {
3252
+ this.replyId++;
3253
+ const buf = createUDPHeader(COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
3254
+ try {
3255
+ const reply = await this.requestData(buf);
3256
+ const header = decodeUDPHeader(reply.subarray(0, 8));
3257
+ switch (header.commandId) {
3258
+ case COMMANDS.CMD_DATA:
3259
+ return { data: reply.subarray(8), err: null };
3260
+ case COMMANDS.CMD_ACK_OK:
3261
+ case COMMANDS.CMD_PREPARE_DATA:
3262
+ return await this.handleChunkedData(reply, header.commandId, cb);
3263
+ default:
3264
+ throw new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId));
3265
+ }
3266
+ }
3267
+ catch (err) {
3268
+ return { data: null, err: err };
3269
+ }
3270
+ }
3271
+ async handleChunkedData(reply, commandId, cb) {
3272
+ return new Promise((resolve) => {
3273
+ const recvData = reply.subarray(8);
3274
+ const size = recvData.readUIntLE(1, 4);
3275
+ let totalBuffer = Buffer.from([]);
3276
+ const timeout = 3000;
3277
+ let timer = setTimeout(() => {
3278
+ this.socket.removeListener('message', handleOnData);
3279
+ resolve({ data: null, err: new Error('TIMEOUT WHEN RECEIVING PACKET') });
3280
+ }, timeout);
3281
+ const internalCallback = (replyData, err = null) => {
3282
+ this.socket.removeListener('message', handleOnData);
3283
+ clearTimeout(timer);
3284
+ resolve({ data: err ? null : replyData, err });
3285
+ };
3286
+ const handleOnData = (reply) => {
3287
+ if (checkNotEventUDP(reply))
3288
+ return;
3289
+ clearTimeout(timer);
3290
+ timer = setTimeout(() => {
3291
+ internalCallback(totalBuffer, new Error(`TIMEOUT !! ${(size - totalBuffer.length) / size} % REMAIN !`));
3292
+ }, timeout);
3293
+ const header = decodeUDPHeader(reply);
3294
+ switch (header.commandId) {
3295
+ case COMMANDS.CMD_PREPARE_DATA:
3296
+ break;
3297
+ case COMMANDS.CMD_DATA:
3298
+ totalBuffer = Buffer.concat([totalBuffer, reply.subarray(8)]);
3299
+ cb && cb(totalBuffer.length, size);
3300
+ break;
3301
+ case COMMANDS.CMD_ACK_OK:
3302
+ if (totalBuffer.length === size) {
3303
+ internalCallback(totalBuffer);
3304
+ }
3305
+ break;
3306
+ default:
3307
+ internalCallback(Buffer.from([]), new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)));
3308
+ }
3309
+ };
3310
+ this.socket.on('message', handleOnData);
3311
+ const chunkCount = Math.ceil(size / Constants.MAX_CHUNK);
3312
+ for (let i = 0; i < chunkCount; i++) {
3313
+ const start = i * Constants.MAX_CHUNK;
3314
+ const chunkSize = (i === chunkCount - 1) ? size % Constants.MAX_CHUNK : Constants.MAX_CHUNK;
3315
+ this.sendChunkRequest(start, chunkSize).catch(err => {
3316
+ internalCallback(Buffer.from([]), err);
3317
+ });
3318
+ }
3319
+ });
3320
+ }
3321
+ async getUsers() {
3322
+ try {
3323
+ if (this.socket) {
3324
+ await this.freeData();
3325
+ }
3326
+ const data = await this.readWithBuffer(REQUEST_DATA.GET_USERS);
3327
+ if (this.socket) {
3328
+ await this.freeData();
3329
+ }
3330
+ const USER_PACKET_SIZE = 28;
3331
+ let userData = data.data?.subarray(4) || Buffer.from([]);
3332
+ const users = [];
3333
+ while (userData.length >= USER_PACKET_SIZE) {
3334
+ const user = decodeUserData28(userData.subarray(0, USER_PACKET_SIZE));
3335
+ users.push(user);
3336
+ userData = userData.subarray(USER_PACKET_SIZE);
3337
+ }
3338
+ return { data: users };
3339
+ }
3340
+ catch (err) {
3341
+ throw new Error(err.message);
3342
+ }
3343
+ }
3344
+ async getAttendances(callbackInProcess) {
3345
+ try {
3346
+ if (this.socket) {
3347
+ await this.freeData();
3348
+ }
3349
+ const data = await this.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS);
3350
+ if (this.socket) {
3351
+ await this.freeData();
3352
+ }
3353
+ const RECORD_PACKET_SIZE = 16;
3354
+ let recordData = data.data?.subarray(4) || Buffer.from([]);
3355
+ const records = [];
3356
+ while (recordData.length >= RECORD_PACKET_SIZE) {
3357
+ const record = decodeRecordData16(recordData.subarray(0, RECORD_PACKET_SIZE));
3358
+ records.push({ ...record, ip: this.ip });
3359
+ recordData = recordData.subarray(RECORD_PACKET_SIZE);
3360
+ }
3361
+ return { data: records, err: data.err };
3362
+ }
3363
+ catch (err) {
3364
+ return { data: [], err: err };
3365
+ }
3366
+ }
3367
+ async freeData() {
3368
+ try {
3369
+ const resp = await this.executeCmd(COMMANDS.CMD_FREE_DATA, Buffer.alloc(0));
3370
+ return !!resp;
3371
+ }
3372
+ catch (err) {
3373
+ console.error('Error freeing data:', err);
3374
+ throw err;
3375
+ }
3376
+ }
3377
+ async getInfo() {
3378
+ try {
3379
+ const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, Buffer.alloc(0));
3380
+ return {
3381
+ userCounts: data.readUIntLE(24, 4),
3382
+ logCounts: data.readUIntLE(40, 4),
3383
+ logCapacity: data.readUIntLE(72, 4)
3384
+ };
3385
+ }
3386
+ catch (err) {
3387
+ console.error('Error retrieving info:', err);
3388
+ throw err;
3389
+ }
3390
+ }
3391
+ async getTime() {
3392
+ try {
3393
+ const response = await this.executeCmd(COMMANDS.CMD_GET_TIME, Buffer.alloc(0));
3394
+ const timeValue = response.readUInt32LE(8);
3395
+ return timeParser.decode(timeValue);
3396
+ }
3397
+ catch (err) {
3398
+ console.error('Error retrieving time:', err);
3399
+ throw err;
3400
+ }
3401
+ }
3402
+ async setTime(tm) {
3403
+ try {
3404
+ const commandBuffer = Buffer.alloc(32);
3405
+ commandBuffer.writeUInt32LE(timeParser.encode(new Date(tm)), 0);
3406
+ await this.executeCmd(COMMANDS.CMD_SET_TIME, commandBuffer);
3407
+ return true;
3408
+ }
3409
+ catch (err) {
3410
+ console.error('Error setting time:', err);
3411
+ throw err;
3412
+ }
3413
+ }
3414
+ async clearAttendanceLog() {
3415
+ try {
3416
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
3417
+ }
3418
+ catch (err) {
3419
+ console.error('Error clearing attendance log:', err);
3420
+ throw err;
3421
+ }
3422
+ }
3423
+ async clearData() {
3424
+ try {
3425
+ return !!await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
3426
+ }
3427
+ catch (err) {
3428
+ console.error('Error clearing data:', err);
3429
+ throw err;
3430
+ }
3431
+ }
3432
+ async disableDevice() {
3433
+ try {
3434
+ const resp = await this.executeCmd(COMMANDS.CMD_DISABLEDEVICE, REQUEST_DATA.DISABLE_DEVICE);
3435
+ return !!resp;
3436
+ }
3437
+ catch (err) {
3438
+ console.error('Error disabling device:', err);
3439
+ throw err;
3440
+ }
3441
+ }
3442
+ async enableDevice() {
3443
+ try {
3444
+ const resp = await this.executeCmd(COMMANDS.CMD_ENABLEDEVICE, Buffer.alloc(0));
3445
+ return !!resp;
3446
+ }
3447
+ catch (err) {
3448
+ console.error('Error enabling device:', err);
3449
+ throw err;
3450
+ }
3451
+ }
3452
+ async disconnect() {
3453
+ try {
3454
+ await this.executeCmd(COMMANDS.CMD_EXIT, Buffer.alloc(0));
3455
+ }
3456
+ catch (err) {
3457
+ console.error('Error executing disconnect command:', err);
3458
+ }
3459
+ try {
3460
+ await this.closeSocket();
3461
+ }
3462
+ catch (err) {
3463
+ console.error('Error closing the socket:', err);
3464
+ }
3465
+ }
3466
+ async getRealTimeLogs(cb = () => { }) {
3467
+ this.replyId++;
3468
+ const buf = createUDPHeader(COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, REQUEST_DATA.GET_REAL_TIME_EVENT);
3469
+ try {
3470
+ this.socket.send(buf, 0, buf.length, this.port, this.ip, (err) => {
3471
+ if (err) {
3472
+ console.error('Error sending UDP message:', err);
3473
+ return;
3474
+ }
3475
+ console.log('UDP message sent successfully');
3476
+ });
3477
+ }
3478
+ catch (err) {
3479
+ console.error('Error during send operation:', err);
3480
+ return;
3481
+ }
3482
+ const handleMessage = (data) => {
3483
+ if (!checkNotEventUDP(data))
3484
+ return;
3485
+ if (data.length === 18) {
3486
+ cb(decodeRecordRealTimeLog18(data));
3487
+ }
3488
+ };
3489
+ if (this.socket.listenerCount('message') === 0) {
3490
+ this.socket.on('message', handleMessage);
3491
+ }
3492
+ else {
3493
+ console.warn('Multiple message listeners detected. Ensure only one listener is attached.');
3494
+ }
3495
+ }
3496
+ }
3497
+
7
3498
  class Zklib {
8
3499
  set connectionType(value) {
9
3500
  this._connectionType = value;
@@ -31,8 +3522,8 @@ class Zklib {
31
3522
  constructor(ip, port = 4370, timeout = 5000, inport = 10000, comm_key = 0, verbose = false) {
32
3523
  this.ip = ip;
33
3524
  this.comm_key = comm_key;
34
- this.ztcp = new ztcp_1.ZTCP(ip, port, timeout, comm_key, verbose);
35
- this.zudp = new zudp_1.ZUDP(ip, port, timeout, inport);
3525
+ this.ztcp = new ZTCP(ip, port, timeout, comm_key, verbose);
3526
+ this.zudp = new ZUDP(ip, port, timeout, inport);
36
3527
  }
37
3528
  async functionWrapper(tcpCallback, udpCallback, command) {
38
3529
  try {
@@ -42,21 +3533,21 @@ class Zklib {
42
3533
  return await tcpCallback();
43
3534
  }
44
3535
  else {
45
- throw new handler_1.ZkError(new Error("TCP socket isn't connected!"), `[TCP] ${command}`, this.ip);
3536
+ throw new ZkError(new Error("TCP socket isn't connected!"), `[TCP] ${command}`, this.ip);
46
3537
  }
47
3538
  case "udp":
48
3539
  if (this.zudp && this.zudp.socket) {
49
3540
  return await udpCallback();
50
3541
  }
51
3542
  else {
52
- throw new handler_1.ZkError(new Error("UDP socket isn't connected!"), `[UDP] ${command}`, this.ip);
3543
+ throw new ZkError(new Error("UDP socket isn't connected!"), `[UDP] ${command}`, this.ip);
53
3544
  }
54
3545
  default:
55
- throw new handler_1.ZkError(new Error("Unsupported connection type or socket isn't connected!"), "", this.ip);
3546
+ throw new ZkError(new Error("Unsupported connection type or socket isn't connected!"), "", this.ip);
56
3547
  }
57
3548
  }
58
3549
  catch (err) {
59
- throw new handler_1.ZkError(err, `[${this._connectionType?.toUpperCase()}] ${command}`, this.ip);
3550
+ throw new ZkError(err, `[${this._connectionType?.toUpperCase()}] ${command}`, this.ip);
60
3551
  }
61
3552
  }
62
3553
  async createSocket(cbErr, cbClose) {
@@ -69,7 +3560,7 @@ class Zklib {
69
3560
  return true;
70
3561
  }
71
3562
  catch (err) {
72
- throw new handler_1.ZkError(err, "TCP CONNECT", this.ip);
3563
+ throw new ZkError(err, "TCP CONNECT", this.ip);
73
3564
  }
74
3565
  }
75
3566
  else {
@@ -81,7 +3572,7 @@ class Zklib {
81
3572
  return true;
82
3573
  }
83
3574
  catch (err) {
84
- throw new handler_1.ZkError(err, "TCP CONNECT", this.ip);
3575
+ throw new ZkError(err, "TCP CONNECT", this.ip);
85
3576
  }
86
3577
  }
87
3578
  }
@@ -93,8 +3584,8 @@ class Zklib {
93
3584
  catch (disconnectErr) {
94
3585
  console.error("Error disconnecting TCP:", disconnectErr);
95
3586
  }
96
- if (err.code !== handler_1.ERROR_TYPES.ECONNREFUSED) {
97
- throw new handler_1.ZkError(err, "TCP CONNECT", this.ip);
3587
+ if (err.code !== ERROR_TYPES.ECONNREFUSED) {
3588
+ throw new ZkError(err, "TCP CONNECT", this.ip);
98
3589
  }
99
3590
  try {
100
3591
  if (!this.zudp.socket) {
@@ -114,7 +3605,7 @@ class Zklib {
114
3605
  catch (disconnectErr) {
115
3606
  console.error("Error disconnecting UDP:", disconnectErr);
116
3607
  }
117
- throw new handler_1.ZkError(err, "UDP CONNECT", this.ip);
3608
+ throw new ZkError(err, "UDP CONNECT", this.ip);
118
3609
  }
119
3610
  this._connectionType = "udp";
120
3611
  return true;
@@ -339,4 +3830,5 @@ class Zklib {
339
3830
  }, "NETWORK_PARAMS");
340
3831
  }
341
3832
  }
342
- exports.default = Zklib;
3833
+
3834
+ module.exports = Zklib;