caobo171 / node-zklib

This is a lightweight of node.js module to connect to biometrix attendance device for examples ZKTeco
70 stars 59 forks source link

Attendance data is not consistent #48

Open b1rigu opened 7 months ago

b1rigu commented 7 months ago

Sometimes it gets all data, and sometimes it doesn't with an error like this: "TIME OUT !! 14 PACKETS REMAIN". This is random and the amount of packets remaining is random ranging from 1 to n. I have tried a package in python and it consistently gets all the data. So I don't there is an internal device issue. Device is: zk face702 info: {userCounts: 80, logCounts: 27538, logCapacity: 120000} trying to get attendance data by getAttendances()

b1rigu commented 7 months ago

I fixed the large attendance data issue by adding a check when packet is fully sent. Just change the whole function to this

readWithBuffer(reqData, cb = null) {
    return new Promise(async (resolve, reject) => {

      this.replyId++;
      const buf = createTCPHeader(COMMANDS.CMD_DATA_WRRQ, this.sessionId, this.replyId, reqData)
      let reply = null;

      try {
        reply = await this.requestData(buf)
        //console.log(reply.toString('hex'));

      } catch (err) {
        reject(err)
      }

      const header = decodeTCPHeader(reply.subarray(0, 16))
      switch (header.commandId) {
        case COMMANDS.CMD_DATA: {
          resolve({ data: reply.subarray(16), mode: 8 })
          break;
        }
        case COMMANDS.CMD_ACK_OK:
        case COMMANDS.CMD_PREPARE_DATA: {
          // this case show that data is prepared => send command to get these data 
          // reply variable includes information about the size of following data
          const recvData = reply.subarray(16)
          const size = recvData.readUIntLE(1, 4)

          // We need to split the data to many chunks to receive , because it's to large
          // After receiving all chunk data , we concat it to TotalBuffer variable , that 's the data we want
          let remain = size % MAX_CHUNK
          let numberChunks = Math.round(size - remain) / MAX_CHUNK
          let totalPackets = numberChunks + (remain > 0 ? 1 : 0)
          let replyData = Buffer.from([])

          let totalBuffer = Buffer.from([])
          let realTotalBuffer = Buffer.from([])
          let isDataOnGoing = false

          const timeout = 10000
          let timer = setTimeout(() => {
            internalCallback(replyData, new Error('TIMEOUT WHEN RECEIVING PACKET'))
          }, timeout)

          const internalCallback = (replyData, err = null) => {
            // this.socket && this.socket.removeListener('data', handleOnData)
            timer && clearTimeout(timer)
            resolve({ data: replyData, err })
          }

          const handleOnData = (reply) => {
            if (checkNotEventTCP(reply)) return;
            clearTimeout(timer)
            timer = setTimeout(() => {
              internalCallback(replyData,
                new Error(`TIME OUT !! ${totalPackets} PACKETS REMAIN !`))
            }, timeout)

            totalBuffer = Buffer.concat([totalBuffer, reply])
            const packetLength = totalBuffer.readUIntLE(4, 2)
            if (totalBuffer.length >= 8 + packetLength) {

              realTotalBuffer = Buffer.concat([realTotalBuffer, totalBuffer.subarray(16, 8 + packetLength)])
              totalBuffer = totalBuffer.subarray(8 + packetLength)

              if ((totalPackets > 1 && realTotalBuffer.length === MAX_CHUNK + 8)
                || (totalPackets === 1 && realTotalBuffer.length === remain + 8)) {

                replyData = Buffer.concat([replyData, realTotalBuffer.subarray(8)])
                totalBuffer = Buffer.from([])
                realTotalBuffer = Buffer.from([])

                totalPackets -= 1
                isDataOnGoing = false;
                cb && cb(replyData.length, size)

                if (totalPackets <= 0) {
                  internalCallback(replyData)
                }
              }
            }
          }

          this.socket.once('close', () => {
            internalCallback(replyData, new Error('Socket is disconnected unexpectedly'))
          })

          this.socket.on('data', handleOnData)

          for (let i = 0; i <= numberChunks; i++) {
            if (i === numberChunks) {
              this.sendChunkRequest(numberChunks * MAX_CHUNK, remain)
            } else {
              this.sendChunkRequest(i * MAX_CHUNK, MAX_CHUNK)
            }
            isDataOnGoing = true;
            while (isDataOnGoing) {
              await new Promise(r => setTimeout(r, 100))
            }
          }

          break;
        }
        default: {
          reject(new Error('ERROR_IN_UNHANDLE_CMD ' + exportErrorMessage(header.commandId)))
        }
      }
    })
  }