zklib-ts 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +3508 -16
- package/dist/index.es.js +3489 -16
- package/dist/ztcp.cjs.js +802 -0
- package/dist/ztcp.js +800 -0
- package/dist/zudp.cjs.js +531 -0
- package/dist/zudp.d.ts +1 -1
- package/dist/zudp.js +510 -0
- package/package.json +18 -1
- package/dist/asd.csv +0 -1142
package/dist/ztcp.cjs.js
ADDED
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var net = require('net');
|
|
4
|
+
var command = require('./helper/command');
|
|
5
|
+
var handler = require('./exceptions/handler');
|
|
6
|
+
var utils = require('./helper/utils');
|
|
7
|
+
var user_service = require('./services/user.service');
|
|
8
|
+
var transaction_service = require('./services/transaction.service');
|
|
9
|
+
var options_service = require('./services/options.service');
|
|
10
|
+
|
|
11
|
+
class ZTCP {
|
|
12
|
+
/**
|
|
13
|
+
* @param_ip ip address of device
|
|
14
|
+
* @param_port port number of device
|
|
15
|
+
* @param_timeout connection timout
|
|
16
|
+
* @param_comm_key communication key of device (if the case)
|
|
17
|
+
* @return Zkteco TCP socket connection instance
|
|
18
|
+
*/
|
|
19
|
+
ip;
|
|
20
|
+
port;
|
|
21
|
+
timeout;
|
|
22
|
+
sessionId = 0;
|
|
23
|
+
replyId = 0;
|
|
24
|
+
socket;
|
|
25
|
+
comm_key;
|
|
26
|
+
user_count = 0;
|
|
27
|
+
fp_count = 0;
|
|
28
|
+
pwd_count = 0;
|
|
29
|
+
oplog_count = 0;
|
|
30
|
+
attlog_count = 0;
|
|
31
|
+
fp_cap = 0;
|
|
32
|
+
user_cap = 0;
|
|
33
|
+
attlog_cap = 0;
|
|
34
|
+
fp_av = 0;
|
|
35
|
+
user_av = 0;
|
|
36
|
+
attlog_av = 0;
|
|
37
|
+
face_count = 0;
|
|
38
|
+
face_cap = 0;
|
|
39
|
+
userPacketSize = 72;
|
|
40
|
+
verbose = false;
|
|
41
|
+
packetNumber = 0;
|
|
42
|
+
replyData = Buffer.from([]);
|
|
43
|
+
_optionsService;
|
|
44
|
+
_transactionService;
|
|
45
|
+
_userService;
|
|
46
|
+
constructor(ip, port, timeout, comm_key, verbose) {
|
|
47
|
+
this.ip = ip;
|
|
48
|
+
this.port = port;
|
|
49
|
+
this.timeout = timeout ? timeout : 10000;
|
|
50
|
+
this.replyId = 0;
|
|
51
|
+
this.comm_key = comm_key;
|
|
52
|
+
this.verbose = verbose;
|
|
53
|
+
this._optionsService = new options_service.OptionsService(this);
|
|
54
|
+
this._userService = new user_service.UserService(this);
|
|
55
|
+
this._transactionService = new transaction_service.TransactionService(this);
|
|
56
|
+
}
|
|
57
|
+
createSocket(cbError, cbClose) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
this.socket = new net.Socket();
|
|
60
|
+
// Handle socket error
|
|
61
|
+
this.socket.once("error", (err) => {
|
|
62
|
+
this.socket = undefined; // Ensure socket reference is cleared
|
|
63
|
+
reject(err);
|
|
64
|
+
if (typeof cbError === "function")
|
|
65
|
+
cbError(err);
|
|
66
|
+
});
|
|
67
|
+
// Handle successful connection
|
|
68
|
+
this.socket.once("connect", () => {
|
|
69
|
+
resolve(this.socket);
|
|
70
|
+
});
|
|
71
|
+
// Handle socket closure
|
|
72
|
+
this.socket.once("close", () => {
|
|
73
|
+
this.socket = undefined; // Ensure socket reference is cleared
|
|
74
|
+
if (typeof cbClose === "function")
|
|
75
|
+
cbClose("tcp");
|
|
76
|
+
});
|
|
77
|
+
// Set socket timeout if provided
|
|
78
|
+
if (this.timeout) {
|
|
79
|
+
this.socket.setTimeout(this.timeout);
|
|
80
|
+
}
|
|
81
|
+
// Initiate connection
|
|
82
|
+
this.socket.connect(this.port, this.ip);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async connect() {
|
|
86
|
+
try {
|
|
87
|
+
let reply = await this.executeCmd(command.COMMANDS.CMD_CONNECT, "");
|
|
88
|
+
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_OK) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_UNAUTH) {
|
|
92
|
+
const hashedCommkey = utils.authKey(this.comm_key, this.sessionId);
|
|
93
|
+
reply = await this.executeCmd(command.COMMANDS.CMD_AUTH, hashedCommkey);
|
|
94
|
+
if (reply.readUInt16LE(0) === command.COMMANDS.CMD_ACK_OK) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error("error de authenticacion");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// No reply received; throw an error
|
|
103
|
+
throw new Error("NO_REPLY_ON_CMD_CONNECT");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
// Log the error for debugging, if necessary
|
|
108
|
+
console.error("Failed to connect:", err);
|
|
109
|
+
// Re-throw the error for handling by the caller
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async closeSocket() {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
// If no socket is present, resolve immediately
|
|
116
|
+
if (!this.socket) {
|
|
117
|
+
return resolve(true);
|
|
118
|
+
}
|
|
119
|
+
// Clean up listeners to avoid potential memory leaks or duplicate handling
|
|
120
|
+
this.socket.removeAllListeners("data");
|
|
121
|
+
// Set a timeout to handle cases where socket.end might not resolve
|
|
122
|
+
const timer = setTimeout(() => {
|
|
123
|
+
this.socket?.destroy(); // Forcibly close the socket if not closed properly
|
|
124
|
+
resolve(true); // Resolve even if the socket was not closed properly
|
|
125
|
+
}, 2000);
|
|
126
|
+
// Close the socket and clear the timeout upon successful completion
|
|
127
|
+
this.socket.end(() => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
resolve(true); // Resolve once the socket has ended
|
|
130
|
+
});
|
|
131
|
+
// Handle socket errors during closing
|
|
132
|
+
this.socket.once("error", (err) => {
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
reject(err); // Reject the promise with the error
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
writeMessage(msg, connect, cb) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
// Check if the socket is initialized
|
|
141
|
+
if (!this.socket) {
|
|
142
|
+
return reject(new Error("Socket is not initialized"));
|
|
143
|
+
}
|
|
144
|
+
// Define a variable for the timeout reference
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
// Check if the socket is still valid before trying to remove the listener
|
|
147
|
+
cleanUp();
|
|
148
|
+
reject(new Error("TIMEOUT_ON_WRITING_MESSAGE")); // Reject the promise on timeout
|
|
149
|
+
}, connect ? 3000 : this.timeout);
|
|
150
|
+
// Handle incoming data
|
|
151
|
+
const onData = (data) => {
|
|
152
|
+
// Check if the socket is still valid before trying to remove the listener
|
|
153
|
+
cleanUp(); // Clear the timeout once data is received
|
|
154
|
+
resolve(cb(data)); // Resolve the promise with the received data
|
|
155
|
+
};
|
|
156
|
+
const cleanUp = () => {
|
|
157
|
+
if (timer)
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
if (this.socket) {
|
|
160
|
+
this.socket.removeListener("data", onData); // Remove the data event listener
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
// Attach the data event listener
|
|
164
|
+
this.socket.on("data", onData);
|
|
165
|
+
// Attempt to write the message to the socket
|
|
166
|
+
this.socket.write(msg, undefined, (err) => {
|
|
167
|
+
if (err) {
|
|
168
|
+
cleanUp();
|
|
169
|
+
reject(err); // Reject the promise with the write error
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async requestData(msg) {
|
|
175
|
+
try {
|
|
176
|
+
return await new Promise((resolve, reject) => {
|
|
177
|
+
let timer = null;
|
|
178
|
+
let replyBuffer = Buffer.from([]);
|
|
179
|
+
// Internal callback to handle data reception
|
|
180
|
+
const internalCallback = (data_1) => {
|
|
181
|
+
if (this.socket) {
|
|
182
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener
|
|
183
|
+
}
|
|
184
|
+
if (timer)
|
|
185
|
+
clearTimeout(timer); // Clear the timeout
|
|
186
|
+
resolve(data_1); // Resolve the promise with the data
|
|
187
|
+
};
|
|
188
|
+
const onTimeOut = () => setTimeout(() => {
|
|
189
|
+
if (this.socket) {
|
|
190
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener on timeout
|
|
191
|
+
}
|
|
192
|
+
reject(new Error("TIMEOUT_IN_RECEIVING_RESPONSE_AFTER_REQUESTING_DATA")); // Reject on timeout
|
|
193
|
+
}, this.timeout);
|
|
194
|
+
// Handle incoming data
|
|
195
|
+
const handleOnData = (data_3) => {
|
|
196
|
+
replyBuffer = Buffer.concat([replyBuffer, data_3]); // Accumulate data
|
|
197
|
+
// Check if the data is a valid TCP event
|
|
198
|
+
if (utils.checkNotEventTCP(data_3))
|
|
199
|
+
return;
|
|
200
|
+
// Decode the TCP header
|
|
201
|
+
const header = utils.decodeTCPHeader(replyBuffer.subarray(0, 16));
|
|
202
|
+
if (this.verbose) {
|
|
203
|
+
console.log("response command: ", header.commandId, command.COMMANDS[header.commandId], "replyid: ", header.replyId);
|
|
204
|
+
}
|
|
205
|
+
// Handle based on command ID
|
|
206
|
+
if (header.commandId === command.COMMANDS.CMD_DATA) {
|
|
207
|
+
// Set a timeout to handle delayed responses
|
|
208
|
+
timer = setTimeout(() => {
|
|
209
|
+
internalCallback(replyBuffer); // Resolve with accumulated buffer
|
|
210
|
+
}, 1000);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// Set a timeout to handle errors
|
|
214
|
+
timer = onTimeOut();
|
|
215
|
+
// Extract packet length and handle accordingly
|
|
216
|
+
const packetLength = data_3.readUIntLE(4, 2);
|
|
217
|
+
if (packetLength > 8) {
|
|
218
|
+
internalCallback(data_3); // Resolve immediately if sufficient data
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
// Ensure the socket is valid before attaching the listener
|
|
223
|
+
if (this.socket) {
|
|
224
|
+
this.socket.on("data", handleOnData);
|
|
225
|
+
// Write the message to the socket
|
|
226
|
+
this.socket.write(msg, undefined, (err) => {
|
|
227
|
+
if (err) {
|
|
228
|
+
if (this.socket) {
|
|
229
|
+
this.socket.removeListener("data", handleOnData); // Clean up listener on error
|
|
230
|
+
}
|
|
231
|
+
return reject(err); // Reject the promise with the error
|
|
232
|
+
}
|
|
233
|
+
// Set a timeout to handle cases where no response is received
|
|
234
|
+
timer = onTimeOut();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
reject(new Error("SOCKET_NOT_INITIALIZED")); // Reject if socket is not initialized
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch (err_1) {
|
|
243
|
+
console.error("Promise Rejected:", err_1); // Log the rejection reason
|
|
244
|
+
throw err_1; // Re-throw the error to be handled by the caller
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
*
|
|
249
|
+
* @param {*} command
|
|
250
|
+
* @param {*} data
|
|
251
|
+
*
|
|
252
|
+
*
|
|
253
|
+
* reject error when command fail and resolve data when success
|
|
254
|
+
*/
|
|
255
|
+
async executeCmd(command$1, data) {
|
|
256
|
+
// Reset sessionId and replyId for connection commands
|
|
257
|
+
if (command$1 === command.COMMANDS.CMD_CONNECT) {
|
|
258
|
+
this.sessionId = 0;
|
|
259
|
+
this.replyId = 0;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.replyId++;
|
|
263
|
+
}
|
|
264
|
+
const currentReply = this.replyId;
|
|
265
|
+
const buf = utils.createTCPHeader(command$1, this.sessionId, this.replyId, data);
|
|
266
|
+
const callback = (responseData) => {
|
|
267
|
+
const packets = utils.splitTcpPackets(responseData);
|
|
268
|
+
for (const packet of packets) {
|
|
269
|
+
const headers = utils.decodeTCPHeader(packet);
|
|
270
|
+
if (this.verbose) {
|
|
271
|
+
const JOIN_CMD = { ...command.COMMANDS, ...command.DISCOVERED_CMD };
|
|
272
|
+
console.debug("request command:", command.COMMANDS[command$1], "\nresponse command: ", JOIN_CMD[headers.commandId], "replyid: ", headers.replyId);
|
|
273
|
+
}
|
|
274
|
+
if (+headers.replyId === currentReply + 1) {
|
|
275
|
+
return packet;
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
return new Promise((Resolve, Reject) => {
|
|
281
|
+
// Write the message to the socket and wait for a response
|
|
282
|
+
this.writeMessage(buf, command$1 === command.COMMANDS.CMD_CONNECT || command$1 === command.COMMANDS.CMD_EXIT, callback)
|
|
283
|
+
.then((reply) => {
|
|
284
|
+
// Remove TCP header from the response
|
|
285
|
+
let rReply;
|
|
286
|
+
try {
|
|
287
|
+
rReply = utils.removeTcpHeader(reply);
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
console.log("reply", reply);
|
|
291
|
+
}
|
|
292
|
+
// Update sessionId for connection command responses
|
|
293
|
+
if (command$1 === command.COMMANDS.CMD_CONNECT &&
|
|
294
|
+
rReply &&
|
|
295
|
+
rReply.length >= 6) {
|
|
296
|
+
// Assuming sessionId is located at offset 4 and is 2 bytes long
|
|
297
|
+
this.sessionId = rReply.readUInt16LE(4);
|
|
298
|
+
}
|
|
299
|
+
Resolve(rReply);
|
|
300
|
+
})
|
|
301
|
+
.catch((err) => {
|
|
302
|
+
// Log or handle the error if necessary
|
|
303
|
+
console.error("Error executing command:", err);
|
|
304
|
+
Reject(err); // Re-throw the error for handling by the caller
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async sendChunkRequest(start, size) {
|
|
309
|
+
this.replyId++;
|
|
310
|
+
const reqData = Buffer.alloc(8);
|
|
311
|
+
reqData.writeUInt32LE(start, 0);
|
|
312
|
+
reqData.writeUInt32LE(size, 4);
|
|
313
|
+
const buf = utils.createTCPHeader(command.COMMANDS.CMD_DATA_RDY, this.sessionId, this.replyId, reqData);
|
|
314
|
+
try {
|
|
315
|
+
const promise = new Promise((resolve, reject) => {
|
|
316
|
+
this.socket?.write(buf, undefined, (err) => {
|
|
317
|
+
if (err) {
|
|
318
|
+
console.error(`[TCP][SEND_CHUNK_REQUEST] Error sending chunk request: ${err.message}`);
|
|
319
|
+
reject(err); // Reject the promise if there is an error
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
resolve(true); // Resolve the promise if the write operation succeeds
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
await promise;
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
// Handle or log the error as needed
|
|
330
|
+
console.error(`[TCP][SEND_CHUNK_REQUEST] Exception: ${err.message}`);
|
|
331
|
+
throw err; // Re-throw the error for handling by the caller
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
*
|
|
336
|
+
* @param {Buffer} reqData - indicate the type of data that need to receive ( user or attLog)
|
|
337
|
+
* @param {Function} cb - callback is triggered when receiving packets
|
|
338
|
+
*
|
|
339
|
+
* readWithBuffer will reject error if it'wrong when starting request data
|
|
340
|
+
* readWithBuffer will return { data: replyData , err: Error } when receiving requested data
|
|
341
|
+
*/
|
|
342
|
+
readWithBuffer(reqData, cb) {
|
|
343
|
+
return new Promise(async (resolve, reject) => {
|
|
344
|
+
this.replyId++;
|
|
345
|
+
const buf = utils.createTCPHeader(command.COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData);
|
|
346
|
+
let reply;
|
|
347
|
+
try {
|
|
348
|
+
reply = await this.requestData(buf);
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
reject(err);
|
|
352
|
+
}
|
|
353
|
+
const header = utils.decodeTCPHeader(reply?.subarray(0, 16));
|
|
354
|
+
switch (header.commandId) {
|
|
355
|
+
case command.COMMANDS.CMD_DATA: {
|
|
356
|
+
resolve({ data: reply.subarray(16), mode: 8 });
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
case command.COMMANDS.CMD_ACK_OK:
|
|
360
|
+
case command.COMMANDS.CMD_PREPARE_DATA: {
|
|
361
|
+
// this case show that data is prepared => send command to get these data
|
|
362
|
+
// reply variable includes information about the size of following data
|
|
363
|
+
const recvData = reply.subarray(16);
|
|
364
|
+
const size = recvData.readUIntLE(1, 4);
|
|
365
|
+
// We need to split the data to many chunks to receive , because it's to large
|
|
366
|
+
// After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
|
|
367
|
+
const remain = size % command.Constants.MAX_CHUNK;
|
|
368
|
+
const numberChunks = Math.round(size - remain) / command.Constants.MAX_CHUNK;
|
|
369
|
+
this.packetNumber = numberChunks + (remain > 0 ? 1 : 0);
|
|
370
|
+
//let replyData = Buffer.from([])
|
|
371
|
+
let totalBuffer = Buffer.from([]);
|
|
372
|
+
let realTotalBuffer = Buffer.from([]);
|
|
373
|
+
let timer = setTimeout(() => {
|
|
374
|
+
internalCallback(this.replyData, new Error("TIMEOUT WHEN RECEIVING PACKET"));
|
|
375
|
+
}, this.timeout);
|
|
376
|
+
const internalCallback = (replyData, err = null) => {
|
|
377
|
+
this.socket && this.socket.removeAllListeners("data");
|
|
378
|
+
timer && clearTimeout(timer);
|
|
379
|
+
resolve({ data: replyData, err });
|
|
380
|
+
};
|
|
381
|
+
this.socket?.once("close", () => {
|
|
382
|
+
internalCallback(this.replyData, new Error("Socket is disconnected unexpectedly"));
|
|
383
|
+
});
|
|
384
|
+
for (let i = 0; i <= numberChunks; i++) {
|
|
385
|
+
const data = await new Promise((resolve2, reject2) => {
|
|
386
|
+
try {
|
|
387
|
+
this.sendChunkRequest(i * command.Constants.MAX_CHUNK, i === numberChunks ? remain : command.Constants.MAX_CHUNK);
|
|
388
|
+
this.socket?.on("data", (reply) => {
|
|
389
|
+
clearTimeout(timer);
|
|
390
|
+
timer = setTimeout(() => {
|
|
391
|
+
internalCallback(this.replyData, new Error(`TIME OUT !! ${this.packetNumber} PACKETS REMAIN !`));
|
|
392
|
+
}, this.timeout);
|
|
393
|
+
if (this.verbose && reply.length >= 8) {
|
|
394
|
+
const headers = utils.decodeTCPHeader(reply);
|
|
395
|
+
if (command.COMMANDS[headers.commandId]) {
|
|
396
|
+
switch (headers.commandId) {
|
|
397
|
+
case command.COMMANDS.CMD_ACK_OK:
|
|
398
|
+
case command.COMMANDS.CMD_DATA:
|
|
399
|
+
this.verbose &&
|
|
400
|
+
console.log("CMD received: ", command.COMMANDS[headers.commandId]);
|
|
401
|
+
break;
|
|
402
|
+
case command.COMMANDS.CMD_PREPARE_DATA:
|
|
403
|
+
this.verbose &&
|
|
404
|
+
console.log("CMD received: ", command.COMMANDS[headers.commandId]);
|
|
405
|
+
this.verbose &&
|
|
406
|
+
console.log(`recieve chunk: prepare data size is ${headers.payloadSize}`);
|
|
407
|
+
break;
|
|
408
|
+
default:
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
totalBuffer = Buffer.concat([totalBuffer, reply]);
|
|
414
|
+
const packetLength = totalBuffer.readUIntLE(4, 2);
|
|
415
|
+
if (totalBuffer.length >= 8 + packetLength) {
|
|
416
|
+
realTotalBuffer = Buffer.concat([
|
|
417
|
+
realTotalBuffer,
|
|
418
|
+
totalBuffer.subarray(16, 8 + packetLength),
|
|
419
|
+
]);
|
|
420
|
+
totalBuffer = totalBuffer.subarray(8 + packetLength);
|
|
421
|
+
if ((this.packetNumber > 1 &&
|
|
422
|
+
realTotalBuffer.length === command.Constants.MAX_CHUNK + 8) ||
|
|
423
|
+
(this.packetNumber === 1 &&
|
|
424
|
+
realTotalBuffer.length === remain + 8)) {
|
|
425
|
+
this.packetNumber--;
|
|
426
|
+
cb && cb(realTotalBuffer.length, size);
|
|
427
|
+
resolve2(realTotalBuffer.subarray(8));
|
|
428
|
+
totalBuffer = Buffer.from([]);
|
|
429
|
+
realTotalBuffer = Buffer.from([]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
catch (e) {
|
|
435
|
+
reject2(e);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
this.replyData = Buffer.concat([
|
|
439
|
+
this.replyData,
|
|
440
|
+
data,
|
|
441
|
+
]);
|
|
442
|
+
this.socket?.removeAllListeners("data");
|
|
443
|
+
if (this.packetNumber <= 0) {
|
|
444
|
+
resolve({ data: this.replyData });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
default: {
|
|
450
|
+
reject(new Error("ERROR_IN_UNHANDLE_CMD " + utils.exportErrorMessage(header.commandId)));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
*
|
|
457
|
+
* @param {*} callbackInProcess
|
|
458
|
+
* reject error when starting request data
|
|
459
|
+
* return { data: records, err: Error } when receiving requested data
|
|
460
|
+
*/
|
|
461
|
+
async freeData() {
|
|
462
|
+
try {
|
|
463
|
+
const resp = await this.executeCmd(command.COMMANDS.CMD_FREE_DATA, "");
|
|
464
|
+
return !!resp;
|
|
465
|
+
}
|
|
466
|
+
catch (err) {
|
|
467
|
+
console.error("Error freeing data:", err);
|
|
468
|
+
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async disableDevice() {
|
|
472
|
+
try {
|
|
473
|
+
const resp = await this.executeCmd(command.COMMANDS.CMD_DISABLEDEVICE, command.REQUEST_DATA.DISABLE_DEVICE);
|
|
474
|
+
return !!resp;
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
console.error("Error disabling device:", err);
|
|
478
|
+
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async enableDevice() {
|
|
482
|
+
try {
|
|
483
|
+
const resp = await this.executeCmd(command.COMMANDS.CMD_ENABLEDEVICE, "");
|
|
484
|
+
return !!resp;
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
console.error("Error enabling device:", err);
|
|
488
|
+
throw err; // Optionally, re-throw the error if you need to handle it upstream
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async disconnect() {
|
|
492
|
+
try {
|
|
493
|
+
// Attempt to execute the disconnect command
|
|
494
|
+
await this.executeCmd(command.COMMANDS.CMD_EXIT, "");
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
// Log any errors encountered during command execution
|
|
498
|
+
console.error("Error during disconnection:", err);
|
|
499
|
+
// Optionally, add more handling or recovery logic here
|
|
500
|
+
}
|
|
501
|
+
// Attempt to close the socket and return the result
|
|
502
|
+
try {
|
|
503
|
+
await this.closeSocket();
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
// Log any errors encountered while closing the socket
|
|
507
|
+
console.error("Error during socket closure:", err);
|
|
508
|
+
// Optionally, rethrow or handle the error if necessary
|
|
509
|
+
throw err; // Re-throwing to propagate the error
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async getInfo() {
|
|
513
|
+
try {
|
|
514
|
+
// Execute the command to retrieve free sizes from the device
|
|
515
|
+
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
516
|
+
// Parse the response data to extract and return relevant information
|
|
517
|
+
return {
|
|
518
|
+
userCounts: data.readUIntLE(24, 4), // Number of users
|
|
519
|
+
logCounts: data.readUIntLE(40, 4), // Number of logs
|
|
520
|
+
logCapacity: data.readUIntLE(72, 4), // Capacity of logs in bytes
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
// Log the error for debugging purposes
|
|
525
|
+
console.error("Error getting device info:", err);
|
|
526
|
+
// Re-throw the error to allow upstream error handling
|
|
527
|
+
throw err;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async getSizes() {
|
|
531
|
+
try {
|
|
532
|
+
// Execute the command to retrieve free sizes from the device
|
|
533
|
+
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
534
|
+
// Parse the response data to extract and return relevant information
|
|
535
|
+
const buf = data.slice(8); // remove header
|
|
536
|
+
this.user_count = buf.readUIntLE(16, 4);
|
|
537
|
+
this.fp_count = buf.readUIntLE(24, 4);
|
|
538
|
+
this.pwd_count = buf.readUIntLE(52, 4);
|
|
539
|
+
this.oplog_count = buf.readUIntLE(40, 4);
|
|
540
|
+
this.attlog_count = buf.readUIntLE(32, 4);
|
|
541
|
+
this.fp_cap = buf.readUIntLE(56, 4);
|
|
542
|
+
this.user_cap = buf.readUIntLE(60, 4);
|
|
543
|
+
this.attlog_cap = buf.readUIntLE(64, 4);
|
|
544
|
+
this.fp_av = buf.readUIntLE(68, 4);
|
|
545
|
+
this.user_av = buf.readUIntLE(72, 4);
|
|
546
|
+
this.attlog_av = buf.readUIntLE(76, 4);
|
|
547
|
+
this.face_count = buf.readUIntLE(80, 4);
|
|
548
|
+
this.face_cap = buf.readUIntLE(88, 4);
|
|
549
|
+
return {
|
|
550
|
+
userCounts: this.user_count, // Number of users
|
|
551
|
+
logCounts: this.attlog_count, // Number of logs
|
|
552
|
+
fingerCount: this.fp_count,
|
|
553
|
+
adminCount: this.pwd_count,
|
|
554
|
+
opLogCount: this.oplog_count,
|
|
555
|
+
logCapacity: this.attlog_cap, // Capacity of logs in bytes
|
|
556
|
+
fingerCapacity: this.fp_cap,
|
|
557
|
+
userCapacity: this.user_cap,
|
|
558
|
+
attLogCapacity: this.attlog_cap,
|
|
559
|
+
fingerAvailable: this.fp_av,
|
|
560
|
+
userAvailable: this.user_av,
|
|
561
|
+
attLogAvailable: this.attlog_av,
|
|
562
|
+
faceCount: this.face_count,
|
|
563
|
+
faceCapacity: this.face_cap,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
// Log the error for debugging purposes
|
|
568
|
+
console.error("Error getting device info:", err);
|
|
569
|
+
// Re-throw the error to allow upstream error handling
|
|
570
|
+
throw err;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
#listeners = new Map();
|
|
574
|
+
async getAttendanceSize() {
|
|
575
|
+
try {
|
|
576
|
+
// Execute command to get free sizes
|
|
577
|
+
const data = await this.executeCmd(command.COMMANDS.CMD_GET_FREE_SIZES, "");
|
|
578
|
+
// Parse and return the attendance size
|
|
579
|
+
return data.readUIntLE(40, 4); // Assuming data at offset 40 represents the attendance size
|
|
580
|
+
}
|
|
581
|
+
catch (err) {
|
|
582
|
+
// Log error details for debugging
|
|
583
|
+
console.error("Error getting attendance size:", err);
|
|
584
|
+
// Re-throw the error to be handled by the caller
|
|
585
|
+
throw err;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// Clears the attendance logs on the device
|
|
589
|
+
async clearAttendanceLog() {
|
|
590
|
+
return await this._transactionService.clearAttendanceLog();
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Clears all data on the device
|
|
594
|
+
* @value 1 Attendance records
|
|
595
|
+
* @value 2 Fingerprint templates
|
|
596
|
+
* @value 3 None
|
|
597
|
+
* @value 4 Operation records
|
|
598
|
+
* @value 5 User information
|
|
599
|
+
* @default 0 Delete all
|
|
600
|
+
*/
|
|
601
|
+
async clearData(value) {
|
|
602
|
+
try {
|
|
603
|
+
// Execute the command to clear all data
|
|
604
|
+
await this.disableDevice();
|
|
605
|
+
if (!value)
|
|
606
|
+
value = 3;
|
|
607
|
+
const buf = await this.executeCmd(command.COMMANDS.CMD_CLEAR_DATA, value.toString());
|
|
608
|
+
await this.refreshData();
|
|
609
|
+
await this.enableDevice();
|
|
610
|
+
return !!buf;
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
// Log the error for debugging purposes
|
|
614
|
+
console.error("Error clearing data:", err);
|
|
615
|
+
// Re-throw the error to be handled by the caller
|
|
616
|
+
throw err;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
async getRealTimeLogs(cb = (realTimeLog) => { }) {
|
|
620
|
+
this.replyId++; // Increment the reply ID for this request
|
|
621
|
+
try {
|
|
622
|
+
// Create a buffer with the command header to request real-time logs
|
|
623
|
+
const buf = utils.createTCPHeader(command.COMMANDS.CMD_REG_EVENT, this.sessionId, this.replyId, Buffer.from([0x01, 0x00, 0x00, 0x00]));
|
|
624
|
+
// Send the request to the device
|
|
625
|
+
this.socket?.write(buf, undefined, (err) => {
|
|
626
|
+
if (err) {
|
|
627
|
+
// Log and reject the promise if there is an error writing to the socket
|
|
628
|
+
console.error("Error sending real-time logs request:", err);
|
|
629
|
+
throw err;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
// Ensure data listeners are added only once
|
|
633
|
+
if (this.socket?.listenerCount("data") === 0) {
|
|
634
|
+
console.log("entraaa");
|
|
635
|
+
this.socket.on("data", (data) => {
|
|
636
|
+
// Check if the data is an event and not just a regular response
|
|
637
|
+
if (utils.checkNotEventTCP(data)) {
|
|
638
|
+
// Process the data if it is of the expected length
|
|
639
|
+
if (data.length > 16) {
|
|
640
|
+
// Decode and pass the log to the callback
|
|
641
|
+
cb(utils.decodeRTEvent(data));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
// Handle errors and reject the promise
|
|
649
|
+
console.error("Error getting real-time logs:", err);
|
|
650
|
+
throw err;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get all Finger objects
|
|
655
|
+
* @returns {Record<string, Finger[]>}
|
|
656
|
+
*/
|
|
657
|
+
async getTemplates(callbackInProcess = () => { }) {
|
|
658
|
+
return await this._userService.getTemplates(callbackInProcess);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Return size
|
|
662
|
+
* @param packet
|
|
663
|
+
*/
|
|
664
|
+
testTcpTop(packet) {
|
|
665
|
+
// Check if packet is too small
|
|
666
|
+
if (packet.length <= 8)
|
|
667
|
+
return 0;
|
|
668
|
+
// Extract header values using little-endian format
|
|
669
|
+
const headerValue1 = packet.readUInt16LE(0);
|
|
670
|
+
const headerValue2 = packet.readUInt16LE(2);
|
|
671
|
+
const size = packet.readUInt32LE(4);
|
|
672
|
+
// Check if magic numbers match
|
|
673
|
+
if (headerValue1 === command.Constants.MACHINE_PREPARE_DATA_1 &&
|
|
674
|
+
headerValue2 === command.Constants.MACHINE_PREPARE_DATA_2) {
|
|
675
|
+
return size;
|
|
676
|
+
}
|
|
677
|
+
return 0;
|
|
678
|
+
}
|
|
679
|
+
async refreshData() {
|
|
680
|
+
try {
|
|
681
|
+
const reply = await this.executeCmd(command.COMMANDS.CMD_REFRESHDATA, "");
|
|
682
|
+
return !!reply;
|
|
683
|
+
}
|
|
684
|
+
catch (err) {
|
|
685
|
+
console.error("Error getting user templates: ", err);
|
|
686
|
+
throw err;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
async sendWithBuffer(buffer) {
|
|
690
|
+
const MAX_CHUNK = 1024;
|
|
691
|
+
const size = buffer.length;
|
|
692
|
+
await this.freeData();
|
|
693
|
+
const commandString = Buffer.alloc(4); // 'I' is 4 bytes
|
|
694
|
+
commandString.writeUInt32LE(size, 0);
|
|
695
|
+
try {
|
|
696
|
+
const cmdResponse = await this.executeCmd(command.COMMANDS.CMD_PREPARE_DATA, commandString);
|
|
697
|
+
// responds with 2000 = CMD_ACK_OK
|
|
698
|
+
if (!cmdResponse) {
|
|
699
|
+
throw new Error("Can't prepare data");
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
catch (e) {
|
|
703
|
+
console.error(e);
|
|
704
|
+
}
|
|
705
|
+
const remain = size % MAX_CHUNK;
|
|
706
|
+
const packets = Math.floor((size - remain) / MAX_CHUNK);
|
|
707
|
+
let start = 0;
|
|
708
|
+
try {
|
|
709
|
+
for (let i = 0; i < packets; i++) {
|
|
710
|
+
const resp = await this.sendChunk(buffer.slice(start, start + MAX_CHUNK));
|
|
711
|
+
if (resp) {
|
|
712
|
+
start += MAX_CHUNK;
|
|
713
|
+
if (i == packets - 1 && remain) {
|
|
714
|
+
const lastPacket = await this.sendChunk(buffer.slice(start, start + remain));
|
|
715
|
+
return lastPacket;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch (e) {
|
|
721
|
+
console.error(e);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
async sendChunk(commandString) {
|
|
725
|
+
try {
|
|
726
|
+
return await new Promise((resolve, reject) => {
|
|
727
|
+
resolve(this.executeCmd(command.COMMANDS.CMD_DATA, commandString));
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
catch (e) {
|
|
731
|
+
throw new handler.ZkError(e, command.COMMANDS.CMD_DATA, this.ip);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async readSocket(length, cb = null) {
|
|
735
|
+
let replyBufer = Buffer.from([]);
|
|
736
|
+
const totalPackets = 0;
|
|
737
|
+
return new Promise((resolve, reject) => {
|
|
738
|
+
let timer = setTimeout(() => {
|
|
739
|
+
internalCallback(replyBufer, new Error("TIMEOUT WHEN RECEIVING PACKET"));
|
|
740
|
+
}, this.timeout);
|
|
741
|
+
const internalCallback = (replyData, err = null) => {
|
|
742
|
+
this.socket && this.socket.removeListener("data", onDataEnroll);
|
|
743
|
+
timer && clearTimeout(timer);
|
|
744
|
+
resolve({ data: replyData, err: err });
|
|
745
|
+
};
|
|
746
|
+
function onDataEnroll(data) {
|
|
747
|
+
clearTimeout(timer);
|
|
748
|
+
timer = setTimeout(() => {
|
|
749
|
+
internalCallback(replyBufer, new Error(`TIME OUT !! ${totalPackets} PACKETS REMAIN !`));
|
|
750
|
+
}, this.timeout);
|
|
751
|
+
replyBufer = Buffer.concat([replyBufer, data], replyBufer.length + data.length);
|
|
752
|
+
if (data.length == length) {
|
|
753
|
+
internalCallback(data);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
this.socket.once("close", () => {
|
|
757
|
+
internalCallback(replyBufer, new Error("Socket is disconnected unexpectedly"));
|
|
758
|
+
});
|
|
759
|
+
this.socket.on("data", onDataEnroll);
|
|
760
|
+
}).catch((err) => {
|
|
761
|
+
console.error("Promise Rejected:", err); // Log the rejection reason
|
|
762
|
+
throw err; // Re-throw the error to be handled by the caller
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Register events
|
|
767
|
+
* @param {number} flags - Event flags
|
|
768
|
+
* @returns {Promise<void>}
|
|
769
|
+
* @throws {ZKErrorResponse} If registration fails
|
|
770
|
+
*/
|
|
771
|
+
async regEvent(flags) {
|
|
772
|
+
try {
|
|
773
|
+
const commandString = Buffer.alloc(4); // 'I' format is 4 bytes
|
|
774
|
+
commandString.writeUInt32LE(flags, 0); // Little-endian unsigned int
|
|
775
|
+
const cmdResponse = await this.executeCmd(command.COMMANDS.CMD_REG_EVENT, commandString);
|
|
776
|
+
if (this.verbose)
|
|
777
|
+
console.log("regEvent: ", cmdResponse.readUInt16LE(0));
|
|
778
|
+
}
|
|
779
|
+
catch (e) {
|
|
780
|
+
throw new handler.ZkError(e, command.COMMANDS.CMD_REG_EVENT, this.ip);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
async cancelCapture() {
|
|
784
|
+
try {
|
|
785
|
+
const reply = await this.executeCmd(command.COMMANDS.CMD_CANCELCAPTURE, "");
|
|
786
|
+
return !!reply;
|
|
787
|
+
}
|
|
788
|
+
catch (e) {
|
|
789
|
+
throw new handler.ZkError(e, command.COMMANDS.CMD_CANCELCAPTURE, this.ip);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
async restartDevice() {
|
|
793
|
+
try {
|
|
794
|
+
await this.executeCmd(command.COMMANDS.CMD_RESTART, "");
|
|
795
|
+
}
|
|
796
|
+
catch (e) {
|
|
797
|
+
throw new handler.ZkError(e, command.COMMANDS.CMD_RESTART, this.ip);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
exports.ZTCP = ZTCP;
|