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.cjs.js
CHANGED
|
@@ -26,99 +26,190 @@ var dgram__namespace = /*#__PURE__*/_interopNamespaceDefault(dgram);
|
|
|
26
26
|
var COMMANDS;
|
|
27
27
|
(function (COMMANDS) {
|
|
28
28
|
COMMANDS[COMMANDS["CMD_ACK_DATA"] = 2002] = "CMD_ACK_DATA";
|
|
29
|
+
/** There was an error when processing the request.*/
|
|
29
30
|
COMMANDS[COMMANDS["CMD_ACK_ERROR"] = 2001] = "CMD_ACK_ERROR";
|
|
30
31
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_CMD"] = 65533] = "CMD_ACK_ERROR_CMD";
|
|
31
32
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_DATA"] = 65531] = "CMD_ACK_ERROR_DATA";
|
|
32
33
|
COMMANDS[COMMANDS["CMD_ACK_ERROR_INIT"] = 65532] = "CMD_ACK_ERROR_INIT";
|
|
34
|
+
/** [0xD0, 0x07] The request was processed sucessfully. */
|
|
33
35
|
COMMANDS[COMMANDS["CMD_ACK_OK"] = 2000] = "CMD_ACK_OK";
|
|
34
36
|
COMMANDS[COMMANDS["CMD_ACK_REPEAT"] = 2004] = "CMD_ACK_REPEAT";
|
|
35
37
|
COMMANDS[COMMANDS["CMD_ACK_RETRY"] = 2003] = "CMD_ACK_RETRY";
|
|
38
|
+
/** [0xD5, 0x07] Connection not authorized. */
|
|
36
39
|
COMMANDS[COMMANDS["CMD_ACK_UNAUTH"] = 2005] = "CMD_ACK_UNAUTH";
|
|
40
|
+
/** Received unknown command. */
|
|
37
41
|
COMMANDS[COMMANDS["CMD_ACK_UNKNOWN"] = 65535] = "CMD_ACK_UNKNOWN";
|
|
42
|
+
/** Request attendance log. */
|
|
38
43
|
COMMANDS[COMMANDS["CMD_ATTLOG_RRQ"] = 13] = "CMD_ATTLOG_RRQ";
|
|
44
|
+
/** [0x4E, 0x04] Request to begin session using commkey. */
|
|
39
45
|
COMMANDS[COMMANDS["CMD_AUTH"] = 1102] = "CMD_AUTH";
|
|
46
|
+
/** Disable normal authentication of users. */
|
|
40
47
|
COMMANDS[COMMANDS["CMD_CANCELCAPTURE"] = 62] = "CMD_CANCELCAPTURE";
|
|
48
|
+
/** Capture fingerprint picture. */
|
|
41
49
|
COMMANDS[COMMANDS["CMD_CAPTUREFINGER"] = 1009] = "CMD_CAPTUREFINGER";
|
|
50
|
+
/** Capture the entire image. */
|
|
42
51
|
COMMANDS[COMMANDS["CMD_CAPTUREIMAGE"] = 1012] = "CMD_CAPTUREIMAGE";
|
|
52
|
+
/** Change transmission speed. */
|
|
43
53
|
COMMANDS[COMMANDS["CMD_CHANGE_SPEED"] = 1101] = "CMD_CHANGE_SPEED";
|
|
54
|
+
/** [0x77, 0x00] Get checksum of machine's buffer. */
|
|
44
55
|
COMMANDS[COMMANDS["CMD_CHECKSUM_BUFFER"] = 119] = "CMD_CHECKSUM_BUFFER";
|
|
56
|
+
/** Restore access control to default. */
|
|
45
57
|
COMMANDS[COMMANDS["CMD_CLEAR_ACC"] = 32] = "CMD_CLEAR_ACC";
|
|
58
|
+
/** Clears admins privileges. */
|
|
46
59
|
COMMANDS[COMMANDS["CMD_CLEAR_ADMIN"] = 20] = "CMD_CLEAR_ADMIN";
|
|
60
|
+
/** Delete attendance record. */
|
|
47
61
|
COMMANDS[COMMANDS["CMD_CLEAR_ATTLOG"] = 15] = "CMD_CLEAR_ATTLOG";
|
|
62
|
+
/** Delete data. */
|
|
48
63
|
COMMANDS[COMMANDS["CMD_CLEAR_DATA"] = 14] = "CMD_CLEAR_DATA";
|
|
64
|
+
/** Clear screen captions. */
|
|
49
65
|
COMMANDS[COMMANDS["CMD_CLEAR_LCD"] = 67] = "CMD_CLEAR_LCD";
|
|
66
|
+
/** Delete operations log. */
|
|
50
67
|
COMMANDS[COMMANDS["CMD_CLEAR_OPLOG"] = 33] = "CMD_CLEAR_OPLOG";
|
|
68
|
+
/** [0xE8, 0x03] Begin connection. */
|
|
51
69
|
COMMANDS[COMMANDS["CMD_CONNECT"] = 1000] = "CMD_CONNECT";
|
|
70
|
+
/** [0xDD, 0x05] Data packet. */
|
|
52
71
|
COMMANDS[COMMANDS["CMD_DATA"] = 1501] = "CMD_DATA";
|
|
72
|
+
/** Indicates that it is ready to receive data. */
|
|
53
73
|
COMMANDS[COMMANDS["CMD_DATA_RDY"] = 1504] = "CMD_DATA_RDY";
|
|
74
|
+
/** Read/Write a large data set. */
|
|
54
75
|
COMMANDS[COMMANDS["CMD_DATA_WRRQ"] = 1503] = "CMD_DATA_WRRQ";
|
|
76
|
+
/** Read saved data. */
|
|
55
77
|
COMMANDS[COMMANDS["CMD_DB_RRQ"] = 7] = "CMD_DB_RRQ";
|
|
78
|
+
/** Deletes fingerprint template. */
|
|
56
79
|
COMMANDS[COMMANDS["CMD_DEL_FPTMP"] = 134] = "CMD_DEL_FPTMP";
|
|
80
|
+
/** Delete short message. */
|
|
57
81
|
COMMANDS[COMMANDS["CMD_DELETE_SMS"] = 72] = "CMD_DELETE_SMS";
|
|
82
|
+
/** Delete user short message. */
|
|
58
83
|
COMMANDS[COMMANDS["CMD_DELETE_UDATA"] = 74] = "CMD_DELETE_UDATA";
|
|
84
|
+
/** Delete user. */
|
|
59
85
|
COMMANDS[COMMANDS["CMD_DELETE_USER"] = 18] = "CMD_DELETE_USER";
|
|
86
|
+
/** Delete user fingerprint template. */
|
|
60
87
|
COMMANDS[COMMANDS["CMD_DELETE_USERTEMP"] = 19] = "CMD_DELETE_USERTEMP";
|
|
88
|
+
/** Disables fingerprint, rfid reader and keyboard. */
|
|
61
89
|
COMMANDS[COMMANDS["CMD_DISABLEDEVICE"] = 1003] = "CMD_DISABLEDEVICE";
|
|
90
|
+
/** Get door state. */
|
|
62
91
|
COMMANDS[COMMANDS["CMD_DOORSTATE_RRQ"] = 75] = "CMD_DOORSTATE_RRQ";
|
|
92
|
+
/** Clear Mifare card. */
|
|
63
93
|
COMMANDS[COMMANDS["CMD_EMPTY_MIFARE"] = 78] = "CMD_EMPTY_MIFARE";
|
|
94
|
+
/** Enables the ":" in screen clock. */
|
|
64
95
|
COMMANDS[COMMANDS["CMD_ENABLE_CLOCK"] = 57] = "CMD_ENABLE_CLOCK";
|
|
96
|
+
/** Change machine state to "normal work". */
|
|
65
97
|
COMMANDS[COMMANDS["CMD_ENABLEDEVICE"] = 1002] = "CMD_ENABLEDEVICE";
|
|
98
|
+
/** [0xE9, 0x03] Disconnect. */
|
|
66
99
|
COMMANDS[COMMANDS["CMD_EXIT"] = 1001] = "CMD_EXIT";
|
|
100
|
+
/** [0xDE, 0x05] Release buffer used for data transmission. */
|
|
67
101
|
COMMANDS[COMMANDS["CMD_FREE_DATA"] = 1502] = "CMD_FREE_DATA";
|
|
102
|
+
/** Request machine status (remaining space). */
|
|
68
103
|
COMMANDS[COMMANDS["CMD_GET_FREE_SIZES"] = 50] = "CMD_GET_FREE_SIZES";
|
|
104
|
+
/** Request max size for users id. */
|
|
69
105
|
COMMANDS[COMMANDS["CMD_GET_PINWIDTH"] = 69] = "CMD_GET_PINWIDTH";
|
|
106
|
+
/** Request machine time. */
|
|
70
107
|
COMMANDS[COMMANDS["CMD_GET_TIME"] = 201] = "CMD_GET_TIME";
|
|
71
108
|
COMMANDS[COMMANDS["CMD_GET_USERTEMP"] = 88] = "CMD_GET_USERTEMP";
|
|
109
|
+
/** Request the firmware edition. */
|
|
72
110
|
COMMANDS[COMMANDS["CMD_GET_VERSION"] = 1100] = "CMD_GET_VERSION";
|
|
111
|
+
/** Get group timezone. */
|
|
73
112
|
COMMANDS[COMMANDS["CMD_GRPTZ_RRQ"] = 25] = "CMD_GRPTZ_RRQ";
|
|
113
|
+
/** Set group timezone. */
|
|
74
114
|
COMMANDS[COMMANDS["CMD_GRPTZ_WRQ"] = 26] = "CMD_GRPTZ_WRQ";
|
|
115
|
+
/** Read operations log. */
|
|
75
116
|
COMMANDS[COMMANDS["CMD_OPLOG_RRQ"] = 34] = "CMD_OPLOG_RRQ";
|
|
117
|
+
/** Read configuration value of the machine. */
|
|
76
118
|
COMMANDS[COMMANDS["CMD_OPTIONS_RRQ"] = 11] = "CMD_OPTIONS_RRQ";
|
|
119
|
+
/** Change configuration value of the machine. */
|
|
77
120
|
COMMANDS[COMMANDS["CMD_OPTIONS_WRQ"] = 12] = "CMD_OPTIONS_WRQ";
|
|
121
|
+
/** Shut-down machine. */
|
|
78
122
|
COMMANDS[COMMANDS["CMD_POWEROFF"] = 1005] = "CMD_POWEROFF";
|
|
123
|
+
/** [0xDC, 0x05] Prepare for data transmission. */
|
|
79
124
|
COMMANDS[COMMANDS["CMD_PREPARE_DATA"] = 1500] = "CMD_PREPARE_DATA";
|
|
125
|
+
/** [0xF5, 0x03] Refresh the machine stored data. */
|
|
80
126
|
COMMANDS[COMMANDS["CMD_REFRESHDATA"] = 1013] = "CMD_REFRESHDATA";
|
|
127
|
+
/** Refresh the configuration parameters. */
|
|
81
128
|
COMMANDS[COMMANDS["CMD_REFRESHOPTION"] = 1014] = "CMD_REFRESHOPTION";
|
|
129
|
+
/** Realtime events. */
|
|
82
130
|
COMMANDS[COMMANDS["CMD_REG_EVENT"] = 500] = "CMD_REG_EVENT";
|
|
131
|
+
/** Restart machine. */
|
|
83
132
|
COMMANDS[COMMANDS["CMD_RESTART"] = 1004] = "CMD_RESTART";
|
|
133
|
+
/** Change machine state to "awaken". */
|
|
84
134
|
COMMANDS[COMMANDS["CMD_RESUME"] = 1007] = "CMD_RESUME";
|
|
135
|
+
/** Set machine time. */
|
|
85
136
|
COMMANDS[COMMANDS["CMD_SET_TIME"] = 202] = "CMD_SET_TIME";
|
|
137
|
+
/** Change machine state to "idle". */
|
|
86
138
|
COMMANDS[COMMANDS["CMD_SLEEP"] = 1006] = "CMD_SLEEP";
|
|
139
|
+
/** Download short message. */
|
|
87
140
|
COMMANDS[COMMANDS["CMD_SMS_RRQ"] = 71] = "CMD_SMS_RRQ";
|
|
141
|
+
/** Upload short message. */
|
|
88
142
|
COMMANDS[COMMANDS["CMD_SMS_WRQ"] = 70] = "CMD_SMS_WRQ";
|
|
143
|
+
/** Start enroll procedure. */
|
|
89
144
|
COMMANDS[COMMANDS["CMD_STARTENROLL"] = 61] = "CMD_STARTENROLL";
|
|
145
|
+
/** Set the machine to authentication state. */
|
|
90
146
|
COMMANDS[COMMANDS["CMD_STARTVERIFY"] = 60] = "CMD_STARTVERIFY";
|
|
147
|
+
/** Query state. */
|
|
91
148
|
COMMANDS[COMMANDS["CMD_STATE_RRQ"] = 64] = "CMD_STATE_RRQ";
|
|
149
|
+
/** Test if fingerprint exists. */
|
|
92
150
|
COMMANDS[COMMANDS["CMD_TEST_TEMP"] = 1011] = "CMD_TEST_TEMP";
|
|
151
|
+
/** Test voice. */
|
|
93
152
|
COMMANDS[COMMANDS["CMD_TESTVOICE"] = 1017] = "CMD_TESTVOICE";
|
|
153
|
+
/** [0x77, 0x00] Transfer fp template from buffer. */
|
|
94
154
|
COMMANDS[COMMANDS["CMD_TMP_WRITE"] = 87] = "CMD_TMP_WRITE";
|
|
155
|
+
/** Get device timezones. */
|
|
95
156
|
COMMANDS[COMMANDS["CMD_TZ_RRQ"] = 27] = "CMD_TZ_RRQ";
|
|
157
|
+
/** Set device timezones. */
|
|
96
158
|
COMMANDS[COMMANDS["CMD_TZ_WRQ"] = 28] = "CMD_TZ_WRQ";
|
|
159
|
+
/** Set user short message. */
|
|
97
160
|
COMMANDS[COMMANDS["CMD_UDATA_WRQ"] = 73] = "CMD_UDATA_WRQ";
|
|
161
|
+
/** Get group combination to unlock. */
|
|
98
162
|
COMMANDS[COMMANDS["CMD_ULG_RRQ"] = 29] = "CMD_ULG_RRQ";
|
|
163
|
+
/** Set group combination to unlock. */
|
|
99
164
|
COMMANDS[COMMANDS["CMD_ULG_WRQ"] = 30] = "CMD_ULG_WRQ";
|
|
165
|
+
/** Unlock door for a specified amount of time. */
|
|
100
166
|
COMMANDS[COMMANDS["CMD_UNLOCK"] = 31] = "CMD_UNLOCK";
|
|
167
|
+
/** Upload user data. */
|
|
101
168
|
COMMANDS[COMMANDS["CMD_USER_WRQ"] = 8] = "CMD_USER_WRQ";
|
|
169
|
+
/** Read user group. */
|
|
102
170
|
COMMANDS[COMMANDS["CMD_USERGRP_RRQ"] = 21] = "CMD_USERGRP_RRQ";
|
|
171
|
+
/** Set user group. */
|
|
103
172
|
COMMANDS[COMMANDS["CMD_USERGRP_WRQ"] = 22] = "CMD_USERGRP_WRQ";
|
|
173
|
+
/** [0x09, 0x00] Read user fingerprint template. */
|
|
104
174
|
COMMANDS[COMMANDS["CMD_USERTEMP_RRQ"] = 9] = "CMD_USERTEMP_RRQ";
|
|
175
|
+
/** Upload user fingerprint template. */
|
|
105
176
|
COMMANDS[COMMANDS["CMD_USERTEMP_WRQ"] = 10] = "CMD_USERTEMP_WRQ";
|
|
177
|
+
/** Get user timezones. */
|
|
106
178
|
COMMANDS[COMMANDS["CMD_USERTZ_RRQ"] = 23] = "CMD_USERTZ_RRQ";
|
|
179
|
+
/** Set the user timezones. */
|
|
107
180
|
COMMANDS[COMMANDS["CMD_USERTZ_WRQ"] = 24] = "CMD_USERTZ_WRQ";
|
|
181
|
+
/** Read verification style of a given user. */
|
|
108
182
|
COMMANDS[COMMANDS["CMD_VERIFY_RRQ"] = 80] = "CMD_VERIFY_RRQ";
|
|
183
|
+
/** Change verification style of a given user. */
|
|
109
184
|
COMMANDS[COMMANDS["CMD_VERIFY_WRQ"] = 79] = "CMD_VERIFY_WRQ";
|
|
185
|
+
/** Prints chars to the device screen. */
|
|
110
186
|
COMMANDS[COMMANDS["CMD_WRITE_LCD"] = 66] = "CMD_WRITE_LCD";
|
|
187
|
+
/** Write data to Mifare card. */
|
|
111
188
|
COMMANDS[COMMANDS["CMD_WRITE_MIFARE"] = 76] = "CMD_WRITE_MIFARE";
|
|
189
|
+
/** Triggered alarm. */
|
|
112
190
|
COMMANDS[COMMANDS["EF_ALARM"] = 512] = "EF_ALARM";
|
|
191
|
+
/** Attendance entry. */
|
|
113
192
|
COMMANDS[COMMANDS["EF_ATTLOG"] = 1] = "EF_ATTLOG";
|
|
193
|
+
/** Pressed keyboard key. */
|
|
114
194
|
COMMANDS[COMMANDS["EF_BUTTON"] = 16] = "EF_BUTTON";
|
|
195
|
+
/** Upload user data. */
|
|
115
196
|
COMMANDS[COMMANDS["EF_ENROLLFINGER"] = 8] = "EF_ENROLLFINGER";
|
|
197
|
+
/** Enrolled user. */
|
|
116
198
|
COMMANDS[COMMANDS["EF_ENROLLUSER"] = 4] = "EF_ENROLLUSER";
|
|
199
|
+
/** Pressed finger. */
|
|
117
200
|
COMMANDS[COMMANDS["EF_FINGER"] = 2] = "EF_FINGER";
|
|
201
|
+
/** Fingerprint score in enroll procedure. */
|
|
118
202
|
COMMANDS[COMMANDS["EF_FPFTR"] = 256] = "EF_FPFTR";
|
|
203
|
+
/** Restore access control to default. */
|
|
119
204
|
COMMANDS[COMMANDS["EF_UNLOCK"] = 32] = "EF_UNLOCK";
|
|
205
|
+
/** Registered user placed finger. */
|
|
120
206
|
COMMANDS[COMMANDS["EF_VERIFY"] = 128] = "EF_VERIFY";
|
|
121
207
|
})(COMMANDS || (COMMANDS = {}));
|
|
208
|
+
var DISCOVERED_CMD;
|
|
209
|
+
(function (DISCOVERED_CMD) {
|
|
210
|
+
/** Returned when the Finger id not exists in the user uid, when attempting to download single finger template */
|
|
211
|
+
DISCOVERED_CMD[DISCOVERED_CMD["FID_NOT_FOUND"] = 4993] = "FID_NOT_FOUND";
|
|
212
|
+
})(DISCOVERED_CMD || (DISCOVERED_CMD = {}));
|
|
122
213
|
var Constants;
|
|
123
214
|
(function (Constants) {
|
|
124
215
|
Constants[Constants["USHRT_MAX"] = 65535] = "USHRT_MAX";
|
|
@@ -127,6 +218,7 @@ var Constants;
|
|
|
127
218
|
Constants[Constants["MACHINE_PREPARE_DATA_2"] = 32130] = "MACHINE_PREPARE_DATA_2";
|
|
128
219
|
})(Constants || (Constants = {}));
|
|
129
220
|
const REQUEST_DATA = {
|
|
221
|
+
START_TAG: Buffer.from([0x50, 0x50, 0x82, 0x7d]),
|
|
130
222
|
DISABLE_DEVICE: Buffer.from([0, 0, 0, 0]),
|
|
131
223
|
GET_REAL_TIME_EVENT: Buffer.from([0x01, 0x00, 0x00, 0x00]),
|
|
132
224
|
GET_ATTENDANCE_LOGS: Buffer.from([0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
|
@@ -274,18 +366,23 @@ class User {
|
|
|
274
366
|
}
|
|
275
367
|
|
|
276
368
|
class Attendance {
|
|
369
|
+
/** Internal serial number for the user */
|
|
277
370
|
sn;
|
|
371
|
+
/** User ID/Pin stored as a string */
|
|
278
372
|
user_id;
|
|
279
|
-
|
|
373
|
+
/** Verification type */
|
|
280
374
|
type;
|
|
375
|
+
/** Time of the attendance event */
|
|
376
|
+
record_time;
|
|
377
|
+
/** Verify state */
|
|
281
378
|
state;
|
|
282
379
|
ip;
|
|
283
|
-
constructor(sn, user_id,
|
|
380
|
+
constructor(sn, user_id, type, record_time, state) {
|
|
284
381
|
this.sn = sn;
|
|
285
382
|
this.user_id = user_id;
|
|
383
|
+
this.type = type || undefined;
|
|
286
384
|
this.record_time = record_time;
|
|
287
|
-
this.
|
|
288
|
-
this.state = state;
|
|
385
|
+
this.state = state || undefined;
|
|
289
386
|
}
|
|
290
387
|
}
|
|
291
388
|
|
|
@@ -329,17 +426,7 @@ const createUDPHeader = (command, sessionId, replyId, data) => {
|
|
|
329
426
|
return buf;
|
|
330
427
|
};
|
|
331
428
|
const createTCPHeader = (command, sessionId, replyId, data) => {
|
|
332
|
-
const
|
|
333
|
-
const buf = Buffer.alloc(8 + dataBuffer.length);
|
|
334
|
-
buf.writeUInt16LE(command, 0);
|
|
335
|
-
buf.writeUInt16LE(0, 2);
|
|
336
|
-
buf.writeUInt16LE(sessionId, 4);
|
|
337
|
-
buf.writeUInt16LE(replyId, 6);
|
|
338
|
-
dataBuffer.copy(buf, 8);
|
|
339
|
-
const chksum2 = createChkSum(buf);
|
|
340
|
-
buf.writeUInt16LE(chksum2, 2);
|
|
341
|
-
replyId = (replyId + 1) % Constants.USHRT_MAX;
|
|
342
|
-
buf.writeUInt16LE(replyId, 6);
|
|
429
|
+
const buf = createUDPHeader(command, sessionId, replyId, data);
|
|
343
430
|
const prefixBuf = Buffer.from([0x50, 0x50, 0x82, 0x7d, 0x13, 0x00, 0x00, 0x00]);
|
|
344
431
|
prefixBuf.writeUInt16LE(buf.length, 4);
|
|
345
432
|
return Buffer.concat([prefixBuf, buf]);
|
|
@@ -399,7 +486,7 @@ const decodeRecordData40 = (recordData) => {
|
|
|
399
486
|
.slice(2, 2 + 9)
|
|
400
487
|
.toString('ascii')
|
|
401
488
|
.split('\0')
|
|
402
|
-
.shift() || '',
|
|
489
|
+
.shift() || '', recordData.readUIntLE(26, 1), parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(31, 1));
|
|
403
490
|
};
|
|
404
491
|
const decodeRecordData16 = (recordData) => {
|
|
405
492
|
return {
|
|
@@ -515,6 +602,59 @@ const authKey = (comKey, sessionId) => {
|
|
|
515
602
|
return Array.from(response);
|
|
516
603
|
};
|
|
517
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Error types for device communication
|
|
607
|
+
*/
|
|
608
|
+
const ERROR_TYPES = {
|
|
609
|
+
ECONNRESET: 'ECONNRESET',
|
|
610
|
+
ECONNREFUSED: 'ECONNREFUSED'};
|
|
611
|
+
/**
|
|
612
|
+
* Custom error class for device communication errors
|
|
613
|
+
*/
|
|
614
|
+
class ZkError {
|
|
615
|
+
err;
|
|
616
|
+
ip;
|
|
617
|
+
command;
|
|
618
|
+
/**
|
|
619
|
+
* Creates a new ZkError instance
|
|
620
|
+
* @param err The error object
|
|
621
|
+
* @param command The command that caused the error
|
|
622
|
+
* @param ip The IP address of the device
|
|
623
|
+
*/
|
|
624
|
+
constructor(err, command, ip) {
|
|
625
|
+
this.err = err;
|
|
626
|
+
this.ip = ip;
|
|
627
|
+
this.command = command;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Gets a user-friendly error message
|
|
631
|
+
* @returns A formatted error message
|
|
632
|
+
*/
|
|
633
|
+
toast() {
|
|
634
|
+
if (this.err.code === ERROR_TYPES.ECONNRESET) {
|
|
635
|
+
return 'Another device is connecting to the device so the connection is interrupted';
|
|
636
|
+
}
|
|
637
|
+
else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
|
|
638
|
+
return 'IP of the device is refused';
|
|
639
|
+
}
|
|
640
|
+
return this.err.message;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Gets detailed error information
|
|
644
|
+
* @returns An object containing error details
|
|
645
|
+
*/
|
|
646
|
+
getError() {
|
|
647
|
+
return {
|
|
648
|
+
err: {
|
|
649
|
+
message: this.err.message,
|
|
650
|
+
code: this.err.code
|
|
651
|
+
},
|
|
652
|
+
ip: this.ip,
|
|
653
|
+
command: this.command
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
518
658
|
/**
|
|
519
659
|
* Represents a fingerprint template with associated metadata
|
|
520
660
|
*/
|
|
@@ -588,56 +728,547 @@ class Finger {
|
|
|
588
728
|
}
|
|
589
729
|
}
|
|
590
730
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
731
|
+
class UserService {
|
|
732
|
+
_zkTcp;
|
|
733
|
+
_users;
|
|
734
|
+
constructor(zkTcp) {
|
|
735
|
+
this._zkTcp = zkTcp;
|
|
736
|
+
}
|
|
737
|
+
async getUserByUserId(user_id) {
|
|
738
|
+
if (!this._users) {
|
|
739
|
+
await this.getUsers();
|
|
740
|
+
}
|
|
741
|
+
if (this._users.has(String(user_id))) {
|
|
742
|
+
return this._users.get(String(user_id));
|
|
743
|
+
}
|
|
744
|
+
else
|
|
745
|
+
throw new Error("user_id not exists");
|
|
746
|
+
}
|
|
747
|
+
async getUsers() {
|
|
748
|
+
try {
|
|
749
|
+
// Free any existing buffer data to prepare for a new request
|
|
750
|
+
if (this._users) {
|
|
751
|
+
return { data: Array.from(this._users.values()) };
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
this._users = new Map([]);
|
|
755
|
+
}
|
|
756
|
+
if (this._zkTcp.socket) {
|
|
757
|
+
await this._zkTcp.freeData();
|
|
758
|
+
}
|
|
759
|
+
// Request user data
|
|
760
|
+
const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_USERS);
|
|
761
|
+
// Free buffer data after receiving the data
|
|
762
|
+
if (this._zkTcp.socket) {
|
|
763
|
+
await this._zkTcp.freeData();
|
|
764
|
+
}
|
|
765
|
+
// Constants for user data processing
|
|
766
|
+
const USER_PACKET_SIZE = 72;
|
|
767
|
+
// Ensure data.data is a valid buffer
|
|
768
|
+
if (!data.data || !(data.data instanceof Buffer)) {
|
|
769
|
+
throw new Error('Invalid data received');
|
|
770
|
+
}
|
|
771
|
+
let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
|
|
772
|
+
const users = [];
|
|
773
|
+
// Process each user packet
|
|
774
|
+
while (userData.length >= USER_PACKET_SIZE) {
|
|
775
|
+
// Decode user data and add to the users array
|
|
776
|
+
const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
|
|
777
|
+
users.push(user);
|
|
778
|
+
this._users.set(user.user_id, user);
|
|
779
|
+
userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
|
|
780
|
+
}
|
|
781
|
+
// Return the list of users
|
|
782
|
+
return { data: users };
|
|
783
|
+
}
|
|
784
|
+
catch (err) {
|
|
785
|
+
// Log the error for debugging
|
|
786
|
+
console.error('Error getting users:', err);
|
|
787
|
+
// Re-throw the error to be handled by the caller
|
|
788
|
+
throw err;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
async setUser(user_id, name, password, role = 0, cardno = 0) {
|
|
792
|
+
let user;
|
|
793
|
+
try {
|
|
794
|
+
user = await this.getUserByUserId(user_id);
|
|
795
|
+
}
|
|
796
|
+
catch (err) {
|
|
797
|
+
if (err.message.includes("user_id not exists")) {
|
|
798
|
+
user.uid = Math.max(...Array.from(this._users.values()).map(usr => usr.uid)) + 1;
|
|
799
|
+
this._users.set(user_id, user);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
// Validate input parameters
|
|
804
|
+
if (user_id.length > 9 ||
|
|
805
|
+
name.length > 24 ||
|
|
806
|
+
password.length > 8 ||
|
|
807
|
+
typeof role !== 'number' ||
|
|
808
|
+
cardno.toString().length > 10) {
|
|
809
|
+
throw new Error('Invalid input parameters');
|
|
810
|
+
}
|
|
811
|
+
// Allocate and initialize the buffer
|
|
812
|
+
const commandBuffer = Buffer.alloc(72);
|
|
813
|
+
// Fill the buffer with user data
|
|
814
|
+
commandBuffer.writeUInt16LE(user.uid, 0);
|
|
815
|
+
commandBuffer.writeUInt16LE(role, 2);
|
|
816
|
+
commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
|
|
817
|
+
commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
|
|
818
|
+
commandBuffer.writeUInt16LE(cardno, 35);
|
|
819
|
+
commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
|
|
820
|
+
commandBuffer.write(user_id.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
|
|
821
|
+
// Send the command and return the result
|
|
822
|
+
const created = await this._zkTcp.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
|
|
823
|
+
return !!created;
|
|
824
|
+
}
|
|
825
|
+
catch (err) {
|
|
826
|
+
// Log error details for debugging
|
|
827
|
+
console.error('Error setting user:', err);
|
|
828
|
+
// Re-throw error for upstream handling
|
|
829
|
+
throw err;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async DeleteUser(user_id) {
|
|
833
|
+
try {
|
|
834
|
+
const user = await this.getUserByUserId(user_id);
|
|
835
|
+
// Allocate and initialize the buffer
|
|
836
|
+
const commandBuffer = Buffer.alloc(72);
|
|
837
|
+
// Write UID to the buffer
|
|
838
|
+
commandBuffer.writeUInt16LE(user.uid, 0);
|
|
839
|
+
// Send the delete command and return the result
|
|
840
|
+
const deleted = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
|
|
841
|
+
return !!deleted;
|
|
842
|
+
}
|
|
843
|
+
catch (err) {
|
|
844
|
+
// Log error details for debugging
|
|
845
|
+
console.error('Error deleting user:', err);
|
|
846
|
+
// Re-throw error for upstream handling
|
|
847
|
+
throw err;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
async getTemplates(callbackInProcess = () => { }) {
|
|
851
|
+
let templates = [];
|
|
852
|
+
try {
|
|
853
|
+
if (this._zkTcp.socket) {
|
|
854
|
+
await this._zkTcp.freeData();
|
|
855
|
+
}
|
|
856
|
+
await this._zkTcp.getSizes();
|
|
857
|
+
if (this._zkTcp.fp_count == 0)
|
|
858
|
+
return { data: [] };
|
|
859
|
+
await this._zkTcp.disableDevice();
|
|
860
|
+
const resp = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
|
|
861
|
+
let templateData = resp.data.subarray(4);
|
|
862
|
+
let totalSize = resp.data.readUIntLE(0, 4);
|
|
863
|
+
while (totalSize) {
|
|
864
|
+
const buf = templateData.subarray(0, 6);
|
|
865
|
+
const size = buf.readUIntLE(0, 2);
|
|
866
|
+
const uid = buf.readUIntLE(2, 2);
|
|
867
|
+
const fid = buf.readUIntLE(4, 1);
|
|
868
|
+
const valid = buf.readUIntLE(5, 1);
|
|
869
|
+
// Force-copy bytes so we don't retain the entire big backing buffer
|
|
870
|
+
const tplBytes = Buffer.from(templateData.subarray(6, size));
|
|
871
|
+
templates.push(new Finger(uid, fid, valid, tplBytes));
|
|
872
|
+
templateData = templateData.subarray(size);
|
|
873
|
+
totalSize -= size;
|
|
874
|
+
}
|
|
875
|
+
return { data: templates };
|
|
876
|
+
}
|
|
877
|
+
catch (err) {
|
|
878
|
+
this._zkTcp.verbose && console.log("Error getting templates", err);
|
|
879
|
+
return { data: templates };
|
|
880
|
+
}
|
|
881
|
+
finally {
|
|
882
|
+
await this._zkTcp.freeData();
|
|
883
|
+
await this._zkTcp.enableDevice();
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async DownloadFp(user_id, fid) {
|
|
887
|
+
try {
|
|
888
|
+
const user = await this.getUserByUserId(user_id);
|
|
889
|
+
if (0 > fid || fid > 9)
|
|
890
|
+
throw new Error('fid must be between 0 and 9');
|
|
891
|
+
// Allocate and initialize the buffer
|
|
892
|
+
const data = Buffer.alloc(3);
|
|
893
|
+
// Fill the buffer with user data
|
|
894
|
+
data.writeUInt16LE(user.uid, 0);
|
|
895
|
+
data.writeUIntLE(fid, 2, 1);
|
|
896
|
+
this._zkTcp.replyId++;
|
|
897
|
+
const packet = createTCPHeader(COMMANDS.CMD_USERTEMP_RRQ, this._zkTcp.sessionId, this._zkTcp.replyId, data);
|
|
898
|
+
let fingerSize = 0;
|
|
899
|
+
let fingerTemplate = Buffer.from([]);
|
|
900
|
+
return await new Promise((resolve, reject) => {
|
|
901
|
+
let timeout;
|
|
902
|
+
const cleanup = () => {
|
|
903
|
+
if (this._zkTcp.socket) {
|
|
904
|
+
this._zkTcp.socket.removeListener('data', receiveData);
|
|
905
|
+
}
|
|
906
|
+
if (timeout)
|
|
907
|
+
clearTimeout(timeout);
|
|
908
|
+
};
|
|
909
|
+
let timer = () => setTimeout(() => {
|
|
910
|
+
cleanup();
|
|
911
|
+
reject(new Error('Time Out, Could not retrieve data'));
|
|
912
|
+
}, this._zkTcp.timeout);
|
|
913
|
+
const receiveData = (data) => {
|
|
914
|
+
timeout = timer();
|
|
915
|
+
if (data.length === 0)
|
|
916
|
+
return;
|
|
917
|
+
try {
|
|
918
|
+
if (data.length == 0)
|
|
919
|
+
return;
|
|
920
|
+
const headers = decodeTCPHeader(data);
|
|
921
|
+
switch (headers.commandId) {
|
|
922
|
+
case DISCOVERED_CMD.FID_NOT_FOUND:
|
|
923
|
+
throw new Error('Could not retrieve data. maybe finger id not exists?');
|
|
924
|
+
case COMMANDS.CMD_PREPARE_DATA:
|
|
925
|
+
fingerSize = data.readUIntLE(16, 2);
|
|
926
|
+
break;
|
|
927
|
+
case COMMANDS.CMD_DATA:
|
|
928
|
+
// A single 'data' event might contain multiple TCP packets combined by the OS
|
|
929
|
+
// in this method, is possible to get CMD_DATA and CMD_ACK_OK in the same event,
|
|
930
|
+
// so It's important to split data received for remove CMD_ACK_OK headers
|
|
931
|
+
fingerTemplate = Buffer.concat([fingerTemplate, data.subarray(16, fingerSize + 10)]);
|
|
932
|
+
// @ts-ignore
|
|
933
|
+
resolve(fingerTemplate);
|
|
934
|
+
break;
|
|
935
|
+
case COMMANDS.CMD_ACK_OK:
|
|
936
|
+
cleanup();
|
|
937
|
+
// @ts-ignore
|
|
938
|
+
resolve(fingerTemplate);
|
|
939
|
+
return;
|
|
940
|
+
default:
|
|
941
|
+
// If it's not a recognized command but has data, it might be raw template data
|
|
942
|
+
if (headers.commandId > 2000 && headers.commandId < 3000) {
|
|
943
|
+
// Likely another ACK or system msg
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
fingerTemplate = Buffer.concat([fingerTemplate, data]);
|
|
947
|
+
}
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
clearTimeout(timeout);
|
|
951
|
+
}
|
|
952
|
+
catch (e) {
|
|
953
|
+
cleanup();
|
|
954
|
+
reject(e);
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
if (this._zkTcp.socket) {
|
|
958
|
+
this._zkTcp.socket.on('data', receiveData);
|
|
959
|
+
this._zkTcp.socket.write(packet, (err) => {
|
|
960
|
+
if (err) {
|
|
961
|
+
cleanup();
|
|
962
|
+
reject(err);
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
reject(new Error('Socket not initialized'));
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
catch (err) {
|
|
972
|
+
throw err;
|
|
973
|
+
}
|
|
974
|
+
finally {
|
|
975
|
+
await this._zkTcp.refreshData();
|
|
976
|
+
}
|
|
614
977
|
}
|
|
615
978
|
/**
|
|
616
|
-
*
|
|
617
|
-
* @
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
979
|
+
*
|
|
980
|
+
* @param user_id {string} user
|
|
981
|
+
* @param fingers {Finger[]} array of finger templates instances
|
|
982
|
+
* */
|
|
983
|
+
async saveTemplates(user_id, fingers = []) {
|
|
984
|
+
if (fingers.length > 9 || fingers.length == 0)
|
|
985
|
+
throw new Error("maximum finger length is 10 and can't be empty");
|
|
986
|
+
try {
|
|
987
|
+
await this._zkTcp.disableDevice();
|
|
988
|
+
// check users exists
|
|
989
|
+
const user = await this.getUserByUserId(user_id);
|
|
990
|
+
let fpack = Buffer.alloc(0);
|
|
991
|
+
let table = Buffer.alloc(0);
|
|
992
|
+
const fnum = 0x10;
|
|
993
|
+
let tstart = 0;
|
|
994
|
+
for (const finger of fingers) {
|
|
995
|
+
const tfp = finger.repackOnly();
|
|
996
|
+
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
|
|
997
|
+
tableEntry.writeInt8(2, 0);
|
|
998
|
+
tableEntry.writeUInt16LE(user.uid, 1);
|
|
999
|
+
tableEntry.writeInt8(fnum + finger.fid, 3);
|
|
1000
|
+
tableEntry.writeUInt32LE(tstart, 4);
|
|
1001
|
+
table = Buffer.concat([table, tableEntry]);
|
|
1002
|
+
tstart += tfp.length;
|
|
1003
|
+
fpack = Buffer.concat([fpack, tfp]);
|
|
1004
|
+
}
|
|
1005
|
+
let upack;
|
|
1006
|
+
if (this._zkTcp.userPacketSize === 28) {
|
|
1007
|
+
upack = user.repack29();
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
upack = user.repack73();
|
|
1011
|
+
}
|
|
1012
|
+
const head = Buffer.alloc(12); // III = 3*4 bytes
|
|
1013
|
+
head.writeUInt32LE(upack.length, 0);
|
|
1014
|
+
head.writeUInt32LE(table.length, 4);
|
|
1015
|
+
head.writeUInt32LE(fpack.length, 8);
|
|
1016
|
+
const packet = Buffer.concat([head, upack, table, fpack]);
|
|
1017
|
+
const bufferResponse = await this._zkTcp.sendWithBuffer(packet);
|
|
1018
|
+
const command = 110;
|
|
1019
|
+
const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
|
|
1020
|
+
commandString.writeUInt32LE(12, 0);
|
|
1021
|
+
commandString.writeUInt16LE(0, 4);
|
|
1022
|
+
commandString.writeUInt16LE(8, 6);
|
|
1023
|
+
const cmdResponse = await this._zkTcp.executeCmd(command, commandString);
|
|
1024
|
+
if (this._zkTcp.verbose)
|
|
1025
|
+
console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
|
|
622
1026
|
}
|
|
623
|
-
|
|
624
|
-
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
throw error;
|
|
1029
|
+
}
|
|
1030
|
+
finally {
|
|
1031
|
+
await this._zkTcp.refreshData();
|
|
1032
|
+
await this._zkTcp.enableDevice();
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async deleteFinger(user_id, fid) {
|
|
1036
|
+
try {
|
|
1037
|
+
if (!this._users.has(user_id))
|
|
1038
|
+
throw new Error("user_id not exists");
|
|
1039
|
+
const user = await this.getUserByUserId(user_id);
|
|
1040
|
+
const buf = Buffer.alloc(4);
|
|
1041
|
+
buf.writeUInt16LE(user_id ? user.uid : 0, 0);
|
|
1042
|
+
buf.writeUint16LE(fid ? fid : 0, 2);
|
|
1043
|
+
const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
|
|
1044
|
+
return !!reply;
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
throw new Error("Can't save utemp");
|
|
1048
|
+
}
|
|
1049
|
+
finally {
|
|
1050
|
+
await this._zkTcp.refreshData();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
async enrollInfo(user_id, tempId) {
|
|
1054
|
+
let done = false;
|
|
1055
|
+
try {
|
|
1056
|
+
const userBuf = Buffer.alloc(24);
|
|
1057
|
+
userBuf.write(user_id, 0, 24, 'ascii');
|
|
1058
|
+
let commandString = Buffer.concat([
|
|
1059
|
+
userBuf,
|
|
1060
|
+
Buffer.from([tempId, 1])
|
|
1061
|
+
]);
|
|
1062
|
+
const sendAckOk = async () => {
|
|
1063
|
+
try {
|
|
1064
|
+
const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this._zkTcp.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
|
|
1065
|
+
this._zkTcp.socket.write(buf);
|
|
1066
|
+
}
|
|
1067
|
+
catch (e) {
|
|
1068
|
+
throw new ZkError(e, COMMANDS.CMD_ACK_OK, this._zkTcp.ip);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
const cancel = await this._zkTcp.cancelCapture();
|
|
1072
|
+
const cmdResponse = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
|
|
1073
|
+
this._zkTcp.timeout = 60000; // 60 seconds timeout
|
|
1074
|
+
let attempts = 3;
|
|
1075
|
+
while (attempts > 0) {
|
|
1076
|
+
if (this._zkTcp.verbose)
|
|
1077
|
+
console.log(`A:${attempts} esperando primer regevent`);
|
|
1078
|
+
let dataRecv = await this._zkTcp.readSocket(17);
|
|
1079
|
+
await sendAckOk();
|
|
1080
|
+
if (dataRecv.length > 16) {
|
|
1081
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1082
|
+
const res = padded.readUInt16LE(16);
|
|
1083
|
+
if (this._zkTcp.verbose)
|
|
1084
|
+
console.log(`res ${res}`);
|
|
1085
|
+
if (res === 0 || res === 6 || res === 4) {
|
|
1086
|
+
if (this._zkTcp.verbose)
|
|
1087
|
+
console.log("posible timeout o reg Fallido");
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (this._zkTcp.verbose)
|
|
1092
|
+
console.log(`A:${attempts} esperando 2do regevent`);
|
|
1093
|
+
dataRecv = await this._zkTcp.readSocket(17);
|
|
1094
|
+
await sendAckOk();
|
|
1095
|
+
if (this._zkTcp.verbose)
|
|
1096
|
+
console.log(dataRecv);
|
|
1097
|
+
if (dataRecv.length > 8) {
|
|
1098
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1099
|
+
const res = padded.readUInt16LE(16);
|
|
1100
|
+
if (this._zkTcp.verbose)
|
|
1101
|
+
console.log(`res ${res}`);
|
|
1102
|
+
if (res === 6 || res === 4) {
|
|
1103
|
+
if (this._zkTcp.verbose)
|
|
1104
|
+
console.log("posible timeout o reg Fallido");
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
else if (res === 0x64) {
|
|
1108
|
+
if (this._zkTcp.verbose)
|
|
1109
|
+
console.log("ok, continue?");
|
|
1110
|
+
attempts--;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (attempts === 0) {
|
|
1115
|
+
const dataRecv = await this._zkTcp.readSocket(17);
|
|
1116
|
+
await sendAckOk();
|
|
1117
|
+
if (this._zkTcp.verbose)
|
|
1118
|
+
console.log(dataRecv.toString('hex'));
|
|
1119
|
+
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1120
|
+
let res = padded.readUInt16LE(16);
|
|
1121
|
+
if (this._zkTcp.verbose)
|
|
1122
|
+
console.log(`res ${res}`);
|
|
1123
|
+
if (res === 5) {
|
|
1124
|
+
if (this._zkTcp.verbose)
|
|
1125
|
+
console.log("finger duplicate");
|
|
1126
|
+
}
|
|
1127
|
+
if (res === 6 || res === 4) {
|
|
1128
|
+
if (this._zkTcp.verbose)
|
|
1129
|
+
console.log("posible timeout");
|
|
1130
|
+
}
|
|
1131
|
+
if (res === 0) {
|
|
1132
|
+
const size = padded.readUInt16LE(10);
|
|
1133
|
+
const pos = padded.readUInt16LE(12);
|
|
1134
|
+
if (this._zkTcp.verbose)
|
|
1135
|
+
console.log(`enroll ok ${size} ${pos}`);
|
|
1136
|
+
done = true;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
//this.__sock.setTimeout(this.__timeout);
|
|
1140
|
+
await this._zkTcp.regEvent(0); // TODO: test
|
|
1141
|
+
return done;
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
throw error;
|
|
1145
|
+
}
|
|
1146
|
+
finally {
|
|
1147
|
+
await this._zkTcp.cancelCapture();
|
|
1148
|
+
await this.verify(user_id);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
async verify(user_id) {
|
|
1152
|
+
try {
|
|
1153
|
+
const user = await this.getUserByUserId(user_id);
|
|
1154
|
+
const command_string = Buffer.alloc(4);
|
|
1155
|
+
command_string.writeUInt32LE(user.uid, 0);
|
|
1156
|
+
const reply = await this._zkTcp.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
|
|
1157
|
+
if (this._zkTcp.verbose)
|
|
1158
|
+
console.log(reply.readUInt16LE(0));
|
|
1159
|
+
return !!reply;
|
|
1160
|
+
}
|
|
1161
|
+
catch (error) {
|
|
1162
|
+
console.error(error);
|
|
1163
|
+
throw error;
|
|
625
1164
|
}
|
|
626
|
-
return this.err.message;
|
|
627
1165
|
}
|
|
628
1166
|
/**
|
|
629
|
-
*
|
|
630
|
-
* @
|
|
1167
|
+
* Upload a single fingerprint for a given user id
|
|
1168
|
+
* @param user_id {string} user id for customer
|
|
1169
|
+
* @param fingerTemplate {string} finger template in base64 string
|
|
1170
|
+
* @param fid {number} finger id is a number between 0 and 9
|
|
1171
|
+
* @param fp_valid {number} finger flag. e.g., valid=1, duress=3
|
|
631
1172
|
*/
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1173
|
+
async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
|
|
1174
|
+
try {
|
|
1175
|
+
const check_ACK_OK = (buf) => {
|
|
1176
|
+
let resp_cmd = initPacket.readUInt16LE(0);
|
|
1177
|
+
if (resp_cmd === COMMANDS.CMD_ACK_OK)
|
|
1178
|
+
return true;
|
|
1179
|
+
else
|
|
1180
|
+
throw new Error(`received unexpected command: ${resp_cmd}`);
|
|
1181
|
+
};
|
|
1182
|
+
const user = this._users.get(user_id);
|
|
1183
|
+
await this._zkTcp.disableDevice();
|
|
1184
|
+
const prep_struct = Buffer.alloc(4);
|
|
1185
|
+
const fingerBuffer = Buffer.from(fingerTemplate, 'base64');
|
|
1186
|
+
const fp_size = fingerBuffer.length;
|
|
1187
|
+
prep_struct.writeUInt16LE(fp_size, 0);
|
|
1188
|
+
const initPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_PREPARE_DATA, prep_struct);
|
|
1189
|
+
check_ACK_OK(initPacket);
|
|
1190
|
+
const fpPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_DATA, fingerBuffer);
|
|
1191
|
+
check_ACK_OK(fpPacket);
|
|
1192
|
+
const cheksumPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_CHECKSUM_BUFFER, '');
|
|
1193
|
+
check_ACK_OK(cheksumPacket);
|
|
1194
|
+
const checksum = cheksumPacket.readUInt32LE(8);
|
|
1195
|
+
const tmp_wreq = Buffer.alloc(6);
|
|
1196
|
+
tmp_wreq.writeUInt16LE(user.uid, 0);
|
|
1197
|
+
tmp_wreq.writeUIntLE(fid, 2, 1);
|
|
1198
|
+
tmp_wreq.writeUIntLE(fp_valid, 3, 1);
|
|
1199
|
+
tmp_wreq.writeUInt16LE(fp_size, 4);
|
|
1200
|
+
const tmp_wreqPacket = await this._zkTcp.executeCmd(COMMANDS.CMD_TMP_WRITE, tmp_wreq);
|
|
1201
|
+
check_ACK_OK(tmp_wreqPacket);
|
|
1202
|
+
const freeData = await this._zkTcp.executeCmd(COMMANDS.CMD_FREE_DATA, '');
|
|
1203
|
+
return check_ACK_OK(freeData);
|
|
1204
|
+
}
|
|
1205
|
+
catch (err) {
|
|
1206
|
+
throw err;
|
|
1207
|
+
}
|
|
1208
|
+
finally {
|
|
1209
|
+
await this._zkTcp.refreshData();
|
|
1210
|
+
await this._zkTcp.enableDevice();
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
class TransactionService {
|
|
1216
|
+
_zkTcp;
|
|
1217
|
+
constructor(zkTcp) {
|
|
1218
|
+
this._zkTcp = zkTcp;
|
|
1219
|
+
}
|
|
1220
|
+
async getAttendances(callbackInProcess = () => { }) {
|
|
1221
|
+
try {
|
|
1222
|
+
// Free any existing buffer data to prepare for a new request
|
|
1223
|
+
if (this._zkTcp.socket) {
|
|
1224
|
+
await this._zkTcp.freeData();
|
|
1225
|
+
}
|
|
1226
|
+
// Request attendance logs and handle chunked data
|
|
1227
|
+
const data = await this._zkTcp.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
|
|
1228
|
+
// Free buffer data after receiving the attendance logs
|
|
1229
|
+
if (this._zkTcp.socket) {
|
|
1230
|
+
await this._zkTcp.freeData();
|
|
1231
|
+
}
|
|
1232
|
+
// Constants for record processing
|
|
1233
|
+
const RECORD_PACKET_SIZE = 40;
|
|
1234
|
+
// Ensure data.data is a valid buffer
|
|
1235
|
+
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1236
|
+
throw new Error('Invalid data received');
|
|
1237
|
+
}
|
|
1238
|
+
// Process the record data
|
|
1239
|
+
let recordData = data.data.subarray(4); // Skip header
|
|
1240
|
+
const records = [];
|
|
1241
|
+
// Process each attendance record
|
|
1242
|
+
while (recordData.length >= RECORD_PACKET_SIZE) {
|
|
1243
|
+
const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
|
|
1244
|
+
records.push({ ...record, ip: this._zkTcp.ip }); // Add IP address to each record
|
|
1245
|
+
recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
|
|
1246
|
+
}
|
|
1247
|
+
// Return the list of attendance records
|
|
1248
|
+
return { data: records };
|
|
1249
|
+
}
|
|
1250
|
+
catch (err) {
|
|
1251
|
+
// Log and re-throw the error
|
|
1252
|
+
console.error('Error getting attendance records:', err);
|
|
1253
|
+
throw err; // Re-throw the error for handling by the caller
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
// Clears the attendance logs on the device
|
|
1257
|
+
async clearAttendanceLog() {
|
|
1258
|
+
try {
|
|
1259
|
+
// Execute the command to clear attendance logs
|
|
1260
|
+
await this._zkTcp.disableDevice();
|
|
1261
|
+
const buf = await this._zkTcp.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
|
|
1262
|
+
await this._zkTcp.refreshData();
|
|
1263
|
+
await this._zkTcp.enableDevice();
|
|
1264
|
+
return !!buf;
|
|
1265
|
+
}
|
|
1266
|
+
catch (err) {
|
|
1267
|
+
// Log the error for debugging purposes
|
|
1268
|
+
console.error('Error clearing attendance log:', err);
|
|
1269
|
+
// Re-throw the error to be handled by the caller
|
|
1270
|
+
throw err;
|
|
1271
|
+
}
|
|
641
1272
|
}
|
|
642
1273
|
}
|
|
643
1274
|
|
|
@@ -673,6 +1304,8 @@ class ZTCP {
|
|
|
673
1304
|
verbose = false;
|
|
674
1305
|
packetNumber = 0;
|
|
675
1306
|
replyData = Buffer.from([]);
|
|
1307
|
+
_userService;
|
|
1308
|
+
_transactionService;
|
|
676
1309
|
constructor(ip, port, timeout, comm_key, verbose) {
|
|
677
1310
|
this.ip = ip;
|
|
678
1311
|
this.port = port;
|
|
@@ -680,6 +1313,8 @@ class ZTCP {
|
|
|
680
1313
|
this.replyId = 0;
|
|
681
1314
|
this.comm_key = comm_key;
|
|
682
1315
|
this.verbose = verbose;
|
|
1316
|
+
this._userService = new UserService(this);
|
|
1317
|
+
this._transactionService = new TransactionService(this);
|
|
683
1318
|
}
|
|
684
1319
|
createSocket(cbError, cbClose) {
|
|
685
1320
|
return new Promise((resolve, reject) => {
|
|
@@ -901,6 +1536,10 @@ class ZTCP {
|
|
|
901
1536
|
try {
|
|
902
1537
|
// Write the message to the socket and wait for a response
|
|
903
1538
|
const reply = await this.writeMessage(buf, command === COMMANDS.CMD_CONNECT || command === COMMANDS.CMD_EXIT);
|
|
1539
|
+
if (this.verbose) {
|
|
1540
|
+
let headers = decodeTCPHeader(reply);
|
|
1541
|
+
console.log('command: ', COMMANDS[headers.commandId], 'replyid: ', headers.replyId);
|
|
1542
|
+
}
|
|
904
1543
|
// Remove TCP header from the response
|
|
905
1544
|
const rReply = removeTcpHeader(reply);
|
|
906
1545
|
// Update sessionId for connection command responses
|
|
@@ -1045,94 +1684,19 @@ class ZTCP {
|
|
|
1045
1684
|
break;
|
|
1046
1685
|
}
|
|
1047
1686
|
default: {
|
|
1048
|
-
reject(new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)));
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
*
|
|
1055
|
-
*
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
await this.freeData();
|
|
1062
|
-
}
|
|
1063
|
-
// Request user data
|
|
1064
|
-
const data = await this.readWithBuffer(REQUEST_DATA.GET_USERS);
|
|
1065
|
-
// Free buffer data after receiving the data
|
|
1066
|
-
if (this.socket) {
|
|
1067
|
-
await this.freeData();
|
|
1068
|
-
}
|
|
1069
|
-
// Constants for user data processing
|
|
1070
|
-
const USER_PACKET_SIZE = 72;
|
|
1071
|
-
// Ensure data.data is a valid buffer
|
|
1072
|
-
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1073
|
-
throw new Error('Invalid data received');
|
|
1074
|
-
}
|
|
1075
|
-
let userData = data.data.subarray(4); // Skip the first 4 bytes (headers)
|
|
1076
|
-
const users = [];
|
|
1077
|
-
// Process each user packet
|
|
1078
|
-
while (userData.length >= USER_PACKET_SIZE) {
|
|
1079
|
-
// Decode user data and add to the users array
|
|
1080
|
-
const user = decodeUserData72(userData.subarray(0, USER_PACKET_SIZE));
|
|
1081
|
-
users.push(user);
|
|
1082
|
-
userData = userData.subarray(USER_PACKET_SIZE); // Move to the next packet
|
|
1083
|
-
}
|
|
1084
|
-
// Return the list of users
|
|
1085
|
-
return { data: users };
|
|
1086
|
-
}
|
|
1087
|
-
catch (err) {
|
|
1088
|
-
// Log the error for debugging
|
|
1089
|
-
console.error('Error getting users:', err);
|
|
1090
|
-
// Re-throw the error to be handled by the caller
|
|
1091
|
-
throw err;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
/**
|
|
1095
|
-
*
|
|
1096
|
-
* @param {*} ip
|
|
1097
|
-
* @param {*} callbackInProcess
|
|
1098
|
-
* reject error when starting request data
|
|
1099
|
-
* return { data: records, err: Error } when receiving requested data
|
|
1100
|
-
*/
|
|
1101
|
-
async getAttendances(callbackInProcess = () => { }) {
|
|
1102
|
-
try {
|
|
1103
|
-
// Free any existing buffer data to prepare for a new request
|
|
1104
|
-
if (this.socket) {
|
|
1105
|
-
await this.freeData();
|
|
1106
|
-
}
|
|
1107
|
-
// Request attendance logs and handle chunked data
|
|
1108
|
-
const data = await this.readWithBuffer(REQUEST_DATA.GET_ATTENDANCE_LOGS, callbackInProcess);
|
|
1109
|
-
// Free buffer data after receiving the attendance logs
|
|
1110
|
-
if (this.socket) {
|
|
1111
|
-
await this.freeData();
|
|
1112
|
-
}
|
|
1113
|
-
// Constants for record processing
|
|
1114
|
-
const RECORD_PACKET_SIZE = 40;
|
|
1115
|
-
// Ensure data.data is a valid buffer
|
|
1116
|
-
if (!data.data || !(data.data instanceof Buffer)) {
|
|
1117
|
-
throw new Error('Invalid data received');
|
|
1118
|
-
}
|
|
1119
|
-
// Process the record data
|
|
1120
|
-
let recordData = data.data.subarray(4); // Skip header
|
|
1121
|
-
const records = [];
|
|
1122
|
-
// Process each attendance record
|
|
1123
|
-
while (recordData.length >= RECORD_PACKET_SIZE) {
|
|
1124
|
-
const record = decodeRecordData40(recordData.subarray(0, RECORD_PACKET_SIZE));
|
|
1125
|
-
records.push({ ...record, ip: this.ip }); // Add IP address to each record
|
|
1126
|
-
recordData = recordData.subarray(RECORD_PACKET_SIZE); // Move to the next packet
|
|
1127
|
-
}
|
|
1128
|
-
// Return the list of attendance records
|
|
1129
|
-
return { data: records };
|
|
1130
|
-
}
|
|
1131
|
-
catch (err) {
|
|
1132
|
-
// Log and re-throw the error
|
|
1133
|
-
console.error('Error getting attendance records:', err);
|
|
1134
|
-
throw err; // Re-throw the error for handling by the caller
|
|
1135
|
-
}
|
|
1687
|
+
reject(new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)));
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
*
|
|
1694
|
+
* @param {*} callbackInProcess
|
|
1695
|
+
* reject error when starting request data
|
|
1696
|
+
* return { data: records, err: Error } when receiving requested data
|
|
1697
|
+
*/
|
|
1698
|
+
async getAttendances(callbackInProcess = () => { }) {
|
|
1699
|
+
return await this._transactionService.getAttendances(callbackInProcess);
|
|
1136
1700
|
}
|
|
1137
1701
|
async freeData() {
|
|
1138
1702
|
try {
|
|
@@ -1459,11 +2023,10 @@ class ZTCP {
|
|
|
1459
2023
|
// Execute the command to get the PIN information
|
|
1460
2024
|
const data = await this.executeCmd(COMMANDS.CMD_OPTIONS_RRQ, keyword);
|
|
1461
2025
|
// Extract and format the PIN information from the response data
|
|
1462
|
-
// Remove null characters
|
|
1463
2026
|
return data.slice(8) // Skip the first 8 bytes (header)
|
|
1464
2027
|
.toString('ascii') // Convert buffer to ASCII string
|
|
1465
2028
|
.replace(`${keyword}=`, '') // Remove the keyword prefix
|
|
1466
|
-
.replace(/\u0000/g, '');
|
|
2029
|
+
.replace(/\u0000/g, ''); // Remove null characters 0x00
|
|
1467
2030
|
}
|
|
1468
2031
|
catch (err) {
|
|
1469
2032
|
// Log the error for debugging
|
|
@@ -1515,7 +2078,9 @@ class ZTCP {
|
|
|
1515
2078
|
// Execute the command to get firmware information
|
|
1516
2079
|
const data = await this.executeCmd(1100, '');
|
|
1517
2080
|
// Extract and return the firmware version from the response data
|
|
1518
|
-
return data.slice(8)
|
|
2081
|
+
return data.slice(8) // Skip the first 8 bytes (header)
|
|
2082
|
+
.toString('ascii') // convert to ASCII string
|
|
2083
|
+
.replace(/\u0000/g, ''); // remove x00
|
|
1519
2084
|
}
|
|
1520
2085
|
catch (err) {
|
|
1521
2086
|
// Log the error for debugging
|
|
@@ -1581,59 +2146,6 @@ class ZTCP {
|
|
|
1581
2146
|
throw err;
|
|
1582
2147
|
}
|
|
1583
2148
|
}
|
|
1584
|
-
async setUser(uid, userid, name, password, role = 0, cardno = 0) {
|
|
1585
|
-
try {
|
|
1586
|
-
// Validate input parameters
|
|
1587
|
-
if (uid <= 0 || uid > 3000 ||
|
|
1588
|
-
userid.length > 9 ||
|
|
1589
|
-
name.length > 24 ||
|
|
1590
|
-
password.length > 8 ||
|
|
1591
|
-
typeof role !== 'number' ||
|
|
1592
|
-
cardno.toString().length > 10) {
|
|
1593
|
-
throw new Error('Invalid input parameters');
|
|
1594
|
-
}
|
|
1595
|
-
// Allocate and initialize the buffer
|
|
1596
|
-
const commandBuffer = Buffer.alloc(72);
|
|
1597
|
-
// Fill the buffer with user data
|
|
1598
|
-
commandBuffer.writeUInt16LE(uid, 0);
|
|
1599
|
-
commandBuffer.writeUInt16LE(role, 2);
|
|
1600
|
-
commandBuffer.write(password.padEnd(8, '\0'), 3, 8); // Ensure password is 8 bytes
|
|
1601
|
-
commandBuffer.write(name.padEnd(24, '\0'), 11, 24); // Ensure name is 24 bytes
|
|
1602
|
-
commandBuffer.writeUInt16LE(cardno, 35);
|
|
1603
|
-
commandBuffer.writeUInt32LE(0, 40); // Placeholder or reserved field
|
|
1604
|
-
commandBuffer.write(userid.padEnd(9, '\0'), 48, 9); // Ensure userid is 9 bytes
|
|
1605
|
-
// Send the command and return the result
|
|
1606
|
-
const created = await this.executeCmd(COMMANDS.CMD_USER_WRQ, commandBuffer);
|
|
1607
|
-
return !!created;
|
|
1608
|
-
}
|
|
1609
|
-
catch (err) {
|
|
1610
|
-
// Log error details for debugging
|
|
1611
|
-
console.error('Error setting user:', err);
|
|
1612
|
-
// Re-throw error for upstream handling
|
|
1613
|
-
throw err;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
async deleteUser(uid) {
|
|
1617
|
-
try {
|
|
1618
|
-
// Validate input parameter
|
|
1619
|
-
if (uid <= 0 || uid > 3000) {
|
|
1620
|
-
throw new Error('Invalid UID: must be between 1 and 3000');
|
|
1621
|
-
}
|
|
1622
|
-
// Allocate and initialize the buffer
|
|
1623
|
-
const commandBuffer = Buffer.alloc(72);
|
|
1624
|
-
// Write UID to the buffer
|
|
1625
|
-
commandBuffer.writeUInt16LE(uid, 0);
|
|
1626
|
-
// Send the delete command and return the result
|
|
1627
|
-
const deleted = await this.executeCmd(COMMANDS.CMD_DELETE_USER, commandBuffer);
|
|
1628
|
-
return !!deleted;
|
|
1629
|
-
}
|
|
1630
|
-
catch (err) {
|
|
1631
|
-
// Log error details for debugging
|
|
1632
|
-
console.error('Error deleting user:', err);
|
|
1633
|
-
// Re-throw error for upstream handling
|
|
1634
|
-
throw err;
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
2149
|
async getAttendanceSize() {
|
|
1638
2150
|
try {
|
|
1639
2151
|
// Execute command to get free sizes
|
|
@@ -1650,22 +2162,27 @@ class ZTCP {
|
|
|
1650
2162
|
}
|
|
1651
2163
|
// Clears the attendance logs on the device
|
|
1652
2164
|
async clearAttendanceLog() {
|
|
1653
|
-
|
|
1654
|
-
// Execute the command to clear attendance logs
|
|
1655
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, '');
|
|
1656
|
-
}
|
|
1657
|
-
catch (err) {
|
|
1658
|
-
// Log the error for debugging purposes
|
|
1659
|
-
console.error('Error clearing attendance log:', err);
|
|
1660
|
-
// Re-throw the error to be handled by the caller
|
|
1661
|
-
throw err;
|
|
1662
|
-
}
|
|
2165
|
+
return await this._transactionService.clearAttendanceLog();
|
|
1663
2166
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
2167
|
+
/**
|
|
2168
|
+
* Clears all data on the device
|
|
2169
|
+
* @value 1 Attendance records
|
|
2170
|
+
* @value 2 Fingerprint templates
|
|
2171
|
+
* @value 3 None
|
|
2172
|
+
* @value 4 Operation records
|
|
2173
|
+
* @value 5 User information
|
|
2174
|
+
* @default 0 Delete all
|
|
2175
|
+
*/
|
|
2176
|
+
async clearData(value) {
|
|
1666
2177
|
try {
|
|
1667
2178
|
// Execute the command to clear all data
|
|
1668
|
-
|
|
2179
|
+
await this.disableDevice();
|
|
2180
|
+
if (!value)
|
|
2181
|
+
value = 3;
|
|
2182
|
+
const buf = await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, value.toString());
|
|
2183
|
+
await this.refreshData();
|
|
2184
|
+
await this.enableDevice();
|
|
2185
|
+
return !!buf;
|
|
1669
2186
|
}
|
|
1670
2187
|
catch (err) {
|
|
1671
2188
|
// Log the error for debugging purposes
|
|
@@ -1712,33 +2229,7 @@ class ZTCP {
|
|
|
1712
2229
|
* @returns {Record<string, Finger[]>}
|
|
1713
2230
|
*/
|
|
1714
2231
|
async getTemplates(callbackInProcess = () => { }) {
|
|
1715
|
-
|
|
1716
|
-
try {
|
|
1717
|
-
await this.getSizes();
|
|
1718
|
-
if (this.fp_count == 0)
|
|
1719
|
-
return { data: [] };
|
|
1720
|
-
await this.freeData();
|
|
1721
|
-
await this.disableDevice();
|
|
1722
|
-
const Buffer = await this.readWithBuffer(REQUEST_DATA.GET_TEMPLATES);
|
|
1723
|
-
let templateData = Buffer.data.subarray(4);
|
|
1724
|
-
let totalSize = Buffer.data.readUIntLE(0, 4);
|
|
1725
|
-
while (totalSize) {
|
|
1726
|
-
const buf = templateData.subarray(0, 6);
|
|
1727
|
-
const size = buf.readUIntLE(0, 2);
|
|
1728
|
-
templates.push(new Finger(buf.readUIntLE(2, 2), buf.readUIntLE(4, 1), buf.readUIntLE(5, 1), templateData.subarray(6, size)));
|
|
1729
|
-
templateData = templateData.subarray(size);
|
|
1730
|
-
totalSize -= size;
|
|
1731
|
-
}
|
|
1732
|
-
return { data: templates };
|
|
1733
|
-
}
|
|
1734
|
-
catch (err) {
|
|
1735
|
-
this.verbose && console.log("Error getting templates", err);
|
|
1736
|
-
return { data: templates };
|
|
1737
|
-
}
|
|
1738
|
-
finally {
|
|
1739
|
-
await this.enableDevice();
|
|
1740
|
-
await this.freeData();
|
|
1741
|
-
}
|
|
2232
|
+
return await this._userService.getTemplates(callbackInProcess);
|
|
1742
2233
|
}
|
|
1743
2234
|
/**
|
|
1744
2235
|
* Return size
|
|
@@ -1814,197 +2305,6 @@ class ZTCP {
|
|
|
1814
2305
|
throw new ZkError(e, COMMANDS.CMD_DATA, this.ip);
|
|
1815
2306
|
}
|
|
1816
2307
|
}
|
|
1817
|
-
/**
|
|
1818
|
-
* save user and template
|
|
1819
|
-
*
|
|
1820
|
-
* @param {User | number | string} user - User class object | uid | user_id
|
|
1821
|
-
* @param {Finger[]} fingers - Array of finger class. `0 <= index <= 9`
|
|
1822
|
-
*/
|
|
1823
|
-
async saveUserTemplate(user, fingers = []) {
|
|
1824
|
-
if (fingers.length > 9 || fingers.length == 0)
|
|
1825
|
-
throw new Error("maximum finger length is 10 and can't be empty");
|
|
1826
|
-
try {
|
|
1827
|
-
await this.disableDevice();
|
|
1828
|
-
const users = await this.getUsers();
|
|
1829
|
-
//check users exists
|
|
1830
|
-
if (!users.data.some(u => u.uid == user.uid || +u.user_id == +user.user_id))
|
|
1831
|
-
throw new Error("error validating user input");
|
|
1832
|
-
if (!(user instanceof User)) {
|
|
1833
|
-
let tusers = users.data.filter(x => x.uid === +user.uid);
|
|
1834
|
-
if (tusers.length === 1) {
|
|
1835
|
-
user = tusers[0];
|
|
1836
|
-
}
|
|
1837
|
-
else {
|
|
1838
|
-
tusers = users.data.filter(x => x.user_id === String(user));
|
|
1839
|
-
if (tusers.length === 1) {
|
|
1840
|
-
user = tusers[0];
|
|
1841
|
-
}
|
|
1842
|
-
else {
|
|
1843
|
-
throw new Error("Can't find user");
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
if (fingers instanceof Finger) {
|
|
1848
|
-
fingers = [fingers];
|
|
1849
|
-
}
|
|
1850
|
-
let fpack = Buffer.alloc(0);
|
|
1851
|
-
let table = Buffer.alloc(0);
|
|
1852
|
-
const fnum = 0x10;
|
|
1853
|
-
let tstart = 0;
|
|
1854
|
-
for (const finger of fingers) {
|
|
1855
|
-
const tfp = finger.repackOnly();
|
|
1856
|
-
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
|
|
1857
|
-
tableEntry.writeInt8(2, 0);
|
|
1858
|
-
tableEntry.writeUInt16LE(user.uid, 1);
|
|
1859
|
-
tableEntry.writeInt8(fnum + finger.fid, 3);
|
|
1860
|
-
tableEntry.writeUInt32LE(tstart, 4);
|
|
1861
|
-
table = Buffer.concat([table, tableEntry]);
|
|
1862
|
-
tstart += tfp.length;
|
|
1863
|
-
fpack = Buffer.concat([fpack, tfp]);
|
|
1864
|
-
}
|
|
1865
|
-
let upack;
|
|
1866
|
-
if (this.userPacketSize === 28) {
|
|
1867
|
-
upack = user.repack29();
|
|
1868
|
-
}
|
|
1869
|
-
else {
|
|
1870
|
-
upack = user.repack73();
|
|
1871
|
-
}
|
|
1872
|
-
const head = Buffer.alloc(12); // III = 3*4 bytes
|
|
1873
|
-
head.writeUInt32LE(upack.length, 0);
|
|
1874
|
-
head.writeUInt32LE(table.length, 4);
|
|
1875
|
-
head.writeUInt32LE(fpack.length, 8);
|
|
1876
|
-
const packet = Buffer.concat([head, upack, table, fpack]);
|
|
1877
|
-
const bufferResponse = await this.sendWithBuffer(packet);
|
|
1878
|
-
const command = 110;
|
|
1879
|
-
const commandString = Buffer.alloc(8); // <IHH = I(4) + H(2) + H(2) = 8 bytes
|
|
1880
|
-
commandString.writeUInt32LE(12, 0);
|
|
1881
|
-
commandString.writeUInt16LE(0, 4);
|
|
1882
|
-
commandString.writeUInt16LE(8, 6);
|
|
1883
|
-
const cmdResponse = await this.executeCmd(command, commandString);
|
|
1884
|
-
if (this.verbose)
|
|
1885
|
-
console.log("finally bulk save user templates: \n", cmdResponse.readUInt16LE(0));
|
|
1886
|
-
}
|
|
1887
|
-
catch (error) {
|
|
1888
|
-
throw error;
|
|
1889
|
-
}
|
|
1890
|
-
finally {
|
|
1891
|
-
await this.refreshData();
|
|
1892
|
-
await this.enableDevice();
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
async deleteFinger(uid, fid) {
|
|
1896
|
-
try {
|
|
1897
|
-
const buf = Buffer.alloc(4);
|
|
1898
|
-
buf.writeUInt16LE(uid, 0);
|
|
1899
|
-
buf.writeUint16LE(fid, 2);
|
|
1900
|
-
const reply = await this.executeCmd(COMMANDS.CMD_DELETE_USERTEMP, buf);
|
|
1901
|
-
return !!reply;
|
|
1902
|
-
}
|
|
1903
|
-
catch (error) {
|
|
1904
|
-
throw new Error("Can't save utemp");
|
|
1905
|
-
}
|
|
1906
|
-
finally {
|
|
1907
|
-
await this.refreshData();
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
async enrollUser(uid, tempId, userId = '') {
|
|
1911
|
-
let done = false;
|
|
1912
|
-
try {
|
|
1913
|
-
//validate user exists
|
|
1914
|
-
const users = await this.getUsers();
|
|
1915
|
-
const filteredUsers = users.data.filter(x => x.uid === uid);
|
|
1916
|
-
if (filteredUsers.length >= 1) {
|
|
1917
|
-
userId = filteredUsers[0].user_id;
|
|
1918
|
-
}
|
|
1919
|
-
else {
|
|
1920
|
-
throw new Error("user not found");
|
|
1921
|
-
}
|
|
1922
|
-
const userBuf = Buffer.alloc(24);
|
|
1923
|
-
userBuf.write(userId.toString(), 0, 24, 'ascii');
|
|
1924
|
-
let commandString = Buffer.concat([
|
|
1925
|
-
userBuf,
|
|
1926
|
-
Buffer.from([tempId, 1])
|
|
1927
|
-
]);
|
|
1928
|
-
const cancel = await this.cancelCapture();
|
|
1929
|
-
const cmdResponse = await this.executeCmd(COMMANDS.CMD_STARTENROLL, commandString);
|
|
1930
|
-
this.timeout = 60000; // 60 seconds timeout
|
|
1931
|
-
let attempts = 3;
|
|
1932
|
-
while (attempts > 0) {
|
|
1933
|
-
if (this.verbose)
|
|
1934
|
-
console.log(`A:${attempts} esperando primer regevent`);
|
|
1935
|
-
let dataRecv = await this.readSocket(17);
|
|
1936
|
-
await this.ackOk();
|
|
1937
|
-
if (dataRecv.length > 16) {
|
|
1938
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1939
|
-
const res = padded.readUInt16LE(16);
|
|
1940
|
-
if (this.verbose)
|
|
1941
|
-
console.log(`res ${res}`);
|
|
1942
|
-
if (res === 0 || res === 6 || res === 4) {
|
|
1943
|
-
if (this.verbose)
|
|
1944
|
-
console.log("posible timeout o reg Fallido");
|
|
1945
|
-
break;
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
if (this.verbose)
|
|
1949
|
-
console.log(`A:${attempts} esperando 2do regevent`);
|
|
1950
|
-
dataRecv = await this.readSocket(17);
|
|
1951
|
-
await this.ackOk();
|
|
1952
|
-
if (this.verbose)
|
|
1953
|
-
console.log(dataRecv);
|
|
1954
|
-
if (dataRecv.length > 8) {
|
|
1955
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1956
|
-
const res = padded.readUInt16LE(16);
|
|
1957
|
-
if (this.verbose)
|
|
1958
|
-
console.log(`res ${res}`);
|
|
1959
|
-
if (res === 6 || res === 4) {
|
|
1960
|
-
if (this.verbose)
|
|
1961
|
-
console.log("posible timeout o reg Fallido");
|
|
1962
|
-
break;
|
|
1963
|
-
}
|
|
1964
|
-
else if (res === 0x64) {
|
|
1965
|
-
if (this.verbose)
|
|
1966
|
-
console.log("ok, continue?");
|
|
1967
|
-
attempts--;
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
if (attempts === 0) {
|
|
1972
|
-
const dataRecv = await this.readSocket(17);
|
|
1973
|
-
await this.ackOk();
|
|
1974
|
-
if (this.verbose)
|
|
1975
|
-
console.log(dataRecv.toString('hex'));
|
|
1976
|
-
const padded = Buffer.concat([dataRecv, Buffer.alloc(24 - dataRecv.length)]);
|
|
1977
|
-
let res = padded.readUInt16LE(16);
|
|
1978
|
-
if (this.verbose)
|
|
1979
|
-
console.log(`res ${res}`);
|
|
1980
|
-
if (res === 5) {
|
|
1981
|
-
if (this.verbose)
|
|
1982
|
-
console.log("finger duplicate");
|
|
1983
|
-
}
|
|
1984
|
-
if (res === 6 || res === 4) {
|
|
1985
|
-
if (this.verbose)
|
|
1986
|
-
console.log("posible timeout");
|
|
1987
|
-
}
|
|
1988
|
-
if (res === 0) {
|
|
1989
|
-
const size = padded.readUInt16LE(10);
|
|
1990
|
-
const pos = padded.readUInt16LE(12);
|
|
1991
|
-
if (this.verbose)
|
|
1992
|
-
console.log(`enroll ok ${size} ${pos}`);
|
|
1993
|
-
done = true;
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
//this.__sock.setTimeout(this.__timeout);
|
|
1997
|
-
await this.regEvent(0); // TODO: test
|
|
1998
|
-
return done;
|
|
1999
|
-
}
|
|
2000
|
-
catch (error) {
|
|
2001
|
-
throw error;
|
|
2002
|
-
}
|
|
2003
|
-
finally {
|
|
2004
|
-
await this.cancelCapture();
|
|
2005
|
-
await this.verifyUser(undefined);
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
2308
|
async readSocket(length, cb = null) {
|
|
2009
2309
|
let replyBufer = Buffer.from([]);
|
|
2010
2310
|
let totalPackets = 0;
|
|
@@ -2054,15 +2354,6 @@ class ZTCP {
|
|
|
2054
2354
|
throw new ZkError(e, COMMANDS.CMD_REG_EVENT, this.ip);
|
|
2055
2355
|
}
|
|
2056
2356
|
}
|
|
2057
|
-
async ackOk() {
|
|
2058
|
-
try {
|
|
2059
|
-
const buf = createTCPHeader(COMMANDS.CMD_ACK_OK, this.sessionId, Constants.USHRT_MAX - 1, Buffer.from([]));
|
|
2060
|
-
this.socket.write(buf);
|
|
2061
|
-
}
|
|
2062
|
-
catch (e) {
|
|
2063
|
-
throw new ZkError(e, COMMANDS.CMD_ACK_OK, this.ip);
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
2357
|
async cancelCapture() {
|
|
2067
2358
|
try {
|
|
2068
2359
|
const reply = await this.executeCmd(COMMANDS.CMD_CANCELCAPTURE, '');
|
|
@@ -2072,22 +2363,6 @@ class ZTCP {
|
|
|
2072
2363
|
throw new ZkError(e, COMMANDS.CMD_CANCELCAPTURE, this.ip);
|
|
2073
2364
|
}
|
|
2074
2365
|
}
|
|
2075
|
-
async verifyUser(uid) {
|
|
2076
|
-
try {
|
|
2077
|
-
let command_string = '';
|
|
2078
|
-
if (uid) {
|
|
2079
|
-
command_string = Buffer.alloc(4);
|
|
2080
|
-
command_string.writeUInt32LE(uid, 0);
|
|
2081
|
-
}
|
|
2082
|
-
const reply = await this.executeCmd(COMMANDS.CMD_STARTVERIFY, command_string);
|
|
2083
|
-
if (this.verbose)
|
|
2084
|
-
console.log(reply.readUInt16LE(0));
|
|
2085
|
-
return !!reply;
|
|
2086
|
-
}
|
|
2087
|
-
catch (error) {
|
|
2088
|
-
console.error(error);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
2366
|
async restartDevice() {
|
|
2092
2367
|
try {
|
|
2093
2368
|
await this.executeCmd(COMMANDS.CMD_RESTART, '');
|
|
@@ -2472,7 +2747,7 @@ class ZUDP {
|
|
|
2472
2747
|
}
|
|
2473
2748
|
async clearAttendanceLog() {
|
|
2474
2749
|
try {
|
|
2475
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
|
|
2750
|
+
return !!await this.executeCmd(COMMANDS.CMD_CLEAR_ATTLOG, Buffer.alloc(0));
|
|
2476
2751
|
}
|
|
2477
2752
|
catch (err) {
|
|
2478
2753
|
console.error('Error clearing attendance log:', err);
|
|
@@ -2481,7 +2756,7 @@ class ZUDP {
|
|
|
2481
2756
|
}
|
|
2482
2757
|
async clearData() {
|
|
2483
2758
|
try {
|
|
2484
|
-
return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
|
|
2759
|
+
return !!await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, Buffer.alloc(0));
|
|
2485
2760
|
}
|
|
2486
2761
|
catch (err) {
|
|
2487
2762
|
console.error('Error clearing data:', err);
|
|
@@ -2672,7 +2947,7 @@ class Zklib {
|
|
|
2672
2947
|
}
|
|
2673
2948
|
}
|
|
2674
2949
|
async getUsers() {
|
|
2675
|
-
return this.functionWrapper(() => this.ztcp.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
|
|
2950
|
+
return this.functionWrapper(() => this.ztcp._userService.getUsers(), () => this.zudp.getUsers(), 'GET_USERS');
|
|
2676
2951
|
}
|
|
2677
2952
|
async getTime() {
|
|
2678
2953
|
return this.functionWrapper(() => this.ztcp.getTime(), () => this.zudp.getTime(), 'GET_TIME');
|
|
@@ -2722,11 +2997,22 @@ class Zklib {
|
|
|
2722
2997
|
async getFirmware() {
|
|
2723
2998
|
return this.functionWrapper(() => this.ztcp.getFirmware(), async () => { throw new Error('UDP get firmware not supported'); }, 'GET_FIRMWARE');
|
|
2724
2999
|
}
|
|
2725
|
-
|
|
2726
|
-
|
|
3000
|
+
/** Update or create a user if user id/pin not exists
|
|
3001
|
+
* @param user_id {string} user id/pin for customer
|
|
3002
|
+
* @param name {string} user name
|
|
3003
|
+
* @param password {string} user password
|
|
3004
|
+
* @param role {number} role/privilege id number
|
|
3005
|
+
* @param cardno {number} card number/id
|
|
3006
|
+
*/
|
|
3007
|
+
async setUser(user_id, name, password, role = 0, cardno = 0) {
|
|
3008
|
+
return this.functionWrapper(() => this.ztcp._userService.setUser(user_id, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
|
|
2727
3009
|
}
|
|
2728
|
-
|
|
2729
|
-
|
|
3010
|
+
/**
|
|
3011
|
+
* Delete user by a given user id/pin
|
|
3012
|
+
* @param user_id {string}
|
|
3013
|
+
*/
|
|
3014
|
+
async deleteUser(user_id) {
|
|
3015
|
+
return this.functionWrapper(() => this.ztcp._userService.DeleteUser(user_id), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
|
|
2730
3016
|
}
|
|
2731
3017
|
async getAttendanceSize() {
|
|
2732
3018
|
return this.functionWrapper(() => this.ztcp.getAttendanceSize(), async () => { throw new Error('UDP get attendance size not supported'); }, 'GET_ATTENDANCE_SIZE');
|
|
@@ -2740,25 +3026,55 @@ class Zklib {
|
|
|
2740
3026
|
async getTemplates() {
|
|
2741
3027
|
return this.functionWrapper(() => this.ztcp.getTemplates(), async () => { throw new Error('UDP get templates not supported'); }, 'GET_TEMPLATES');
|
|
2742
3028
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
3029
|
+
/**
|
|
3030
|
+
* Get a user template for a given user id/pin and finger id
|
|
3031
|
+
* @param user_id {string} user id/pin
|
|
3032
|
+
* @param fid {number} finger index
|
|
3033
|
+
*/
|
|
3034
|
+
async getUserTemplate(user_id, fid) {
|
|
3035
|
+
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');
|
|
3036
|
+
}
|
|
3037
|
+
/**
|
|
3038
|
+
* Upload a single fingerprint for a given user id
|
|
3039
|
+
* @param user_id {string} user id/pin for customer
|
|
3040
|
+
* @param fingerTemplate {string} finger template in base64 string
|
|
3041
|
+
* @param fid {number} finger id is a number between 0 and 9
|
|
3042
|
+
* @param fp_valid {number} finger flag. e.g., valid=1, duress=3
|
|
3043
|
+
*/
|
|
3044
|
+
async uploadFingerTemplate(user_id, fingerTemplate, fid, fp_valid) {
|
|
3045
|
+
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');
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* save user and template
|
|
3049
|
+
*
|
|
3050
|
+
* @param {string} user_id - user id for customer
|
|
3051
|
+
* @param {Finger[]} fingers - Array of finger class
|
|
3052
|
+
*/
|
|
3053
|
+
async saveUserTemplate(user_id, fingers = []) {
|
|
3054
|
+
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');
|
|
2745
3055
|
}
|
|
2746
|
-
|
|
3056
|
+
/**
|
|
3057
|
+
* Delete a single finger template by user id and finger index
|
|
3058
|
+
* @param user_id {string} user id/pin for customer
|
|
3059
|
+
* @param fid {number} finger index
|
|
3060
|
+
*/
|
|
3061
|
+
async deleteFinger(user_id, fid) {
|
|
2747
3062
|
if (fid > 9 || 0 > fid)
|
|
2748
3063
|
throw new Error("fid params out of index");
|
|
2749
|
-
|
|
2750
|
-
throw new Error("fid params out of index");
|
|
2751
|
-
return this.functionWrapper(() => this.ztcp.deleteFinger(uid, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
|
|
3064
|
+
return this.functionWrapper(() => this.ztcp._userService.deleteFinger(user_id, fid), async () => { throw new Error('UDP delete finger not supported'); }, 'DELETE_FINGER');
|
|
2752
3065
|
}
|
|
2753
|
-
|
|
3066
|
+
/**
|
|
3067
|
+
* Start to enroll a finger template
|
|
3068
|
+
* @param user_id {string} user id/pin for customer
|
|
3069
|
+
* @param temp_id {number} finger index
|
|
3070
|
+
*/
|
|
3071
|
+
async enrollUser(user_id, temp_id) {
|
|
2754
3072
|
if (temp_id < 0 || temp_id > 9)
|
|
2755
3073
|
throw new Error("temp_id out of range 0-9");
|
|
2756
|
-
|
|
2757
|
-
throw new Error("uid out of range 1-3000");
|
|
2758
|
-
return this.functionWrapper(() => this.ztcp.enrollUser(uid, temp_id, user_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
|
|
3074
|
+
return this.functionWrapper(() => this.ztcp._userService.enrollInfo(user_id, temp_id), async () => { throw new Error('UDP enroll user not supported'); }, 'ENROLL_USER');
|
|
2759
3075
|
}
|
|
2760
|
-
async verifyUser(
|
|
2761
|
-
return this.functionWrapper(() => this.ztcp.
|
|
3076
|
+
async verifyUser(user_id) {
|
|
3077
|
+
return this.functionWrapper(() => this.ztcp._userService.verify(user_id), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
|
|
2762
3078
|
}
|
|
2763
3079
|
async restartDevice() {
|
|
2764
3080
|
return this.functionWrapper(() => this.ztcp.restartDevice(), async () => { throw new Error('UDP restart device not supported'); }, 'RESTART_DEVICE');
|