zklib-ts 1.0.6 → 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/helper/command.d.ts +11 -5
- package/dist/helper/terminal.d.ts +541 -0
- package/dist/helper/utils.d.ts +42 -5
- package/dist/index.cjs.js +1593 -873
- package/dist/index.d.ts +7 -6
- package/dist/index.es.js +1593 -873
- package/dist/models/Attendance.d.ts +3 -0
- package/dist/models/Finger.d.ts +2 -1
- package/dist/services/options.service.d.ts +49 -0
- package/dist/services/transaction.service.d.ts +1 -1
- package/dist/services/user.service.d.ts +4 -4
- package/dist/ztcp.cjs.js +802 -0
- package/dist/ztcp.d.ts +16 -37
- package/dist/ztcp.js +800 -0
- package/dist/zudp.cjs.js +531 -0
- package/dist/zudp.d.ts +2 -2
- package/dist/zudp.js +510 -0
- package/package.json +87 -69
package/dist/index.es.js
CHANGED
|
@@ -22,7 +22,7 @@ var COMMANDS;
|
|
|
22
22
|
COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
|
|
23
23
|
/** [0x4E, 0x04] Request to begin session using commkey. */
|
|
24
24
|
COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
|
|
25
|
-
/** Disable normal authentication of users. */
|
|
25
|
+
/** [0x3e, 0x00] Disable normal authentication of users. */
|
|
26
26
|
COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
|
|
27
27
|
/** Capture fingerprint picture. */
|
|
28
28
|
COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
|
|
@@ -119,9 +119,9 @@ var COMMANDS;
|
|
|
119
119
|
COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
|
|
120
120
|
/** Upload short message. */
|
|
121
121
|
COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
|
|
122
|
-
/** Start enroll procedure. */
|
|
122
|
+
/** [0x3d, 0x00] Start enroll procedure. */
|
|
123
123
|
COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
|
|
124
|
-
/** Set the machine to authentication state. */
|
|
124
|
+
/** [0x3c, 0x00] Set the machine to authentication state. */
|
|
125
125
|
COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
|
|
126
126
|
/** Query state. */
|
|
127
127
|
COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
|
|
@@ -165,29 +165,36 @@ var COMMANDS;
|
|
|
165
165
|
COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
|
|
166
166
|
/** Write data to Mifare card. */
|
|
167
167
|
COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
|
|
168
|
+
})(COMMANDS || (COMMANDS = {}));
|
|
169
|
+
var RTEvent;
|
|
170
|
+
(function (RTEvent) {
|
|
168
171
|
/** Triggered alarm. */
|
|
169
|
-
|
|
172
|
+
RTEvent[RTEvent["EF_ALARM"] = 512] = "EF_ALARM";
|
|
170
173
|
/** Attendance entry. */
|
|
171
|
-
|
|
174
|
+
RTEvent[RTEvent["EF_ATTLOG"] = 1] = "EF_ATTLOG";
|
|
172
175
|
/** Pressed keyboard key. */
|
|
173
|
-
|
|
176
|
+
RTEvent[RTEvent["EF_BUTTON"] = 16] = "EF_BUTTON";
|
|
174
177
|
/** Upload user data. */
|
|
175
|
-
|
|
178
|
+
RTEvent[RTEvent["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
|
|
176
179
|
/** Enrolled user. */
|
|
177
|
-
|
|
180
|
+
RTEvent[RTEvent["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
|
|
178
181
|
/** Pressed finger. */
|
|
179
|
-
|
|
182
|
+
RTEvent[RTEvent["EF_FINGER"] = 2] = "EF_FINGER";
|
|
180
183
|
/** Fingerprint score in enroll procedure. */
|
|
181
|
-
|
|
184
|
+
RTEvent[RTEvent["EF_FPFTR"] = 256] = "EF_FPFTR";
|
|
182
185
|
/** Restore access control to default. */
|
|
183
|
-
|
|
186
|
+
RTEvent[RTEvent["EF_UNLOCK"] = 32] = "EF_UNLOCK";
|
|
184
187
|
/** Registered user placed finger. */
|
|
185
|
-
|
|
186
|
-
})(
|
|
188
|
+
RTEvent[RTEvent["EF_VERIFY"] = 128] = "EF_VERIFY";
|
|
189
|
+
})(RTEvent || (RTEvent = {}));
|
|
187
190
|
var DISCOVERED_CMD;
|
|
188
191
|
(function (DISCOVERED_CMD) {
|
|
192
|
+
/** [0x7f, 0x13] */
|
|
193
|
+
DISCOVERED_CMD[DISCOVERED_CMD["UNKNOWN"] = 4991] = "UNKNOWN";
|
|
189
194
|
/** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
|
|
190
195
|
DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
|
|
196
|
+
/** [0x87, 0x13] i guess is an error reply code, is returned when attempint to read options ~isABCPinEnable and ~T9FunOn */
|
|
197
|
+
DISCOVERED_CMD[DISCOVERED_CMD["UNKNOWN_OR_NOT_SUPPORTED"] = 4999] = "UNKNOWN_OR_NOT_SUPPORTED";
|
|
191
198
|
})(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
|
|
192
199
|
var Constants;
|
|
193
200
|
(function (Constants) {
|
|
@@ -206,33 +213,57 @@ const REQUEST_DATA = {
|
|
|
206
213
|
};
|
|
207
214
|
|
|
208
215
|
/**
|
|
209
|
-
*
|
|
210
|
-
* @param {number} time
|
|
216
|
+
* Error types for device communication
|
|
211
217
|
*/
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const minute = time % 60;
|
|
216
|
-
time = (time - minute) / 60;
|
|
217
|
-
const hour = time % 24;
|
|
218
|
-
time = (time - hour) / 24;
|
|
219
|
-
const day = time % 31 + 1;
|
|
220
|
-
time = (time - (day - 1)) / 31;
|
|
221
|
-
const month = time % 12;
|
|
222
|
-
time = (time - month) / 12;
|
|
223
|
-
const year = time + 2000;
|
|
224
|
-
return new Date(year, month, day, hour, minute, second);
|
|
225
|
-
};
|
|
218
|
+
const ERROR_TYPES = {
|
|
219
|
+
ECONNRESET: 'ECONNRESET',
|
|
220
|
+
ECONNREFUSED: 'ECONNREFUSED'};
|
|
226
221
|
/**
|
|
227
|
-
*
|
|
228
|
-
* @param {Date} date
|
|
222
|
+
* Custom error class for device communication errors
|
|
229
223
|
*/
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
224
|
+
class ZkError {
|
|
225
|
+
err;
|
|
226
|
+
ip;
|
|
227
|
+
command;
|
|
228
|
+
/**
|
|
229
|
+
* Creates a new ZkError instance
|
|
230
|
+
* @param err The error object
|
|
231
|
+
* @param command The command that caused the error
|
|
232
|
+
* @param ip The IP address of the device
|
|
233
|
+
*/
|
|
234
|
+
constructor(err, command, ip) {
|
|
235
|
+
this.err = err;
|
|
236
|
+
this.ip = ip;
|
|
237
|
+
this.command = command;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Gets a user-friendly error message
|
|
241
|
+
* @returns A formatted error message
|
|
242
|
+
*/
|
|
243
|
+
toast() {
|
|
244
|
+
if (this.err.code === ERROR_TYPES.ECONNRESET) {
|
|
245
|
+
return 'Another device is connecting to the device so the connection is interrupted';
|
|
246
|
+
}
|
|
247
|
+
else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
|
|
248
|
+
return 'IP of the device is refused';
|
|
249
|
+
}
|
|
250
|
+
return this.err.message;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Gets detailed error information
|
|
254
|
+
* @returns An object containing error details
|
|
255
|
+
*/
|
|
256
|
+
getError() {
|
|
257
|
+
return {
|
|
258
|
+
err: {
|
|
259
|
+
message: this.err.message,
|
|
260
|
+
code: this.err.code
|
|
261
|
+
},
|
|
262
|
+
ip: this.ip,
|
|
263
|
+
command: this.command
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
236
267
|
|
|
237
268
|
const parseCurrentTime = () => {
|
|
238
269
|
const currentTime = new Date();
|
|
@@ -274,7 +305,7 @@ class User {
|
|
|
274
305
|
* @param user_id Alternate user ID (default: "")
|
|
275
306
|
* @param card Card number (default: 0)
|
|
276
307
|
*/
|
|
277
|
-
constructor(uid, name, privilege, password =
|
|
308
|
+
constructor(uid, name, privilege, password = '', group_id = '', user_id = '', card = 0) {
|
|
278
309
|
this.uid = uid;
|
|
279
310
|
this.name = name;
|
|
280
311
|
this.privilege = privilege;
|
|
@@ -344,6 +375,9 @@ class User {
|
|
|
344
375
|
}
|
|
345
376
|
}
|
|
346
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Represents an Attendance Records
|
|
380
|
+
*/
|
|
347
381
|
class Attendance {
|
|
348
382
|
/** Internal serial number for the user */
|
|
349
383
|
sn;
|
|
@@ -372,7 +406,7 @@ const parseHexToTime = (hex) => {
|
|
|
372
406
|
date: hex.readUIntLE(2, 1),
|
|
373
407
|
hour: hex.readUIntLE(3, 1),
|
|
374
408
|
minute: hex.readUIntLE(4, 1),
|
|
375
|
-
second: hex.readUIntLE(5, 1)
|
|
409
|
+
second: hex.readUIntLE(5, 1),
|
|
376
410
|
};
|
|
377
411
|
return new Date(2000 + time.year, time.month - 1, time.date, time.hour, time.minute, time.second);
|
|
378
412
|
};
|
|
@@ -406,7 +440,9 @@ const createUDPHeader = (command, sessionId, replyId, data) => {
|
|
|
406
440
|
};
|
|
407
441
|
const createTCPHeader = (command, sessionId, replyId, data) => {
|
|
408
442
|
const buf = createUDPHeader(command, sessionId, replyId, data);
|
|
409
|
-
const prefixBuf = Buffer.from([
|
|
443
|
+
const prefixBuf = Buffer.from([
|
|
444
|
+
0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00,
|
|
445
|
+
]);
|
|
410
446
|
prefixBuf.writeUInt16LE(buf.length, 4);
|
|
411
447
|
return Buffer.concat([prefixBuf, buf]);
|
|
412
448
|
};
|
|
@@ -426,7 +462,7 @@ const parseTimeToDate = (time) => {
|
|
|
426
462
|
time = (time - minute) / 60;
|
|
427
463
|
const hour = time % 24;
|
|
428
464
|
time = (time - hour) / 24;
|
|
429
|
-
const day = time % 31 + 1;
|
|
465
|
+
const day = (time % 31) + 1;
|
|
430
466
|
time = (time - (day - 1)) / 31;
|
|
431
467
|
const month = time % 12;
|
|
432
468
|
time = (time - month) / 12;
|
|
@@ -439,38 +475,34 @@ const decodeUserData28 = (userData) => {
|
|
|
439
475
|
privilege: userData.readUIntLE(2, 1),
|
|
440
476
|
name: userData
|
|
441
477
|
.slice(8, 8 + 8)
|
|
442
|
-
.toString(
|
|
443
|
-
.split(
|
|
444
|
-
.shift() ||
|
|
478
|
+
.toString("ascii")
|
|
479
|
+
.split("\0")
|
|
480
|
+
.shift() || "",
|
|
445
481
|
user_id: userData.readUIntLE(24, 4).toString(),
|
|
446
482
|
};
|
|
447
483
|
};
|
|
448
484
|
const decodeUserData72 = (userData) => {
|
|
449
|
-
return new User(userData.readUIntLE(0, 2), userData
|
|
450
|
-
.slice(11)
|
|
451
|
-
.toString('ascii')
|
|
452
|
-
.split('\0')
|
|
453
|
-
.shift() || '', userData.readUIntLE(2, 1), userData
|
|
485
|
+
return new User(userData.readUIntLE(0, 2), userData.slice(11).toString("ascii").split("\0").shift() || "", userData.readUIntLE(2, 1), userData
|
|
454
486
|
.subarray(3, 3 + 8)
|
|
455
|
-
.toString(
|
|
456
|
-
.split(
|
|
457
|
-
.shift() ||
|
|
487
|
+
.toString("ascii")
|
|
488
|
+
.split("\0")
|
|
489
|
+
.shift() || "", userData.readUIntLE(39, 1), userData
|
|
458
490
|
.slice(48, 48 + 9)
|
|
459
|
-
.toString(
|
|
460
|
-
.split(
|
|
461
|
-
.shift() ||
|
|
491
|
+
.toString("ascii")
|
|
492
|
+
.split("\0")
|
|
493
|
+
.shift() || "", userData.readUIntLE(35, 4));
|
|
462
494
|
};
|
|
463
495
|
const decodeRecordData40 = (recordData) => {
|
|
464
496
|
return new Attendance(recordData.readUIntLE(0, 2), recordData
|
|
465
497
|
.slice(2, 2 + 9)
|
|
466
|
-
.toString(
|
|
467
|
-
.split(
|
|
468
|
-
.shift() ||
|
|
498
|
+
.toString("ascii")
|
|
499
|
+
.split("\0")
|
|
500
|
+
.shift() || "", recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
|
|
469
501
|
};
|
|
470
502
|
const decodeRecordData16 = (recordData) => {
|
|
471
503
|
return {
|
|
472
504
|
user_id: recordData.readUIntLE(0, 2).toString(),
|
|
473
|
-
record_time: parseTimeToDate(recordData.readUInt32LE(4))
|
|
505
|
+
record_time: parseTimeToDate(recordData.readUInt32LE(4)),
|
|
474
506
|
};
|
|
475
507
|
};
|
|
476
508
|
const decodeRecordRealTimeLog18 = (recordData) => {
|
|
@@ -478,22 +510,12 @@ const decodeRecordRealTimeLog18 = (recordData) => {
|
|
|
478
510
|
const record_time = parseHexToTime(recordData.subarray(12, 18));
|
|
479
511
|
return { user_id, record_time };
|
|
480
512
|
};
|
|
481
|
-
const decodeRecordRealTimeLog52 = (recordData) => {
|
|
482
|
-
const payload = removeTcpHeader(recordData);
|
|
483
|
-
const recvData = payload.subarray(8);
|
|
484
|
-
const user_id = recvData.slice(0, 9)
|
|
485
|
-
.toString('ascii')
|
|
486
|
-
.split('\0')
|
|
487
|
-
.shift() || '';
|
|
488
|
-
const record_time = parseHexToTime(recvData.subarray(26, 26 + 6));
|
|
489
|
-
return { user_id, record_time };
|
|
490
|
-
};
|
|
491
513
|
const decodeUDPHeader = (header) => {
|
|
492
514
|
return {
|
|
493
515
|
commandId: header.readUIntLE(0, 2),
|
|
494
516
|
checkSum: header.readUIntLE(2, 2),
|
|
495
517
|
sessionId: header.readUIntLE(4, 2),
|
|
496
|
-
replyId: header.readUIntLE(6, 2)
|
|
518
|
+
replyId: header.readUIntLE(6, 2),
|
|
497
519
|
};
|
|
498
520
|
};
|
|
499
521
|
const decodeTCPHeader = (header) => {
|
|
@@ -504,7 +526,7 @@ const decodeTCPHeader = (header) => {
|
|
|
504
526
|
checkSum: recvData.readUIntLE(2, 2),
|
|
505
527
|
sessionId: recvData.readUIntLE(4, 2),
|
|
506
528
|
replyId: recvData.readUIntLE(6, 2),
|
|
507
|
-
payloadSize
|
|
529
|
+
payloadSize,
|
|
508
530
|
};
|
|
509
531
|
};
|
|
510
532
|
const exportErrorMessage = (commandValue) => {
|
|
@@ -514,17 +536,18 @@ const exportErrorMessage = (commandValue) => {
|
|
|
514
536
|
return key.toString();
|
|
515
537
|
}
|
|
516
538
|
}
|
|
517
|
-
return
|
|
539
|
+
return "AN UNKNOWN ERROR";
|
|
518
540
|
};
|
|
541
|
+
const isRTEvent = (event) => Object.values(RTEvent).includes(event);
|
|
519
542
|
const checkNotEventTCP = (data) => {
|
|
520
543
|
try {
|
|
521
544
|
const cleanedData = removeTcpHeader(data);
|
|
522
545
|
const commandId = cleanedData.readUIntLE(0, 2);
|
|
523
546
|
const event = cleanedData.readUIntLE(4, 2);
|
|
524
|
-
return event
|
|
547
|
+
return isRTEvent(event) && commandId === COMMANDS.CMD_REG_EVENT;
|
|
525
548
|
}
|
|
526
549
|
catch (err) {
|
|
527
|
-
log(`[228] : ${err.toString()} ,${data.toString(
|
|
550
|
+
log(`[228] : ${err.toString()} ,${data.toString("hex")} `);
|
|
528
551
|
return false;
|
|
529
552
|
}
|
|
530
553
|
};
|
|
@@ -532,6 +555,42 @@ const checkNotEventUDP = (data) => {
|
|
|
532
555
|
const { commandId } = decodeUDPHeader(data.subarray(0, 8));
|
|
533
556
|
return commandId === COMMANDS.CMD_REG_EVENT;
|
|
534
557
|
};
|
|
558
|
+
const decodeRTEvent = (data) => {
|
|
559
|
+
const header = decodeTCPHeader(data);
|
|
560
|
+
console.log(header);
|
|
561
|
+
const event = header.sessionId;
|
|
562
|
+
const recvData = data.subarray(16);
|
|
563
|
+
const build = {
|
|
564
|
+
alarm4bytes: function (data) {
|
|
565
|
+
return {
|
|
566
|
+
alarm_type: data.readUintLE(0, 4),
|
|
567
|
+
alarm_cause: data.readUintLE(4, 2),
|
|
568
|
+
user_uid: data.readUintLE(6, 2),
|
|
569
|
+
match_type: data.readUintLE(8, 4),
|
|
570
|
+
};
|
|
571
|
+
},
|
|
572
|
+
enrolledFinger: function (data) {
|
|
573
|
+
return {
|
|
574
|
+
result: data.readUIntLE(0, 2),
|
|
575
|
+
size: data.readUintLE(2, 2),
|
|
576
|
+
pin: data.subarray(4, 4 + 9).toString("ascii"),
|
|
577
|
+
fid: data.readUIntLE(1, 13),
|
|
578
|
+
};
|
|
579
|
+
},
|
|
580
|
+
fingerScore: function (data) {
|
|
581
|
+
return { score: data.readUintLE(0, 1) };
|
|
582
|
+
},
|
|
583
|
+
};
|
|
584
|
+
switch (event) {
|
|
585
|
+
case RTEvent.EF_FINGER:
|
|
586
|
+
return { event };
|
|
587
|
+
case RTEvent.EF_FPFTR:
|
|
588
|
+
return { event, payload: build.fingerScore(recvData) };
|
|
589
|
+
default:
|
|
590
|
+
console.log("utils linea 284");
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
535
594
|
const makeKey = (key, sessionId) => {
|
|
536
595
|
let k = 0;
|
|
537
596
|
for (let i = 0; i < 32; i++) {
|
|
@@ -544,30 +603,30 @@ const makeKey = (key, sessionId) => {
|
|
|
544
603
|
}
|
|
545
604
|
k += sessionId;
|
|
546
605
|
let hex = k.toString(16).padStart(8, "0");
|
|
547
|
-
|
|
606
|
+
const response = new Uint8Array(4);
|
|
548
607
|
let index = 3;
|
|
549
608
|
while (hex.length > 0) {
|
|
550
609
|
response[index] = parseInt(hex.substring(0, 2), 16);
|
|
551
610
|
index--;
|
|
552
611
|
hex = hex.substring(2);
|
|
553
612
|
}
|
|
554
|
-
response[0] ^=
|
|
555
|
-
response[1] ^=
|
|
556
|
-
response[2] ^=
|
|
557
|
-
response[3] ^=
|
|
613
|
+
response[0] ^= "Z".charCodeAt(0);
|
|
614
|
+
response[1] ^= "K".charCodeAt(0);
|
|
615
|
+
response[2] ^= "S".charCodeAt(0);
|
|
616
|
+
response[3] ^= "O".charCodeAt(0);
|
|
558
617
|
let finalKey = response[0] +
|
|
559
618
|
(response[1] << 8) +
|
|
560
619
|
(response[2] << 16) +
|
|
561
620
|
(response[3] << 24);
|
|
562
|
-
|
|
621
|
+
const swp = finalKey >>> 16;
|
|
563
622
|
finalKey = (finalKey << 16) | swp;
|
|
564
623
|
return finalKey >>> 0;
|
|
565
624
|
};
|
|
566
625
|
const authKey = (comKey, sessionId) => {
|
|
567
|
-
|
|
568
|
-
|
|
626
|
+
const k = makeKey(comKey, sessionId) >>> 0;
|
|
627
|
+
const rand = Math.floor(Math.random() * 256);
|
|
569
628
|
let hex = k.toString(16).padStart(8, "0");
|
|
570
|
-
|
|
629
|
+
const response = new Uint8Array(4);
|
|
571
630
|
let index = 3;
|
|
572
631
|
while (index >= 0) {
|
|
573
632
|
response[index] = parseInt(hex.substring(0, 2), 16);
|
|
@@ -580,59 +639,17 @@ const authKey = (comKey, sessionId) => {
|
|
|
580
639
|
response[3] ^= rand;
|
|
581
640
|
return Array.from(response);
|
|
582
641
|
};
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
err;
|
|
595
|
-
ip;
|
|
596
|
-
command;
|
|
597
|
-
/**
|
|
598
|
-
* Creates a new ZkError instance
|
|
599
|
-
* @param err The error object
|
|
600
|
-
* @param command The command that caused the error
|
|
601
|
-
* @param ip The IP address of the device
|
|
602
|
-
*/
|
|
603
|
-
constructor(err, command, ip) {
|
|
604
|
-
this.err = err;
|
|
605
|
-
this.ip = ip;
|
|
606
|
-
this.command = command;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Gets a user-friendly error message
|
|
610
|
-
* @returns A formatted error message
|
|
611
|
-
*/
|
|
612
|
-
toast() {
|
|
613
|
-
if (this.err.code === ERROR_TYPES.ECONNRESET) {
|
|
614
|
-
return 'Another device is connecting to the device so the connection is interrupted';
|
|
615
|
-
}
|
|
616
|
-
else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
|
|
617
|
-
return 'IP of the device is refused';
|
|
618
|
-
}
|
|
619
|
-
return this.err.message;
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Gets detailed error information
|
|
623
|
-
* @returns An object containing error details
|
|
624
|
-
*/
|
|
625
|
-
getError() {
|
|
626
|
-
return {
|
|
627
|
-
err: {
|
|
628
|
-
message: this.err.message,
|
|
629
|
-
code: this.err.code
|
|
630
|
-
},
|
|
631
|
-
ip: this.ip,
|
|
632
|
-
command: this.command
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
}
|
|
642
|
+
/** 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 */
|
|
643
|
+
const splitTcpPackets = (data) => {
|
|
644
|
+
const packets = [];
|
|
645
|
+
let unProcessed = data;
|
|
646
|
+
while (unProcessed.length > 0) {
|
|
647
|
+
const headers = decodeTCPHeader(unProcessed);
|
|
648
|
+
packets.push(unProcessed.subarray(0, headers.payloadSize + 8));
|
|
649
|
+
unProcessed = unProcessed.subarray(headers.payloadSize + 8);
|
|
650
|
+
}
|
|
651
|
+
return packets;
|
|
652
|
+
};
|
|
636
653
|
|
|
637
654
|
/**
|
|
638
655
|
* Represents a fingerprint template with associated metadata
|
|
@@ -640,6 +657,7 @@ class ZkError {
|
|
|
640
657
|
class Finger {
|
|
641
658
|
uid;
|
|
642
659
|
fid;
|
|
660
|
+
/** Flag indicating 0 = invalid | 1 = valid | 3 = duress. if is not initilizaed, default is 1 = valid */
|
|
643
661
|
valid;
|
|
644
662
|
template;
|
|
645
663
|
size;
|
|
@@ -651,10 +669,10 @@ class Finger {
|
|
|
651
669
|
* @param valid Flag indicating 0 = invalid | 1 = valid | 3 = duress
|
|
652
670
|
* @param template Fingerprint template data buffer
|
|
653
671
|
*/
|
|
654
|
-
constructor(uid, fid,
|
|
672
|
+
constructor(uid, fid, template, valid) {
|
|
655
673
|
this.uid = Number(uid);
|
|
656
674
|
this.fid = Number(fid);
|
|
657
|
-
this.valid = Number(valid);
|
|
675
|
+
this.valid = valid ? Number(valid) : 1;
|
|
658
676
|
this.template = template;
|
|
659
677
|
this.size = template.length;
|
|
660
678
|
// Create mark showing first and last 8 bytes as hex
|
|
@@ -735,75 +753,75 @@ class UserService {
|
|
|
735
753
|
if (this._zkTcp.socket) {
|
|
736
754
|
await this._zkTcp.freeData();
|
|
737
755
|
}
|
|
738
|
-
|
|
739
|
-
const
|
|
756
|
+
await this._zkTcp.disableDevice();
|
|
757
|
+
const users = await new Promise((resolve, reject) => {
|
|
758
|
+
// Request user data
|
|
759
|
+
this._zkTcp
|
|
760
|
+
.readWithBuffer(REQUEST_DATA.GET_USERS)
|
|
761
|
+
.then(async (data) => {
|
|
762
|
+
// Ensure data.data is a valid buffer
|
|
763
|
+
if (!data.data || !(data.data instanceof Buffer)) {
|
|
764
|
+
reject(new Error("Invalid data received"));
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
|
|
768
|
+
const users = [];
|
|
769
|
+
// Constants for user data processing
|
|
770
|
+
const USER_PACKET_SIZE = 72;
|
|
771
|
+
// Process each user packet
|
|
772
|
+
while (userData.length >= USER_PACKET_SIZE) {
|
|
773
|
+
// Decode user data and add to the users array
|
|
774
|
+
const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
|
|
775
|
+
users.push(user);
|
|
776
|
+
this._users.set(user.user_id, user);
|
|
777
|
+
userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
|
|
778
|
+
}
|
|
779
|
+
resolve(users);
|
|
780
|
+
})
|
|
781
|
+
.catch(reject);
|
|
782
|
+
});
|
|
740
783
|
// Free buffer data after receiving the data
|
|
741
|
-
if (this._zkTcp.socket) {
|
|
784
|
+
if (this._zkTcp.socket && users) {
|
|
785
|
+
await this._zkTcp.enableDevice();
|
|
742
786
|
await this._zkTcp.freeData();
|
|
743
787
|
}
|
|
744
|
-
// Constants for user data processing
|
|
745
|
-
const USER_PACKET_SIZE = 72;
|
|
746
|
-
// Ensure data.data is a valid buffer
|
|
747
|
-
if (!data.data || !(data.data instanceof Buffer)) {
|
|
748
|
-
throw new Error('Invalid data received');
|
|
749
|
-
}
|
|
750
|
-
let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
|
|
751
|
-
const users = [];
|
|
752
|
-
// Process each user packet
|
|
753
|
-
while (userData.length >= USER_PACKET_SIZE) {
|
|
754
|
-
// Decode user data and add to the users array
|
|
755
|
-
const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
|
|
756
|
-
users.push(user);
|
|
757
|
-
this._users.set(user.user_id, user);
|
|
758
|
-
userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
|
|
759
|
-
}
|
|
760
788
|
// Return the list of users
|
|
761
789
|
return { data: users };
|
|
762
790
|
}
|
|
763
791
|
catch (err) {
|
|
764
792
|
// Log the error for debugging
|
|
765
|
-
console.error(
|
|
793
|
+
console.error("Error getting users:", err);
|
|
766
794
|
// Re-throw the error to be handled by the caller
|
|
767
795
|
throw err;
|
|
768
796
|
}
|
|
769
797
|
}
|
|
770
798
|
async setUser(user_id, name, password, role = 0, cardno = 0) {
|
|
771
|
-
let user;
|
|
772
|
-
try {
|
|
773
|
-
user = await this.getUserByUserId(user_id);
|
|
774
|
-
}
|
|
775
|
-
catch (err) {
|
|
776
|
-
if (err.message.includes("user_id not exists")) {
|
|
777
|
-
user.uid = Math.max(...Array.from(this._users.values()).map(usr => usr.uid)) + 1;
|
|
778
|
-
this._users.set(user_id, user);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
799
|
try {
|
|
782
800
|
// Validate input parameters
|
|
783
801
|
if (user_id.length > 9 ||
|
|
784
802
|
name.length > 24 ||
|
|
785
803
|
password.length > 8 ||
|
|
786
|
-
typeof role !==
|
|
804
|
+
typeof role !== "number" ||
|
|
787
805
|
cardno.toString().length > 10) {
|
|
788
|
-
throw new Error(
|
|
806
|
+
throw new Error("Invalid input parameters");
|
|
789
807
|
}
|
|
790
808
|
// Allocate and initialize the buffer
|
|
791
809
|
const commandBuffer = Buffer.alloc(72);
|
|
792
810
|
// Fill the buffer with user data
|
|
793
|
-
commandBuffer.writeUInt16LE(
|
|
811
|
+
commandBuffer.writeUInt16LE(0, 0); // uid will be set in the device
|
|
794
812
|
commandBuffer.writeUInt16LE(role, 2);
|
|
795
|
-
commandBuffer.write(password.padEnd(8,
|
|
796
|
-
commandBuffer.write(name.padEnd(24,
|
|
813
|
+
commandBuffer.write(password.padEnd(8, "\0"), 3, 8); // Ensure password is 8 bytes
|
|
814
|
+
commandBuffer.write(name.padEnd(24, "\0"), 11, 24); // Ensure name is 24 bytes
|
|
797
815
|
commandBuffer.writeUInt16LE(cardno, 35);
|
|
798
816
|
commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
|
|
799
|
-
commandBuffer.write(user_id.padEnd(9,
|
|
817
|
+
commandBuffer.write(user_id.padEnd(9, "\0"), 48, 9); // Ensure userid is 9 bytes
|
|
800
818
|
// Send the command and return the result
|
|
801
819
|
const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
|
|
802
820
|
return !!created;
|
|
803
821
|
}
|
|
804
822
|
catch (err) {
|
|
805
823
|
// Log error details for debugging
|
|
806
|
-
console.error(
|
|
824
|
+
console.error("Error setting user:", err);
|
|
807
825
|
// Re-throw error for upstream handling
|
|
808
826
|
throw err;
|
|
809
827
|
}
|
|
@@ -821,22 +839,24 @@ class UserService {
|
|
|
821
839
|
}
|
|
822
840
|
catch (err) {
|
|
823
841
|
// Log error details for debugging
|
|
824
|
-
console.error(
|
|
842
|
+
console.error("Error deleting user:", err);
|
|
825
843
|
// Re-throw error for upstream handling
|
|
826
844
|
throw err;
|
|
827
845
|
}
|
|
828
846
|
}
|
|
829
|
-
async getTemplates(
|
|
830
|
-
|
|
847
|
+
async getTemplates(cb) {
|
|
848
|
+
const templates = [];
|
|
831
849
|
try {
|
|
850
|
+
await this._zkTcp.disableDevice();
|
|
832
851
|
if (this._zkTcp.socket) {
|
|
833
852
|
await this._zkTcp.freeData();
|
|
834
853
|
}
|
|
835
|
-
|
|
854
|
+
if (!this._zkTcp.fp_count) {
|
|
855
|
+
await this._zkTcp.getSizes();
|
|
856
|
+
}
|
|
836
857
|
if (this._zkTcp.fp_count == 0)
|
|
837
858
|
return { data: [] };
|
|
838
|
-
await this._zkTcp.
|
|
839
|
-
const resp = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
|
|
859
|
+
const resp = (await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES));
|
|
840
860
|
let templateData = resp.data.subarray(4);
|
|
841
861
|
let totalSize = resp.data.readUIntLE(0, 4);
|
|
842
862
|
while (totalSize) {
|
|
@@ -847,15 +867,17 @@ class UserService {
|
|
|
847
867
|
const valid = buf.readUIntLE(5, 1);
|
|
848
868
|
// Force-copy bytes so we don't retain the entire big backing buffer
|
|
849
869
|
const tplBytes = Buffer.from(templateData.subarray(6, size));
|
|
850
|
-
templates.push(new Finger(uid, fid,
|
|
870
|
+
templates.push(new Finger(uid, fid, tplBytes, valid));
|
|
851
871
|
templateData = templateData.subarray(size);
|
|
852
872
|
totalSize -= size;
|
|
853
873
|
}
|
|
874
|
+
if (cb)
|
|
875
|
+
cb(templates);
|
|
854
876
|
return { data: templates };
|
|
855
877
|
}
|
|
856
878
|
catch (err) {
|
|
857
879
|
this._zkTcp.verbose && console.log("Error getting templates", err);
|
|
858
|
-
|
|
880
|
+
throw err;
|
|
859
881
|
}
|
|
860
882
|
finally {
|
|
861
883
|
await this._zkTcp.freeData();
|
|
@@ -864,9 +886,9 @@ class UserService {
|
|
|
864
886
|
}
|
|
865
887
|
async DownloadFp(user_id, fid) {
|
|
866
888
|
try {
|
|
867
|
-
const user = await this.getUserByUserId(user_id);
|
|
889
|
+
const user = (await this.getUserByUserId(String(user_id)));
|
|
868
890
|
if (0 > fid || fid > 9)
|
|
869
|
-
throw new Error(
|
|
891
|
+
throw new Error("fid must be between 0 and 9");
|
|
870
892
|
// Allocate and initialize the buffer
|
|
871
893
|
const data = Buffer.alloc(3);
|
|
872
894
|
// Fill the buffer with user data
|
|
@@ -876,30 +898,30 @@ class UserService {
|
|
|
876
898
|
const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
|
|
877
899
|
let fingerSize = 0;
|
|
878
900
|
let fingerTemplate = Buffer.from([]);
|
|
879
|
-
|
|
901
|
+
const template = await new Promise((resolve, reject) => {
|
|
880
902
|
let timeout;
|
|
881
903
|
const cleanup = () => {
|
|
882
904
|
if (this._zkTcp.socket) {
|
|
883
|
-
this._zkTcp.socket.removeListener(
|
|
905
|
+
this._zkTcp.socket.removeListener("data", receiveData);
|
|
884
906
|
}
|
|
885
907
|
if (timeout)
|
|
886
908
|
clearTimeout(timeout);
|
|
887
909
|
};
|
|
888
910
|
let timer = () => setTimeout(() => {
|
|
889
911
|
cleanup();
|
|
890
|
-
reject(new Error(
|
|
912
|
+
reject(new Error("Time Out, Could not retrieve data"));
|
|
891
913
|
}, this._zkTcp.timeout);
|
|
892
914
|
const receiveData = (data) => {
|
|
893
915
|
timeout = timer();
|
|
894
916
|
if (data.length === 0)
|
|
895
917
|
return;
|
|
896
918
|
try {
|
|
897
|
-
if (data.length
|
|
919
|
+
if (data.length === 0)
|
|
898
920
|
return;
|
|
899
921
|
const headers = decodeTCPHeader(data);
|
|
900
922
|
switch (headers.commandId) {
|
|
901
923
|
case DISCOVERED_CMD.FID_NOT_FOUND:
|
|
902
|
-
throw new Error(
|
|
924
|
+
throw new Error("Could not retrieve data. maybe finger id not exists?");
|
|
903
925
|
case COMMANDS.CMD_PREPARE_DATA:
|
|
904
926
|
fingerSize = data.readUIntLE(16, 2);
|
|
905
927
|
break;
|
|
@@ -907,7 +929,10 @@ class UserService {
|
|
|
907
929
|
// A single 'data' event might contain multiple TCP packets combined by the OS
|
|
908
930
|
// in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
|
|
909
931
|
// so It's important to split data received for remove CMD_ACK_OK headers
|
|
910
|
-
fingerTemplate = Buffer.concat([
|
|
932
|
+
fingerTemplate = Buffer.concat([
|
|
933
|
+
fingerTemplate,
|
|
934
|
+
data.subarray(16, fingerSize + 10),
|
|
935
|
+
]);
|
|
911
936
|
// @ts-ignore
|
|
912
937
|
resolve(fingerTemplate);
|
|
913
938
|
break;
|
|
@@ -934,8 +959,8 @@ class UserService {
|
|
|
934
959
|
}
|
|
935
960
|
};
|
|
936
961
|
if (this._zkTcp.socket) {
|
|
937
|
-
this._zkTcp.socket.on(
|
|
938
|
-
this._zkTcp.socket.write(packet, (err) => {
|
|
962
|
+
this._zkTcp.socket.on("data", receiveData);
|
|
963
|
+
this._zkTcp.socket.write(packet, undefined, (err) => {
|
|
939
964
|
if (err) {
|
|
940
965
|
cleanup();
|
|
941
966
|
reject(err);
|
|
@@ -943,15 +968,16 @@ class UserService {
|
|
|
943
968
|
});
|
|
944
969
|
}
|
|
945
970
|
else {
|
|
946
|
-
reject(new Error(
|
|
971
|
+
reject(new Error("Socket not initialized"));
|
|
947
972
|
}
|
|
948
973
|
});
|
|
974
|
+
return new Finger(user.uid, fid, template);
|
|
949
975
|
}
|
|
950
976
|
catch (err) {
|
|
951
977
|
throw err;
|
|
952
978
|
}
|
|
953
979
|
finally {
|
|
954
|
-
await this._zkTcp.
|
|
980
|
+
await this._zkTcp.freeData();
|
|
955
981
|
}
|
|
956
982
|
}
|
|
957
983
|
/**
|
|
@@ -993,15 +1019,15 @@ class UserService {
|
|
|
993
1019
|
head.writeUInt32LE(table.length, 4);
|
|
994
1020
|
head.writeUInt32LE(fpack.length, 8);
|
|
995
1021
|
const packet = Buffer.concat([head, upack, table, fpack]);
|
|
996
|
-
|
|
1022
|
+
await this._zkTcp.sendWithBuffer(packet);
|
|
997
1023
|
const command = 110;
|
|
998
1024
|
const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
|
|
999
1025
|
commandString.writeUInt32LE(12, 0);
|
|
1000
1026
|
commandString.writeUInt16LE(0, 4);
|
|
1001
1027
|
commandString.writeUInt16LE(8, 6);
|
|
1002
|
-
|
|
1028
|
+
await this._zkTcp.executeCmd(command, commandString);
|
|
1003
1029
|
if (this._zkTcp.verbose)
|
|
1004
|
-
console.log("finally bulk save user templates: \n",
|
|
1030
|
+
console.log("finally bulk save user templates: \n", "templates saved successfully");
|
|
1005
1031
|
}
|
|
1006
1032
|
catch (error) {
|
|
1007
1033
|
throw error;
|
|
@@ -1013,9 +1039,7 @@ class UserService {
|
|
|
1013
1039
|
}
|
|
1014
1040
|
async deleteFinger(user_id, fid) {
|
|
1015
1041
|
try {
|
|
1016
|
-
|
|
1017
|
-
throw new Error("user_id not exists");
|
|
1018
|
-
const user = await this.getUserByUserId(user_id);
|
|
1042
|
+
const user = (await this.getUserByUserId(user_id));
|
|
1019
1043
|
const buf = Buffer.alloc(4);
|
|
1020
1044
|
buf.writeUInt16LE(user_id ? user.uid : 0, 0);
|
|
1021
1045
|
buf.writeUint16LE(fid ? fid : 0, 2);
|
|
@@ -1030,101 +1054,75 @@ class UserService {
|
|
|
1030
1054
|
}
|
|
1031
1055
|
}
|
|
1032
1056
|
async enrollInfo(user_id, tempId) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
const sendAckOk = async () => {
|
|
1042
|
-
try {
|
|
1043
|
-
const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this._zkTcp.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
|
|
1044
|
-
this._zkTcp.socket.write(buf);
|
|
1045
|
-
}
|
|
1046
|
-
catch (e) {
|
|
1047
|
-
throw new ZkError(e, COMMANDS.CMD_ACK_OK, this._zkTcp.ip);
|
|
1048
|
-
}
|
|
1057
|
+
try {
|
|
1058
|
+
let timer;
|
|
1059
|
+
const setTimeoutTimer = (cb) => {
|
|
1060
|
+
if (timer)
|
|
1061
|
+
clearTimeout(timer);
|
|
1062
|
+
return setTimeout(() => {
|
|
1063
|
+
cb(new Error("[ENROLL_INFO] time out"));
|
|
1064
|
+
}, 1000 * 20);
|
|
1049
1065
|
};
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1066
|
+
const cleanUp = () => {
|
|
1067
|
+
if (timer)
|
|
1068
|
+
clearTimeout(timer);
|
|
1069
|
+
};
|
|
1070
|
+
return await new Promise((resolve, reject) => {
|
|
1071
|
+
const handleRtEvent = (rtEvent) => {
|
|
1072
|
+
switch (rtEvent.event) {
|
|
1073
|
+
case RTEvent.EF_FPFTR:
|
|
1074
|
+
timer = setTimeoutTimer(reject);
|
|
1075
|
+
break;
|
|
1076
|
+
case RTEvent.EF_FINGER:
|
|
1077
|
+
console.log(rtEvent);
|
|
1078
|
+
break;
|
|
1079
|
+
case RTEvent.EF_ENROLLFINGER:
|
|
1080
|
+
cleanUp();
|
|
1081
|
+
resolve(rtEvent);
|
|
1082
|
+
break;
|
|
1083
|
+
default:
|
|
1084
|
+
console.log(rtEvent);
|
|
1085
|
+
break;
|
|
1068
1086
|
}
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
if (this._zkTcp.verbose)
|
|
1083
|
-
console.log("posible timeout o reg Fallido");
|
|
1084
|
-
break;
|
|
1087
|
+
};
|
|
1088
|
+
// Start enroll process
|
|
1089
|
+
this.getUserByUserId(user_id)
|
|
1090
|
+
.then(async (user) => {
|
|
1091
|
+
/** First check if Finger index already exists, and if so, it must be deleted */
|
|
1092
|
+
try {
|
|
1093
|
+
const exists = await this.DownloadFp(user_id, tempId);
|
|
1094
|
+
console.log("exists: ", exists);
|
|
1095
|
+
if (exists) {
|
|
1096
|
+
this._zkTcp.verbose &&
|
|
1097
|
+
console.debug("Deleting Finger index before start enroll");
|
|
1098
|
+
await this.deleteFinger(user_id, tempId);
|
|
1099
|
+
}
|
|
1085
1100
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
console.
|
|
1089
|
-
attempts--;
|
|
1101
|
+
catch (e) {
|
|
1102
|
+
this._zkTcp.verbose &&
|
|
1103
|
+
console.debug("Finger index is empty, skipping delete");
|
|
1090
1104
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
if (res === 6 || res === 4) {
|
|
1107
|
-
if (this._zkTcp.verbose)
|
|
1108
|
-
console.log("posible timeout");
|
|
1109
|
-
}
|
|
1110
|
-
if (res === 0) {
|
|
1111
|
-
const size = padded.readUInt16LE(10);
|
|
1112
|
-
const pos = padded.readUInt16LE(12);
|
|
1113
|
-
if (this._zkTcp.verbose)
|
|
1114
|
-
console.log(`enroll ok ${size} ${pos}`);
|
|
1115
|
-
done = true;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
//this.__sock.setTimeout(this.__timeout);
|
|
1119
|
-
await this._zkTcp.regEvent(0); // TODO: test
|
|
1120
|
-
return done;
|
|
1105
|
+
const userBuf = Buffer.alloc(24);
|
|
1106
|
+
userBuf.write(user_id, 0, 24, "ascii");
|
|
1107
|
+
const commandString = Buffer.concat([
|
|
1108
|
+
userBuf,
|
|
1109
|
+
Buffer.from([tempId, 1]),
|
|
1110
|
+
]);
|
|
1111
|
+
await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString); // #5
|
|
1112
|
+
this._zkTcp.timeout = 60000; // 60 seconds timeout
|
|
1113
|
+
await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, ""); // #17
|
|
1114
|
+
timer = setTimeoutTimer(reject);
|
|
1115
|
+
void (await this._zkTcp.getRealTimeLogs(handleRtEvent)); // #9
|
|
1116
|
+
})
|
|
1117
|
+
.catch(reject);
|
|
1118
|
+
});
|
|
1121
1119
|
}
|
|
1122
1120
|
catch (error) {
|
|
1123
1121
|
throw error;
|
|
1124
1122
|
}
|
|
1125
1123
|
finally {
|
|
1126
|
-
await this._zkTcp.cancelCapture();
|
|
1127
|
-
await this.verify(user_id);
|
|
1124
|
+
//await this._zkTcp.cancelCapture();
|
|
1125
|
+
//await this.verify(user_id);
|
|
1128
1126
|
}
|
|
1129
1127
|
}
|
|
1130
1128
|
async verify(user_id) {
|
|
@@ -1151,35 +1149,38 @@ class UserService {
|
|
|
1151
1149
|
*/
|
|
1152
1150
|
async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
|
|
1153
1151
|
try {
|
|
1154
|
-
const check_ACK_OK = (buf) => {
|
|
1155
|
-
let resp_cmd = initPacket.readUInt16LE(0);
|
|
1156
|
-
if (resp_cmd === COMMANDS.CMD_ACK_OK)
|
|
1157
|
-
return true;
|
|
1158
|
-
else
|
|
1159
|
-
throw new Error(`received unexpected command: ${resp_cmd}`);
|
|
1160
|
-
};
|
|
1161
1152
|
const user = this._users.get(user_id);
|
|
1162
1153
|
await this._zkTcp.disableDevice();
|
|
1163
1154
|
const prep_struct = Buffer.alloc(4);
|
|
1164
|
-
const fingerBuffer = Buffer.from(fingerTemplate,
|
|
1155
|
+
const fingerBuffer = Buffer.from(fingerTemplate, "base64");
|
|
1165
1156
|
const fp_size = fingerBuffer.length;
|
|
1166
1157
|
prep_struct.writeUInt16LE(fp_size, 0);
|
|
1167
1158
|
const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
|
|
1168
|
-
|
|
1159
|
+
if (initPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
|
|
1160
|
+
throw new Error(`received unexpected command: ${initPacket.readUInt16LE(0)}`);
|
|
1161
|
+
}
|
|
1169
1162
|
const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const
|
|
1163
|
+
if (fpPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
|
|
1164
|
+
throw new Error(`received unexpected command: ${fpPacket.readUInt16LE(0)}`);
|
|
1165
|
+
}
|
|
1166
|
+
const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, "");
|
|
1167
|
+
if (cheksumPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
|
|
1168
|
+
throw new Error(`received unexpected command: ${cheksumPacket.readUInt16LE(0)}`);
|
|
1169
|
+
}
|
|
1174
1170
|
const tmp_wreq = Buffer.alloc(6);
|
|
1175
1171
|
tmp_wreq.writeUInt16LE(user.uid, 0);
|
|
1176
1172
|
tmp_wreq.writeUIntLE(fid, 2, 1);
|
|
1177
1173
|
tmp_wreq.writeUIntLE(fp_valid, 3, 1);
|
|
1178
1174
|
tmp_wreq.writeUInt16LE(fp_size, 4);
|
|
1179
1175
|
const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1176
|
+
if (tmp_wreqPacket.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
|
|
1177
|
+
throw new Error(`received unexpected command: ${tmp_wreqPacket.readUInt16LE(0)}`);
|
|
1178
|
+
}
|
|
1179
|
+
const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, "");
|
|
1180
|
+
if (freeData.readUInt16LE(0) !== COMMANDS.CMD_ACK_OK) {
|
|
1181
|
+
throw new Error(`received unexpected command: ${freeData.readUInt16LE(0)}`);
|
|
1182
|
+
}
|
|
1183
|
+
return true;
|
|
1183
1184
|
}
|
|
1184
1185
|
catch (err) {
|
|
1185
1186
|
throw err;
|
|
@@ -1251,40 +1252,1016 @@ class TransactionService {
|
|
|
1251
1252
|
}
|
|
1252
1253
|
}
|
|
1253
1254
|
|
|
1254
|
-
|
|
1255
|
+
/**
|
|
1256
|
+
*
|
|
1257
|
+
* @param {number} time
|
|
1258
|
+
*/
|
|
1259
|
+
const decode = time => {
|
|
1260
|
+
const second = time % 60;
|
|
1261
|
+
time = (time - second) / 60;
|
|
1262
|
+
const minute = time % 60;
|
|
1263
|
+
time = (time - minute) / 60;
|
|
1264
|
+
const hour = time % 24;
|
|
1265
|
+
time = (time - hour) / 24;
|
|
1266
|
+
const day = time % 31 + 1;
|
|
1267
|
+
time = (time - (day - 1)) / 31;
|
|
1268
|
+
const month = time % 12;
|
|
1269
|
+
time = (time - month) / 12;
|
|
1270
|
+
const year = time + 2000;
|
|
1271
|
+
return new Date(year, month, day, hour, minute, second);
|
|
1272
|
+
};
|
|
1273
|
+
/**
|
|
1274
|
+
*
|
|
1275
|
+
* @param {Date} date
|
|
1276
|
+
*/
|
|
1277
|
+
const encode = date => {
|
|
1278
|
+
return (((date.getFullYear() % 100) * 12 * 31 + date.getMonth() * 31 + date.getDate() - 1) * (24 * 60 * 60) +
|
|
1279
|
+
(date.getHours() * 60 + date.getMinutes()) * 60 +
|
|
1280
|
+
date.getSeconds());
|
|
1281
|
+
};
|
|
1282
|
+
var timeParser = { encode, decode };
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* SDK Parameters Enum
|
|
1286
|
+
*
|
|
1287
|
+
* Generated from SDK parameter table with the following structure:
|
|
1288
|
+
* - Keys: Parameter names in uppercase without ~ prefix
|
|
1289
|
+
* - Values: Original parameter names
|
|
1290
|
+
* - JSDoc: Includes description, permissions, and notes
|
|
1291
|
+
*/
|
|
1292
|
+
var SdkParameter;
|
|
1293
|
+
(function (SdkParameter) {
|
|
1255
1294
|
/**
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1295
|
+
* Device ID.
|
|
1296
|
+
* Permissions: RW
|
|
1297
|
+
* Notes: Value ranges from 1 to 254.
|
|
1298
|
+
*/
|
|
1299
|
+
SdkParameter["DEVICE_ID"] = "DeviceID";
|
|
1300
|
+
/**
|
|
1301
|
+
* Language.
|
|
1302
|
+
* Permissions: RW
|
|
1303
|
+
* Notes: For english it is 97.
|
|
1304
|
+
*/
|
|
1305
|
+
SdkParameter["NEW_LNG"] = "NewLng";
|
|
1306
|
+
/**
|
|
1307
|
+
* The machine will enter standby state or power off, after this time elapses.
|
|
1308
|
+
* Permissions: RW
|
|
1309
|
+
* Notes: Given in minutes.
|
|
1310
|
+
*/
|
|
1311
|
+
SdkParameter["IDLE_MINUTE"] = "IdleMinute";
|
|
1312
|
+
/**
|
|
1313
|
+
* Lock control time.
|
|
1314
|
+
* Permissions: RW
|
|
1315
|
+
* Notes: Given in seconds.
|
|
1316
|
+
*/
|
|
1317
|
+
SdkParameter["LOCK_ON"] = "LockOn";
|
|
1318
|
+
/**
|
|
1319
|
+
* Attendance record quantity alarm.
|
|
1320
|
+
* Permissions: RW
|
|
1321
|
+
* Notes:
|
|
1322
|
+
*/
|
|
1323
|
+
SdkParameter["ALARM_ATT_LOG"] = "AlarmAttLog";
|
|
1324
|
+
/**
|
|
1325
|
+
* Operation record quantity alarm.
|
|
1326
|
+
* Permissions: RW
|
|
1327
|
+
* Notes:
|
|
1328
|
+
*/
|
|
1329
|
+
SdkParameter["ALARM_OP_LOG"] = "AlarmOpLog";
|
|
1330
|
+
/**
|
|
1331
|
+
* Minimun time to record the same attendance state.
|
|
1332
|
+
* Permissions: RW
|
|
1333
|
+
* Notes: Units are unknown.
|
|
1334
|
+
*/
|
|
1335
|
+
SdkParameter["ALARM_RE_REC"] = "AlarmReRec";
|
|
1336
|
+
/**
|
|
1337
|
+
* Baud rate for RS232/485.
|
|
1338
|
+
* Permissions: RW
|
|
1339
|
+
* Notes: Valid values are 1200, 2400, 4800, 9600, 19200, 38400 57600, 115200.
|
|
1340
|
+
*/
|
|
1341
|
+
SdkParameter["RS232_BAUD_RATE"] = "RS232BaudRate";
|
|
1342
|
+
/**
|
|
1343
|
+
* Enable flag for network functions.
|
|
1344
|
+
* Permissions: RW
|
|
1345
|
+
* Notes:
|
|
1346
|
+
*/
|
|
1347
|
+
SdkParameter["NETWORK_ON"] = "NetworkOn";
|
|
1348
|
+
/**
|
|
1349
|
+
* Enable flag for RS232.
|
|
1350
|
+
* Permissions: RW
|
|
1351
|
+
* Notes:
|
|
1352
|
+
*/
|
|
1353
|
+
SdkParameter["RS232_ON"] = "RS232On";
|
|
1354
|
+
/**
|
|
1355
|
+
* Enable flag for RS485.
|
|
1356
|
+
* Permissions: RW
|
|
1357
|
+
* Notes:
|
|
1358
|
+
*/
|
|
1359
|
+
SdkParameter["RS485_ON"] = "RS485On";
|
|
1360
|
+
/**
|
|
1361
|
+
* Enable announcements(voice).
|
|
1362
|
+
* Permissions: RW
|
|
1363
|
+
* Notes:
|
|
1364
|
+
*/
|
|
1365
|
+
SdkParameter["VOICE_ON"] = "VoiceOn";
|
|
1366
|
+
/**
|
|
1367
|
+
* Perform high-speed comparison.
|
|
1368
|
+
* Permissions: RW
|
|
1369
|
+
* Notes: Value codification is unknown.
|
|
1370
|
+
*/
|
|
1371
|
+
SdkParameter["MSPEED"] = "MSpeed";
|
|
1372
|
+
/**
|
|
1373
|
+
* Idle mode.
|
|
1374
|
+
* Permissions: RW
|
|
1375
|
+
* Notes: 87 indicates shutdown and 88 indicates hibernation.
|
|
1376
|
+
*/
|
|
1377
|
+
SdkParameter["IDLE_POWER"] = "IdlePower";
|
|
1378
|
+
/**
|
|
1379
|
+
* Automatic shutdown time.
|
|
1380
|
+
* Permissions: RW
|
|
1381
|
+
* Notes: Value 255 indicates the machine to not shutdown automatically.
|
|
1382
|
+
*/
|
|
1383
|
+
SdkParameter["AUTO_POWER_OFF"] = "AutoPowerOff";
|
|
1384
|
+
/**
|
|
1385
|
+
* Automatic startup time.
|
|
1386
|
+
* Permissions: RW
|
|
1387
|
+
* Notes: Value 255 indicates the machine to not startup automatically.
|
|
1388
|
+
*/
|
|
1389
|
+
SdkParameter["AUTO_POWER_ON"] = "AutoPowerOn";
|
|
1390
|
+
/**
|
|
1391
|
+
* Automatic hibernation time.
|
|
1392
|
+
* Permissions: RW
|
|
1393
|
+
* Notes: Value 255 indicates the machine to not suspend automatically.
|
|
1394
|
+
*/
|
|
1395
|
+
SdkParameter["AUTO_POWER_SUSPEND"] = "AutoPowerSuspend";
|
|
1396
|
+
/**
|
|
1397
|
+
* Alarm 1 time.
|
|
1398
|
+
* Permissions: RW
|
|
1399
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1400
|
+
*/
|
|
1401
|
+
SdkParameter["AUTO_ALARM1"] = "AutoAlarm1";
|
|
1402
|
+
/**
|
|
1403
|
+
* 1:N comparison threshold.
|
|
1404
|
+
* Permissions: RW
|
|
1405
|
+
* Notes: Integer.
|
|
1406
|
+
*/
|
|
1407
|
+
SdkParameter["MTHRESHOLD"] = "MThreshold";
|
|
1408
|
+
/**
|
|
1409
|
+
* Registration threshold.
|
|
1410
|
+
* Permissions: RW
|
|
1411
|
+
* Notes: Integer.
|
|
1412
|
+
*/
|
|
1413
|
+
SdkParameter["ETHRESHOLD"] = "EThreshold";
|
|
1414
|
+
/**
|
|
1415
|
+
* 1:1 comparison threshold.
|
|
1416
|
+
* Permissions: RW
|
|
1417
|
+
* Notes: Integer.
|
|
1418
|
+
*/
|
|
1419
|
+
SdkParameter["VTHRESHOLD"] = "VThreshold";
|
|
1420
|
+
/**
|
|
1421
|
+
* Display matching score during verification.
|
|
1422
|
+
* Permissions: RW
|
|
1423
|
+
* Notes: Bool.
|
|
1424
|
+
*/
|
|
1425
|
+
SdkParameter["SHOW_SCORE"] = "ShowScore";
|
|
1426
|
+
/**
|
|
1427
|
+
* Number of people that may unlock the door at the same time.
|
|
1428
|
+
* Permissions: RW
|
|
1429
|
+
* Notes: Integer.
|
|
1430
|
+
*/
|
|
1431
|
+
SdkParameter["UNLOCK_PERSON"] = "UnlockPerson";
|
|
1432
|
+
/**
|
|
1433
|
+
* Verify only the card number.
|
|
1434
|
+
* Permissions: RW
|
|
1435
|
+
* Notes: Bool.
|
|
1436
|
+
*/
|
|
1437
|
+
SdkParameter["ONLY_PIN_CARD"] = "OnlyPINCard";
|
|
1438
|
+
/**
|
|
1439
|
+
* Network speed.
|
|
1440
|
+
* Permissions: RW
|
|
1441
|
+
* Notes: Value correspondence: 1=100M-H, 4=10M-F, 5=100M-F, 8=Auto, others=10M-H.
|
|
1442
|
+
*/
|
|
1443
|
+
SdkParameter["HI_SPEED_NET"] = "HiSpeedNet";
|
|
1444
|
+
/**
|
|
1445
|
+
* Accept only registered cards.
|
|
1446
|
+
* Permissions: RW
|
|
1447
|
+
* Notes: Bool.
|
|
1448
|
+
*/
|
|
1449
|
+
SdkParameter["MUST_ENROLL"] = "MustEnroll";
|
|
1450
|
+
/**
|
|
1451
|
+
* Timeout to return to the initial state.
|
|
1452
|
+
* Permissions: RW
|
|
1453
|
+
* Notes: Given in seconds.
|
|
1454
|
+
*/
|
|
1455
|
+
SdkParameter["TO_STATE"] = "TOState";
|
|
1456
|
+
/**
|
|
1457
|
+
* Timeout to return to the initial state if there are no inputs after entering PIN.
|
|
1458
|
+
* Permissions: RW
|
|
1459
|
+
* Notes: Given in seconds.
|
|
1460
|
+
*/
|
|
1461
|
+
SdkParameter["TO_STATE_PIN"] = "TOState";
|
|
1462
|
+
/**
|
|
1463
|
+
* Timeout to return to the initial state if there are no inputs after entering menu.
|
|
1464
|
+
* Permissions: RW
|
|
1465
|
+
* Notes: Given in seconds.
|
|
1466
|
+
*/
|
|
1467
|
+
SdkParameter["TO_MENU"] = "TOMenu";
|
|
1468
|
+
/**
|
|
1469
|
+
* Time format.
|
|
1470
|
+
* Permissions: NA
|
|
1471
|
+
* Notes: Value codification is unknown.
|
|
1472
|
+
*/
|
|
1473
|
+
SdkParameter["DT_FMT"] = "DtFmt";
|
|
1474
|
+
/**
|
|
1475
|
+
* Flag for mandatory 1:1 comparison.
|
|
1476
|
+
* Permissions: RW
|
|
1477
|
+
* Notes: Bool.
|
|
1478
|
+
*/
|
|
1479
|
+
SdkParameter["MUST_1TO1"] = "Must1To1";
|
|
1480
|
+
/**
|
|
1481
|
+
* Alarm 2 time.
|
|
1482
|
+
* Permissions: RW
|
|
1483
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1484
|
+
*/
|
|
1485
|
+
SdkParameter["AUTO_ALARM2"] = "AutoAlarm2";
|
|
1486
|
+
/**
|
|
1487
|
+
* Alarm 3 time.
|
|
1488
|
+
* Permissions: RW
|
|
1489
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1490
|
+
*/
|
|
1491
|
+
SdkParameter["AUTO_ALARM3"] = "AutoAlarm3";
|
|
1492
|
+
/**
|
|
1493
|
+
* Alarm 4 time.
|
|
1494
|
+
* Permissions: RW
|
|
1495
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1496
|
+
*/
|
|
1497
|
+
SdkParameter["AUTO_ALARM4"] = "AutoAlarm4";
|
|
1498
|
+
/**
|
|
1499
|
+
* Alarm 5 time.
|
|
1500
|
+
* Permissions: RW
|
|
1501
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1502
|
+
*/
|
|
1503
|
+
SdkParameter["AUTO_ALARM5"] = "AutoAlarm5";
|
|
1504
|
+
/**
|
|
1505
|
+
* Alarm 6 time.
|
|
1506
|
+
* Permissions: RW
|
|
1507
|
+
* Notes: Value 65535 disables the alarm(t).
|
|
1508
|
+
*/
|
|
1509
|
+
SdkParameter["AUTO_ALARM6"] = "AutoAlarm6";
|
|
1510
|
+
/**
|
|
1511
|
+
* Automatic status changing times.
|
|
1512
|
+
* Permissions: ?
|
|
1513
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1514
|
+
*/
|
|
1515
|
+
SdkParameter["AS1"] = "AS1";
|
|
1516
|
+
/**
|
|
1517
|
+
* Automatic status changing times.
|
|
1518
|
+
* Permissions: ?
|
|
1519
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1520
|
+
*/
|
|
1521
|
+
SdkParameter["AS2"] = "AS2";
|
|
1522
|
+
/**
|
|
1523
|
+
* Automatic status changing times.
|
|
1524
|
+
* Permissions: ?
|
|
1525
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1526
|
+
*/
|
|
1527
|
+
SdkParameter["AS3"] = "AS3";
|
|
1528
|
+
/**
|
|
1529
|
+
* Automatic status changing times.
|
|
1530
|
+
* Permissions: ?
|
|
1531
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1532
|
+
*/
|
|
1533
|
+
SdkParameter["AS4"] = "AS4";
|
|
1534
|
+
/**
|
|
1535
|
+
* Automatic status changing times.
|
|
1536
|
+
* Permissions: ?
|
|
1537
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1538
|
+
*/
|
|
1539
|
+
SdkParameter["AS5"] = "AS5";
|
|
1540
|
+
/**
|
|
1541
|
+
* Automatic status changing times.
|
|
1542
|
+
* Permissions: ?
|
|
1543
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1544
|
+
*/
|
|
1545
|
+
SdkParameter["AS6"] = "AS6";
|
|
1546
|
+
/**
|
|
1547
|
+
* Automatic status changing times.
|
|
1548
|
+
* Permissions: ?
|
|
1549
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1550
|
+
*/
|
|
1551
|
+
SdkParameter["AS7"] = "AS7";
|
|
1552
|
+
/**
|
|
1553
|
+
* Automatic status changing times.
|
|
1554
|
+
* Permissions: ?
|
|
1555
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1556
|
+
*/
|
|
1557
|
+
SdkParameter["AS8"] = "AS8";
|
|
1558
|
+
/**
|
|
1559
|
+
* Automatic status changing times.
|
|
1560
|
+
* Permissions: ?
|
|
1561
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1562
|
+
*/
|
|
1563
|
+
SdkParameter["AS9"] = "AS9";
|
|
1564
|
+
/**
|
|
1565
|
+
* Automatic status changing times.
|
|
1566
|
+
* Permissions: ?
|
|
1567
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1568
|
+
*/
|
|
1569
|
+
SdkParameter["AS10"] = "AS10";
|
|
1570
|
+
/**
|
|
1571
|
+
* Automatic status changing times.
|
|
1572
|
+
* Permissions: ?
|
|
1573
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1574
|
+
*/
|
|
1575
|
+
SdkParameter["AS11"] = "AS11";
|
|
1576
|
+
/**
|
|
1577
|
+
* Automatic status changing times.
|
|
1578
|
+
* Permissions: ?
|
|
1579
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1580
|
+
*/
|
|
1581
|
+
SdkParameter["AS12"] = "AS12";
|
|
1582
|
+
/**
|
|
1583
|
+
* Automatic status changing times.
|
|
1584
|
+
* Permissions: ?
|
|
1585
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1586
|
+
*/
|
|
1587
|
+
SdkParameter["AS13"] = "AS13";
|
|
1588
|
+
/**
|
|
1589
|
+
* Automatic status changing times.
|
|
1590
|
+
* Permissions: ?
|
|
1591
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1592
|
+
*/
|
|
1593
|
+
SdkParameter["AS14"] = "AS14";
|
|
1594
|
+
/**
|
|
1595
|
+
* Automatic status changing times.
|
|
1596
|
+
* Permissions: ?
|
|
1597
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1598
|
+
*/
|
|
1599
|
+
SdkParameter["AS15"] = "AS15";
|
|
1600
|
+
/**
|
|
1601
|
+
* Automatic status changing times.
|
|
1602
|
+
* Permissions: ?
|
|
1603
|
+
* Notes: -1 value indicates that the status will not change automatically.
|
|
1604
|
+
*/
|
|
1605
|
+
SdkParameter["AS16"] = "AS16";
|
|
1606
|
+
/**
|
|
1607
|
+
* Wiegand failure ID.
|
|
1608
|
+
* Permissions: ?
|
|
1609
|
+
* Notes:
|
|
1610
|
+
*/
|
|
1611
|
+
SdkParameter["WG_FAILED_ID"] = "WGFailedID";
|
|
1612
|
+
/**
|
|
1613
|
+
* Wiegand duress ID.
|
|
1614
|
+
* Permissions: ?
|
|
1615
|
+
* Notes:
|
|
1616
|
+
*/
|
|
1617
|
+
SdkParameter["WG_DURESS_ID"] = "WGDuressID";
|
|
1618
|
+
/**
|
|
1619
|
+
* Wiegand zone bit.
|
|
1620
|
+
* Permissions: ?
|
|
1621
|
+
* Notes:
|
|
1622
|
+
*/
|
|
1623
|
+
SdkParameter["WG_SITE_CODE"] = "WGSiteCode";
|
|
1624
|
+
/**
|
|
1625
|
+
* Pulse width of Wiegand outputs.
|
|
1626
|
+
* Permissions: ?
|
|
1627
|
+
* Notes:
|
|
1628
|
+
*/
|
|
1629
|
+
SdkParameter["WG_PULSE_WIDTH"] = "WGPulseWidth";
|
|
1630
|
+
/**
|
|
1631
|
+
* Pulse interval for Wiegand outputs.
|
|
1632
|
+
* Permissions: ?
|
|
1633
|
+
* Notes:
|
|
1634
|
+
*/
|
|
1635
|
+
SdkParameter["WG_PULSE_INTERVAL"] = "WGPulseInterval";
|
|
1636
|
+
/**
|
|
1637
|
+
* ID of the start sector on the Mifare card where fingerprints are stored.
|
|
1638
|
+
* Permissions: ?
|
|
1639
|
+
* Notes:
|
|
1640
|
+
*/
|
|
1641
|
+
SdkParameter["RF_START"] = "RFSStart";
|
|
1642
|
+
/**
|
|
1643
|
+
* Total number of sectors on the Mifare card where fingerprints are stored.
|
|
1644
|
+
* Permissions: ?
|
|
1645
|
+
* Notes:
|
|
1646
|
+
*/
|
|
1647
|
+
SdkParameter["RF_LEN"] = "RFSLen";
|
|
1648
|
+
/**
|
|
1649
|
+
* Number of fingerprints stored on the Mifare card.
|
|
1650
|
+
* Permissions: ?
|
|
1651
|
+
* Notes:
|
|
1652
|
+
*/
|
|
1653
|
+
SdkParameter["RF_FPC"] = "RFFPC";
|
|
1654
|
+
/**
|
|
1655
|
+
* Forbidden.
|
|
1656
|
+
* Permissions: NA
|
|
1657
|
+
* Notes:
|
|
1658
|
+
*/
|
|
1659
|
+
SdkParameter["FORBIDDEN"] = "Forbidden";
|
|
1660
|
+
/**
|
|
1661
|
+
* Wheter to display the attendance status.
|
|
1662
|
+
* Permissions: RW
|
|
1663
|
+
* Notes:
|
|
1664
|
+
*/
|
|
1665
|
+
SdkParameter["SHOW_STATE"] = "~ShowState";
|
|
1666
|
+
/**
|
|
1667
|
+
* TCP Port.
|
|
1668
|
+
* Permissions: ?
|
|
1669
|
+
* Notes:
|
|
1670
|
+
*/
|
|
1671
|
+
SdkParameter["TCP_PORT"] = "TCPPort";
|
|
1672
|
+
/**
|
|
1673
|
+
* UDP Port.
|
|
1674
|
+
* Permissions: ?
|
|
1675
|
+
* Notes:
|
|
1676
|
+
*/
|
|
1677
|
+
SdkParameter["UDP_PORT"] = "UDPPort";
|
|
1678
|
+
/**
|
|
1679
|
+
* Fingerprint algorithm version.
|
|
1680
|
+
* Permissions: R
|
|
1681
|
+
* Notes:
|
|
1682
|
+
*/
|
|
1683
|
+
SdkParameter["ZK_FP_VERSION"] = "~ZKFPVersion";
|
|
1684
|
+
/**
|
|
1685
|
+
* Face algorithm version.
|
|
1686
|
+
* Permissions: R
|
|
1687
|
+
* Notes:
|
|
1688
|
+
*/
|
|
1689
|
+
SdkParameter["ZK_FACE_VERSION"] = "~ZKFaceVersion";
|
|
1690
|
+
/**
|
|
1691
|
+
* Finger vein version.
|
|
1692
|
+
* Permissions: R
|
|
1693
|
+
* Notes:
|
|
1694
|
+
*/
|
|
1695
|
+
SdkParameter["ZK_FV_VERSION"] = "~ZKFVVersion";
|
|
1696
|
+
/**
|
|
1697
|
+
* Face function.
|
|
1698
|
+
* Permissions: R
|
|
1699
|
+
* Notes:
|
|
1700
|
+
*/
|
|
1701
|
+
SdkParameter["FACE_FUN_ON"] = "~FaceFunOn";
|
|
1702
|
+
/**
|
|
1703
|
+
* User id max length.
|
|
1704
|
+
* Permissions: R
|
|
1705
|
+
* Notes:
|
|
1706
|
+
*/
|
|
1707
|
+
SdkParameter["PIN2_WIDTH"] = "~PIN2Width";
|
|
1708
|
+
/**
|
|
1709
|
+
* Does the user id support chars.
|
|
1710
|
+
* Permissions: R
|
|
1711
|
+
* Notes:
|
|
1712
|
+
*/
|
|
1713
|
+
SdkParameter["IS_SUPPORT_ABC_PIN"] = "IsSupportABCPin";
|
|
1714
|
+
/**
|
|
1715
|
+
* ?
|
|
1716
|
+
* Permissions: ?
|
|
1717
|
+
* Notes:
|
|
1718
|
+
*/
|
|
1719
|
+
SdkParameter["IME_FUN_ON"] = "IMEFunOn";
|
|
1720
|
+
/**
|
|
1721
|
+
* ?
|
|
1722
|
+
* Permissions: ?
|
|
1723
|
+
* Notes:
|
|
1724
|
+
*/
|
|
1725
|
+
SdkParameter["IS_SUPPORT_ALARM_EXT"] = "IsSupportAlarmExt";
|
|
1726
|
+
/**
|
|
1727
|
+
* ?
|
|
1728
|
+
* Permissions: ?
|
|
1729
|
+
* Notes:
|
|
1730
|
+
*/
|
|
1731
|
+
SdkParameter["DCTZ"] = "~DCTZ";
|
|
1732
|
+
/**
|
|
1733
|
+
* ?
|
|
1734
|
+
* Permissions: ?
|
|
1735
|
+
* Notes:
|
|
1736
|
+
*/
|
|
1737
|
+
SdkParameter["DOTZ"] = "~DOTZ";
|
|
1738
|
+
/**
|
|
1739
|
+
* -
|
|
1740
|
+
* Permissions: ?
|
|
1741
|
+
* Notes:
|
|
1742
|
+
*/
|
|
1743
|
+
SdkParameter["EXTEND_OP_LOG"] = "ExtendOPLog";
|
|
1744
|
+
/**
|
|
1745
|
+
* Bool
|
|
1746
|
+
* Permissions: RW
|
|
1747
|
+
* Notes:
|
|
1748
|
+
*/
|
|
1749
|
+
SdkParameter["WORK_CODE"] = "WorkCode";
|
|
1750
|
+
/**
|
|
1751
|
+
* Integer
|
|
1752
|
+
* Permissions: RW
|
|
1753
|
+
* Notes:
|
|
1754
|
+
*/
|
|
1755
|
+
SdkParameter["LANGUAGE"] = "Language";
|
|
1756
|
+
/**
|
|
1757
|
+
* -
|
|
1758
|
+
* Permissions: ?
|
|
1759
|
+
* Notes:
|
|
1760
|
+
*/
|
|
1761
|
+
SdkParameter["BIOMETRIC_TYPE"] = "BiometricType";
|
|
1762
|
+
/**
|
|
1763
|
+
* Bool
|
|
1764
|
+
* Permissions: RW
|
|
1765
|
+
* Notes:
|
|
1766
|
+
*/
|
|
1767
|
+
SdkParameter["FINGER_FUN_ON"] = "FingerFunOn";
|
|
1768
|
+
/**
|
|
1769
|
+
* Bool
|
|
1770
|
+
* Permissions: ?
|
|
1771
|
+
* Notes:
|
|
1772
|
+
*/
|
|
1773
|
+
SdkParameter["IS_ONLY_RF_MACHINE"] = "~IsOnlyRFMachine";
|
|
1774
|
+
/**
|
|
1775
|
+
* Vendor name
|
|
1776
|
+
* Permissions: ?
|
|
1777
|
+
* Notes:
|
|
1778
|
+
*/
|
|
1779
|
+
SdkParameter["OEM_VENDOR"] = "~OEMVendor";
|
|
1780
|
+
/**
|
|
1781
|
+
* Device name
|
|
1782
|
+
* Permissions: ?
|
|
1783
|
+
* Notes:
|
|
1784
|
+
*/
|
|
1785
|
+
SdkParameter["DEVICE_NAME"] = "~DeviceName";
|
|
1786
|
+
/**
|
|
1787
|
+
* MAC address
|
|
1788
|
+
* Permissions: ?
|
|
1789
|
+
* Notes:
|
|
1790
|
+
*/
|
|
1791
|
+
SdkParameter["MAC"] = "MAC";
|
|
1792
|
+
/**
|
|
1793
|
+
* Serial number
|
|
1794
|
+
* Permissions: ?
|
|
1795
|
+
* Notes:
|
|
1796
|
+
*/
|
|
1797
|
+
SdkParameter["SERIAL_NUMBER"] = "~SerialNumber";
|
|
1798
|
+
/**
|
|
1799
|
+
* Date string
|
|
1800
|
+
* Permissions: ?
|
|
1801
|
+
* Notes:
|
|
1802
|
+
*/
|
|
1803
|
+
SdkParameter["PRODUCT_TIME"] = "~ProductTime";
|
|
1804
|
+
/**
|
|
1805
|
+
* Bool
|
|
1806
|
+
* Permissions: ?
|
|
1807
|
+
* Notes:
|
|
1808
|
+
*/
|
|
1809
|
+
SdkParameter["IS_ABC_PIN_ENABLE"] = "~IsABCPinEnable";
|
|
1810
|
+
/**
|
|
1811
|
+
* Bool
|
|
1812
|
+
* Permissions: ?
|
|
1813
|
+
* Notes:
|
|
1814
|
+
*/
|
|
1815
|
+
SdkParameter["T9_FUN_ON"] = "~T9FunOn";
|
|
1816
|
+
/**
|
|
1817
|
+
* Date Time
|
|
1818
|
+
* Permissions: RW
|
|
1819
|
+
*/
|
|
1820
|
+
SdkParameter["DATETIME"] = "DateTime";
|
|
1821
|
+
/**
|
|
1822
|
+
*
|
|
1823
|
+
*/
|
|
1824
|
+
SdkParameter["OS"] = "~OS";
|
|
1825
|
+
})(SdkParameter || (SdkParameter = {}));
|
|
1826
|
+
|
|
1827
|
+
class OptionsService {
|
|
1828
|
+
_zkTcp;
|
|
1829
|
+
constructor(zkTcp) {
|
|
1830
|
+
this._zkTcp = zkTcp;
|
|
1831
|
+
}
|
|
1832
|
+
/** Ask if the device doesn't support alphanumeric symbols for user id values. */
|
|
1833
|
+
async isAbcPinEnable() {
|
|
1834
|
+
const keyword = "~IsABCPinEnable";
|
|
1835
|
+
try {
|
|
1836
|
+
// Execute the command to get the PIN information
|
|
1837
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1838
|
+
// Extract and format the PIN information from the response data
|
|
1839
|
+
return data
|
|
1840
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1841
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
1842
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1843
|
+
.replace(/\u0000/g, ""); // Remove null characters 0x00
|
|
1844
|
+
}
|
|
1845
|
+
catch (err) {
|
|
1846
|
+
// Log the error for debugging
|
|
1847
|
+
console.error("Error getting PIN:", err);
|
|
1848
|
+
// Re-throw the error to be handled by the caller
|
|
1849
|
+
throw err;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
/** Ask if the device doesn't support alphanumeric symbols for user id values. */
|
|
1853
|
+
async isT9FunOn() {
|
|
1854
|
+
const keyword = "~T9FunOn";
|
|
1855
|
+
try {
|
|
1856
|
+
// Execute the command to get the PIN information
|
|
1857
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1858
|
+
// Extract and format the PIN information from the response data
|
|
1859
|
+
return data
|
|
1860
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1861
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
1862
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1863
|
+
.replace(/\u0000/g, ""); // Remove null characters 0x00
|
|
1864
|
+
}
|
|
1865
|
+
catch (err) {
|
|
1866
|
+
// Log the error for debugging
|
|
1867
|
+
console.error("Error getting PIN:", err);
|
|
1868
|
+
// Re-throw the error to be handled by the caller
|
|
1869
|
+
throw err;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
async getDeviceId() {
|
|
1873
|
+
const keyword = SdkParameter.DEVICE_ID;
|
|
1874
|
+
try {
|
|
1875
|
+
let result;
|
|
1876
|
+
let retry = true;
|
|
1877
|
+
while (retry) {
|
|
1878
|
+
// Execute the command to get the device name
|
|
1879
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1880
|
+
// Extract and format the device name from the response data
|
|
1881
|
+
result = data
|
|
1882
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1883
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
1884
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1885
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
1886
|
+
retry = result.includes("=");
|
|
1887
|
+
}
|
|
1888
|
+
return result;
|
|
1889
|
+
}
|
|
1890
|
+
catch (err) {
|
|
1891
|
+
// Log the error for debugging
|
|
1892
|
+
console.error("Error getting vendor:", err);
|
|
1893
|
+
// Re-throw the error for higher-level handling
|
|
1894
|
+
throw err;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Change Device ID
|
|
1899
|
+
* @param id a number between 1 and 254
|
|
1900
|
+
*/
|
|
1901
|
+
async setDeviceId(id) {
|
|
1902
|
+
if (id < 1 || id > 254)
|
|
1903
|
+
throw new Error("Device ID must be a number between 1 and 254");
|
|
1904
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_WRQ, `DeviceID=${id}\x00`);
|
|
1905
|
+
return data.readUInt16LE(0) === COMMANDS.CMD_ACK_OK;
|
|
1906
|
+
}
|
|
1907
|
+
async getVendor() {
|
|
1908
|
+
const keyword = "~OEMVendor";
|
|
1909
|
+
try {
|
|
1910
|
+
let result;
|
|
1911
|
+
let retry = true;
|
|
1912
|
+
while (retry) {
|
|
1913
|
+
// Execute the command to get the device name
|
|
1914
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1915
|
+
// Extract and format the device name from the response data
|
|
1916
|
+
result = data
|
|
1917
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1918
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
1919
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1920
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
1921
|
+
retry = result.includes("=");
|
|
1922
|
+
}
|
|
1923
|
+
return result;
|
|
1924
|
+
}
|
|
1925
|
+
catch (err) {
|
|
1926
|
+
// Log the error for debugging
|
|
1927
|
+
console.error("Error getting vendor:", err);
|
|
1928
|
+
// Re-throw the error for higher-level handling
|
|
1929
|
+
throw err;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
async getProductTime() {
|
|
1933
|
+
const keyword = "~ProductTime";
|
|
1934
|
+
try {
|
|
1935
|
+
// Execute the command to get serial number
|
|
1936
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1937
|
+
// Extract and format the serial number from the response data
|
|
1938
|
+
const ProductTime = data
|
|
1939
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1940
|
+
.toString("ascii") // Convert buffer to string
|
|
1941
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1942
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
1943
|
+
return new Date(ProductTime);
|
|
1944
|
+
}
|
|
1945
|
+
catch (err) {
|
|
1946
|
+
// Log the error for debugging
|
|
1947
|
+
console.error("Error getting Product Time:", err);
|
|
1948
|
+
// Re-throw the error for higher-level handling
|
|
1949
|
+
throw err;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
async getMacAddress() {
|
|
1953
|
+
const keyword = "MAC";
|
|
1954
|
+
try {
|
|
1955
|
+
// Execute the command to get serial number
|
|
1956
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1957
|
+
// Extract and format the serial number from the response data
|
|
1958
|
+
const macAddr = data
|
|
1959
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
1960
|
+
.toString("ascii") // Convert buffer to string
|
|
1961
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1962
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
1963
|
+
return macAddr;
|
|
1964
|
+
}
|
|
1965
|
+
catch (err) {
|
|
1966
|
+
// Log the error for debugging
|
|
1967
|
+
console.error("Error getting MAC address:", err);
|
|
1968
|
+
// Re-throw the error for higher-level handling
|
|
1969
|
+
throw err;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
async getNetworkParams() {
|
|
1973
|
+
try {
|
|
1974
|
+
const params = {
|
|
1975
|
+
IPAddress: "",
|
|
1976
|
+
NetMask: "",
|
|
1977
|
+
GATEIPAddress: "",
|
|
1978
|
+
TCPPort: "",
|
|
1979
|
+
};
|
|
1980
|
+
const keywords = Object.keys(params);
|
|
1981
|
+
for await (const keyword of keywords) {
|
|
1982
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1983
|
+
params[keyword] = data
|
|
1984
|
+
.slice(8)
|
|
1985
|
+
.toString("utf-8")
|
|
1986
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
1987
|
+
.replace(/\u0000/g, "")
|
|
1988
|
+
.replace("=", "."); // Replace equal simbol to dot, due to sometimes there are parsing errors
|
|
1989
|
+
}
|
|
1990
|
+
return params;
|
|
1991
|
+
}
|
|
1992
|
+
catch (err) {
|
|
1993
|
+
console.error("Error getting Network Params: ", err);
|
|
1994
|
+
throw err;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
async getSerialNumber() {
|
|
1998
|
+
const keyword = "~SerialNumber";
|
|
1999
|
+
let serialNumber = "";
|
|
2000
|
+
let count = 10;
|
|
2001
|
+
try {
|
|
2002
|
+
// Execute the command to get serial number
|
|
2003
|
+
/**
|
|
2004
|
+
* @dev implemented a counter and a while loop because sometimes serial number parses wrong for some reason
|
|
2005
|
+
* */
|
|
2006
|
+
while (serialNumber.length !== 13 && count > 0) {
|
|
2007
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2008
|
+
// Extract and format the serial number from the response data
|
|
2009
|
+
const SN = data
|
|
2010
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2011
|
+
.toString("utf-8") // Convert buffer to string
|
|
2012
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2013
|
+
.replace("=", "") // Remove sometines last number is a character equal to = or unknow character
|
|
2014
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
2015
|
+
count--;
|
|
2016
|
+
serialNumber = SN;
|
|
2017
|
+
}
|
|
2018
|
+
return serialNumber;
|
|
2019
|
+
}
|
|
2020
|
+
catch (err) {
|
|
2021
|
+
// Log the error for debugging
|
|
2022
|
+
console.error("Error getting serial number:", err);
|
|
2023
|
+
// Re-throw the error for higher-level handling
|
|
2024
|
+
throw err;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* get Zkteko Template version
|
|
2029
|
+
* @returns number
|
|
2030
|
+
*/
|
|
2031
|
+
async getDeviceVersion() {
|
|
2032
|
+
const keyword = "~ZKFPVersion";
|
|
2033
|
+
try {
|
|
2034
|
+
// Execute the command to get device version
|
|
2035
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2036
|
+
// Extract and format the device version from the response data
|
|
2037
|
+
// Remove null characters
|
|
2038
|
+
return data
|
|
2039
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2040
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2041
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2042
|
+
.replace(/\u0000/g, "");
|
|
2043
|
+
}
|
|
2044
|
+
catch (err) {
|
|
2045
|
+
// Log the error for debugging
|
|
2046
|
+
console.error("Error getting device version:", err);
|
|
2047
|
+
// Re-throw the error for higher-level handling
|
|
2048
|
+
throw err;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* get Device/Model Name
|
|
2053
|
+
* @returns
|
|
2054
|
+
*/
|
|
2055
|
+
async getDeviceName() {
|
|
2056
|
+
const keyword = "~DeviceName";
|
|
2057
|
+
try {
|
|
2058
|
+
let result;
|
|
2059
|
+
let retry = true;
|
|
2060
|
+
while (retry) {
|
|
2061
|
+
// Execute the command to get the device name
|
|
2062
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2063
|
+
// Extract and format the device name from the response data
|
|
2064
|
+
result = data
|
|
2065
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2066
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2067
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2068
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
2069
|
+
retry = result.includes("=");
|
|
2070
|
+
}
|
|
2071
|
+
return result;
|
|
2072
|
+
}
|
|
2073
|
+
catch (err) {
|
|
2074
|
+
// Log the error for debugging
|
|
2075
|
+
console.error("Error getting device name:", err);
|
|
2076
|
+
// Re-throw the error for higher-level handling
|
|
2077
|
+
throw err;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
async getPlatform() {
|
|
2081
|
+
const keyword = "~Platform";
|
|
2082
|
+
try {
|
|
2083
|
+
let result;
|
|
2084
|
+
let retry = true;
|
|
2085
|
+
while (retry) {
|
|
2086
|
+
// Execute the command to get the device name
|
|
2087
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2088
|
+
// Extract and format the device name from the response data
|
|
2089
|
+
result = data
|
|
2090
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2091
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2092
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2093
|
+
.replace(/\u0000/g, ""); // Remove null characters
|
|
2094
|
+
retry = result.includes("=");
|
|
2095
|
+
}
|
|
2096
|
+
return result;
|
|
2097
|
+
}
|
|
2098
|
+
catch (err) {
|
|
2099
|
+
// Log the error for debugging
|
|
2100
|
+
console.error("Error getting platform information:", err);
|
|
2101
|
+
// Re-throw the error for higher-level handling
|
|
2102
|
+
throw err;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
async getOS() {
|
|
2106
|
+
const keyword = "~OS";
|
|
2107
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2108
|
+
return data
|
|
2109
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2110
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2111
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2112
|
+
.replace(/\u0000/g, "");
|
|
2113
|
+
}
|
|
2114
|
+
async getWorkCode() {
|
|
2115
|
+
const keyword = "WorkCode";
|
|
2116
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2117
|
+
// Extract and format the WorkCode information from the response data
|
|
2118
|
+
// Remove null characters
|
|
2119
|
+
return data
|
|
2120
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2121
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2122
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2123
|
+
.replace(/\u0000/g, "");
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* get User ID max length
|
|
2127
|
+
* @returns
|
|
2128
|
+
*/
|
|
2129
|
+
async getPIN() {
|
|
2130
|
+
const keyword = "~PIN2Width";
|
|
2131
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2132
|
+
return data
|
|
2133
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2134
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2135
|
+
.replace(`${keyword}=`, "") // Remove the keyword prefix
|
|
2136
|
+
.replace(/\u0000/g, "");
|
|
2137
|
+
}
|
|
2138
|
+
async getFaceOn() {
|
|
2139
|
+
const keyword = "FaceFunOn";
|
|
2140
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2141
|
+
const status = data
|
|
2142
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2143
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2144
|
+
.replace(`${keyword}=`, ""); // Remove the keyword prefix
|
|
2145
|
+
// Determine and return the face function status
|
|
2146
|
+
return status.includes("0") ? "No" : "Yes";
|
|
2147
|
+
}
|
|
2148
|
+
async getSSR() {
|
|
2149
|
+
const keyword = "~SSR";
|
|
2150
|
+
const data = await this._zkTcp.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2151
|
+
return data
|
|
2152
|
+
.slice(8) // Skip the first 8 bytes (header)
|
|
2153
|
+
.toString("ascii") // Convert buffer to ASCII string
|
|
2154
|
+
.replace(`${keyword}=`, "");
|
|
2155
|
+
}
|
|
2156
|
+
async getFirmware() {
|
|
2157
|
+
try {
|
|
2158
|
+
// Execute the command to get firmware information
|
|
2159
|
+
const data = await this._zkTcp.executeCmd(1100, "");
|
|
2160
|
+
// Extract and return the firmware version from the response data
|
|
2161
|
+
return data.slice(8).toString("ascii"); // Skip the first 8 bytes (header) and convert to ASCII string
|
|
2162
|
+
}
|
|
2163
|
+
catch (err) {
|
|
2164
|
+
// Log the error for debugging
|
|
2165
|
+
console.error("Error getting firmware version:", err);
|
|
2166
|
+
// Re-throw the error to be handled by the caller
|
|
2167
|
+
throw err;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
async getTime() {
|
|
2171
|
+
try {
|
|
2172
|
+
// Execute the command to get the current time
|
|
2173
|
+
const response = await this._zkTcp.executeCmd(COMMANDS.CMD_GET_TIME, "");
|
|
2174
|
+
// Check if the response is valid
|
|
2175
|
+
if (!response || response.length < 12) {
|
|
2176
|
+
throw new Error("Invalid response received for time command");
|
|
2177
|
+
}
|
|
2178
|
+
// Extract and decode the time value from the response
|
|
2179
|
+
const timeValue = response.readUInt32LE(8); // Read 4 bytes starting at offset 8
|
|
2180
|
+
const time = timeParser.decode(timeValue); // Parse and return the decoded time
|
|
2181
|
+
return time;
|
|
2182
|
+
}
|
|
2183
|
+
catch (err) {
|
|
2184
|
+
// Log the error for debugging
|
|
2185
|
+
console.error("Error getting time:", err);
|
|
2186
|
+
// Re-throw the error for the caller to handle
|
|
2187
|
+
throw err;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
async setTime(tm) {
|
|
2191
|
+
try {
|
|
2192
|
+
// Validate the input time
|
|
2193
|
+
if (!(tm instanceof Date) && typeof tm !== "number") {
|
|
2194
|
+
throw new TypeError("Invalid time parameter. Must be a Date object or a timestamp.");
|
|
2195
|
+
}
|
|
2196
|
+
// Convert the input time to a Date object if it's not already
|
|
2197
|
+
const date = tm instanceof Date ? tm : new Date(tm);
|
|
2198
|
+
// Encode the time into the required format
|
|
2199
|
+
const encodedTime = timeParser.encode(date);
|
|
2200
|
+
// Create a buffer and write the encoded time
|
|
2201
|
+
const commandString = Buffer.alloc(32);
|
|
2202
|
+
commandString.writeUInt32LE(encodedTime, 0);
|
|
2203
|
+
// Send the command to set the time
|
|
2204
|
+
const time = await this._zkTcp.executeCmd(COMMANDS.CMD_SET_TIME, commandString);
|
|
2205
|
+
return !!time;
|
|
2206
|
+
}
|
|
2207
|
+
catch (err) {
|
|
2208
|
+
// Log the error for debugging
|
|
2209
|
+
console.error("Error setting time:", err);
|
|
2210
|
+
// Re-throw the error for the caller to handle
|
|
2211
|
+
throw err;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
async voiceTest() {
|
|
2215
|
+
try {
|
|
2216
|
+
// Define the command data for the voice test
|
|
2217
|
+
const commandData = Buffer.from("\x00\x00", "binary");
|
|
2218
|
+
await this._zkTcp.executeCmd(COMMANDS.CMD_TESTVOICE, commandData);
|
|
2219
|
+
// Execute the command and return the result
|
|
2220
|
+
}
|
|
2221
|
+
catch (err) {
|
|
2222
|
+
// Log the error for debugging purposes
|
|
2223
|
+
console.error("Error executing voice test:", err);
|
|
2224
|
+
// Re-throw the error to be handled by the caller
|
|
2225
|
+
throw err;
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
class ZTCP {
|
|
2231
|
+
/**
|
|
2232
|
+
* @param_ip ip address of device
|
|
2233
|
+
* @param_port port number of device
|
|
2234
|
+
* @param_timeout connection timout
|
|
2235
|
+
* @param_comm_key communication key of device (if the case)
|
|
2236
|
+
* @return Zkteco TCP socket connection instance
|
|
2237
|
+
*/
|
|
2238
|
+
ip;
|
|
2239
|
+
port;
|
|
2240
|
+
timeout;
|
|
2241
|
+
sessionId = 0;
|
|
2242
|
+
replyId = 0;
|
|
2243
|
+
socket;
|
|
2244
|
+
comm_key;
|
|
2245
|
+
user_count = 0;
|
|
2246
|
+
fp_count = 0;
|
|
2247
|
+
pwd_count = 0;
|
|
2248
|
+
oplog_count = 0;
|
|
2249
|
+
attlog_count = 0;
|
|
2250
|
+
fp_cap = 0;
|
|
2251
|
+
user_cap = 0;
|
|
2252
|
+
attlog_cap = 0;
|
|
2253
|
+
fp_av = 0;
|
|
2254
|
+
user_av = 0;
|
|
2255
|
+
attlog_av = 0;
|
|
2256
|
+
face_count = 0;
|
|
2257
|
+
face_cap = 0;
|
|
2258
|
+
userPacketSize = 72;
|
|
2259
|
+
verbose = false;
|
|
2260
|
+
packetNumber = 0;
|
|
1285
2261
|
replyData = Buffer.from([]);
|
|
1286
|
-
|
|
2262
|
+
_optionsService;
|
|
1287
2263
|
_transactionService;
|
|
2264
|
+
_userService;
|
|
1288
2265
|
constructor(ip, port, timeout, comm_key, verbose) {
|
|
1289
2266
|
this.ip = ip;
|
|
1290
2267
|
this.port = port;
|
|
@@ -1292,6 +2269,7 @@ class ZTCP {
|
|
|
1292
2269
|
this.replyId = 0;
|
|
1293
2270
|
this.comm_key = comm_key;
|
|
1294
2271
|
this.verbose = verbose;
|
|
2272
|
+
this._optionsService = new OptionsService(this);
|
|
1295
2273
|
this._userService = new UserService(this);
|
|
1296
2274
|
this._transactionService = new TransactionService(this);
|
|
1297
2275
|
}
|
|
@@ -1299,21 +2277,21 @@ class ZTCP {
|
|
|
1299
2277
|
return new Promise((resolve, reject) => {
|
|
1300
2278
|
this.socket = new Socket();
|
|
1301
2279
|
// Handle socket error
|
|
1302
|
-
this.socket.once(
|
|
2280
|
+
this.socket.once("error", (err) => {
|
|
1303
2281
|
this.socket = undefined; // Ensure socket reference is cleared
|
|
1304
2282
|
reject(err);
|
|
1305
|
-
if (typeof cbError ===
|
|
2283
|
+
if (typeof cbError === "function")
|
|
1306
2284
|
cbError(err);
|
|
1307
2285
|
});
|
|
1308
2286
|
// Handle successful connection
|
|
1309
|
-
this.socket.once(
|
|
2287
|
+
this.socket.once("connect", () => {
|
|
1310
2288
|
resolve(this.socket);
|
|
1311
2289
|
});
|
|
1312
2290
|
// Handle socket closure
|
|
1313
|
-
this.socket.once(
|
|
2291
|
+
this.socket.once("close", () => {
|
|
1314
2292
|
this.socket = undefined; // Ensure socket reference is cleared
|
|
1315
|
-
if (typeof cbClose ===
|
|
1316
|
-
cbClose(
|
|
2293
|
+
if (typeof cbClose === "function")
|
|
2294
|
+
cbClose("tcp");
|
|
1317
2295
|
});
|
|
1318
2296
|
// Set socket timeout if provided
|
|
1319
2297
|
if (this.timeout) {
|
|
@@ -1325,7 +2303,7 @@ class ZTCP {
|
|
|
1325
2303
|
}
|
|
1326
2304
|
async connect() {
|
|
1327
2305
|
try {
|
|
1328
|
-
let reply = await this.executeCmd(COMMANDS.CMD_CONNECT,
|
|
2306
|
+
let reply = await this.executeCmd(COMMANDS.CMD_CONNECT, "");
|
|
1329
2307
|
if (reply.readUInt16LE(0) === COMMANDS.CMD_ACK_OK) {
|
|
1330
2308
|
return true;
|
|
1331
2309
|
}
|
|
@@ -1341,12 +2319,12 @@ class ZTCP {
|
|
|
1341
2319
|
}
|
|
1342
2320
|
else {
|
|
1343
2321
|
// No reply received; throw an error
|
|
1344
|
-
throw new Error(
|
|
2322
|
+
throw new Error("NO_REPLY_ON_CMD_CONNECT");
|
|
1345
2323
|
}
|
|
1346
2324
|
}
|
|
1347
2325
|
catch (err) {
|
|
1348
2326
|
// Log the error for debugging, if necessary
|
|
1349
|
-
console.error(
|
|
2327
|
+
console.error("Failed to connect:", err);
|
|
1350
2328
|
// Re-throw the error for handling by the caller
|
|
1351
2329
|
throw err;
|
|
1352
2330
|
}
|
|
@@ -1358,10 +2336,10 @@ class ZTCP {
|
|
|
1358
2336
|
return resolve(true);
|
|
1359
2337
|
}
|
|
1360
2338
|
// Clean up listeners to avoid potential memory leaks or duplicate handling
|
|
1361
|
-
this.socket.removeAllListeners(
|
|
2339
|
+
this.socket.removeAllListeners("data");
|
|
1362
2340
|
// Set a timeout to handle cases where socket.end might not resolve
|
|
1363
2341
|
const timer = setTimeout(() => {
|
|
1364
|
-
this.socket
|
|
2342
|
+
this.socket?.destroy(); // Forcibly close the socket if not closed properly
|
|
1365
2343
|
resolve(true); // Resolve even if the socket was not closed properly
|
|
1366
2344
|
}, 2000);
|
|
1367
2345
|
// Close the socket and clear the timeout upon successful completion
|
|
@@ -1370,49 +2348,44 @@ class ZTCP {
|
|
|
1370
2348
|
resolve(true); // Resolve once the socket has ended
|
|
1371
2349
|
});
|
|
1372
2350
|
// Handle socket errors during closing
|
|
1373
|
-
this.socket.once(
|
|
2351
|
+
this.socket.once("error", (err) => {
|
|
1374
2352
|
clearTimeout(timer);
|
|
1375
2353
|
reject(err); // Reject the promise with the error
|
|
1376
2354
|
});
|
|
1377
2355
|
});
|
|
1378
2356
|
}
|
|
1379
|
-
writeMessage(msg, connect) {
|
|
2357
|
+
writeMessage(msg, connect, cb) {
|
|
1380
2358
|
return new Promise((resolve, reject) => {
|
|
1381
2359
|
// Check if the socket is initialized
|
|
1382
2360
|
if (!this.socket) {
|
|
1383
|
-
return reject(new Error(
|
|
2361
|
+
return reject(new Error("Socket is not initialized"));
|
|
1384
2362
|
}
|
|
1385
2363
|
// Define a variable for the timeout reference
|
|
1386
|
-
|
|
2364
|
+
const timer = setTimeout(() => {
|
|
2365
|
+
// Check if the socket is still valid before trying to remove the listener
|
|
2366
|
+
cleanUp();
|
|
2367
|
+
reject(new Error("TIMEOUT_ON_WRITING_MESSAGE")); // Reject the promise on timeout
|
|
2368
|
+
}, connect ? 3000 : this.timeout);
|
|
1387
2369
|
// Handle incoming data
|
|
1388
2370
|
const onData = (data) => {
|
|
1389
2371
|
// Check if the socket is still valid before trying to remove the listener
|
|
2372
|
+
cleanUp(); // Clear the timeout once data is received
|
|
2373
|
+
resolve(cb(data)); // Resolve the promise with the received data
|
|
2374
|
+
};
|
|
2375
|
+
const cleanUp = () => {
|
|
2376
|
+
if (timer)
|
|
2377
|
+
clearTimeout(timer);
|
|
1390
2378
|
if (this.socket) {
|
|
1391
|
-
this.socket.removeListener(
|
|
2379
|
+
this.socket.removeListener("data", onData); // Remove the data event listener
|
|
1392
2380
|
}
|
|
1393
|
-
clearTimeout(timer); // Clear the timeout once data is received
|
|
1394
|
-
resolve(data); // Resolve the promise with the received data
|
|
1395
2381
|
};
|
|
1396
2382
|
// Attach the data event listener
|
|
1397
|
-
this.socket.
|
|
2383
|
+
this.socket.on("data", onData);
|
|
1398
2384
|
// Attempt to write the message to the socket
|
|
1399
|
-
this.socket.write(msg,
|
|
2385
|
+
this.socket.write(msg, undefined, (err) => {
|
|
1400
2386
|
if (err) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
this.socket.removeListener('data', onData); // Clean up listener on write error
|
|
1404
|
-
}
|
|
1405
|
-
return reject(err); // Reject the promise with the write error
|
|
1406
|
-
}
|
|
1407
|
-
// If a timeout is set, configure it
|
|
1408
|
-
if (this.timeout) {
|
|
1409
|
-
timer = setTimeout(() => {
|
|
1410
|
-
// Check if the socket is still valid before trying to remove the listener
|
|
1411
|
-
if (this.socket) {
|
|
1412
|
-
this.socket.removeListener('data', onData); // Remove listener on timeout
|
|
1413
|
-
}
|
|
1414
|
-
reject(new Error('TIMEOUT_ON_WRITING_MESSAGE')); // Reject the promise on timeout
|
|
1415
|
-
}, connect ? 2000 : this.timeout);
|
|
2387
|
+
cleanUp();
|
|
2388
|
+
reject(err); // Reject the promise with the write error
|
|
1416
2389
|
}
|
|
1417
2390
|
});
|
|
1418
2391
|
});
|
|
@@ -1425,12 +2398,18 @@ class ZTCP {
|
|
|
1425
2398
|
// Internal callback to handle data reception
|
|
1426
2399
|
const internalCallback = (data_1) => {
|
|
1427
2400
|
if (this.socket) {
|
|
1428
|
-
this.socket.removeListener(
|
|
2401
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener
|
|
1429
2402
|
}
|
|
1430
2403
|
if (timer)
|
|
1431
2404
|
clearTimeout(timer); // Clear the timeout
|
|
1432
2405
|
resolve(data_1); // Resolve the promise with the data
|
|
1433
2406
|
};
|
|
2407
|
+
const onTimeOut = () => setTimeout(() => {
|
|
2408
|
+
if (this.socket) {
|
|
2409
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener on timeout
|
|
2410
|
+
}
|
|
2411
|
+
reject(new Error("TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA")); // Reject on timeout
|
|
2412
|
+
}, this.timeout);
|
|
1434
2413
|
// Handle incoming data
|
|
1435
2414
|
const handleOnData = (data_3) => {
|
|
1436
2415
|
replyBuffer = Buffer.concat([replyBuffer, data_3]); // Accumulate data
|
|
@@ -1440,7 +2419,7 @@ class ZTCP {
|
|
|
1440
2419
|
// Decode the TCP header
|
|
1441
2420
|
const header = decodeTCPHeader(replyBuffer.subarray(0, 16));
|
|
1442
2421
|
if (this.verbose) {
|
|
1443
|
-
console.log("
|
|
2422
|
+
console.log("response command: ", header.commandId, COMMANDS[header.commandId], "replyid: ", header.replyId);
|
|
1444
2423
|
}
|
|
1445
2424
|
// Handle based on command ID
|
|
1446
2425
|
if (header.commandId === COMMANDS.CMD_DATA) {
|
|
@@ -1451,12 +2430,7 @@ class ZTCP {
|
|
|
1451
2430
|
}
|
|
1452
2431
|
else {
|
|
1453
2432
|
// Set a timeout to handle errors
|
|
1454
|
-
timer =
|
|
1455
|
-
if (this.socket) {
|
|
1456
|
-
this.socket.removeListener('data', handleOnData); // Clean up listener on timeout
|
|
1457
|
-
}
|
|
1458
|
-
reject(new Error('TIMEOUT_ON_RECEIVING_REQUEST_DATA')); // Reject on timeout
|
|
1459
|
-
}, this.timeout);
|
|
2433
|
+
timer = onTimeOut();
|
|
1460
2434
|
// Extract packet length and handle accordingly
|
|
1461
2435
|
const packetLength = data_3.readUIntLE(4, 2);
|
|
1462
2436
|
if (packetLength > 8) {
|
|
@@ -1466,26 +2440,21 @@ class ZTCP {
|
|
|
1466
2440
|
};
|
|
1467
2441
|
// Ensure the socket is valid before attaching the listener
|
|
1468
2442
|
if (this.socket) {
|
|
1469
|
-
this.socket.on(
|
|
2443
|
+
this.socket.on("data", handleOnData);
|
|
1470
2444
|
// Write the message to the socket
|
|
1471
|
-
this.socket.write(msg,
|
|
2445
|
+
this.socket.write(msg, undefined, (err) => {
|
|
1472
2446
|
if (err) {
|
|
1473
2447
|
if (this.socket) {
|
|
1474
|
-
this.socket.removeListener(
|
|
2448
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener on error
|
|
1475
2449
|
}
|
|
1476
2450
|
return reject(err); // Reject the promise with the error
|
|
1477
2451
|
}
|
|
1478
2452
|
// Set a timeout to handle cases where no response is received
|
|
1479
|
-
timer =
|
|
1480
|
-
if (this.socket) {
|
|
1481
|
-
this.socket.removeListener('data', handleOnData); // Clean up listener on timeout
|
|
1482
|
-
}
|
|
1483
|
-
reject(new Error('TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA')); // Reject on timeout
|
|
1484
|
-
}, this.timeout);
|
|
2453
|
+
timer = onTimeOut();
|
|
1485
2454
|
});
|
|
1486
2455
|
}
|
|
1487
2456
|
else {
|
|
1488
|
-
reject(new Error(
|
|
2457
|
+
reject(new Error("SOCKET_NOT_INITIALIZED")); // Reject if socket is not initialized
|
|
1489
2458
|
}
|
|
1490
2459
|
});
|
|
1491
2460
|
}
|
|
@@ -1511,27 +2480,49 @@ class ZTCP {
|
|
|
1511
2480
|
else {
|
|
1512
2481
|
this.replyId++;
|
|
1513
2482
|
}
|
|
2483
|
+
const currentReply = this.replyId;
|
|
1514
2484
|
const buf = createTCPHeader(command, this.sessionId, this.replyId, data);
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
2485
|
+
const callback = (responseData) => {
|
|
2486
|
+
const packets = splitTcpPackets(responseData);
|
|
2487
|
+
for (const packet of packets) {
|
|
2488
|
+
const headers = decodeTCPHeader(packet);
|
|
2489
|
+
if (this.verbose) {
|
|
2490
|
+
const JOIN_CMD = { ...COMMANDS, ...DISCOVERED_CMD };
|
|
2491
|
+
console.debug("request command:", COMMANDS[command], "\nresponse command: ", JOIN_CMD[headers.commandId], "replyid: ", headers.replyId);
|
|
2492
|
+
}
|
|
2493
|
+
if (+headers.replyId === currentReply + 1) {
|
|
2494
|
+
return packet;
|
|
2495
|
+
}
|
|
2496
|
+
continue;
|
|
1527
2497
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2498
|
+
};
|
|
2499
|
+
return new Promise((Resolve, Reject) => {
|
|
2500
|
+
// Write the message to the socket and wait for a response
|
|
2501
|
+
this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT, callback)
|
|
2502
|
+
.then((reply) => {
|
|
2503
|
+
// Remove TCP header from the response
|
|
2504
|
+
let rReply;
|
|
2505
|
+
try {
|
|
2506
|
+
rReply = removeTcpHeader(reply);
|
|
2507
|
+
}
|
|
2508
|
+
catch (e) {
|
|
2509
|
+
console.log("reply", reply);
|
|
2510
|
+
}
|
|
2511
|
+
// Update sessionId for connection command responses
|
|
2512
|
+
if (command === COMMANDS.CMD_CONNECT &&
|
|
2513
|
+
rReply &&
|
|
2514
|
+
rReply.length >= 6) {
|
|
2515
|
+
// Assuming sessionId is located at offset 4 and is 2 bytes long
|
|
2516
|
+
this.sessionId = rReply.readUInt16LE(4);
|
|
2517
|
+
}
|
|
2518
|
+
Resolve(rReply);
|
|
2519
|
+
})
|
|
2520
|
+
.catch((err) => {
|
|
2521
|
+
// Log or handle the error if necessary
|
|
2522
|
+
console.error("Error executing command:", err);
|
|
2523
|
+
Reject(err); // Re-throw the error for handling by the caller
|
|
2524
|
+
});
|
|
2525
|
+
});
|
|
1535
2526
|
}
|
|
1536
2527
|
async sendChunkRequest(start, size) {
|
|
1537
2528
|
this.replyId++;
|
|
@@ -1540,8 +2531,8 @@ class ZTCP {
|
|
|
1540
2531
|
reqData.writeUInt32LE(size, 4);
|
|
1541
2532
|
const buf = createTCPHeader(COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
|
|
1542
2533
|
try {
|
|
1543
|
-
|
|
1544
|
-
this.socket
|
|
2534
|
+
const promise = new Promise((resolve, reject) => {
|
|
2535
|
+
this.socket?.write(buf, undefined, (err) => {
|
|
1545
2536
|
if (err) {
|
|
1546
2537
|
console.error(`[TCP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
|
|
1547
2538
|
reject(err); // Reject the promise if there is an error
|
|
@@ -1551,6 +2542,7 @@ class ZTCP {
|
|
|
1551
2542
|
}
|
|
1552
2543
|
});
|
|
1553
2544
|
});
|
|
2545
|
+
await promise;
|
|
1554
2546
|
}
|
|
1555
2547
|
catch (err) {
|
|
1556
2548
|
// Handle or log the error as needed
|
|
@@ -1566,18 +2558,18 @@ class ZTCP {
|
|
|
1566
2558
|
* readWithBuffer will reject error if it'wrong when starting request data
|
|
1567
2559
|
* readWithBuffer will return { data: replyData , err: Error } when receiving requested data
|
|
1568
2560
|
*/
|
|
1569
|
-
readWithBuffer(reqData, cb
|
|
2561
|
+
readWithBuffer(reqData, cb) {
|
|
1570
2562
|
return new Promise(async (resolve, reject) => {
|
|
1571
2563
|
this.replyId++;
|
|
1572
2564
|
const buf = createTCPHeader(COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
|
|
1573
|
-
let reply
|
|
2565
|
+
let reply;
|
|
1574
2566
|
try {
|
|
1575
2567
|
reply = await this.requestData(buf);
|
|
1576
2568
|
}
|
|
1577
2569
|
catch (err) {
|
|
1578
2570
|
reject(err);
|
|
1579
2571
|
}
|
|
1580
|
-
const header = decodeTCPHeader(reply
|
|
2572
|
+
const header = decodeTCPHeader(reply?.subarray(0, 16));
|
|
1581
2573
|
switch (header.commandId) {
|
|
1582
2574
|
case COMMANDS.CMD_DATA: {
|
|
1583
2575
|
resolve({ data: reply.subarray(16), mode: 8 });
|
|
@@ -1591,56 +2583,64 @@ class ZTCP {
|
|
|
1591
2583
|
const size = recvData.readUIntLE(1, 4);
|
|
1592
2584
|
// We need to split the data to many chunks to receive , because it's to large
|
|
1593
2585
|
// After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
|
|
1594
|
-
|
|
1595
|
-
|
|
2586
|
+
const remain = size % Constants.MAX_CHUNK;
|
|
2587
|
+
const numberChunks = Math.round(size - remain) / Constants.MAX_CHUNK;
|
|
1596
2588
|
this.packetNumber = numberChunks + (remain > 0 ? 1 : 0);
|
|
1597
2589
|
//let replyData = Buffer.from([])
|
|
1598
2590
|
let totalBuffer = Buffer.from([]);
|
|
1599
2591
|
let realTotalBuffer = Buffer.from([]);
|
|
1600
2592
|
let timer = setTimeout(() => {
|
|
1601
|
-
internalCallback(this.replyData, new Error(
|
|
2593
|
+
internalCallback(this.replyData, new Error("TIMEOUT WHEN RECEIVING PACKET"));
|
|
1602
2594
|
}, this.timeout);
|
|
1603
2595
|
const internalCallback = (replyData, err = null) => {
|
|
1604
|
-
this.socket && this.socket.removeAllListeners(
|
|
2596
|
+
this.socket && this.socket.removeAllListeners("data");
|
|
1605
2597
|
timer && clearTimeout(timer);
|
|
1606
2598
|
resolve({ data: replyData, err });
|
|
1607
2599
|
};
|
|
1608
|
-
this.socket
|
|
1609
|
-
internalCallback(this.replyData, new Error(
|
|
2600
|
+
this.socket?.once("close", () => {
|
|
2601
|
+
internalCallback(this.replyData, new Error("Socket is disconnected unexpectedly"));
|
|
1610
2602
|
});
|
|
1611
2603
|
for (let i = 0; i <= numberChunks; i++) {
|
|
1612
2604
|
const data = await new Promise((resolve2, reject2) => {
|
|
1613
2605
|
try {
|
|
1614
|
-
this.sendChunkRequest(i * Constants.MAX_CHUNK,
|
|
1615
|
-
|
|
1616
|
-
: Constants.MAX_CHUNK);
|
|
1617
|
-
this.socket.on('data', (reply) => {
|
|
2606
|
+
this.sendChunkRequest(i * Constants.MAX_CHUNK, i === numberChunks ? remain : Constants.MAX_CHUNK);
|
|
2607
|
+
this.socket?.on("data", (reply) => {
|
|
1618
2608
|
clearTimeout(timer);
|
|
1619
2609
|
timer = setTimeout(() => {
|
|
1620
2610
|
internalCallback(this.replyData, new Error(`TIME OUT !! ${this.packetNumber} PACKETS REMAIN !`));
|
|
1621
2611
|
}, this.timeout);
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2612
|
+
if (this.verbose && reply.length >= 8) {
|
|
2613
|
+
const headers = decodeTCPHeader(reply);
|
|
2614
|
+
if (COMMANDS[headers.commandId]) {
|
|
2615
|
+
switch (headers.commandId) {
|
|
2616
|
+
case COMMANDS.CMD_ACK_OK:
|
|
2617
|
+
case COMMANDS.CMD_DATA:
|
|
2618
|
+
this.verbose &&
|
|
2619
|
+
console.log("CMD received: ", COMMANDS[headers.commandId]);
|
|
2620
|
+
break;
|
|
2621
|
+
case COMMANDS.CMD_PREPARE_DATA:
|
|
2622
|
+
this.verbose &&
|
|
2623
|
+
console.log("CMD received: ", COMMANDS[headers.commandId]);
|
|
2624
|
+
this.verbose &&
|
|
2625
|
+
console.log(`recieve chunk: prepare data size is ${headers.payloadSize}`);
|
|
2626
|
+
break;
|
|
2627
|
+
default:
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
1635
2630
|
}
|
|
1636
2631
|
}
|
|
1637
2632
|
totalBuffer = Buffer.concat([totalBuffer, reply]);
|
|
1638
2633
|
const packetLength = totalBuffer.readUIntLE(4, 2);
|
|
1639
2634
|
if (totalBuffer.length >= 8 + packetLength) {
|
|
1640
|
-
realTotalBuffer = Buffer.concat([
|
|
2635
|
+
realTotalBuffer = Buffer.concat([
|
|
2636
|
+
realTotalBuffer,
|
|
2637
|
+
totalBuffer.subarray(16, 8 + packetLength),
|
|
2638
|
+
]);
|
|
1641
2639
|
totalBuffer = totalBuffer.subarray(8 + packetLength);
|
|
1642
|
-
if ((this.packetNumber > 1 &&
|
|
1643
|
-
|
|
2640
|
+
if ((this.packetNumber > 1 &&
|
|
2641
|
+
realTotalBuffer.length === Constants.MAX_CHUNK + 8) ||
|
|
2642
|
+
(this.packetNumber === 1 &&
|
|
2643
|
+
realTotalBuffer.length === remain + 8)) {
|
|
1644
2644
|
this.packetNumber--;
|
|
1645
2645
|
cb && cb(realTotalBuffer.length, size);
|
|
1646
2646
|
resolve2(realTotalBuffer.subarray(8));
|
|
@@ -1654,8 +2654,11 @@ class ZTCP {
|
|
|
1654
2654
|
reject2(e);
|
|
1655
2655
|
}
|
|
1656
2656
|
});
|
|
1657
|
-
this.replyData = Buffer.concat([
|
|
1658
|
-
|
|
2657
|
+
this.replyData = Buffer.concat([
|
|
2658
|
+
this.replyData,
|
|
2659
|
+
data,
|
|
2660
|
+
]);
|
|
2661
|
+
this.socket?.removeAllListeners("data");
|
|
1659
2662
|
if (this.packetNumber <= 0) {
|
|
1660
2663
|
resolve({ data: this.replyData });
|
|
1661
2664
|
}
|
|
@@ -1663,7 +2666,7 @@ class ZTCP {
|
|
|
1663
2666
|
break;
|
|
1664
2667
|
}
|
|
1665
2668
|
default: {
|
|
1666
|
-
reject(new Error(
|
|
2669
|
+
reject(new Error("ERROR_IN_UNHANDLE_CMD " + exportErrorMessage(header.commandId)));
|
|
1667
2670
|
}
|
|
1668
2671
|
}
|
|
1669
2672
|
});
|
|
@@ -1674,16 +2677,13 @@ class ZTCP {
|
|
|
1674
2677
|
* reject error when starting request data
|
|
1675
2678
|
* return { data: records, err: Error } when receiving requested data
|
|
1676
2679
|
*/
|
|
1677
|
-
async getAttendances(callbackInProcess = () => { }) {
|
|
1678
|
-
return await this._transactionService.getAttendances(callbackInProcess);
|
|
1679
|
-
}
|
|
1680
2680
|
async freeData() {
|
|
1681
2681
|
try {
|
|
1682
|
-
const resp = await this.executeCmd(COMMANDS.CMD_FREE_DATA,
|
|
2682
|
+
const resp = await this.executeCmd(COMMANDS.CMD_FREE_DATA, "");
|
|
1683
2683
|
return !!resp;
|
|
1684
2684
|
}
|
|
1685
2685
|
catch (err) {
|
|
1686
|
-
console.error(
|
|
2686
|
+
console.error("Error freeing data:", err);
|
|
1687
2687
|
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
1688
2688
|
}
|
|
1689
2689
|
}
|
|
@@ -1693,28 +2693,28 @@ class ZTCP {
|
|
|
1693
2693
|
return !!resp;
|
|
1694
2694
|
}
|
|
1695
2695
|
catch (err) {
|
|
1696
|
-
console.error(
|
|
2696
|
+
console.error("Error disabling device:", err);
|
|
1697
2697
|
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
1698
2698
|
}
|
|
1699
2699
|
}
|
|
1700
2700
|
async enableDevice() {
|
|
1701
2701
|
try {
|
|
1702
|
-
const resp = await this.executeCmd(COMMANDS.CMD_ENABLEDEVICE,
|
|
2702
|
+
const resp = await this.executeCmd(COMMANDS.CMD_ENABLEDEVICE, "");
|
|
1703
2703
|
return !!resp;
|
|
1704
2704
|
}
|
|
1705
2705
|
catch (err) {
|
|
1706
|
-
console.error(
|
|
2706
|
+
console.error("Error enabling device:", err);
|
|
1707
2707
|
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
1708
2708
|
}
|
|
1709
2709
|
}
|
|
1710
2710
|
async disconnect() {
|
|
1711
2711
|
try {
|
|
1712
2712
|
// Attempt to execute the disconnect command
|
|
1713
|
-
await this.executeCmd(COMMANDS.CMD_EXIT,
|
|
2713
|
+
await this.executeCmd(COMMANDS.CMD_EXIT, "");
|
|
1714
2714
|
}
|
|
1715
2715
|
catch (err) {
|
|
1716
2716
|
// Log any errors encountered during command execution
|
|
1717
|
-
console.error(
|
|
2717
|
+
console.error("Error during disconnection:", err);
|
|
1718
2718
|
// Optionally, add more handling or recovery logic here
|
|
1719
2719
|
}
|
|
1720
2720
|
// Attempt to close the socket and return the result
|
|
@@ -1723,7 +2723,7 @@ class ZTCP {
|
|
|
1723
2723
|
}
|
|
1724
2724
|
catch (err) {
|
|
1725
2725
|
// Log any errors encountered while closing the socket
|
|
1726
|
-
console.error(
|
|
2726
|
+
console.error("Error during socket closure:", err);
|
|
1727
2727
|
// Optionally, rethrow or handle the error if necessary
|
|
1728
2728
|
throw err; // Re-throwing to propagate the error
|
|
1729
2729
|
}
|
|
@@ -1731,17 +2731,17 @@ class ZTCP {
|
|
|
1731
2731
|
async getInfo() {
|
|
1732
2732
|
try {
|
|
1733
2733
|
// Execute the command to retrieve free sizes from the device
|
|
1734
|
-
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES,
|
|
2734
|
+
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
1735
2735
|
// Parse the response data to extract and return relevant information
|
|
1736
2736
|
return {
|
|
1737
2737
|
userCounts: data.readUIntLE(24, 4), // Number of users
|
|
1738
2738
|
logCounts: data.readUIntLE(40, 4), // Number of logs
|
|
1739
|
-
logCapacity: data.readUIntLE(72, 4) // Capacity of logs in bytes
|
|
2739
|
+
logCapacity: data.readUIntLE(72, 4), // Capacity of logs in bytes
|
|
1740
2740
|
};
|
|
1741
2741
|
}
|
|
1742
2742
|
catch (err) {
|
|
1743
2743
|
// Log the error for debugging purposes
|
|
1744
|
-
console.error(
|
|
2744
|
+
console.error("Error getting device info:", err);
|
|
1745
2745
|
// Re-throw the error to allow upstream error handling
|
|
1746
2746
|
throw err;
|
|
1747
2747
|
}
|
|
@@ -1749,7 +2749,7 @@ class ZTCP {
|
|
|
1749
2749
|
async getSizes() {
|
|
1750
2750
|
try {
|
|
1751
2751
|
// Execute the command to retrieve free sizes from the device
|
|
1752
|
-
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES,
|
|
2752
|
+
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
1753
2753
|
// Parse the response data to extract and return relevant information
|
|
1754
2754
|
const buf = data.slice(8); // remove header
|
|
1755
2755
|
this.user_count = buf.readUIntLE(16, 4);
|
|
@@ -1779,362 +2779,27 @@ class ZTCP {
|
|
|
1779
2779
|
userAvailable: this.user_av,
|
|
1780
2780
|
attLogAvailable: this.attlog_av,
|
|
1781
2781
|
faceCount: this.face_count,
|
|
1782
|
-
faceCapacity: this.face_cap
|
|
2782
|
+
faceCapacity: this.face_cap,
|
|
1783
2783
|
};
|
|
1784
2784
|
}
|
|
1785
2785
|
catch (err) {
|
|
1786
2786
|
// Log the error for debugging purposes
|
|
1787
|
-
console.error(
|
|
2787
|
+
console.error("Error getting device info:", err);
|
|
1788
2788
|
// Re-throw the error to allow upstream error handling
|
|
1789
2789
|
throw err;
|
|
1790
2790
|
}
|
|
1791
2791
|
}
|
|
1792
|
-
|
|
1793
|
-
const keyword = '~OEMVendor';
|
|
1794
|
-
try {
|
|
1795
|
-
// Execute the command to get oem vendor
|
|
1796
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1797
|
-
// Extract and format the oem bendor from the response data
|
|
1798
|
-
const vendor = data.slice(8) // Skip the first 8 bytes (header)
|
|
1799
|
-
.toString('ascii') // Convert buffer to string
|
|
1800
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1801
|
-
.replace(/\u0000/g, ''); // Remove null characters
|
|
1802
|
-
return vendor;
|
|
1803
|
-
}
|
|
1804
|
-
catch (err) {
|
|
1805
|
-
// Log the error for debugging
|
|
1806
|
-
console.error('Error getting vendor:', err);
|
|
1807
|
-
// Re-throw the error for higher-level handling
|
|
1808
|
-
throw err;
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
async getProductTime() {
|
|
1812
|
-
const keyword = '~ProductTime';
|
|
1813
|
-
try {
|
|
1814
|
-
// Execute the command to get serial number
|
|
1815
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1816
|
-
// Extract and format the serial number from the response data
|
|
1817
|
-
const ProductTime = data.slice(8) // Skip the first 8 bytes (header)
|
|
1818
|
-
.toString('ascii') // Convert buffer to string
|
|
1819
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1820
|
-
.replace(/\u0000/g, ''); // Remove null characters
|
|
1821
|
-
return new Date(ProductTime);
|
|
1822
|
-
}
|
|
1823
|
-
catch (err) {
|
|
1824
|
-
// Log the error for debugging
|
|
1825
|
-
console.error('Error getting Product Time:', err);
|
|
1826
|
-
// Re-throw the error for higher-level handling
|
|
1827
|
-
throw err;
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
async getMacAddress() {
|
|
1831
|
-
const keyword = 'MAC';
|
|
1832
|
-
try {
|
|
1833
|
-
// Execute the command to get serial number
|
|
1834
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1835
|
-
// Extract and format the serial number from the response data
|
|
1836
|
-
const macAddr = data.slice(8) // Skip the first 8 bytes (header)
|
|
1837
|
-
.toString('ascii') // Convert buffer to string
|
|
1838
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1839
|
-
.replace(/\u0000/g, ''); // Remove null characters
|
|
1840
|
-
return macAddr;
|
|
1841
|
-
}
|
|
1842
|
-
catch (err) {
|
|
1843
|
-
// Log the error for debugging
|
|
1844
|
-
console.error('Error getting MAC address:', err);
|
|
1845
|
-
// Re-throw the error for higher-level handling
|
|
1846
|
-
throw err;
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
async getNetworkParams() {
|
|
1850
|
-
try {
|
|
1851
|
-
const params = {
|
|
1852
|
-
IPAddress: this.ip,
|
|
1853
|
-
NetMask: '',
|
|
1854
|
-
GATEIPAddress: ''
|
|
1855
|
-
};
|
|
1856
|
-
const keywords = Object.keys(params);
|
|
1857
|
-
for await (const keyword of keywords) {
|
|
1858
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1859
|
-
params[keyword] = data.slice(8)
|
|
1860
|
-
.toString('utf-8')
|
|
1861
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1862
|
-
.replace(/\u0000/g, '')
|
|
1863
|
-
.replace('=', '.'); // Replace equal simbol to dot, due to sometimes there are parsing errors
|
|
1864
|
-
}
|
|
1865
|
-
return params;
|
|
1866
|
-
}
|
|
1867
|
-
catch (err) {
|
|
1868
|
-
console.error("Error getting Network Params: ", err);
|
|
1869
|
-
throw err;
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
async getSerialNumber() {
|
|
1873
|
-
const keyword = '~SerialNumber';
|
|
1874
|
-
let serialNumber = '';
|
|
1875
|
-
let count = 10;
|
|
1876
|
-
try {
|
|
1877
|
-
// Execute the command to get serial number
|
|
1878
|
-
/**
|
|
1879
|
-
* @dev implemented a counter and a while loop because sometimes serial number parses wrong for some reason
|
|
1880
|
-
* */
|
|
1881
|
-
while (serialNumber.length !== 13 && count > 0) {
|
|
1882
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1883
|
-
// Extract and format the serial number from the response data
|
|
1884
|
-
const SN = data.slice(8) // Skip the first 8 bytes (header)
|
|
1885
|
-
.toString('utf-8') // Convert buffer to string
|
|
1886
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1887
|
-
.replace('=', '') // Remove sometines last number is a character equal to = or unknow character
|
|
1888
|
-
.replace(/\u0000/g, ''); // Remove null characters
|
|
1889
|
-
if (serialNumber.length !== 13 && this.verbose) {
|
|
1890
|
-
console.warn('Serial number length not equal to 13, check');
|
|
1891
|
-
}
|
|
1892
|
-
count--;
|
|
1893
|
-
serialNumber = SN;
|
|
1894
|
-
}
|
|
1895
|
-
return serialNumber;
|
|
1896
|
-
}
|
|
1897
|
-
catch (err) {
|
|
1898
|
-
// Log the error for debugging
|
|
1899
|
-
console.error('Error getting serial number:', err);
|
|
1900
|
-
// Re-throw the error for higher-level handling
|
|
1901
|
-
throw err;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
async getDeviceVersion() {
|
|
1905
|
-
const keyword = '~ZKFPVersion';
|
|
1906
|
-
try {
|
|
1907
|
-
// Execute the command to get device version
|
|
1908
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1909
|
-
// Extract and format the device version from the response data
|
|
1910
|
-
// Remove null characters
|
|
1911
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1912
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
1913
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1914
|
-
.replace(/\u0000/g, '');
|
|
1915
|
-
}
|
|
1916
|
-
catch (err) {
|
|
1917
|
-
// Log the error for debugging
|
|
1918
|
-
console.error('Error getting device version:', err);
|
|
1919
|
-
// Re-throw the error for higher-level handling
|
|
1920
|
-
throw err;
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
async getDeviceName() {
|
|
1924
|
-
const keyword = '~DeviceName';
|
|
1925
|
-
try {
|
|
1926
|
-
// Execute the command to get the device name
|
|
1927
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1928
|
-
// Extract and format the device name from the response data
|
|
1929
|
-
// Remove null characters
|
|
1930
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1931
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
1932
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1933
|
-
.replace(/\u0000/g, '');
|
|
1934
|
-
}
|
|
1935
|
-
catch (err) {
|
|
1936
|
-
// Log the error for debugging
|
|
1937
|
-
console.error('Error getting device name:', err);
|
|
1938
|
-
// Re-throw the error for higher-level handling
|
|
1939
|
-
throw err;
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
async getPlatform() {
|
|
1943
|
-
const keyword = '~Platform';
|
|
1944
|
-
try {
|
|
1945
|
-
// Execute the command to get the platform information
|
|
1946
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1947
|
-
// Extract and format the platform information from the response data
|
|
1948
|
-
// Remove null characters
|
|
1949
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1950
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
1951
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1952
|
-
.replace(/\u0000/g, '');
|
|
1953
|
-
}
|
|
1954
|
-
catch (err) {
|
|
1955
|
-
// Log the error for debugging
|
|
1956
|
-
console.error('Error getting platform information:', err);
|
|
1957
|
-
// Re-throw the error for higher-level handling
|
|
1958
|
-
throw err;
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
async getOS() {
|
|
1962
|
-
const keyword = '~OS';
|
|
1963
|
-
try {
|
|
1964
|
-
// Execute the command to get the OS information
|
|
1965
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1966
|
-
// Extract and format the OS information from the response data
|
|
1967
|
-
// Remove null characters
|
|
1968
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1969
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
1970
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1971
|
-
.replace(/\u0000/g, '');
|
|
1972
|
-
}
|
|
1973
|
-
catch (err) {
|
|
1974
|
-
// Log the error for debugging
|
|
1975
|
-
console.error('Error getting OS information:', err);
|
|
1976
|
-
// Re-throw the error for higher-level handling
|
|
1977
|
-
throw err;
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
async getWorkCode() {
|
|
1981
|
-
const keyword = 'WorkCode';
|
|
1982
|
-
try {
|
|
1983
|
-
// Execute the command to get the WorkCode information
|
|
1984
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1985
|
-
// Extract and format the WorkCode information from the response data
|
|
1986
|
-
// Remove null characters
|
|
1987
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1988
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
1989
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1990
|
-
.replace(/\u0000/g, '');
|
|
1991
|
-
}
|
|
1992
|
-
catch (err) {
|
|
1993
|
-
// Log the error for debugging
|
|
1994
|
-
console.error('Error getting WorkCode:', err);
|
|
1995
|
-
// Re-throw the error to be handled by the caller
|
|
1996
|
-
throw err;
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
async getPIN() {
|
|
2000
|
-
const keyword = '~PIN2Width';
|
|
2001
|
-
try {
|
|
2002
|
-
// Execute the command to get the PIN information
|
|
2003
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2004
|
-
// Extract and format the PIN information from the response data
|
|
2005
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
2006
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
2007
|
-
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
2008
|
-
.replace(/\u0000/g, ''); // Remove null characters 0x00
|
|
2009
|
-
}
|
|
2010
|
-
catch (err) {
|
|
2011
|
-
// Log the error for debugging
|
|
2012
|
-
console.error('Error getting PIN:', err);
|
|
2013
|
-
// Re-throw the error to be handled by the caller
|
|
2014
|
-
throw err;
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
async getFaceOn() {
|
|
2018
|
-
const keyword = 'FaceFunOn';
|
|
2019
|
-
try {
|
|
2020
|
-
// Execute the command to get the face function status
|
|
2021
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2022
|
-
// Extract and process the status from the response data
|
|
2023
|
-
const status = data.slice(8) // Skip the first 8 bytes (header)
|
|
2024
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
2025
|
-
.replace(`${keyword}=`, ''); // Remove the keyword prefix
|
|
2026
|
-
// Determine and return the face function status
|
|
2027
|
-
return status.includes('0') ? 'No' : 'Yes';
|
|
2028
|
-
}
|
|
2029
|
-
catch (err) {
|
|
2030
|
-
// Log the error for debugging
|
|
2031
|
-
console.error('Error getting face function status:', err);
|
|
2032
|
-
// Re-throw the error to be handled by the caller
|
|
2033
|
-
throw err;
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
async getSSR() {
|
|
2037
|
-
const keyword = '~SSR';
|
|
2038
|
-
try {
|
|
2039
|
-
// Execute the command to get the SSR value
|
|
2040
|
-
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
2041
|
-
// Extract and process the SSR value from the response data
|
|
2042
|
-
// Remove the keyword prefix
|
|
2043
|
-
// Return the SSR value
|
|
2044
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
2045
|
-
.toString('ascii') // Convert buffer to ASCII string
|
|
2046
|
-
.replace(`${keyword}=`, '');
|
|
2047
|
-
}
|
|
2048
|
-
catch (err) {
|
|
2049
|
-
// Log the error for debugging
|
|
2050
|
-
console.error('Error getting SSR value:', err);
|
|
2051
|
-
// Re-throw the error to be handled by the caller
|
|
2052
|
-
throw err;
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
async getFirmware() {
|
|
2056
|
-
try {
|
|
2057
|
-
// Execute the command to get firmware information
|
|
2058
|
-
const data = await this.executeCmd(1100, '');
|
|
2059
|
-
// Extract and return the firmware version from the response data
|
|
2060
|
-
return data.slice(8) // Skip the first 8 bytes (header)
|
|
2061
|
-
.toString('ascii') // convert to ASCII string
|
|
2062
|
-
.replace(/\u0000/g, ''); // remove x00
|
|
2063
|
-
}
|
|
2064
|
-
catch (err) {
|
|
2065
|
-
// Log the error for debugging
|
|
2066
|
-
console.error('Error getting firmware version:', err);
|
|
2067
|
-
// Re-throw the error to be handled by the caller
|
|
2068
|
-
throw err;
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
async getTime() {
|
|
2072
|
-
try {
|
|
2073
|
-
// Execute the command to get the current time
|
|
2074
|
-
const response = await this.executeCmd(COMMANDS.CMD_GET_TIME, '');
|
|
2075
|
-
// Check if the response is valid
|
|
2076
|
-
if (!response || response.length < 12) {
|
|
2077
|
-
throw new Error('Invalid response received for time command');
|
|
2078
|
-
}
|
|
2079
|
-
// Extract and decode the time value from the response
|
|
2080
|
-
const timeValue = response.readUInt32LE(8); // Read 4 bytes starting at offset 8
|
|
2081
|
-
return timeParser.decode(timeValue); // Parse and return the decoded time
|
|
2082
|
-
}
|
|
2083
|
-
catch (err) {
|
|
2084
|
-
// Log the error for debugging
|
|
2085
|
-
console.error('Error getting time:', err);
|
|
2086
|
-
// Re-throw the error for the caller to handle
|
|
2087
|
-
throw err;
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
async setTime(tm) {
|
|
2091
|
-
try {
|
|
2092
|
-
// Validate the input time
|
|
2093
|
-
if (!(tm instanceof Date) && typeof tm !== 'number') {
|
|
2094
|
-
throw new TypeError('Invalid time parameter. Must be a Date object or a timestamp.');
|
|
2095
|
-
}
|
|
2096
|
-
// Convert the input time to a Date object if it's not already
|
|
2097
|
-
const date = (tm instanceof Date) ? tm : new Date(tm);
|
|
2098
|
-
// Encode the time into the required format
|
|
2099
|
-
const encodedTime = timeParser.encode(date);
|
|
2100
|
-
// Create a buffer and write the encoded time
|
|
2101
|
-
const commandString = Buffer.alloc(32);
|
|
2102
|
-
commandString.writeUInt32LE(encodedTime, 0);
|
|
2103
|
-
// Send the command to set the time
|
|
2104
|
-
const time = await this.executeCmd(COMMANDS.CMD_SET_TIME, commandString);
|
|
2105
|
-
return !!time;
|
|
2106
|
-
}
|
|
2107
|
-
catch (err) {
|
|
2108
|
-
// Log the error for debugging
|
|
2109
|
-
console.error('Error setting time:', err);
|
|
2110
|
-
// Re-throw the error for the caller to handle
|
|
2111
|
-
throw err;
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
async voiceTest() {
|
|
2115
|
-
try {
|
|
2116
|
-
// Define the command data for the voice test
|
|
2117
|
-
const commandData = Buffer.from('\x00\x00', 'binary');
|
|
2118
|
-
await this.executeCmd(COMMANDS.CMD_TESTVOICE, commandData);
|
|
2119
|
-
// Execute the command and return the result
|
|
2120
|
-
}
|
|
2121
|
-
catch (err) {
|
|
2122
|
-
// Log the error for debugging purposes
|
|
2123
|
-
console.error('Error executing voice test:', err);
|
|
2124
|
-
// Re-throw the error to be handled by the caller
|
|
2125
|
-
throw err;
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2792
|
+
#listeners = new Map();
|
|
2128
2793
|
async getAttendanceSize() {
|
|
2129
2794
|
try {
|
|
2130
2795
|
// Execute command to get free sizes
|
|
2131
|
-
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES,
|
|
2796
|
+
const data = await this.executeCmd(COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
2132
2797
|
// Parse and return the attendance size
|
|
2133
2798
|
return data.readUIntLE(40, 4); // Assuming data at offset 40 represents the attendance size
|
|
2134
2799
|
}
|
|
2135
2800
|
catch (err) {
|
|
2136
2801
|
// Log error details for debugging
|
|
2137
|
-
console.error(
|
|
2802
|
+
console.error("Error getting attendance size:", err);
|
|
2138
2803
|
// Re-throw the error to be handled by the caller
|
|
2139
2804
|
throw err;
|
|
2140
2805
|
}
|
|
@@ -2165,7 +2830,7 @@ class ZTCP {
|
|
|
2165
2830
|
}
|
|
2166
2831
|
catch (err) {
|
|
2167
2832
|
// Log the error for debugging purposes
|
|
2168
|
-
console.error(
|
|
2833
|
+
console.error("Error clearing data:", err);
|
|
2169
2834
|
// Re-throw the error to be handled by the caller
|
|
2170
2835
|
throw err;
|
|
2171
2836
|
}
|
|
@@ -2176,22 +2841,23 @@ class ZTCP {
|
|
|
2176
2841
|
// Create a buffer with the command header to request real-time logs
|
|
2177
2842
|
const buf = createTCPHeader(COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, Buffer.from([0x01, 0x00, 0x00, 0x00]));
|
|
2178
2843
|
// Send the request to the device
|
|
2179
|
-
this.socket
|
|
2844
|
+
this.socket?.write(buf, undefined, (err) => {
|
|
2180
2845
|
if (err) {
|
|
2181
2846
|
// Log and reject the promise if there is an error writing to the socket
|
|
2182
|
-
console.error(
|
|
2847
|
+
console.error("Error sending real-time logs request:", err);
|
|
2183
2848
|
throw err;
|
|
2184
2849
|
}
|
|
2185
2850
|
});
|
|
2186
2851
|
// Ensure data listeners are added only once
|
|
2187
|
-
if (this.socket
|
|
2188
|
-
|
|
2852
|
+
if (this.socket?.listenerCount("data") === 0) {
|
|
2853
|
+
console.log("entraaa");
|
|
2854
|
+
this.socket.on("data", (data) => {
|
|
2189
2855
|
// Check if the data is an event and not just a regular response
|
|
2190
2856
|
if (checkNotEventTCP(data)) {
|
|
2191
2857
|
// Process the data if it is of the expected length
|
|
2192
2858
|
if (data.length > 16) {
|
|
2193
2859
|
// Decode and pass the log to the callback
|
|
2194
|
-
cb(
|
|
2860
|
+
cb(decodeRTEvent(data));
|
|
2195
2861
|
}
|
|
2196
2862
|
}
|
|
2197
2863
|
});
|
|
@@ -2199,7 +2865,7 @@ class ZTCP {
|
|
|
2199
2865
|
}
|
|
2200
2866
|
catch (err) {
|
|
2201
2867
|
// Handle errors and reject the promise
|
|
2202
|
-
console.error(
|
|
2868
|
+
console.error("Error getting real-time logs:", err);
|
|
2203
2869
|
throw err;
|
|
2204
2870
|
}
|
|
2205
2871
|
}
|
|
@@ -2231,11 +2897,11 @@ class ZTCP {
|
|
|
2231
2897
|
}
|
|
2232
2898
|
async refreshData() {
|
|
2233
2899
|
try {
|
|
2234
|
-
const reply = await this.executeCmd(COMMANDS.CMD_REFRESHDATA,
|
|
2900
|
+
const reply = await this.executeCmd(COMMANDS.CMD_REFRESHDATA, "");
|
|
2235
2901
|
return !!reply;
|
|
2236
2902
|
}
|
|
2237
2903
|
catch (err) {
|
|
2238
|
-
console.error(
|
|
2904
|
+
console.error("Error getting user templates: ", err);
|
|
2239
2905
|
throw err;
|
|
2240
2906
|
}
|
|
2241
2907
|
}
|
|
@@ -2286,13 +2952,13 @@ class ZTCP {
|
|
|
2286
2952
|
}
|
|
2287
2953
|
async readSocket(length, cb = null) {
|
|
2288
2954
|
let replyBufer = Buffer.from([]);
|
|
2289
|
-
|
|
2955
|
+
const totalPackets = 0;
|
|
2290
2956
|
return new Promise((resolve, reject) => {
|
|
2291
2957
|
let timer = setTimeout(() => {
|
|
2292
|
-
internalCallback(replyBufer, new Error(
|
|
2958
|
+
internalCallback(replyBufer, new Error("TIMEOUT WHEN RECEIVING PACKET"));
|
|
2293
2959
|
}, this.timeout);
|
|
2294
2960
|
const internalCallback = (replyData, err = null) => {
|
|
2295
|
-
this.socket && this.socket.removeListener(
|
|
2961
|
+
this.socket && this.socket.removeListener("data", onDataEnroll);
|
|
2296
2962
|
timer && clearTimeout(timer);
|
|
2297
2963
|
resolve({ data: replyData, err: err });
|
|
2298
2964
|
};
|
|
@@ -2306,10 +2972,10 @@ class ZTCP {
|
|
|
2306
2972
|
internalCallback(data);
|
|
2307
2973
|
}
|
|
2308
2974
|
}
|
|
2309
|
-
this.socket.once(
|
|
2310
|
-
internalCallback(replyBufer, new Error(
|
|
2975
|
+
this.socket.once("close", () => {
|
|
2976
|
+
internalCallback(replyBufer, new Error("Socket is disconnected unexpectedly"));
|
|
2311
2977
|
});
|
|
2312
|
-
this.socket.on(
|
|
2978
|
+
this.socket.on("data", onDataEnroll);
|
|
2313
2979
|
}).catch((err) => {
|
|
2314
2980
|
console.error("Promise Rejected:", err); // Log the rejection reason
|
|
2315
2981
|
throw err; // Re-throw the error to be handled by the caller
|
|
@@ -2335,7 +3001,7 @@ class ZTCP {
|
|
|
2335
3001
|
}
|
|
2336
3002
|
async cancelCapture() {
|
|
2337
3003
|
try {
|
|
2338
|
-
const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE,
|
|
3004
|
+
const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, "");
|
|
2339
3005
|
return !!reply;
|
|
2340
3006
|
}
|
|
2341
3007
|
catch (e) {
|
|
@@ -2344,7 +3010,7 @@ class ZTCP {
|
|
|
2344
3010
|
}
|
|
2345
3011
|
async restartDevice() {
|
|
2346
3012
|
try {
|
|
2347
|
-
await this.executeCmd(COMMANDS.CMD_RESTART,
|
|
3013
|
+
await this.executeCmd(COMMANDS.CMD_RESTART, "");
|
|
2348
3014
|
}
|
|
2349
3015
|
catch (e) {
|
|
2350
3016
|
throw new ZkError(e, COMMANDS.CMD_RESTART, this.ip);
|
|
@@ -2413,7 +3079,7 @@ class ZUDP {
|
|
|
2413
3079
|
return true;
|
|
2414
3080
|
}
|
|
2415
3081
|
else {
|
|
2416
|
-
throw new Error(
|
|
3082
|
+
throw new Error('Authentication error');
|
|
2417
3083
|
}
|
|
2418
3084
|
}
|
|
2419
3085
|
else {
|
|
@@ -2841,22 +3507,22 @@ class Zklib {
|
|
|
2841
3507
|
async functionWrapper(tcpCallback, udpCallback, command) {
|
|
2842
3508
|
try {
|
|
2843
3509
|
switch (this._connectionType) {
|
|
2844
|
-
case
|
|
3510
|
+
case "tcp":
|
|
2845
3511
|
if (this.ztcp && this.ztcp.socket) {
|
|
2846
3512
|
return await tcpCallback();
|
|
2847
3513
|
}
|
|
2848
3514
|
else {
|
|
2849
|
-
throw new ZkError(new Error(
|
|
3515
|
+
throw new ZkError(new Error("TCP socket isn't connected!"), `[TCP] ${command}`, this.ip);
|
|
2850
3516
|
}
|
|
2851
|
-
case
|
|
3517
|
+
case "udp":
|
|
2852
3518
|
if (this.zudp && this.zudp.socket) {
|
|
2853
3519
|
return await udpCallback();
|
|
2854
3520
|
}
|
|
2855
3521
|
else {
|
|
2856
|
-
throw new ZkError(new Error(
|
|
3522
|
+
throw new ZkError(new Error("UDP socket isn't connected!"), `[UDP] ${command}`, this.ip);
|
|
2857
3523
|
}
|
|
2858
3524
|
default:
|
|
2859
|
-
throw new ZkError(new Error(
|
|
3525
|
+
throw new ZkError(new Error("Unsupported connection type or socket isn't connected!"), "", this.ip);
|
|
2860
3526
|
}
|
|
2861
3527
|
}
|
|
2862
3528
|
catch (err) {
|
|
@@ -2868,24 +3534,24 @@ class Zklib {
|
|
|
2868
3534
|
if (this.ztcp.socket) {
|
|
2869
3535
|
try {
|
|
2870
3536
|
await this.ztcp.connect();
|
|
2871
|
-
console.log(
|
|
2872
|
-
this._connectionType =
|
|
3537
|
+
console.log("TCP reconnection successful");
|
|
3538
|
+
this._connectionType = "tcp";
|
|
2873
3539
|
return true;
|
|
2874
3540
|
}
|
|
2875
3541
|
catch (err) {
|
|
2876
|
-
throw new ZkError(err,
|
|
3542
|
+
throw new ZkError(err, "TCP CONNECT", this.ip);
|
|
2877
3543
|
}
|
|
2878
3544
|
}
|
|
2879
3545
|
else {
|
|
2880
3546
|
try {
|
|
2881
3547
|
await this.ztcp.createSocket(cbErr, cbClose);
|
|
2882
3548
|
await this.ztcp.connect();
|
|
2883
|
-
console.log(
|
|
2884
|
-
this._connectionType =
|
|
3549
|
+
console.log("TCP connection successful");
|
|
3550
|
+
this._connectionType = "tcp";
|
|
2885
3551
|
return true;
|
|
2886
3552
|
}
|
|
2887
3553
|
catch (err) {
|
|
2888
|
-
throw new ZkError(err,
|
|
3554
|
+
throw new ZkError(err, "TCP CONNECT", this.ip);
|
|
2889
3555
|
}
|
|
2890
3556
|
}
|
|
2891
3557
|
}
|
|
@@ -2895,86 +3561,114 @@ class Zklib {
|
|
|
2895
3561
|
await this.ztcp.disconnect();
|
|
2896
3562
|
}
|
|
2897
3563
|
catch (disconnectErr) {
|
|
2898
|
-
console.error(
|
|
3564
|
+
console.error("Error disconnecting TCP:", disconnectErr);
|
|
2899
3565
|
}
|
|
2900
3566
|
if (err.code !== ERROR_TYPES.ECONNREFUSED) {
|
|
2901
|
-
throw new ZkError(err,
|
|
3567
|
+
throw new ZkError(err, "TCP CONNECT", this.ip);
|
|
2902
3568
|
}
|
|
2903
3569
|
try {
|
|
2904
3570
|
if (!this.zudp.socket) {
|
|
2905
3571
|
await this.zudp.createSocket(cbErr, cbClose);
|
|
2906
3572
|
}
|
|
2907
3573
|
await this.zudp.connect();
|
|
2908
|
-
console.log(
|
|
2909
|
-
this._connectionType =
|
|
3574
|
+
console.log("UDP connection successful");
|
|
3575
|
+
this._connectionType = "udp";
|
|
2910
3576
|
return true;
|
|
2911
3577
|
}
|
|
2912
3578
|
catch (err) {
|
|
2913
|
-
if (err.message !==
|
|
3579
|
+
if (err.message !== "EADDRINUSE") {
|
|
2914
3580
|
this._connectionType = null;
|
|
2915
3581
|
try {
|
|
2916
3582
|
await this.zudp.disconnect();
|
|
2917
3583
|
}
|
|
2918
3584
|
catch (disconnectErr) {
|
|
2919
|
-
console.error(
|
|
3585
|
+
console.error("Error disconnecting UDP:", disconnectErr);
|
|
2920
3586
|
}
|
|
2921
|
-
throw new ZkError(err,
|
|
3587
|
+
throw new ZkError(err, "UDP CONNECT", this.ip);
|
|
2922
3588
|
}
|
|
2923
|
-
this._connectionType =
|
|
3589
|
+
this._connectionType = "udp";
|
|
2924
3590
|
return true;
|
|
2925
3591
|
}
|
|
2926
3592
|
}
|
|
2927
3593
|
}
|
|
2928
3594
|
async getUsers() {
|
|
2929
|
-
return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(),
|
|
3595
|
+
return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(), "GET_USERS");
|
|
2930
3596
|
}
|
|
2931
3597
|
async getTime() {
|
|
2932
|
-
return this.functionWrapper(() => this.ztcp.getTime(), () => this.zudp.getTime(),
|
|
3598
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getTime(), () => this.zudp.getTime(), "GET_TIME");
|
|
2933
3599
|
}
|
|
2934
3600
|
async setTime(t) {
|
|
2935
|
-
return this.functionWrapper(() => this.ztcp.setTime(t), () => this.zudp.setTime(t),
|
|
3601
|
+
return this.functionWrapper(() => this.ztcp._optionsService.setTime(t), () => this.zudp.setTime(t), "SET_TIME");
|
|
2936
3602
|
}
|
|
2937
3603
|
async voiceTest() {
|
|
2938
|
-
return this.functionWrapper(() => this.ztcp.voiceTest(), async () => {
|
|
3604
|
+
return this.functionWrapper(() => this.ztcp._optionsService.voiceTest(), async () => {
|
|
3605
|
+
throw new Error("UDP voice test not supported");
|
|
3606
|
+
}, "VOICE_TEST");
|
|
2939
3607
|
}
|
|
2940
3608
|
async getProductTime() {
|
|
2941
|
-
return this.functionWrapper(() => this.ztcp.getProductTime(), async () => {
|
|
3609
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getProductTime(), async () => {
|
|
3610
|
+
throw new Error("UDP get product time not supported");
|
|
3611
|
+
}, "GET_PRODUCT_TIME");
|
|
2942
3612
|
}
|
|
2943
3613
|
async getVendor() {
|
|
2944
|
-
return this.functionWrapper(() => this.ztcp.getVendor(), async () => {
|
|
3614
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getVendor(), async () => {
|
|
3615
|
+
throw new Error("UDP get vendor not supported");
|
|
3616
|
+
}, "GET_VENDOR");
|
|
2945
3617
|
}
|
|
2946
3618
|
async getMacAddress() {
|
|
2947
|
-
return this.functionWrapper(() => this.ztcp.getMacAddress(), async () => {
|
|
3619
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getMacAddress(), async () => {
|
|
3620
|
+
throw new Error("UDP get MAC address not supported");
|
|
3621
|
+
}, "GET_MAC_ADDRESS");
|
|
2948
3622
|
}
|
|
2949
3623
|
async getSerialNumber() {
|
|
2950
|
-
return this.functionWrapper(() => this.ztcp.getSerialNumber(), async () => {
|
|
3624
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getSerialNumber(), async () => {
|
|
3625
|
+
throw new Error("UDP get serial number not supported");
|
|
3626
|
+
}, "GET_SERIAL_NUMBER");
|
|
2951
3627
|
}
|
|
2952
3628
|
async getDeviceVersion() {
|
|
2953
|
-
return this.functionWrapper(() => this.ztcp.getDeviceVersion(), async () => {
|
|
3629
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getDeviceVersion(), async () => {
|
|
3630
|
+
throw new Error("UDP get device version not supported");
|
|
3631
|
+
}, "GET_DEVICE_VERSION");
|
|
2954
3632
|
}
|
|
2955
3633
|
async getDeviceName() {
|
|
2956
|
-
return this.functionWrapper(() => this.ztcp.getDeviceName(), async () => {
|
|
3634
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getDeviceName(), async () => {
|
|
3635
|
+
throw new Error("UDP get device name not supported");
|
|
3636
|
+
}, "GET_DEVICE_NAME");
|
|
2957
3637
|
}
|
|
2958
3638
|
async getPlatform() {
|
|
2959
|
-
return this.functionWrapper(() => this.ztcp.getPlatform(), async () => {
|
|
3639
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getPlatform(), async () => {
|
|
3640
|
+
throw new Error("UDP get platform not supported");
|
|
3641
|
+
}, "GET_PLATFORM");
|
|
2960
3642
|
}
|
|
2961
3643
|
async getOS() {
|
|
2962
|
-
return this.functionWrapper(() => this.ztcp.getOS(), async () => {
|
|
3644
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getOS(), async () => {
|
|
3645
|
+
throw new Error("UDP get OS not supported");
|
|
3646
|
+
}, "GET_OS");
|
|
2963
3647
|
}
|
|
2964
3648
|
async getWorkCode() {
|
|
2965
|
-
return this.functionWrapper(() => this.ztcp.getWorkCode(), async () => {
|
|
3649
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getWorkCode(), async () => {
|
|
3650
|
+
throw new Error("UDP get work code not supported");
|
|
3651
|
+
}, "GET_WORK_CODE");
|
|
2966
3652
|
}
|
|
2967
3653
|
async getPIN() {
|
|
2968
|
-
return this.functionWrapper(() => this.ztcp.getPIN(), async () => {
|
|
3654
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getPIN(), async () => {
|
|
3655
|
+
throw new Error("UDP get PIN not supported");
|
|
3656
|
+
}, "GET_PIN");
|
|
2969
3657
|
}
|
|
2970
3658
|
async getFaceOn() {
|
|
2971
|
-
return this.functionWrapper(() => this.ztcp.getFaceOn(), async () => {
|
|
3659
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getFaceOn(), async () => {
|
|
3660
|
+
throw new Error("UDP get face on not supported");
|
|
3661
|
+
}, "GET_FACE_ON");
|
|
2972
3662
|
}
|
|
2973
3663
|
async getSSR() {
|
|
2974
|
-
return this.functionWrapper(() => this.ztcp.getSSR(), async () => {
|
|
3664
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getSSR(), async () => {
|
|
3665
|
+
throw new Error("UDP get SSR not supported");
|
|
3666
|
+
}, "GET_SSR");
|
|
2975
3667
|
}
|
|
2976
3668
|
async getFirmware() {
|
|
2977
|
-
return this.functionWrapper(() => this.ztcp.getFirmware(), async () => {
|
|
3669
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getFirmware(), async () => {
|
|
3670
|
+
throw new Error("UDP get firmware not supported");
|
|
3671
|
+
}, "GET_FIRMWARE");
|
|
2978
3672
|
}
|
|
2979
3673
|
/** Update or create a user if user id/pin not exists
|
|
2980
3674
|
* @param user_id {string} user id/pin for customer
|
|
@@ -2984,26 +3678,34 @@ class Zklib {
|
|
|
2984
3678
|
* @param cardno {number} card number/id
|
|
2985
3679
|
*/
|
|
2986
3680
|
async setUser(user_id, name, password, role = 0, cardno = 0) {
|
|
2987
|
-
return this.functionWrapper(() => this.ztcp._userService.setUser(user_id, name, password, role, cardno), async () => {
|
|
3681
|
+
return this.functionWrapper(() => this.ztcp._userService.setUser(user_id, name, password, role, cardno), async () => {
|
|
3682
|
+
throw new Error("UDP set user not supported");
|
|
3683
|
+
}, "SET_USER");
|
|
2988
3684
|
}
|
|
2989
3685
|
/**
|
|
2990
3686
|
* Delete user by a given user id/pin
|
|
2991
3687
|
* @param user_id {string}
|
|
2992
3688
|
*/
|
|
2993
3689
|
async deleteUser(user_id) {
|
|
2994
|
-
return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => {
|
|
3690
|
+
return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => {
|
|
3691
|
+
throw new Error("UDP delete user not supported");
|
|
3692
|
+
}, "DELETE_USER");
|
|
2995
3693
|
}
|
|
2996
3694
|
async getAttendanceSize() {
|
|
2997
|
-
return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => {
|
|
3695
|
+
return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => {
|
|
3696
|
+
throw new Error("UDP get attendance size not supported");
|
|
3697
|
+
}, "GET_ATTENDANCE_SIZE");
|
|
2998
3698
|
}
|
|
2999
3699
|
async getAttendances(cb) {
|
|
3000
|
-
return this.functionWrapper(() => this.ztcp.getAttendances(cb), () => this.zudp.getAttendances(cb),
|
|
3700
|
+
return this.functionWrapper(() => this.ztcp._transactionService.getAttendances(cb), () => this.zudp.getAttendances(cb), "GET_ATTENDANCES");
|
|
3001
3701
|
}
|
|
3002
3702
|
async getRealTimeLogs(cb) {
|
|
3003
|
-
return this.functionWrapper(() => this.ztcp.getRealTimeLogs(cb), () => this.zudp.getRealTimeLogs(cb),
|
|
3703
|
+
return this.functionWrapper(() => this.ztcp.getRealTimeLogs(cb), () => this.zudp.getRealTimeLogs(cb), "GET_REAL_TIME_LOGS");
|
|
3004
3704
|
}
|
|
3005
3705
|
async getTemplates() {
|
|
3006
|
-
return this.functionWrapper(() => this.ztcp.getTemplates(), async () => {
|
|
3706
|
+
return this.functionWrapper(() => this.ztcp.getTemplates(), async () => {
|
|
3707
|
+
throw new Error("UDP get templates not supported");
|
|
3708
|
+
}, "GET_TEMPLATES");
|
|
3007
3709
|
}
|
|
3008
3710
|
/**
|
|
3009
3711
|
* Get a user template for a given user id/pin and finger id
|
|
@@ -3011,7 +3713,9 @@ class Zklib {
|
|
|
3011
3713
|
* @param fid {number} finger index
|
|
3012
3714
|
*/
|
|
3013
3715
|
async getUserTemplate(user_id, fid) {
|
|
3014
|
-
return await this.functionWrapper(async () => await this.ztcp._userService.DownloadFp(user_id, fid), async () => {
|
|
3716
|
+
return await this.functionWrapper(async () => await this.ztcp._userService.DownloadFp(user_id, fid), async () => {
|
|
3717
|
+
throw new Error("UDP get user template not implemented");
|
|
3718
|
+
}, "GET_USER_TEMPLATE");
|
|
3015
3719
|
}
|
|
3016
3720
|
/**
|
|
3017
3721
|
* Upload a single fingerprint for a given user id
|
|
@@ -3021,7 +3725,9 @@ class Zklib {
|
|
|
3021
3725
|
* @param fp_valid {number} finger flag. e.g., valid=1, duress=3
|
|
3022
3726
|
*/
|
|
3023
3727
|
async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
|
|
3024
|
-
return await this.functionWrapper(async () => await this.ztcp._userService.uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid), async () => {
|
|
3728
|
+
return await this.functionWrapper(async () => await this.ztcp._userService.uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid), async () => {
|
|
3729
|
+
throw new Error("UDP get user template not implemented");
|
|
3730
|
+
}, "UPLOAD_USER_TEMPLATE");
|
|
3025
3731
|
}
|
|
3026
3732
|
/**
|
|
3027
3733
|
* save user and template
|
|
@@ -3030,7 +3736,9 @@ class Zklib {
|
|
|
3030
3736
|
* @param {Finger[]} fingers - Array of finger class
|
|
3031
3737
|
*/
|
|
3032
3738
|
async saveUserTemplate(user_id, fingers = []) {
|
|
3033
|
-
return await this.functionWrapper(async () => await this.ztcp._userService.saveTemplates(user_id, fingers), async () => {
|
|
3739
|
+
return await this.functionWrapper(async () => await this.ztcp._userService.saveTemplates(user_id, fingers), async () => {
|
|
3740
|
+
throw new Error("UDP save user template not supported");
|
|
3741
|
+
}, "SAVE_USER_TEMPLATE");
|
|
3034
3742
|
}
|
|
3035
3743
|
/**
|
|
3036
3744
|
* Delete a single finger template by user id and finger index
|
|
@@ -3040,7 +3748,9 @@ class Zklib {
|
|
|
3040
3748
|
async deleteFinger(user_id, fid) {
|
|
3041
3749
|
if (fid > 9 || 0 > fid)
|
|
3042
3750
|
throw new Error("fid params out of index");
|
|
3043
|
-
return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => {
|
|
3751
|
+
return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => {
|
|
3752
|
+
throw new Error("UDP delete finger not supported");
|
|
3753
|
+
}, "DELETE_FINGER");
|
|
3044
3754
|
}
|
|
3045
3755
|
/**
|
|
3046
3756
|
* Start to enroll a finger template
|
|
@@ -3050,43 +3760,53 @@ class Zklib {
|
|
|
3050
3760
|
async enrollUser(user_id, temp_id) {
|
|
3051
3761
|
if (temp_id < 0 || temp_id > 9)
|
|
3052
3762
|
throw new Error("temp_id out of range 0-9");
|
|
3053
|
-
return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => {
|
|
3763
|
+
return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => {
|
|
3764
|
+
throw new Error("UDP enroll user not supported");
|
|
3765
|
+
}, "ENROLL_USER");
|
|
3054
3766
|
}
|
|
3055
3767
|
async verifyUser(user_id) {
|
|
3056
|
-
return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => {
|
|
3768
|
+
return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => {
|
|
3769
|
+
throw new Error("UDP verify user not supported");
|
|
3770
|
+
}, "VERIFY_USER");
|
|
3057
3771
|
}
|
|
3058
3772
|
async restartDevice() {
|
|
3059
|
-
return this.functionWrapper(() => this.ztcp.restartDevice(), async () => {
|
|
3773
|
+
return this.functionWrapper(() => this.ztcp.restartDevice(), async () => {
|
|
3774
|
+
throw new Error("UDP restart device not supported");
|
|
3775
|
+
}, "RESTART_DEVICE");
|
|
3060
3776
|
}
|
|
3061
3777
|
async getSizes() {
|
|
3062
|
-
return this.functionWrapper(() => this.ztcp.getSizes(), () => {
|
|
3778
|
+
return this.functionWrapper(() => this.ztcp.getSizes(), () => {
|
|
3779
|
+
throw new Error("not implemented ofr UDP");
|
|
3780
|
+
}, "GET_SIZES");
|
|
3063
3781
|
}
|
|
3064
3782
|
async disconnect() {
|
|
3065
|
-
return this.functionWrapper(() => this.ztcp.disconnect(), () => this.zudp.disconnect(),
|
|
3783
|
+
return this.functionWrapper(() => this.ztcp.disconnect(), () => this.zudp.disconnect(), "DISCONNECT");
|
|
3066
3784
|
}
|
|
3067
3785
|
async freeData() {
|
|
3068
|
-
return this.functionWrapper(() => this.ztcp.freeData(), () => this.zudp.freeData(),
|
|
3786
|
+
return this.functionWrapper(() => this.ztcp.freeData(), () => this.zudp.freeData(), "FREE_DATA");
|
|
3069
3787
|
}
|
|
3070
3788
|
async disableDevice() {
|
|
3071
|
-
return this.functionWrapper(() => this.ztcp.disableDevice(), () => this.zudp.disableDevice(),
|
|
3789
|
+
return this.functionWrapper(() => this.ztcp.disableDevice(), () => this.zudp.disableDevice(), "DISABLE_DEVICE");
|
|
3072
3790
|
}
|
|
3073
3791
|
async enableDevice() {
|
|
3074
|
-
return this.functionWrapper(() => this.ztcp.enableDevice(), () => this.zudp.enableDevice(),
|
|
3792
|
+
return this.functionWrapper(() => this.ztcp.enableDevice(), () => this.zudp.enableDevice(), "ENABLE_DEVICE");
|
|
3075
3793
|
}
|
|
3076
3794
|
async getInfo() {
|
|
3077
|
-
return this.functionWrapper(() => this.ztcp.getInfo(), () => this.zudp.getInfo(),
|
|
3795
|
+
return this.functionWrapper(() => this.ztcp.getInfo(), () => this.zudp.getInfo(), "GET_INFO");
|
|
3078
3796
|
}
|
|
3079
3797
|
async clearAttendanceLog() {
|
|
3080
|
-
return this.functionWrapper(() => this.ztcp.clearAttendanceLog(), () => this.zudp.clearAttendanceLog(),
|
|
3798
|
+
return this.functionWrapper(() => this.ztcp.clearAttendanceLog(), () => this.zudp.clearAttendanceLog(), "CLEAR_ATTENDANCE_LOG");
|
|
3081
3799
|
}
|
|
3082
3800
|
async clearData() {
|
|
3083
|
-
return this.functionWrapper(() => this.ztcp.clearData(), () => this.zudp.clearData(),
|
|
3801
|
+
return this.functionWrapper(() => this.ztcp.clearData(), () => this.zudp.clearData(), "CLEAR_DATA");
|
|
3084
3802
|
}
|
|
3085
|
-
async executeCmd(command, data =
|
|
3086
|
-
return this.functionWrapper(() => this.ztcp.executeCmd(command, data), () => this.zudp.executeCmd(command, data),
|
|
3803
|
+
async executeCmd(command, data = "") {
|
|
3804
|
+
return this.functionWrapper(() => this.ztcp.executeCmd(command, data), () => this.zudp.executeCmd(command, data), "EXECUTE_CMD");
|
|
3087
3805
|
}
|
|
3088
3806
|
async getNetworkParams() {
|
|
3089
|
-
return this.functionWrapper(() => this.ztcp.getNetworkParams(), async () => {
|
|
3807
|
+
return this.functionWrapper(() => this.ztcp._optionsService.getNetworkParams(), async () => {
|
|
3808
|
+
throw new Error("UDP getNetworkParams not implemented");
|
|
3809
|
+
}, "NETWORK_PARAMS");
|
|
3090
3810
|
}
|
|
3091
3811
|
}
|
|
3092
3812
|
|