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.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
- record_time;
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, record_time, type, state) {
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.type = type;
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 dataBuffer = Buffer.from(data);
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() || '', parseTimeToDate(recordData.readUInt32LE(27)), recordData.readUIntLE(26, 1), recordData.readUIntLE(31, 1));
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
- * Error types for device communication
593
- */
594
- const ERROR_TYPES = {
595
- ECONNRESET: 'ECONNRESET',
596
- ECONNREFUSED: 'ECONNREFUSED'};
597
- /**
598
- * Custom error class for device communication errors
599
- */
600
- class ZkError {
601
- err;
602
- ip;
603
- command;
604
- /**
605
- * Creates a new ZkError instance
606
- * @param err The error object
607
- * @param command The command that caused the error
608
- * @param ip The IP address of the device
609
- */
610
- constructor(err, command, ip) {
611
- this.err = err;
612
- this.ip = ip;
613
- this.command = command;
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
- * Gets a user-friendly error message
617
- * @returns A formatted error message
618
- */
619
- toast() {
620
- if (this.err.code === ERROR_TYPES.ECONNRESET) {
621
- return 'Another device is connecting to the device so the connection is interrupted';
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
- else if (this.err.code === ERROR_TYPES.ECONNREFUSED) {
624
- return 'IP of the device is refused';
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
- * Gets detailed error information
630
- * @returns An object containing error details
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
- getError() {
633
- return {
634
- err: {
635
- message: this.err.message,
636
- code: this.err.code
637
- },
638
- ip: this.ip,
639
- command: this.command
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
- * reject error when starting request data
1055
- * @return {Record<string, User[] | Error>} when receiving requested data
1056
- */
1057
- async getUsers() {
1058
- try {
1059
- // Free any existing buffer data to prepare for a new request
1060
- if (this.socket) {
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).toString('ascii'); // Skip the first 8 bytes (header) and convert to ASCII string
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
- try {
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
- // Clears all data on the device
1665
- async clearData() {
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
- return await this.executeCmd(COMMANDS.CMD_CLEAR_DATA, '');
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
- let templates = [];
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
- async setUser(uid, userid, name, password, role = 0, cardno = 0) {
2726
- return this.functionWrapper(() => this.ztcp.setUser(uid, userid, name, password, role, cardno), async () => { throw new Error('UDP set user not supported'); }, 'SET_USER');
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
- async deleteUser(uid) {
2729
- return this.functionWrapper(() => this.ztcp.deleteUser(uid), async () => { throw new Error('UDP delete user not supported'); }, 'DELETE_USER');
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
- async saveUserTemplate(user, fingers = []) {
2744
- return await this.functionWrapper(async () => await this.ztcp.saveUserTemplate(user, fingers), async () => { throw new Error('UDP save user template not supported'); }, 'SAVE_USER_TEMPLATE');
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
- async deleteFinger(uid, fid) {
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
- if (uid > 3000 || uid < 1)
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
- async enrollUser(uid, temp_id, user_id) {
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
- if (uid < 1 || uid > 3000)
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(uid) {
2761
- return this.functionWrapper(() => this.ztcp.verifyUser(uid), async () => { throw new Error('UDP verify user not supported'); }, 'VERIFY_USER');
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');