zklib-ts 1.0.5 → 1.0.6
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/README.md +18 -0
- package/dist/helper/command.d.ts +177 -85
- package/dist/index.cjs.js +794 -478
- package/dist/index.d.ts +47 -6
- package/dist/index.es.js +794 -478
- package/dist/models/Attendance.d.ts +7 -2
- package/dist/services/transaction.service.d.ts +9 -0
- package/dist/services/user.service.d.ts +33 -0
- package/dist/ztcp.d.ts +22 -31
- package/dist/zudp.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -5,99 +5,190 @@ import * as dgram from 'node:dgram';
|
|
|
5
5
|
var COMMANDS;
|
|
6
6
|
(function (COMMANDS) {
|
|
7
7
|
COMMANDS[COMMANDS["CMD_ACK_DATA"] = 2002] = "CMD_ACK_DATA";
|
|
8
|
+
/** There was an error when processing the request.*/
|
|
8
9
|
COMMANDS[COMMANDS["CMD_ACK_ERROR"] = 2001] = "CMD_ACK_ERROR";
|
|
9
10
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_CMD"] = 65533] = "CMD_ACK_ERROR_CMD";
|
|
10
11
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_DATA"] = 65531] = "CMD_ACK_ERROR_DATA";
|
|
11
12
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_INIT"] = 65532] = "CMD_ACK_ERROR_INIT";
|
|
13
|
+
/** [0xD0, 0x07] The request was processed sucessfully. */
|
|
12
14
|
COMMANDS[COMMANDS["CMD_ACK_OK"] = 2000] = "CMD_ACK_OK";
|
|
13
15
|
COMMANDS[COMMANDS["CMD_ACK_REPEAT"] = 2004] = "CMD_ACK_REPEAT";
|
|
14
16
|
COMMANDS[COMMANDS["CMD_ACK_RETRY"] = 2003] = "CMD_ACK_RETRY";
|
|
17
|
+
/** [0xD5, 0x07] Connection not authorized. */
|
|
15
18
|
COMMANDS[COMMANDS["CMD_ACK_UNAUTH"] = 2005] = "CMD_ACK_UNAUTH";
|
|
19
|
+
/** Received unknown command. */
|
|
16
20
|
COMMANDS[COMMANDS["CMD_ACK_UNKNOWN"] = 65535] = "CMD_ACK_UNKNOWN";
|
|
21
|
+
/** Request attendance log. */
|
|
17
22
|
COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
|
|
23
|
+
/** [0x4E, 0x04] Request to begin session using commkey. */
|
|
18
24
|
COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
|
|
25
|
+
/** Disable normal authentication of users. */
|
|
19
26
|
COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
|
|
27
|
+
/** Capture fingerprint picture. */
|
|
20
28
|
COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
|
|
29
|
+
/** Capture the entire image. */
|
|
21
30
|
COMMANDS[COMMANDS["CMD_CAPTUREIMAGE"] = 1012] = "CMD_CAPTUREIMAGE";
|
|
31
|
+
/** Change transmission speed. */
|
|
22
32
|
COMMANDS[COMMANDS["CMD_CHANGE_SPEED"] = 1101] = "CMD_CHANGE_SPEED";
|
|
33
|
+
/** [0x77, 0x00] Get checksum of machine's buffer. */
|
|
23
34
|
COMMANDS[COMMANDS["CMD_CHECKSUM_BUFFER"] = 119] = "CMD_CHECKSUM_BUFFER";
|
|
35
|
+
/** Restore access control to default. */
|
|
24
36
|
COMMANDS[COMMANDS["CMD_CLEAR_ACC"] = 32] = "CMD_CLEAR_ACC";
|
|
37
|
+
/** Clears admins privileges. */
|
|
25
38
|
COMMANDS[COMMANDS["CMD_CLEAR_ADMIN"] = 20] = "CMD_CLEAR_ADMIN";
|
|
39
|
+
/** Delete attendance record. */
|
|
26
40
|
COMMANDS[COMMANDS["CMD_CLEAR_ATTLOG"] = 15] = "CMD_CLEAR_ATTLOG";
|
|
41
|
+
/** Delete data. */
|
|
27
42
|
COMMANDS[COMMANDS["CMD_CLEAR_DATA"] = 14] = "CMD_CLEAR_DATA";
|
|
43
|
+
/** Clear screen captions. */
|
|
28
44
|
COMMANDS[COMMANDS["CMD_CLEAR_LCD"] = 67] = "CMD_CLEAR_LCD";
|
|
45
|
+
/** Delete operations log. */
|
|
29
46
|
COMMANDS[COMMANDS["CMD_CLEAR_OPLOG"] = 33] = "CMD_CLEAR_OPLOG";
|
|
47
|
+
/** [0xE8, 0x03] Begin connection. */
|
|
30
48
|
COMMANDS[COMMANDS["CMD_CONNECT"] = 1000] = "CMD_CONNECT";
|
|
49
|
+
/** [0xDD, 0x05] Data packet. */
|
|
31
50
|
COMMANDS[COMMANDS["CMD_DATA"] = 1501] = "CMD_DATA";
|
|
51
|
+
/** Indicates that it is ready to receive data. */
|
|
32
52
|
COMMANDS[COMMANDS["CMD_DATA_RDY"] = 1504] = "CMD_DATA_RDY";
|
|
53
|
+
/** Read/Write a large data set. */
|
|
33
54
|
COMMANDS[COMMANDS["CMD_DATA_WRRQ"] = 1503] = "CMD_DATA_WRRQ";
|
|
55
|
+
/** Read saved data. */
|
|
34
56
|
COMMANDS[COMMANDS["CMD_DB_RRQ"] = 7] = "CMD_DB_RRQ";
|
|
57
|
+
/** Deletes fingerprint template. */
|
|
35
58
|
COMMANDS[COMMANDS["CMD_DEL_FPTMP"] = 134] = "CMD_DEL_FPTMP";
|
|
59
|
+
/** Delete short message. */
|
|
36
60
|
COMMANDS[COMMANDS["CMD_DELETE_SMS"] = 72] = "CMD_DELETE_SMS";
|
|
61
|
+
/** Delete user short message. */
|
|
37
62
|
COMMANDS[COMMANDS["CMD_DELETE_UDATA"] = 74] = "CMD_DELETE_UDATA";
|
|
63
|
+
/** Delete user. */
|
|
38
64
|
COMMANDS[COMMANDS["CMD_DELETE_USER"] = 18] = "CMD_DELETE_USER";
|
|
65
|
+
/** Delete user fingerprint template. */
|
|
39
66
|
COMMANDS[COMMANDS["CMD_DELETE_USERTEMP"] = 19] = "CMD_DELETE_USERTEMP";
|
|
67
|
+
/** Disables fingerprint, rfid reader and keyboard. */
|
|
40
68
|
COMMANDS[COMMANDS["CMD_DISABLEDEVICE"] = 1003] = "CMD_DISABLEDEVICE";
|
|
69
|
+
/** Get door state. */
|
|
41
70
|
COMMANDS[COMMANDS["CMD_DOORSTATE_RRQ"] = 75] = "CMD_DOORSTATE_RRQ";
|
|
71
|
+
/** Clear Mifare card. */
|
|
42
72
|
COMMANDS[COMMANDS["CMD_EMPTY_MIFARE"] = 78] = "CMD_EMPTY_MIFARE";
|
|
73
|
+
/** Enables the ":" in screen clock. */
|
|
43
74
|
COMMANDS[COMMANDS["CMD_ENABLE_CLOCK"] = 57] = "CMD_ENABLE_CLOCK";
|
|
75
|
+
/** Change machine state to "normal work". */
|
|
44
76
|
COMMANDS[COMMANDS["CMD_ENABLEDEVICE"] = 1002] = "CMD_ENABLEDEVICE";
|
|
77
|
+
/** [0xE9, 0x03] Disconnect. */
|
|
45
78
|
COMMANDS[COMMANDS["CMD_EXIT"] = 1001] = "CMD_EXIT";
|
|
79
|
+
/** [0xDE, 0x05] Release buffer used for data transmission. */
|
|
46
80
|
COMMANDS[COMMANDS["CMD_FREE_DATA"] = 1502] = "CMD_FREE_DATA";
|
|
81
|
+
/** Request machine status (remaining space). */
|
|
47
82
|
COMMANDS[COMMANDS["CMD_GET_FREE_SIZES"] = 50] = "CMD_GET_FREE_SIZES";
|
|
83
|
+
/** Request max size for users id. */
|
|
48
84
|
COMMANDS[COMMANDS["CMD_GET_PINWIDTH"] = 69] = "CMD_GET_PINWIDTH";
|
|
85
|
+
/** Request machine time. */
|
|
49
86
|
COMMANDS[COMMANDS["CMD_GET_TIME"] = 201] = "CMD_GET_TIME";
|
|
50
87
|
COMMANDS[COMMANDS["CMD_GET_USERTEMP"] = 88] = "CMD_GET_USERTEMP";
|
|
88
|
+
/** Request the firmware edition. */
|
|
51
89
|
COMMANDS[COMMANDS["CMD_GET_VERSION"] = 1100] = "CMD_GET_VERSION";
|
|
90
|
+
/** Get group timezone. */
|
|
52
91
|
COMMANDS[COMMANDS["CMD_GRPTZ_RRQ"] = 25] = "CMD_GRPTZ_RRQ";
|
|
92
|
+
/** Set group timezone. */
|
|
53
93
|
COMMANDS[COMMANDS["CMD_GRPTZ_WRQ"] = 26] = "CMD_GRPTZ_WRQ";
|
|
94
|
+
/** Read operations log. */
|
|
54
95
|
COMMANDS[COMMANDS["CMD_OPLOG_RRQ"] = 34] = "CMD_OPLOG_RRQ";
|
|
96
|
+
/** Read configuration value of the machine. */
|
|
55
97
|
COMMANDS[COMMANDS["CMD_OPTIONS_RRQ"] = 11] = "CMD_OPTIONS_RRQ";
|
|
98
|
+
/** Change configuration value of the machine. */
|
|
56
99
|
COMMANDS[COMMANDS["CMD_OPTIONS_WRQ"] = 12] = "CMD_OPTIONS_WRQ";
|
|
100
|
+
/** Shut-down machine. */
|
|
57
101
|
COMMANDS[COMMANDS["CMD_POWEROFF"] = 1005] = "CMD_POWEROFF";
|
|
102
|
+
/** [0xDC, 0x05] Prepare for data transmission. */
|
|
58
103
|
COMMANDS[COMMANDS["CMD_PREPARE_DATA"] = 1500] = "CMD_PREPARE_DATA";
|
|
104
|
+
/** [0xF5, 0x03] Refresh the machine stored data. */
|
|
59
105
|
COMMANDS[COMMANDS["CMD_REFRESHDATA"] = 1013] = "CMD_REFRESHDATA";
|
|
106
|
+
/** Refresh the configuration parameters. */
|
|
60
107
|
COMMANDS[COMMANDS["CMD_REFRESHOPTION"] = 1014] = "CMD_REFRESHOPTION";
|
|
108
|
+
/** Realtime events. */
|
|
61
109
|
COMMANDS[COMMANDS["CMD_REG_EVENT"] = 500] = "CMD_REG_EVENT";
|
|
110
|
+
/** Restart machine. */
|
|
62
111
|
COMMANDS[COMMANDS["CMD_RESTART"] = 1004] = "CMD_RESTART";
|
|
112
|
+
/** Change machine state to "awaken". */
|
|
63
113
|
COMMANDS[COMMANDS["CMD_RESUME"] = 1007] = "CMD_RESUME";
|
|
114
|
+
/** Set machine time. */
|
|
64
115
|
COMMANDS[COMMANDS["CMD_SET_TIME"] = 202] = "CMD_SET_TIME";
|
|
116
|
+
/** Change machine state to "idle". */
|
|
65
117
|
COMMANDS[COMMANDS["CMD_SLEEP"] = 1006] = "CMD_SLEEP";
|
|
118
|
+
/** Download short message. */
|
|
66
119
|
COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
|
|
120
|
+
/** Upload short message. */
|
|
67
121
|
COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
|
|
122
|
+
/** Start enroll procedure. */
|
|
68
123
|
COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
|
|
124
|
+
/** Set the machine to authentication state. */
|
|
69
125
|
COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
|
|
126
|
+
/** Query state. */
|
|
70
127
|
COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
|
|
128
|
+
/** Test if fingerprint exists. */
|
|
71
129
|
COMMANDS[COMMANDS["CMD_TEST_TEMP"] = 1011] = "CMD_TEST_TEMP";
|
|
130
|
+
/** Test voice. */
|
|
72
131
|
COMMANDS[COMMANDS["CMD_TESTVOICE"] = 1017] = "CMD_TESTVOICE";
|
|
132
|
+
/** [0x77, 0x00] Transfer fp template from buffer. */
|
|
73
133
|
COMMANDS[COMMANDS["CMD_TMP_WRITE"] = 87] = "CMD_TMP_WRITE";
|
|
134
|
+
/** Get device timezones. */
|
|
74
135
|
COMMANDS[COMMANDS["CMD_TZ_RRQ"] = 27] = "CMD_TZ_RRQ";
|
|
136
|
+
/** Set device timezones. */
|
|
75
137
|
COMMANDS[COMMANDS["CMD_TZ_WRQ"] = 28] = "CMD_TZ_WRQ";
|
|
138
|
+
/** Set user short message. */
|
|
76
139
|
COMMANDS[COMMANDS["CMD_UDATA_WRQ"] = 73] = "CMD_UDATA_WRQ";
|
|
140
|
+
/** Get group combination to unlock. */
|
|
77
141
|
COMMANDS[COMMANDS["CMD_ULG_RRQ"] = 29] = "CMD_ULG_RRQ";
|
|
142
|
+
/** Set group combination to unlock. */
|
|
78
143
|
COMMANDS[COMMANDS["CMD_ULG_WRQ"] = 30] = "CMD_ULG_WRQ";
|
|
144
|
+
/** Unlock door for a specified amount of time. */
|
|
79
145
|
COMMANDS[COMMANDS["CMD_UNLOCK"] = 31] = "CMD_UNLOCK";
|
|
146
|
+
/** Upload user data. */
|
|
80
147
|
COMMANDS[COMMANDS["CMD_USER_WRQ"] = 8] = "CMD_USER_WRQ";
|
|
148
|
+
/** Read user group. */
|
|
81
149
|
COMMANDS[COMMANDS["CMD_USERGRP_RRQ"] = 21] = "CMD_USERGRP_RRQ";
|
|
150
|
+
/** Set user group. */
|
|
82
151
|
COMMANDS[COMMANDS["CMD_USERGRP_WRQ"] = 22] = "CMD_USERGRP_WRQ";
|
|
152
|
+
/** [0x09, 0x00] Read user fingerprint template. */
|
|
83
153
|
COMMANDS[COMMANDS["CMD_USERTEMP_RRQ"] = 9] = "CMD_USERTEMP_RRQ";
|
|
154
|
+
/** Upload user fingerprint template. */
|
|
84
155
|
COMMANDS[COMMANDS["CMD_USERTEMP_WRQ"] = 10] = "CMD_USERTEMP_WRQ";
|
|
156
|
+
/** Get user timezones. */
|
|
85
157
|
COMMANDS[COMMANDS["CMD_USERTZ_RRQ"] = 23] = "CMD_USERTZ_RRQ";
|
|
158
|
+
/** Set the user timezones. */
|
|
86
159
|
COMMANDS[COMMANDS["CMD_USERTZ_WRQ"] = 24] = "CMD_USERTZ_WRQ";
|
|
160
|
+
/** Read verification style of a given user. */
|
|
87
161
|
COMMANDS[COMMANDS["CMD_VERIFY_RRQ"] = 80] = "CMD_VERIFY_RRQ";
|
|
162
|
+
/** Change verification style of a given user. */
|
|
88
163
|
COMMANDS[COMMANDS["CMD_VERIFY_WRQ"] = 79] = "CMD_VERIFY_WRQ";
|
|
164
|
+
/** Prints chars to the device screen. */
|
|
89
165
|
COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
|
|
166
|
+
/** Write data to Mifare card. */
|
|
90
167
|
COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
|
|
168
|
+
/** Triggered alarm. */
|
|
91
169
|
COMMANDS[COMMANDS["EF_ALARM"] = 512] = "EF_ALARM";
|
|
170
|
+
/** Attendance entry. */
|
|
92
171
|
COMMANDS[COMMANDS["EF_ATTLOG"] = 1] = "EF_ATTLOG";
|
|
172
|
+
/** Pressed keyboard key. */
|
|
93
173
|
COMMANDS[COMMANDS["EF_BUTTON"] = 16] = "EF_BUTTON";
|
|
174
|
+
/** Upload user data. */
|
|
94
175
|
COMMANDS[COMMANDS["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
|
|
176
|
+
/** Enrolled user. */
|
|
95
177
|
COMMANDS[COMMANDS["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
|
|
178
|
+
/** Pressed finger. */
|
|
96
179
|
COMMANDS[COMMANDS["EF_FINGER"] = 2] = "EF_FINGER";
|
|
180
|
+
/** Fingerprint score in enroll procedure. */
|
|
97
181
|
COMMANDS[COMMANDS["EF_FPFTR"] = 256] = "EF_FPFTR";
|
|
182
|
+
/** Restore access control to default. */
|
|
98
183
|
COMMANDS[COMMANDS["EF_UNLOCK"] = 32] = "EF_UNLOCK";
|
|
184
|
+
/** Registered user placed finger. */
|
|
99
185
|
COMMANDS[COMMANDS["EF_VERIFY"] = 128] = "EF_VERIFY";
|
|
100
186
|
})(COMMANDS || (COMMANDS = {}));
|
|
187
|
+
var DISCOVERED_CMD;
|
|
188
|
+
(function (DISCOVERED_CMD) {
|
|
189
|
+
/** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
|
|
190
|
+
DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
|
|
191
|
+
})(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
|
|
101
192
|
var Constants;
|
|
102
193
|
(function (Constants) {
|
|
103
194
|
Constants[Constants["USHRT_MAX"] = 65535] = "USHRT_MAX";
|
|
@@ -106,6 +197,7 @@ var Constants;
|
|
|
106
197
|
Constants[Constants["MACHINE_PREPARE_DATA_2"] = 32130] = "MACHINE_PREPARE_DATA_2";
|
|
107
198
|
})(Constants || (Constants = {}));
|
|
108
199
|
const REQUEST_DATA = {
|
|
200
|
+
START_TAG: Buffer.from([0x50, 0x50, 0x82, 0x7d]),
|
|
109
201
|
DISABLE_DEVICE: Buffer.from([0, 0, 0, 0]),
|
|
110
202
|
GET_REAL_TIME_EVENT: Buffer.from([0x01, 0x00, 0x00, 0x00]),
|
|
111
203
|
GET_ATTENDANCE_LOGS: Buffer.from([0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
|
@@ -253,18 +345,23 @@ class User {
|
|
|
253
345
|
}
|
|
254
346
|
|
|
255
347
|
class Attendance {
|
|
348
|
+
/** Internal serial number for the user */
|
|
256
349
|
sn;
|
|
350
|
+
/** User ID/Pin stored as a string */
|
|
257
351
|
user_id;
|
|
258
|
-
|
|
352
|
+
/** Verification type */
|
|
259
353
|
type;
|
|
354
|
+
/** Time of the attendance event */
|
|
355
|
+
record_time;
|
|
356
|
+
/** Verify state */
|
|
260
357
|
state;
|
|
261
358
|
ip;
|
|
262
|
-
constructor(sn, user_id,
|
|
359
|
+
constructor(sn, user_id, type, record_time, state) {
|
|
263
360
|
this.sn = sn;
|
|
264
361
|
this.user_id = user_id;
|
|
362
|
+
this.type = type || undefined;
|
|
265
363
|
this.record_time = record_time;
|
|
266
|
-
this.
|
|
267
|
-
this.state = state;
|
|
364
|
+
this.state = state || undefined;
|
|
268
365
|
}
|
|
269
366
|
}
|
|
270
367
|
|
|
@@ -308,17 +405,7 @@ const createUDPHeader = (command, sessionId, replyId, data) => {
|
|
|
308
405
|
return buf;
|
|
309
406
|
};
|
|
310
407
|
const createTCPHeader = (command, sessionId, replyId, data) => {
|
|
311
|
-
const
|
|
312
|
-
const buf = Buffer.alloc(8 + dataBuffer.length);
|
|
313
|
-
buf.writeUInt16LE(command, 0);
|
|
314
|
-
buf.writeUInt16LE(0, 2);
|
|
315
|
-
buf.writeUInt16LE(sessionId, 4);
|
|
316
|
-
buf.writeUInt16LE(replyId, 6);
|
|
317
|
-
dataBuffer.copy(buf, 8);
|
|
318
|
-
const chksum2 = createChkSum(buf);
|
|
319
|
-
buf.writeUInt16LE(chksum2, 2);
|
|
320
|
-
replyId = (replyId + 1) % Constants.USHRT_MAX;
|
|
321
|
-
buf.writeUInt16LE(replyId, 6);
|
|
408
|
+
const buf = createUDPHeader(command, sessionId, replyId, data);
|
|
322
409
|
const prefixBuf = Buffer.from([0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00]);
|
|
323
410
|
prefixBuf.writeUInt16LE(buf.length, 4);
|
|
324
411
|
return Buffer.concat([prefixBuf, buf]);
|
|
@@ -378,7 +465,7 @@ const decodeRecordData40 = (recordData) => {
|
|
|
378
465
|
.slice(2, 2 + 9)
|
|
379
466
|
.toString('ascii')
|
|
380
467
|
.split('\0')
|
|
381
|
-
.shift() || '',
|
|
468
|
+
.shift() || '', recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
|
|
382
469
|
};
|
|
383
470
|
const decodeRecordData16 = (recordData) => {
|
|
384
471
|
return {
|
|
@@ -494,6 +581,59 @@ const authKey = (comKey, sessionId) => {
|
|
|
494
581
|
return Array.from(response);
|
|
495
582
|
};
|
|
496
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Error types for device communication
|
|
586
|
+
*/
|
|
587
|
+
const ERROR_TYPES = {
|
|
588
|
+
ECONNRESET: 'ECONNRESET',
|
|
589
|
+
ECONNREFUSED: 'ECONNREFUSED'};
|
|
590
|
+
/**
|
|
591
|
+
* Custom error class for device communication errors
|
|
592
|
+
*/
|
|
593
|
+
class ZkError {
|
|
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
|
+
}
|
|
636
|
+
|
|
497
637
|
/**
|
|
498
638
|
* Represents a fingerprint template with associated metadata
|
|
499
639
|
*/
|
|
@@ -567,56 +707,547 @@ class Finger {
|
|
|
567
707
|
}
|
|
568
708
|
}
|
|
569
709
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
710
|
+
class UserService {
|
|
711
|
+
_zkTcp;
|
|
712
|
+
_users;
|
|
713
|
+
constructor(zkTcp) {
|
|
714
|
+
this._zkTcp = zkTcp;
|
|
715
|
+
}
|
|
716
|
+
async getUserByUserId(user_id) {
|
|
717
|
+
if (!this._users) {
|
|
718
|
+
await this.getUsers();
|
|
719
|
+
}
|
|
720
|
+
if (this._users.has(String(user_id))) {
|
|
721
|
+
return this._users.get(String(user_id));
|
|
722
|
+
}
|
|
723
|
+
else
|
|
724
|
+
throw new Error("user_id not exists");
|
|
725
|
+
}
|
|
726
|
+
async getUsers() {
|
|
727
|
+
try {
|
|
728
|
+
// Free any existing buffer data to prepare for a new request
|
|
729
|
+
if (this._users) {
|
|
730
|
+
return { data: Array.from(this._users.values()) };
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
this._users = new Map([]);
|
|
734
|
+
}
|
|
735
|
+
if (this._zkTcp.socket) {
|
|
736
|
+
await this._zkTcp.freeData();
|
|
737
|
+
}
|
|
738
|
+
// Request user data
|
|
739
|
+
const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_USERS);
|
|
740
|
+
// Free buffer data after receiving the data
|
|
741
|
+
if (this._zkTcp.socket) {
|
|
742
|
+
await this._zkTcp.freeData();
|
|
743
|
+
}
|
|
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
|
+
// Return the list of users
|
|
761
|
+
return { data: users };
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
// Log the error for debugging
|
|
765
|
+
console.error('Error getting users:', err);
|
|
766
|
+
// Re-throw the error to be handled by the caller
|
|
767
|
+
throw err;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
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
|
+
try {
|
|
782
|
+
// Validate input parameters
|
|
783
|
+
if (user_id.length > 9 ||
|
|
784
|
+
name.length > 24 ||
|
|
785
|
+
password.length > 8 ||
|
|
786
|
+
typeof role !== 'number' ||
|
|
787
|
+
cardno.toString().length > 10) {
|
|
788
|
+
throw new Error('Invalid input parameters');
|
|
789
|
+
}
|
|
790
|
+
// Allocate and initialize the buffer
|
|
791
|
+
const commandBuffer = Buffer.alloc(72);
|
|
792
|
+
// Fill the buffer with user data
|
|
793
|
+
commandBuffer.writeUInt16LE(user.uid, 0);
|
|
794
|
+
commandBuffer.writeUInt16LE(role, 2);
|
|
795
|
+
commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
|
|
796
|
+
commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
|
|
797
|
+
commandBuffer.writeUInt16LE(cardno, 35);
|
|
798
|
+
commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
|
|
799
|
+
commandBuffer.write(user_id.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
|
|
800
|
+
// Send the command and return the result
|
|
801
|
+
const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
|
|
802
|
+
return !!created;
|
|
803
|
+
}
|
|
804
|
+
catch (err) {
|
|
805
|
+
// Log error details for debugging
|
|
806
|
+
console.error('Error setting user:', err);
|
|
807
|
+
// Re-throw error for upstream handling
|
|
808
|
+
throw err;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
async DeleteUser(user_id) {
|
|
812
|
+
try {
|
|
813
|
+
const user = await this.getUserByUserId(user_id);
|
|
814
|
+
// Allocate and initialize the buffer
|
|
815
|
+
const commandBuffer = Buffer.alloc(72);
|
|
816
|
+
// Write UID to the buffer
|
|
817
|
+
commandBuffer.writeUInt16LE(user.uid, 0);
|
|
818
|
+
// Send the delete command and return the result
|
|
819
|
+
const deleted = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
|
|
820
|
+
return !!deleted;
|
|
821
|
+
}
|
|
822
|
+
catch (err) {
|
|
823
|
+
// Log error details for debugging
|
|
824
|
+
console.error('Error deleting user:', err);
|
|
825
|
+
// Re-throw error for upstream handling
|
|
826
|
+
throw err;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
async getTemplates(callbackInProcess = () => { }) {
|
|
830
|
+
let templates = [];
|
|
831
|
+
try {
|
|
832
|
+
if (this._zkTcp.socket) {
|
|
833
|
+
await this._zkTcp.freeData();
|
|
834
|
+
}
|
|
835
|
+
await this._zkTcp.getSizes();
|
|
836
|
+
if (this._zkTcp.fp_count == 0)
|
|
837
|
+
return { data: [] };
|
|
838
|
+
await this._zkTcp.disableDevice();
|
|
839
|
+
const resp = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
|
|
840
|
+
let templateData = resp.data.subarray(4);
|
|
841
|
+
let totalSize = resp.data.readUIntLE(0, 4);
|
|
842
|
+
while (totalSize) {
|
|
843
|
+
const buf = templateData.subarray(0, 6);
|
|
844
|
+
const size = buf.readUIntLE(0, 2);
|
|
845
|
+
const uid = buf.readUIntLE(2, 2);
|
|
846
|
+
const fid = buf.readUIntLE(4, 1);
|
|
847
|
+
const valid = buf.readUIntLE(5, 1);
|
|
848
|
+
// Force-copy bytes so we don't retain the entire big backing buffer
|
|
849
|
+
const tplBytes = Buffer.from(templateData.subarray(6, size));
|
|
850
|
+
templates.push(new Finger(uid, fid, valid, tplBytes));
|
|
851
|
+
templateData = templateData.subarray(size);
|
|
852
|
+
totalSize -= size;
|
|
853
|
+
}
|
|
854
|
+
return { data: templates };
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
this._zkTcp.verbose && console.log("Error getting templates", err);
|
|
858
|
+
return { data: templates };
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
await this._zkTcp.freeData();
|
|
862
|
+
await this._zkTcp.enableDevice();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
async DownloadFp(user_id, fid) {
|
|
866
|
+
try {
|
|
867
|
+
const user = await this.getUserByUserId(user_id);
|
|
868
|
+
if (0 > fid || fid > 9)
|
|
869
|
+
throw new Error('fid must be between 0 and 9');
|
|
870
|
+
// Allocate and initialize the buffer
|
|
871
|
+
const data = Buffer.alloc(3);
|
|
872
|
+
// Fill the buffer with user data
|
|
873
|
+
data.writeUInt16LE(user.uid, 0);
|
|
874
|
+
data.writeUIntLE(fid, 2, 1);
|
|
875
|
+
this._zkTcp.replyId++;
|
|
876
|
+
const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
|
|
877
|
+
let fingerSize = 0;
|
|
878
|
+
let fingerTemplate = Buffer.from([]);
|
|
879
|
+
return await new Promise((resolve, reject) => {
|
|
880
|
+
let timeout;
|
|
881
|
+
const cleanup = () => {
|
|
882
|
+
if (this._zkTcp.socket) {
|
|
883
|
+
this._zkTcp.socket.removeListener('data', receiveData);
|
|
884
|
+
}
|
|
885
|
+
if (timeout)
|
|
886
|
+
clearTimeout(timeout);
|
|
887
|
+
};
|
|
888
|
+
let timer = () => setTimeout(() => {
|
|
889
|
+
cleanup();
|
|
890
|
+
reject(new Error('Time Out, Could not retrieve data'));
|
|
891
|
+
}, this._zkTcp.timeout);
|
|
892
|
+
const receiveData = (data) => {
|
|
893
|
+
timeout = timer();
|
|
894
|
+
if (data.length === 0)
|
|
895
|
+
return;
|
|
896
|
+
try {
|
|
897
|
+
if (data.length == 0)
|
|
898
|
+
return;
|
|
899
|
+
const headers = decodeTCPHeader(data);
|
|
900
|
+
switch (headers.commandId) {
|
|
901
|
+
case DISCOVERED_CMD.FID_NOT_FOUND:
|
|
902
|
+
throw new Error('Could not retrieve data. maybe finger id not exists?');
|
|
903
|
+
case COMMANDS.CMD_PREPARE_DATA:
|
|
904
|
+
fingerSize = data.readUIntLE(16, 2);
|
|
905
|
+
break;
|
|
906
|
+
case COMMANDS.CMD_DATA:
|
|
907
|
+
// A single 'data' event might contain multiple TCP packets combined by the OS
|
|
908
|
+
// in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
|
|
909
|
+
// so It's important to split data received for remove CMD_ACK_OK headers
|
|
910
|
+
fingerTemplate = Buffer.concat([fingerTemplate, data.subarray(16, fingerSize + 10)]);
|
|
911
|
+
// @ts-ignore
|
|
912
|
+
resolve(fingerTemplate);
|
|
913
|
+
break;
|
|
914
|
+
case COMMANDS.CMD_ACK_OK:
|
|
915
|
+
cleanup();
|
|
916
|
+
// @ts-ignore
|
|
917
|
+
resolve(fingerTemplate);
|
|
918
|
+
return;
|
|
919
|
+
default:
|
|
920
|
+
// If it's not a recognized command but has data, it might be raw template data
|
|
921
|
+
if (headers.commandId > 2000 && headers.commandId < 3000) {
|
|
922
|
+
// Likely another ACK or system msg
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
fingerTemplate = Buffer.concat([fingerTemplate, data]);
|
|
926
|
+
}
|
|
927
|
+
break;
|
|
928
|
+
}
|
|
929
|
+
clearTimeout(timeout);
|
|
930
|
+
}
|
|
931
|
+
catch (e) {
|
|
932
|
+
cleanup();
|
|
933
|
+
reject(e);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
if (this._zkTcp.socket) {
|
|
937
|
+
this._zkTcp.socket.on('data', receiveData);
|
|
938
|
+
this._zkTcp.socket.write(packet, (err) => {
|
|
939
|
+
if (err) {
|
|
940
|
+
cleanup();
|
|
941
|
+
reject(err);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
reject(new Error('Socket not initialized'));
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
catch (err) {
|
|
951
|
+
throw err;
|
|
952
|
+
}
|
|
953
|
+
finally {
|
|
954
|
+
await this._zkTcp.refreshData();
|
|
955
|
+
}
|
|
593
956
|
}
|
|
594
957
|
/**
|
|
595
|
-
*
|
|
596
|
-
* @
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
958
|
+
*
|
|
959
|
+
* @param user_id {string} user
|
|
960
|
+
* @param fingers {Finger[]} array of finger templates instances
|
|
961
|
+
* */
|
|
962
|
+
async saveTemplates(user_id, fingers = []) {
|
|
963
|
+
if (fingers.length > 9 || fingers.length == 0)
|
|
964
|
+
throw new Error("maximum finger length is 10 and can't be empty");
|
|
965
|
+
try {
|
|
966
|
+
await this._zkTcp.disableDevice();
|
|
967
|
+
// check users exists
|
|
968
|
+
const user = await this.getUserByUserId(user_id);
|
|
969
|
+
let fpack = Buffer.alloc(0);
|
|
970
|
+
let table = Buffer.alloc(0);
|
|
971
|
+
const fnum = 0x10;
|
|
972
|
+
let tstart = 0;
|
|
973
|
+
for (const finger of fingers) {
|
|
974
|
+
const tfp = finger.repackOnly();
|
|
975
|
+
const tableEntry = Buffer.alloc(11); // b=1, H=2, b=1, I=4 => 1+2+1+4=8? Wait, bHbI is 1+2+1+4=8 bytes
|
|
976
|
+
tableEntry.writeInt8(2, 0);
|
|
977
|
+
tableEntry.writeUInt16LE(user.uid, 1);
|
|
978
|
+
tableEntry.writeInt8(fnum + finger.fid, 3);
|
|
979
|
+
tableEntry.writeUInt32LE(tstart, 4);
|
|
980
|
+
table = Buffer.concat([table, tableEntry]);
|
|
981
|
+
tstart += tfp.length;
|
|
982
|
+
fpack = Buffer.concat([fpack, tfp]);
|
|
983
|
+
}
|
|
984
|
+
let upack;
|
|
985
|
+
if (this._zkTcp.userPacketSize === 28) {
|
|
986
|
+
upack = user.repack29();
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
upack = user.repack73();
|
|
990
|
+
}
|
|
991
|
+
const head = Buffer.alloc(12); // III = 3*4 bytes
|
|
992
|
+
head.writeUInt32LE(upack.length, 0);
|
|
993
|
+
head.writeUInt32LE(table.length, 4);
|
|
994
|
+
head.writeUInt32LE(fpack.length, 8);
|
|
995
|
+
const packet = Buffer.concat([head, upack, table, fpack]);
|
|
996
|
+
const bufferResponse = await this._zkTcp.sendWithBuffer(packet);
|
|
997
|
+
const command = 110;
|
|
998
|
+
const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
|
|
999
|
+
commandString.writeUInt32LE(12, 0);
|
|
1000
|
+
commandString.writeUInt16LE(0, 4);
|
|
1001
|
+
commandString.writeUInt16LE(8, 6);
|
|
1002
|
+
const cmdResponse = await this._zkTcp.executeCmd(command, commandString);
|
|
1003
|
+
if (this._zkTcp.verbose)
|
|
1004
|
+
console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
|
|
601
1005
|
}
|
|
602
|
-
|
|
603
|
-
|
|
1006
|
+
catch (error) {
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
finally {
|
|
1010
|
+
await this._zkTcp.refreshData();
|
|
1011
|
+
await this._zkTcp.enableDevice();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
async deleteFinger(user_id, fid) {
|
|
1015
|
+
try {
|
|
1016
|
+
if (!this._users.has(user_id))
|
|
1017
|
+
throw new Error("user_id not exists");
|
|
1018
|
+
const user = await this.getUserByUserId(user_id);
|
|
1019
|
+
const buf = Buffer.alloc(4);
|
|
1020
|
+
buf.writeUInt16LE(user_id ? user.uid : 0, 0);
|
|
1021
|
+
buf.writeUint16LE(fid ? fid : 0, 2);
|
|
1022
|
+
const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
|
|
1023
|
+
return !!reply;
|
|
1024
|
+
}
|
|
1025
|
+
catch (error) {
|
|
1026
|
+
throw new Error("Can't save utemp");
|
|
1027
|
+
}
|
|
1028
|
+
finally {
|
|
1029
|
+
await this._zkTcp.refreshData();
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
async enrollInfo(user_id, tempId) {
|
|
1033
|
+
let done = false;
|
|
1034
|
+
try {
|
|
1035
|
+
const userBuf = Buffer.alloc(24);
|
|
1036
|
+
userBuf.write(user_id, 0, 24, 'ascii');
|
|
1037
|
+
let commandString = Buffer.concat([
|
|
1038
|
+
userBuf,
|
|
1039
|
+
Buffer.from([tempId, 1])
|
|
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
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
const cancel = await this._zkTcp.cancelCapture();
|
|
1051
|
+
const cmdResponse = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
|
|
1052
|
+
this._zkTcp.timeout = 60000; // 60 seconds timeout
|
|
1053
|
+
let attempts = 3;
|
|
1054
|
+
while (attempts > 0) {
|
|
1055
|
+
if (this._zkTcp.verbose)
|
|
1056
|
+
console.log(`A:${attempts} esperando primer regevent`);
|
|
1057
|
+
let dataRecv = await this._zkTcp.readSocket(17);
|
|
1058
|
+
await sendAckOk();
|
|
1059
|
+
if (dataRecv.length > 16) {
|
|
1060
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1061
|
+
const res = padded.readUInt16LE(16);
|
|
1062
|
+
if (this._zkTcp.verbose)
|
|
1063
|
+
console.log(`res ${res}`);
|
|
1064
|
+
if (res === 0 || res === 6 || res === 4) {
|
|
1065
|
+
if (this._zkTcp.verbose)
|
|
1066
|
+
console.log("posible timeout o reg Fallido");
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
if (this._zkTcp.verbose)
|
|
1071
|
+
console.log(`A:${attempts} esperando 2do regevent`);
|
|
1072
|
+
dataRecv = await this._zkTcp.readSocket(17);
|
|
1073
|
+
await sendAckOk();
|
|
1074
|
+
if (this._zkTcp.verbose)
|
|
1075
|
+
console.log(dataRecv);
|
|
1076
|
+
if (dataRecv.length > 8) {
|
|
1077
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1078
|
+
const res = padded.readUInt16LE(16);
|
|
1079
|
+
if (this._zkTcp.verbose)
|
|
1080
|
+
console.log(`res ${res}`);
|
|
1081
|
+
if (res === 6 || res === 4) {
|
|
1082
|
+
if (this._zkTcp.verbose)
|
|
1083
|
+
console.log("posible timeout o reg Fallido");
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
else if (res === 0x64) {
|
|
1087
|
+
if (this._zkTcp.verbose)
|
|
1088
|
+
console.log("ok, continue?");
|
|
1089
|
+
attempts--;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (attempts === 0) {
|
|
1094
|
+
const dataRecv = await this._zkTcp.readSocket(17);
|
|
1095
|
+
await sendAckOk();
|
|
1096
|
+
if (this._zkTcp.verbose)
|
|
1097
|
+
console.log(dataRecv.toString('hex'));
|
|
1098
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1099
|
+
let res = padded.readUInt16LE(16);
|
|
1100
|
+
if (this._zkTcp.verbose)
|
|
1101
|
+
console.log(`res ${res}`);
|
|
1102
|
+
if (res === 5) {
|
|
1103
|
+
if (this._zkTcp.verbose)
|
|
1104
|
+
console.log("finger duplicate");
|
|
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;
|
|
1121
|
+
}
|
|
1122
|
+
catch (error) {
|
|
1123
|
+
throw error;
|
|
1124
|
+
}
|
|
1125
|
+
finally {
|
|
1126
|
+
await this._zkTcp.cancelCapture();
|
|
1127
|
+
await this.verify(user_id);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
async verify(user_id) {
|
|
1131
|
+
try {
|
|
1132
|
+
const user = await this.getUserByUserId(user_id);
|
|
1133
|
+
const command_string = Buffer.alloc(4);
|
|
1134
|
+
command_string.writeUInt32LE(user.uid, 0);
|
|
1135
|
+
const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
|
|
1136
|
+
if (this._zkTcp.verbose)
|
|
1137
|
+
console.log(reply.readUInt16LE(0));
|
|
1138
|
+
return !!reply;
|
|
1139
|
+
}
|
|
1140
|
+
catch (error) {
|
|
1141
|
+
console.error(error);
|
|
1142
|
+
throw error;
|
|
604
1143
|
}
|
|
605
|
-
return this.err.message;
|
|
606
1144
|
}
|
|
607
1145
|
/**
|
|
608
|
-
*
|
|
609
|
-
* @
|
|
1146
|
+
* Upload a single fingerprint for a given user id
|
|
1147
|
+
* @param user_id {string} user id for customer
|
|
1148
|
+
* @param fingerTemplate {string} finger template in base64 string
|
|
1149
|
+
* @param fid {number} finger id is a number between 0 and 9
|
|
1150
|
+
* @param fp_valid {number} finger flag. e.g., valid=1, duress=3
|
|
610
1151
|
*/
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1152
|
+
async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
|
|
1153
|
+
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
|
+
const user = this._users.get(user_id);
|
|
1162
|
+
await this._zkTcp.disableDevice();
|
|
1163
|
+
const prep_struct = Buffer.alloc(4);
|
|
1164
|
+
const fingerBuffer = Buffer.from(fingerTemplate, 'base64');
|
|
1165
|
+
const fp_size = fingerBuffer.length;
|
|
1166
|
+
prep_struct.writeUInt16LE(fp_size, 0);
|
|
1167
|
+
const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
|
|
1168
|
+
check_ACK_OK(initPacket);
|
|
1169
|
+
const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
|
|
1170
|
+
check_ACK_OK(fpPacket);
|
|
1171
|
+
const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, '');
|
|
1172
|
+
check_ACK_OK(cheksumPacket);
|
|
1173
|
+
const checksum = cheksumPacket.readUInt32LE(8);
|
|
1174
|
+
const tmp_wreq = Buffer.alloc(6);
|
|
1175
|
+
tmp_wreq.writeUInt16LE(user.uid, 0);
|
|
1176
|
+
tmp_wreq.writeUIntLE(fid, 2, 1);
|
|
1177
|
+
tmp_wreq.writeUIntLE(fp_valid, 3, 1);
|
|
1178
|
+
tmp_wreq.writeUInt16LE(fp_size, 4);
|
|
1179
|
+
const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
|
|
1180
|
+
check_ACK_OK(tmp_wreqPacket);
|
|
1181
|
+
const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, '');
|
|
1182
|
+
return check_ACK_OK(freeData);
|
|
1183
|
+
}
|
|
1184
|
+
catch (err) {
|
|
1185
|
+
throw err;
|
|
1186
|
+
}
|
|
1187
|
+
finally {
|
|
1188
|
+
await this._zkTcp.refreshData();
|
|
1189
|
+
await this._zkTcp.enableDevice();
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
class TransactionService {
|
|
1195
|
+
_zkTcp;
|
|
1196
|
+
constructor(zkTcp) {
|
|
1197
|
+
this._zkTcp = zkTcp;
|
|
1198
|
+
}
|
|
1199
|
+
async getAttendances(callbackInProcess = () => { }) {
|
|
1200
|
+
try {
|
|
1201
|
+
// Free any existing buffer data to prepare for a new request
|
|
1202
|
+
if (this._zkTcp.socket) {
|
|
1203
|
+
await this._zkTcp.freeData();
|
|
1204
|
+
}
|
|
1205
|
+
// Request attendance logs and handle chunked data
|
|
1206
|
+
const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
|
|
1207
|
+
// Free buffer data after receiving the attendance logs
|
|
1208
|
+
if (this._zkTcp.socket) {
|
|
1209
|
+
await this._zkTcp.freeData();
|
|
1210
|
+
}
|
|
1211
|
+
// Constants for record processing
|
|
1212
|
+
const RECORD_PACKET_SIZE = 40;
|
|
1213
|
+
// Ensure data.data is a valid buffer
|
|
1214
|
+
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1215
|
+
throw new Error('Invalid data received');
|
|
1216
|
+
}
|
|
1217
|
+
// Process the record data
|
|
1218
|
+
let recordData = data.data.subarray(4); // Skip header
|
|
1219
|
+
const records = [];
|
|
1220
|
+
// Process each attendance record
|
|
1221
|
+
while (recordData.length >= RECORD_PACKET_SIZE) {
|
|
1222
|
+
const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
|
|
1223
|
+
records.push({ ...record, ip: this._zkTcp.ip }); // Add IP address to each record
|
|
1224
|
+
recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
|
|
1225
|
+
}
|
|
1226
|
+
// Return the list of attendance records
|
|
1227
|
+
return { data: records };
|
|
1228
|
+
}
|
|
1229
|
+
catch (err) {
|
|
1230
|
+
// Log and re-throw the error
|
|
1231
|
+
console.error('Error getting attendance records:', err);
|
|
1232
|
+
throw err; // Re-throw the error for handling by the caller
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
// Clears the attendance logs on the device
|
|
1236
|
+
async clearAttendanceLog() {
|
|
1237
|
+
try {
|
|
1238
|
+
// Execute the command to clear attendance logs
|
|
1239
|
+
await this._zkTcp.disableDevice();
|
|
1240
|
+
const buf = await this._zkTcp.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
|
|
1241
|
+
await this._zkTcp.refreshData();
|
|
1242
|
+
await this._zkTcp.enableDevice();
|
|
1243
|
+
return !!buf;
|
|
1244
|
+
}
|
|
1245
|
+
catch (err) {
|
|
1246
|
+
// Log the error for debugging purposes
|
|
1247
|
+
console.error('Error clearing attendance log:', err);
|
|
1248
|
+
// Re-throw the error to be handled by the caller
|
|
1249
|
+
throw err;
|
|
1250
|
+
}
|
|
620
1251
|
}
|
|
621
1252
|
}
|
|
622
1253
|
|
|
@@ -652,6 +1283,8 @@ class ZTCP {
|
|
|
652
1283
|
verbose = false;
|
|
653
1284
|
packetNumber = 0;
|
|
654
1285
|
replyData = Buffer.from([]);
|
|
1286
|
+
_userService;
|
|
1287
|
+
_transactionService;
|
|
655
1288
|
constructor(ip, port, timeout, comm_key, verbose) {
|
|
656
1289
|
this.ip = ip;
|
|
657
1290
|
this.port = port;
|
|
@@ -659,6 +1292,8 @@ class ZTCP {
|
|
|
659
1292
|
this.replyId = 0;
|
|
660
1293
|
this.comm_key = comm_key;
|
|
661
1294
|
this.verbose = verbose;
|
|
1295
|
+
this._userService = new UserService(this);
|
|
1296
|
+
this._transactionService = new TransactionService(this);
|
|
662
1297
|
}
|
|
663
1298
|
createSocket(cbError, cbClose) {
|
|
664
1299
|
return new Promise((resolve, reject) => {
|
|
@@ -880,6 +1515,10 @@ class ZTCP {
|
|
|
880
1515
|
try {
|
|
881
1516
|
// Write the message to the socket and wait for a response
|
|
882
1517
|
const reply = await this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT);
|
|
1518
|
+
if (this.verbose) {
|
|
1519
|
+
let headers = decodeTCPHeader(reply);
|
|
1520
|
+
console.log('command: ', COMMANDS[headers.commandId], 'replyid: ', headers.replyId);
|
|
1521
|
+
}
|
|
883
1522
|
// Remove TCP header from the response
|
|
884
1523
|
const rReply = removeTcpHeader(reply);
|
|
885
1524
|
// Update sessionId for connection command responses
|
|
@@ -1024,94 +1663,19 @@ class ZTCP {
|
|
|
1024
1663
|
break;
|
|
1025
1664
|
}
|
|
1026
1665
|
default: {
|
|
1027
|
-
reject(new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)));
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
/**
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
await this.freeData();
|
|
1041
|
-
}
|
|
1042
|
-
// Request user data
|
|
1043
|
-
const data = await this.readWithBuffer(REQUEST_DATA.GET_USERS);
|
|
1044
|
-
// Free buffer data after receiving the data
|
|
1045
|
-
if (this.socket) {
|
|
1046
|
-
await this.freeData();
|
|
1047
|
-
}
|
|
1048
|
-
// Constants for user data processing
|
|
1049
|
-
const USER_PACKET_SIZE = 72;
|
|
1050
|
-
// Ensure data.data is a valid buffer
|
|
1051
|
-
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1052
|
-
throw new Error('Invalid data received');
|
|
1053
|
-
}
|
|
1054
|
-
let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
|
|
1055
|
-
const users = [];
|
|
1056
|
-
// Process each user packet
|
|
1057
|
-
while (userData.length >= USER_PACKET_SIZE) {
|
|
1058
|
-
// Decode user data and add to the users array
|
|
1059
|
-
const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
|
|
1060
|
-
users.push(user);
|
|
1061
|
-
userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
|
|
1062
|
-
}
|
|
1063
|
-
// Return the list of users
|
|
1064
|
-
return { data: users };
|
|
1065
|
-
}
|
|
1066
|
-
catch (err) {
|
|
1067
|
-
// Log the error for debugging
|
|
1068
|
-
console.error('Error getting users:', err);
|
|
1069
|
-
// Re-throw the error to be handled by the caller
|
|
1070
|
-
throw err;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
*
|
|
1075
|
-
* @param {*} ip
|
|
1076
|
-
* @param {*} callbackInProcess
|
|
1077
|
-
* reject error when starting request data
|
|
1078
|
-
* return { data: records, err: Error } when receiving requested data
|
|
1079
|
-
*/
|
|
1080
|
-
async getAttendances(callbackInProcess = () => { }) {
|
|
1081
|
-
try {
|
|
1082
|
-
// Free any existing buffer data to prepare for a new request
|
|
1083
|
-
if (this.socket) {
|
|
1084
|
-
await this.freeData();
|
|
1085
|
-
}
|
|
1086
|
-
// Request attendance logs and handle chunked data
|
|
1087
|
-
const data = await this.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
|
|
1088
|
-
// Free buffer data after receiving the attendance logs
|
|
1089
|
-
if (this.socket) {
|
|
1090
|
-
await this.freeData();
|
|
1091
|
-
}
|
|
1092
|
-
// Constants for record processing
|
|
1093
|
-
const RECORD_PACKET_SIZE = 40;
|
|
1094
|
-
// Ensure data.data is a valid buffer
|
|
1095
|
-
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1096
|
-
throw new Error('Invalid data received');
|
|
1097
|
-
}
|
|
1098
|
-
// Process the record data
|
|
1099
|
-
let recordData = data.data.subarray(4); // Skip header
|
|
1100
|
-
const records = [];
|
|
1101
|
-
// Process each attendance record
|
|
1102
|
-
while (recordData.length >= RECORD_PACKET_SIZE) {
|
|
1103
|
-
const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
|
|
1104
|
-
records.push({ ...record, ip: this.ip }); // Add IP address to each record
|
|
1105
|
-
recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
|
|
1106
|
-
}
|
|
1107
|
-
// Return the list of attendance records
|
|
1108
|
-
return { data: records };
|
|
1109
|
-
}
|
|
1110
|
-
catch (err) {
|
|
1111
|
-
// Log and re-throw the error
|
|
1112
|
-
console.error('Error getting attendance records:', err);
|
|
1113
|
-
throw err; // Re-throw the error for handling by the caller
|
|
1114
|
-
}
|
|
1666
|
+
reject(new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)));
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
*
|
|
1673
|
+
* @param {*} callbackInProcess
|
|
1674
|
+
* reject error when starting request data
|
|
1675
|
+
* return { data: records, err: Error } when receiving requested data
|
|
1676
|
+
*/
|
|
1677
|
+
async getAttendances(callbackInProcess = () => { }) {
|
|
1678
|
+
return await this._transactionService.getAttendances(callbackInProcess);
|
|
1115
1679
|
}
|
|
1116
1680
|
async freeData() {
|
|
1117
1681
|
try {
|
|
@@ -1438,11 +2002,10 @@ class ZTCP {
|
|
|
1438
2002
|
// Execute the command to get the PIN information
|
|
1439
2003
|
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1440
2004
|
// Extract and format the PIN information from the response data
|
|
1441
|
-
// Remove null characters
|
|
1442
2005
|
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1443
2006
|
.toString('ascii') // Convert buffer to ASCII string
|
|
1444
2007
|
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1445
|
-
.replace(/\u0000/g, '');
|
|
2008
|
+
.replace(/\u0000/g, ''); // Remove null characters 0x00
|
|
1446
2009
|
}
|
|
1447
2010
|
catch (err) {
|
|
1448
2011
|
// Log the error for debugging
|
|
@@ -1494,7 +2057,9 @@ class ZTCP {
|
|
|
1494
2057
|
// Execute the command to get firmware information
|
|
1495
2058
|
const data = await this.executeCmd(1100, '');
|
|
1496
2059
|
// Extract and return the firmware version from the response data
|
|
1497
|
-
return data.slice(8)
|
|
2060
|
+
return data.slice(8) // Skip the first 8 bytes (header)
|
|
2061
|
+
.toString('ascii') // convert to ASCII string
|
|
2062
|
+
.replace(/\u0000/g, ''); // remove x00
|
|
1498
2063
|
}
|
|
1499
2064
|
catch (err) {
|
|
1500
2065
|
// Log the error for debugging
|
|
@@ -1560,59 +2125,6 @@ class ZTCP {
|
|
|
1560
2125
|
throw err;
|
|
1561
2126
|
}
|
|
1562
2127
|
}
|
|
1563
|
-
async setUser(uid, userid, name, password, role = 0, cardno = 0) {
|
|
1564
|
-
try {
|
|
1565
|
-
// Validate input parameters
|
|
1566
|
-
if (uid <= 0 || uid > 3000 ||
|
|
1567
|
-
userid.length > 9 ||
|
|
1568
|
-
name.length > 24 ||
|
|
1569
|
-
password.length > 8 ||
|
|
1570
|
-
typeof role !== 'number' ||
|
|
1571
|
-
cardno.toString().length > 10) {
|
|
1572
|
-
throw new Error('Invalid input parameters');
|
|
1573
|
-
}
|
|
1574
|
-
// Allocate and initialize the buffer
|
|
1575
|
-
const commandBuffer = Buffer.alloc(72);
|
|
1576
|
-
// Fill the buffer with user data
|
|
1577
|
-
commandBuffer.writeUInt16LE(uid, 0);
|
|
1578
|
-
commandBuffer.writeUInt16LE(role, 2);
|
|
1579
|
-
commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
|
|
1580
|
-
commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
|
|
1581
|
-
commandBuffer.writeUInt16LE(cardno, 35);
|
|
1582
|
-
commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
|
|
1583
|
-
commandBuffer.write(userid.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
|
|
1584
|
-
// Send the command and return the result
|
|
1585
|
-
const created = await this.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
|
|
1586
|
-
return !!created;
|
|
1587
|
-
}
|
|
1588
|
-
catch (err) {
|
|
1589
|
-
// Log error details for debugging
|
|
1590
|
-
console.error('Error setting user:', err);
|
|
1591
|
-
// Re-throw error for upstream handling
|
|
1592
|
-
throw err;
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
async deleteUser(uid) {
|
|
1596
|
-
try {
|
|
1597
|
-
// Validate input parameter
|
|
1598
|
-
if (uid <= 0 || uid > 3000) {
|
|
1599
|
-
throw new Error('Invalid UID: must be between 1 and 3000');
|
|
1600
|
-
}
|
|
1601
|
-
// Allocate and initialize the buffer
|
|
1602
|
-
const commandBuffer = Buffer.alloc(72);
|
|
1603
|
-
// Write UID to the buffer
|
|
1604
|
-
commandBuffer.writeUInt16LE(uid, 0);
|
|
1605
|
-
// Send the delete command and return the result
|
|
1606
|
-
const deleted = await this.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
|
|
1607
|
-
return !!deleted;
|
|
1608
|
-
}
|
|
1609
|
-
catch (err) {
|
|
1610
|
-
// Log error details for debugging
|
|
1611
|
-
console.error('Error deleting user:', err);
|
|
1612
|
-
// Re-throw error for upstream handling
|
|
1613
|
-
throw err;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
2128
|
async getAttendanceSize() {
|
|
1617
2129
|
try {
|
|
1618
2130
|
// Execute command to get free sizes
|
|
@@ -1629,22 +2141,27 @@ class ZTCP {
|
|
|
1629
2141
|
}
|
|
1630
2142
|
// Clears the attendance logs on the device
|
|
1631
2143
|
async clearAttendanceLog() {
|
|
1632
|
-
|
|
1633
|
-
// Execute the command to clear attendance logs
|
|
1634
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
|
|
1635
|
-
}
|
|
1636
|
-
catch (err) {
|
|
1637
|
-
// Log the error for debugging purposes
|
|
1638
|
-
console.error('Error clearing attendance log:', err);
|
|
1639
|
-
// Re-throw the error to be handled by the caller
|
|
1640
|
-
throw err;
|
|
1641
|
-
}
|
|
2144
|
+
return await this._transactionService.clearAttendanceLog();
|
|
1642
2145
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
2146
|
+
/**
|
|
2147
|
+
* Clears all data on the device
|
|
2148
|
+
* @value 1 Attendance records
|
|
2149
|
+
* @value 2 Fingerprint templates
|
|
2150
|
+
* @value 3 None
|
|
2151
|
+
* @value 4 Operation records
|
|
2152
|
+
* @value 5 User information
|
|
2153
|
+
* @default 0 Delete all
|
|
2154
|
+
*/
|
|
2155
|
+
async clearData(value) {
|
|
1645
2156
|
try {
|
|
1646
2157
|
// Execute the command to clear all data
|
|
1647
|
-
|
|
2158
|
+
await this.disableDevice();
|
|
2159
|
+
if (!value)
|
|
2160
|
+
value = 3;
|
|
2161
|
+
const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
|
|
2162
|
+
await this.refreshData();
|
|
2163
|
+
await this.enableDevice();
|
|
2164
|
+
return !!buf;
|
|
1648
2165
|
}
|
|
1649
2166
|
catch (err) {
|
|
1650
2167
|
// Log the error for debugging purposes
|
|
@@ -1691,33 +2208,7 @@ class ZTCP {
|
|
|
1691
2208
|
* @returns {Record<string, Finger[]>}
|
|
1692
2209
|
*/
|
|
1693
2210
|
async getTemplates(callbackInProcess = () => { }) {
|
|
1694
|
-
|
|
1695
|
-
try {
|
|
1696
|
-
await this.getSizes();
|
|
1697
|
-
if (this.fp_count == 0)
|
|
1698
|
-
return { data: [] };
|
|
1699
|
-
await this.freeData();
|
|
1700
|
-
await this.disableDevice();
|
|
1701
|
-
const Buffer = await this.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
|
|
1702
|
-
let templateData = Buffer.data.subarray(4);
|
|
1703
|
-
let totalSize = Buffer.data.readUIntLE(0, 4);
|
|
1704
|
-
while (totalSize) {
|
|
1705
|
-
const buf = templateData.subarray(0, 6);
|
|
1706
|
-
const size = buf.readUIntLE(0, 2);
|
|
1707
|
-
templates.push(new Finger(buf.readUIntLE(2, 2), buf.readUIntLE(4, 1), buf.readUIntLE(5, 1), templateData.subarray(6, size)));
|
|
1708
|
-
templateData = templateData.subarray(size);
|
|
1709
|
-
totalSize -= size;
|
|
1710
|
-
}
|
|
1711
|
-
return { data: templates };
|
|
1712
|
-
}
|
|
1713
|
-
catch (err) {
|
|
1714
|
-
this.verbose && console.log("Error getting templates", err);
|
|
1715
|
-
return { data: templates };
|
|
1716
|
-
}
|
|
1717
|
-
finally {
|
|
1718
|
-
await this.enableDevice();
|
|
1719
|
-
await this.freeData();
|
|
1720
|
-
}
|
|
2211
|
+
return await this._userService.getTemplates(callbackInProcess);
|
|
1721
2212
|
}
|
|
1722
2213
|
/**
|
|
1723
2214
|
* Return size
|
|
@@ -1793,197 +2284,6 @@ class ZTCP {
|
|
|
1793
2284
|
throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
|
|
1794
2285
|
}
|
|
1795
2286
|
}
|
|
1796
|
-
/**
|
|
1797
|
-
* save user and template
|
|
1798
|
-
*
|
|
1799
|
-
* @param {User | number | string} user - User class object | uid | user_id
|
|
1800
|
-
* @param {Finger[]} fingers - Array of finger class. `0 <= index <= 9`
|
|
1801
|
-
*/
|
|
1802
|
-
async saveUserTemplate(user, fingers = []) {
|
|
1803
|
-
if (fingers.length > 9 || fingers.length == 0)
|
|
1804
|
-
throw new Error("maximum finger length is 10 and can't be empty");
|
|
1805
|
-
try {
|
|
1806
|
-
await this.disableDevice();
|
|
1807
|
-
const users = await this.getUsers();
|
|
1808
|
-
//check users exists
|
|
1809
|
-
if (!users.data.some(u => u.uid == user.uid || +u.user_id == +user.user_id))
|
|
1810
|
-
throw new Error("error validating user input");
|
|
1811
|
-
if (!(user instanceof User)) {
|
|
1812
|
-
let tusers = users.data.filter(x => x.uid === +user.uid);
|
|
1813
|
-
if (tusers.length === 1) {
|
|
1814
|
-
user = tusers[0];
|
|
1815
|
-
}
|
|
1816
|
-
else {
|
|
1817
|
-
tusers = users.data.filter(x => x.user_id === String(user));
|
|
1818
|
-
if (tusers.length === 1) {
|
|
1819
|
-
user = tusers[0];
|
|
1820
|
-
}
|
|
1821
|
-
else {
|
|
1822
|
-
throw new Error("Can't find user");
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
if (fingers instanceof Finger) {
|
|
1827
|
-
fingers = [fingers];
|
|
1828
|
-
}
|
|
1829
|
-
let fpack = Buffer.alloc(0);
|
|
1830
|
-
let table = Buffer.alloc(0);
|
|
1831
|
-
const fnum = 0x10;
|
|
1832
|
-
let tstart = 0;
|
|
1833
|
-
for (const finger of fingers) {
|
|
1834
|
-
const tfp = finger.repackOnly();
|
|
1835
|
-
const tableEntry = Buffer.alloc(11); // b=1, H=2, b=1, I=4 => 1+2+1+4=8? Wait, bHbI is 1+2+1+4=8 bytes
|
|
1836
|
-
tableEntry.writeInt8(2, 0);
|
|
1837
|
-
tableEntry.writeUInt16LE(user.uid, 1);
|
|
1838
|
-
tableEntry.writeInt8(fnum + finger.fid, 3);
|
|
1839
|
-
tableEntry.writeUInt32LE(tstart, 4);
|
|
1840
|
-
table = Buffer.concat([table, tableEntry]);
|
|
1841
|
-
tstart += tfp.length;
|
|
1842
|
-
fpack = Buffer.concat([fpack, tfp]);
|
|
1843
|
-
}
|
|
1844
|
-
let upack;
|
|
1845
|
-
if (this.userPacketSize === 28) {
|
|
1846
|
-
upack = user.repack29();
|
|
1847
|
-
}
|
|
1848
|
-
else {
|
|
1849
|
-
upack = user.repack73();
|
|
1850
|
-
}
|
|
1851
|
-
const head = Buffer.alloc(12); // III = 3*4 bytes
|
|
1852
|
-
head.writeUInt32LE(upack.length, 0);
|
|
1853
|
-
head.writeUInt32LE(table.length, 4);
|
|
1854
|
-
head.writeUInt32LE(fpack.length, 8);
|
|
1855
|
-
const packet = Buffer.concat([head, upack, table, fpack]);
|
|
1856
|
-
const bufferResponse = await this.sendWithBuffer(packet);
|
|
1857
|
-
const command = 110;
|
|
1858
|
-
const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
|
|
1859
|
-
commandString.writeUInt32LE(12, 0);
|
|
1860
|
-
commandString.writeUInt16LE(0, 4);
|
|
1861
|
-
commandString.writeUInt16LE(8, 6);
|
|
1862
|
-
const cmdResponse = await this.executeCmd(command, commandString);
|
|
1863
|
-
if (this.verbose)
|
|
1864
|
-
console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
|
|
1865
|
-
}
|
|
1866
|
-
catch (error) {
|
|
1867
|
-
throw error;
|
|
1868
|
-
}
|
|
1869
|
-
finally {
|
|
1870
|
-
await this.refreshData();
|
|
1871
|
-
await this.enableDevice();
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
async deleteFinger(uid, fid) {
|
|
1875
|
-
try {
|
|
1876
|
-
const buf = Buffer.alloc(4);
|
|
1877
|
-
buf.writeUInt16LE(uid, 0);
|
|
1878
|
-
buf.writeUint16LE(fid, 2);
|
|
1879
|
-
const reply = await this.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
|
|
1880
|
-
return !!reply;
|
|
1881
|
-
}
|
|
1882
|
-
catch (error) {
|
|
1883
|
-
throw new Error("Can't save utemp");
|
|
1884
|
-
}
|
|
1885
|
-
finally {
|
|
1886
|
-
await this.refreshData();
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
async enrollUser(uid, tempId, userId = '') {
|
|
1890
|
-
let done = false;
|
|
1891
|
-
try {
|
|
1892
|
-
//validate user exists
|
|
1893
|
-
const users = await this.getUsers();
|
|
1894
|
-
const filteredUsers = users.data.filter(x => x.uid === uid);
|
|
1895
|
-
if (filteredUsers.length >= 1) {
|
|
1896
|
-
userId = filteredUsers[0].user_id;
|
|
1897
|
-
}
|
|
1898
|
-
else {
|
|
1899
|
-
throw new Error("user not found");
|
|
1900
|
-
}
|
|
1901
|
-
const userBuf = Buffer.alloc(24);
|
|
1902
|
-
userBuf.write(userId.toString(), 0, 24, 'ascii');
|
|
1903
|
-
let commandString = Buffer.concat([
|
|
1904
|
-
userBuf,
|
|
1905
|
-
Buffer.from([tempId, 1])
|
|
1906
|
-
]);
|
|
1907
|
-
const cancel = await this.cancelCapture();
|
|
1908
|
-
const cmdResponse = await this.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
|
|
1909
|
-
this.timeout = 60000; // 60 seconds timeout
|
|
1910
|
-
let attempts = 3;
|
|
1911
|
-
while (attempts > 0) {
|
|
1912
|
-
if (this.verbose)
|
|
1913
|
-
console.log(`A:${attempts} esperando primer regevent`);
|
|
1914
|
-
let dataRecv = await this.readSocket(17);
|
|
1915
|
-
await this.ackOk();
|
|
1916
|
-
if (dataRecv.length > 16) {
|
|
1917
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1918
|
-
const res = padded.readUInt16LE(16);
|
|
1919
|
-
if (this.verbose)
|
|
1920
|
-
console.log(`res ${res}`);
|
|
1921
|
-
if (res === 0 || res === 6 || res === 4) {
|
|
1922
|
-
if (this.verbose)
|
|
1923
|
-
console.log("posible timeout o reg Fallido");
|
|
1924
|
-
break;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
if (this.verbose)
|
|
1928
|
-
console.log(`A:${attempts} esperando 2do regevent`);
|
|
1929
|
-
dataRecv = await this.readSocket(17);
|
|
1930
|
-
await this.ackOk();
|
|
1931
|
-
if (this.verbose)
|
|
1932
|
-
console.log(dataRecv);
|
|
1933
|
-
if (dataRecv.length > 8) {
|
|
1934
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1935
|
-
const res = padded.readUInt16LE(16);
|
|
1936
|
-
if (this.verbose)
|
|
1937
|
-
console.log(`res ${res}`);
|
|
1938
|
-
if (res === 6 || res === 4) {
|
|
1939
|
-
if (this.verbose)
|
|
1940
|
-
console.log("posible timeout o reg Fallido");
|
|
1941
|
-
break;
|
|
1942
|
-
}
|
|
1943
|
-
else if (res === 0x64) {
|
|
1944
|
-
if (this.verbose)
|
|
1945
|
-
console.log("ok, continue?");
|
|
1946
|
-
attempts--;
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
if (attempts === 0) {
|
|
1951
|
-
const dataRecv = await this.readSocket(17);
|
|
1952
|
-
await this.ackOk();
|
|
1953
|
-
if (this.verbose)
|
|
1954
|
-
console.log(dataRecv.toString('hex'));
|
|
1955
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1956
|
-
let res = padded.readUInt16LE(16);
|
|
1957
|
-
if (this.verbose)
|
|
1958
|
-
console.log(`res ${res}`);
|
|
1959
|
-
if (res === 5) {
|
|
1960
|
-
if (this.verbose)
|
|
1961
|
-
console.log("finger duplicate");
|
|
1962
|
-
}
|
|
1963
|
-
if (res === 6 || res === 4) {
|
|
1964
|
-
if (this.verbose)
|
|
1965
|
-
console.log("posible timeout");
|
|
1966
|
-
}
|
|
1967
|
-
if (res === 0) {
|
|
1968
|
-
const size = padded.readUInt16LE(10);
|
|
1969
|
-
const pos = padded.readUInt16LE(12);
|
|
1970
|
-
if (this.verbose)
|
|
1971
|
-
console.log(`enroll ok ${size} ${pos}`);
|
|
1972
|
-
done = true;
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
//this.__sock.setTimeout(this.__timeout);
|
|
1976
|
-
await this.regEvent(0); // TODO: test
|
|
1977
|
-
return done;
|
|
1978
|
-
}
|
|
1979
|
-
catch (error) {
|
|
1980
|
-
throw error;
|
|
1981
|
-
}
|
|
1982
|
-
finally {
|
|
1983
|
-
await this.cancelCapture();
|
|
1984
|
-
await this.verifyUser(undefined);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
2287
|
async readSocket(length, cb = null) {
|
|
1988
2288
|
let replyBufer = Buffer.from([]);
|
|
1989
2289
|
let totalPackets = 0;
|
|
@@ -2033,15 +2333,6 @@ class ZTCP {
|
|
|
2033
2333
|
throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
|
|
2034
2334
|
}
|
|
2035
2335
|
}
|
|
2036
|
-
async ackOk() {
|
|
2037
|
-
try {
|
|
2038
|
-
const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
|
|
2039
|
-
this.socket.write(buf);
|
|
2040
|
-
}
|
|
2041
|
-
catch (e) {
|
|
2042
|
-
throw new ZkError(e, COMMANDS.CMD_ACK_OK, this.ip);
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
2336
|
async cancelCapture() {
|
|
2046
2337
|
try {
|
|
2047
2338
|
const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, '');
|
|
@@ -2051,22 +2342,6 @@ class ZTCP {
|
|
|
2051
2342
|
throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
|
|
2052
2343
|
}
|
|
2053
2344
|
}
|
|
2054
|
-
async verifyUser(uid) {
|
|
2055
|
-
try {
|
|
2056
|
-
let command_string = '';
|
|
2057
|
-
if (uid) {
|
|
2058
|
-
command_string = Buffer.alloc(4);
|
|
2059
|
-
command_string.writeUInt32LE(uid, 0);
|
|
2060
|
-
}
|
|
2061
|
-
const reply = await this.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
|
|
2062
|
-
if (this.verbose)
|
|
2063
|
-
console.log(reply.readUInt16LE(0));
|
|
2064
|
-
return !!reply;
|
|
2065
|
-
}
|
|
2066
|
-
catch (error) {
|
|
2067
|
-
console.error(error);
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
2345
|
async restartDevice() {
|
|
2071
2346
|
try {
|
|
2072
2347
|
await this.executeCmd(COMMANDS.CMD_RESTART, '');
|
|
@@ -2451,7 +2726,7 @@ class ZUDP {
|
|
|
2451
2726
|
}
|
|
2452
2727
|
async clearAttendanceLog() {
|
|
2453
2728
|
try {
|
|
2454
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
|
|
2729
|
+
return !!await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
|
|
2455
2730
|
}
|
|
2456
2731
|
catch (err) {
|
|
2457
2732
|
console.error('Error clearing attendance log:', err);
|
|
@@ -2460,7 +2735,7 @@ class ZUDP {
|
|
|
2460
2735
|
}
|
|
2461
2736
|
async clearData() {
|
|
2462
2737
|
try {
|
|
2463
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
|
|
2738
|
+
return !!await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
|
|
2464
2739
|
}
|
|
2465
2740
|
catch (err) {
|
|
2466
2741
|
console.error('Error clearing data:', err);
|
|
@@ -2651,7 +2926,7 @@ class Zklib {
|
|
|
2651
2926
|
}
|
|
2652
2927
|
}
|
|
2653
2928
|
async getUsers() {
|
|
2654
|
-
return this.functionWrapper(() => this.ztcp.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
|
|
2929
|
+
return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
|
|
2655
2930
|
}
|
|
2656
2931
|
async getTime() {
|
|
2657
2932
|
return this.functionWrapper(() => this.ztcp.getTime(), () => this.zudp.getTime(), 'GET_TIME');
|
|
@@ -2701,11 +2976,22 @@ class Zklib {
|
|
|
2701
2976
|
async getFirmware() {
|
|
2702
2977
|
return this.functionWrapper(() => this.ztcp.getFirmware(), async () => { throw new Error('UDP get firmware not supported'); }, 'GET_FIRMWARE');
|
|
2703
2978
|
}
|
|
2704
|
-
|
|
2705
|
-
|
|
2979
|
+
/** Update or create a user if user id/pin not exists
|
|
2980
|
+
* @param user_id {string} user id/pin for customer
|
|
2981
|
+
* @param name {string} user name
|
|
2982
|
+
* @param password {string} user password
|
|
2983
|
+
* @param role {number} role/privilege id number
|
|
2984
|
+
* @param cardno {number} card number/id
|
|
2985
|
+
*/
|
|
2986
|
+
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 () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
|
|
2706
2988
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2989
|
+
/**
|
|
2990
|
+
* Delete user by a given user id/pin
|
|
2991
|
+
* @param user_id {string}
|
|
2992
|
+
*/
|
|
2993
|
+
async deleteUser(user_id) {
|
|
2994
|
+
return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
|
|
2709
2995
|
}
|
|
2710
2996
|
async getAttendanceSize() {
|
|
2711
2997
|
return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => { throw new Error('UDP get attendance size not supported'); }, 'GET_ATTENDANCE_SIZE');
|
|
@@ -2719,25 +3005,55 @@ class Zklib {
|
|
|
2719
3005
|
async getTemplates() {
|
|
2720
3006
|
return this.functionWrapper(() => this.ztcp.getTemplates(), async () => { throw new Error('UDP get templates not supported'); }, 'GET_TEMPLATES');
|
|
2721
3007
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
3008
|
+
/**
|
|
3009
|
+
* Get a user template for a given user id/pin and finger id
|
|
3010
|
+
* @param user_id {string} user id/pin
|
|
3011
|
+
* @param fid {number} finger index
|
|
3012
|
+
*/
|
|
3013
|
+
async getUserTemplate(user_id, fid) {
|
|
3014
|
+
return await this.functionWrapper(async () => await this.ztcp._userService.DownloadFp(user_id, fid), async () => { throw new Error('UDP get user template not implemented'); }, 'GET_USER_TEMPLATE');
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Upload a single fingerprint for a given user id
|
|
3018
|
+
* @param user_id {string} user id/pin for customer
|
|
3019
|
+
* @param fingerTemplate {string} finger template in base64 string
|
|
3020
|
+
* @param fid {number} finger id is a number between 0 and 9
|
|
3021
|
+
* @param fp_valid {number} finger flag. e.g., valid=1, duress=3
|
|
3022
|
+
*/
|
|
3023
|
+
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 () => { throw new Error('UDP get user template not implemented'); }, 'UPLOAD_USER_TEMPLATE');
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* save user and template
|
|
3028
|
+
*
|
|
3029
|
+
* @param {string} user_id - user id for customer
|
|
3030
|
+
* @param {Finger[]} fingers - Array of finger class
|
|
3031
|
+
*/
|
|
3032
|
+
async saveUserTemplate(user_id, fingers = []) {
|
|
3033
|
+
return await this.functionWrapper(async () => await this.ztcp._userService.saveTemplates(user_id, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
|
|
2724
3034
|
}
|
|
2725
|
-
|
|
3035
|
+
/**
|
|
3036
|
+
* Delete a single finger template by user id and finger index
|
|
3037
|
+
* @param user_id {string} user id/pin for customer
|
|
3038
|
+
* @param fid {number} finger index
|
|
3039
|
+
*/
|
|
3040
|
+
async deleteFinger(user_id, fid) {
|
|
2726
3041
|
if (fid > 9 || 0 > fid)
|
|
2727
3042
|
throw new Error("fid params out of index");
|
|
2728
|
-
|
|
2729
|
-
throw new Error("fid params out of index");
|
|
2730
|
-
return this.functionWrapper(() => this.ztcp.deleteFinger(uid, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
|
|
3043
|
+
return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
|
|
2731
3044
|
}
|
|
2732
|
-
|
|
3045
|
+
/**
|
|
3046
|
+
* Start to enroll a finger template
|
|
3047
|
+
* @param user_id {string} user id/pin for customer
|
|
3048
|
+
* @param temp_id {number} finger index
|
|
3049
|
+
*/
|
|
3050
|
+
async enrollUser(user_id, temp_id) {
|
|
2733
3051
|
if (temp_id < 0 || temp_id > 9)
|
|
2734
3052
|
throw new Error("temp_id out of range 0-9");
|
|
2735
|
-
|
|
2736
|
-
throw new Error("uid out of range 1-3000");
|
|
2737
|
-
return this.functionWrapper(() => this.ztcp.enrollUser(uid, temp_id, user_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
|
|
3053
|
+
return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
|
|
2738
3054
|
}
|
|
2739
|
-
async verifyUser(
|
|
2740
|
-
return this.functionWrapper(() => this.ztcp.
|
|
3055
|
+
async verifyUser(user_id) {
|
|
3056
|
+
return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
|
|
2741
3057
|
}
|
|
2742
3058
|
async restartDevice() {
|
|
2743
3059
|
return this.functionWrapper(() => this.ztcp.restartDevice(), async () => { throw new Error('UDP restart device not supported'); }, 'RESTART_DEVICE');
|