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/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
- record_time;
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, record_time, type, state) {
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.type = type;
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 dataBuffer = Buffer.from(data);
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() || '', parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(26, 1), recordData.readUIntLE(31, 1));
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
- * Error types for device communication
572
- */
573
- const ERROR_TYPES = {
574
- ECONNRESET: 'ECONNRESET',
575
- ECONNREFUSED: 'ECONNREFUSED'};
576
- /**
577
- * Custom error class for device communication errors
578
- */
579
- class ZkError {
580
- err;
581
- ip;
582
- command;
583
- /**
584
- * Creates a new ZkError instance
585
- * @param err The error object
586
- * @param command The command that caused the error
587
- * @param ip The IP address of the device
588
- */
589
- constructor(err, command, ip) {
590
- this.err = err;
591
- this.ip = ip;
592
- this.command = command;
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
- * Gets a user-friendly error message
596
- * @returns A formatted error message
597
- */
598
- toast() {
599
- if (this.err.code === ERROR_TYPES.ECONNRESET) {
600
- return 'Another device is connecting to the device so the connection is interrupted';
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
- else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
603
- return 'IP of the device is refused';
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
- * Gets detailed error information
609
- * @returns An object containing error details
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
- getError() {
612
- return {
613
- err: {
614
- message: this.err.message,
615
- code: this.err.code
616
- },
617
- ip: this.ip,
618
- command: this.command
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
- * reject error when starting request data
1034
- * @return {Record<string, User[] | Error>} when receiving requested data
1035
- */
1036
- async getUsers() {
1037
- try {
1038
- // Free any existing buffer data to prepare for a new request
1039
- if (this.socket) {
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).toString('ascii'); // Skip the first 8 bytes (header) and convert to ASCII string
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
- try {
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
- // Clears all data on the device
1644
- async clearData() {
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
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, '');
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
- let templates = [];
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
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
2705
- return this.functionWrapper(() => this.ztcp.setUser(uid, userid, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
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
- async deleteUser(uid) {
2708
- return this.functionWrapper(() => this.ztcp.deleteUser(uid), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
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
- async saveUserTemplate(user, fingers = []) {
2723
- return await this.functionWrapper(async () => await this.ztcp.saveUserTemplate(user, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
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
- async deleteFinger(uid, fid) {
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
- if (uid > 3000 || uid < 1)
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
- async enrollUser(uid, temp_id, user_id) {
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
- if (uid < 1 || uid > 3000)
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(uid) {
2740
- return this.functionWrapper(() => this.ztcp.verifyUser(uid), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
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');