|
@@ -1,6 +1,7 @@
|
|
|
package com.grkj.data.hardware.fingerprint
|
|
package com.grkj.data.hardware.fingerprint
|
|
|
|
|
|
|
|
import android.util.Base64
|
|
import android.util.Base64
|
|
|
|
|
+import com.grkj.data.hardware.face.hlk.LoggerPlugin
|
|
|
import com.grkj.shared.utils.extension.toHexStrings
|
|
import com.grkj.shared.utils.extension.toHexStrings
|
|
|
import com.machinezoo.sourceafis.FingerprintTemplate
|
|
import com.machinezoo.sourceafis.FingerprintTemplate
|
|
|
import com.sik.comm.codec.PassThroughCodec
|
|
import com.sik.comm.codec.PassThroughCodec
|
|
@@ -48,26 +49,33 @@ object FingerprintCaptureService {
|
|
|
|
|
|
|
|
private val chunks = ArrayList<ByteArray>(256)
|
|
private val chunks = ArrayList<ByteArray>(256)
|
|
|
private var isLast = false
|
|
private var isLast = false
|
|
|
- private val policy = object : ChainPolicy {
|
|
|
|
|
|
|
+ private val policy get() = object : ChainPolicy {
|
|
|
override suspend fun afterSendStep(
|
|
override suspend fun afterSendStep(
|
|
|
stepIndex: Int,
|
|
stepIndex: Int,
|
|
|
sent: CommMessage,
|
|
sent: CommMessage,
|
|
|
io: LinkIO
|
|
io: LinkIO
|
|
|
): ChainStepResult {
|
|
): ChainStepResult {
|
|
|
- isLast = false
|
|
|
|
|
- chunks.clear()
|
|
|
|
|
|
|
+ if (stepIndex == 0 || isLast) {
|
|
|
|
|
+ isLast = false
|
|
|
|
|
+ chunks.clear()
|
|
|
|
|
+ }
|
|
|
val deadlineNs = System.nanoTime() + 15_000_000_000L
|
|
val deadlineNs = System.nanoTime() + 15_000_000_000L
|
|
|
while (System.nanoTime() < deadlineNs) {
|
|
while (System.nanoTime() < deadlineNs) {
|
|
|
val rsp = try {
|
|
val rsp = try {
|
|
|
- io.readRaw(timeoutMs = 600, expectedSize = null, silenceGapMs = 8)
|
|
|
|
|
|
|
+ io.readRaw(timeoutMs = 1000, expectedSize = null, silenceGapMs = 40)
|
|
|
} catch (e: Exception) {
|
|
} catch (e: Exception) {
|
|
|
- if (isLast) return ChainStepResult(emptyList(), false)
|
|
|
|
|
|
|
+ if (chunks.isNotEmpty()) {
|
|
|
|
|
+ return ChainStepResult(emptyList(), false)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果已经处理过 LAST(理论上不会到这儿),就结束
|
|
|
|
|
+ if (isLast) return ChainStepResult(emptyList(), true)
|
|
|
delay(10)
|
|
delay(10)
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
log.debug("readRaw(): got {}B", rsp.payload.size)
|
|
log.debug("readRaw(): got {}B", rsp.payload.size)
|
|
|
val frames = framer.feed(rsp.payload)
|
|
val frames = framer.feed(rsp.payload)
|
|
|
for (f in frames) {
|
|
for (f in frames) {
|
|
|
|
|
+ log.debug("指令内容:${f.toHexStrings()}")
|
|
|
when (f.getOrNull(6)) {
|
|
when (f.getOrNull(6)) {
|
|
|
FpmPackets.PID_ACK -> { /* 可读确认码;不强制 */
|
|
FpmPackets.PID_ACK -> { /* 可读确认码;不强制 */
|
|
|
}
|
|
}
|
|
@@ -79,10 +87,16 @@ object FingerprintCaptureService {
|
|
|
FpmPackets.PID_LAST -> {
|
|
FpmPackets.PID_LAST -> {
|
|
|
chunks += FpmPackets.parseDataLike(f)
|
|
chunks += FpmPackets.parseDataLike(f)
|
|
|
isLast = true
|
|
isLast = true
|
|
|
- return ChainStepResult(emptyList(), false)
|
|
|
|
|
|
|
+ val result = ChainStepResult(emptyList(), true)
|
|
|
|
|
+ // 状态保持到下一轮由 stepIndex==0 再清理
|
|
|
|
|
+ return result
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ // 如果本轮读到了若干 DATA,但还没等到 LAST,可以先上交,提升管道吞吐
|
|
|
|
|
+ if (chunks.isNotEmpty()) {
|
|
|
|
|
+ return ChainStepResult(emptyList(), false)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
if (!isLast) error("UP_IMAGE not finished (no LAST) within deadline")
|
|
if (!isLast) error("UP_IMAGE not finished (no LAST) within deadline")
|
|
|
return ChainStepResult(emptyList(), false)
|
|
return ChainStepResult(emptyList(), false)
|
|
@@ -356,94 +370,88 @@ object FingerprintCaptureService {
|
|
|
|
|
|
|
|
/** 发 UP_IMAGE,一直读到 PID=0x08(LAST)为止 */
|
|
/** 发 UP_IMAGE,一直读到 PID=0x08(LAST)为止 */
|
|
|
private suspend fun upImageOnce(proto: ModbusProtocol): ByteArray {
|
|
private suspend fun upImageOnce(proto: ModbusProtocol): ByteArray {
|
|
|
- framer.feed(ByteArray(0)) // 确保内部状态干净(可选)
|
|
|
|
|
|
|
+ framer.feed(ByteArray(0))
|
|
|
val start = FpmPackets.cmd(address, FpmIns.UP_IMAGE)
|
|
val start = FpmPackets.cmd(address, FpmIns.UP_IMAGE)
|
|
|
- val plan = TxPlan(
|
|
|
|
|
- listOf(
|
|
|
|
|
- FpmPackets.toMessage(
|
|
|
|
|
- "FPM_UP_IMAGE",
|
|
|
|
|
- start,
|
|
|
|
|
- timeoutMs = 15000,
|
|
|
|
|
- gapMs = 60
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ val plan = TxPlan(listOf(FpmPackets.toMessage("FPM_UP_IMAGE", start, timeoutMs = 15000, gapMs = 60)))
|
|
|
|
|
|
|
|
val chunks = ArrayList<ByteArray>(256)
|
|
val chunks = ArrayList<ByteArray>(256)
|
|
|
- var isLast = false
|
|
|
|
|
- val policy = object : ChainPolicy {
|
|
|
|
|
- override suspend fun afterSendStep(
|
|
|
|
|
- stepIndex: Int,
|
|
|
|
|
- sent: CommMessage,
|
|
|
|
|
- io: LinkIO
|
|
|
|
|
- ): ChainStepResult {
|
|
|
|
|
- val deadlineNs = System.nanoTime() + 15_000_000_000L
|
|
|
|
|
- while (System.nanoTime() < deadlineNs) {
|
|
|
|
|
- val rsp = try {
|
|
|
|
|
- io.readRaw(timeoutMs = 600, expectedSize = null, silenceGapMs = 8)
|
|
|
|
|
- } catch (e: Exception) {
|
|
|
|
|
- if (isLast) return ChainStepResult(emptyList(), false)
|
|
|
|
|
- delay(10)
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- log.debug("readRaw(): got {}B", rsp.payload.size)
|
|
|
|
|
- val frames = framer.feed(rsp.payload)
|
|
|
|
|
- for (f in frames) {
|
|
|
|
|
- when (f.getOrNull(6)) {
|
|
|
|
|
- FpmPackets.PID_ACK -> { /* 可读确认码;不强制 */
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ val policy = buildReadUntilLastPolicy(framer, chunks, readTimeoutMs = 600, silenceGapMs = 8, hardDeadlineMs = 15000)
|
|
|
|
|
|
|
|
- FpmPackets.PID_DATA -> {
|
|
|
|
|
- chunks += FpmPackets.parseDataLike(f)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ proto.sendChain(deviceId, plan, policy)
|
|
|
|
|
|
|
|
- FpmPackets.PID_LAST -> {
|
|
|
|
|
- chunks += FpmPackets.parseDataLike(f)
|
|
|
|
|
- isLast = true
|
|
|
|
|
- return ChainStepResult(emptyList(), false)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ val total = chunks.sumOf { it.size }
|
|
|
|
|
+ return ByteArray(total).also { out ->
|
|
|
|
|
+ var pos = 0; for (c in chunks) { System.arraycopy(c, 0, out, pos, c.size); pos += c.size }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 统一的读取策略:直到收到 PID_LAST,一次性把 chunks 交上去
|
|
|
|
|
+ private fun buildReadUntilLastPolicy(
|
|
|
|
|
+ framer: FpmFramer,
|
|
|
|
|
+ chunksOut: MutableList<ByteArray>,
|
|
|
|
|
+ lastPid: Byte = FpmPackets.PID_LAST,
|
|
|
|
|
+ dataPid: Byte = FpmPackets.PID_DATA,
|
|
|
|
|
+ ackPid: Byte = FpmPackets.PID_ACK,
|
|
|
|
|
+ readTimeoutMs: Int = 600, // 单次 read
|
|
|
|
|
+ silenceGapMs: Int = 8,
|
|
|
|
|
+ hardDeadlineMs: Long = 15_000 // 整体死线
|
|
|
|
|
+ ): ChainPolicy = object : ChainPolicy {
|
|
|
|
|
+ override suspend fun afterSendStep(stepIndex: Int, sent: CommMessage, io: LinkIO): ChainStepResult {
|
|
|
|
|
+ chunksOut.clear()
|
|
|
|
|
+ val deadlineNs = System.nanoTime() + hardDeadlineMs * 1_000_000
|
|
|
|
|
+ var gotLast = false
|
|
|
|
|
+
|
|
|
|
|
+ while (System.nanoTime() < deadlineNs) {
|
|
|
|
|
+ val rsp = try {
|
|
|
|
|
+ io.readRaw(timeoutMs = readTimeoutMs, expectedSize = null, silenceGapMs = silenceGapMs)
|
|
|
|
|
+ } catch (_: Throwable) {
|
|
|
|
|
+ // 读超时,继续等,交给整体死线兜底
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ val frames = framer.feed(rsp.payload)
|
|
|
|
|
+ for (f in frames) {
|
|
|
|
|
+ when (f.getOrNull(6)) {
|
|
|
|
|
+ ackPid -> { /* ignore or log */ }
|
|
|
|
|
+ dataPid -> chunksOut += FpmPackets.parseDataLike(f)
|
|
|
|
|
+ lastPid -> {
|
|
|
|
|
+ chunksOut += FpmPackets.parseDataLike(f)
|
|
|
|
|
+ gotLast = true
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if (!isLast) error("UP_IMAGE not finished (no LAST) within deadline")
|
|
|
|
|
- return ChainStepResult(emptyList(), false)
|
|
|
|
|
|
|
+ if (gotLast) {
|
|
|
|
|
+ // 一次性交付,停止链路
|
|
|
|
|
+ return ChainStepResult(
|
|
|
|
|
+ received = listOf(CommMessage("CHAIN_AGG", ByteArray(0), emptyMap())),
|
|
|
|
|
+ continueNext = false,
|
|
|
|
|
+ interFrameDelayMs = 0
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ error("UP_IMAGE/GET_CHAR not finished (no LAST) within $hardDeadlineMs ms")
|
|
|
}
|
|
}
|
|
|
- proto.sendChain(deviceId, plan, policy)
|
|
|
|
|
-
|
|
|
|
|
- val total = chunks.sumOf { it.size }
|
|
|
|
|
- val out = ByteArray(total)
|
|
|
|
|
- var pos = 0
|
|
|
|
|
- for (c in chunks) {
|
|
|
|
|
- System.arraycopy(c, 0, out, pos, c.size); pos += c.size
|
|
|
|
|
- }
|
|
|
|
|
- return out
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
/** 发 GET_CHAR,一直读到 PID=0x08(LAST)为止 */
|
|
/** 发 GET_CHAR,一直读到 PID=0x08(LAST)为止 */
|
|
|
private suspend fun getCharOnce(proto: ModbusProtocol): ByteArray {
|
|
private suspend fun getCharOnce(proto: ModbusProtocol): ByteArray {
|
|
|
- framer.feed(ByteArray(0)) // 确保内部状态干净(可选)
|
|
|
|
|
|
|
+ framer.feed(ByteArray(0))
|
|
|
val start = FpmPackets.cmd(address, FpmIns.GET_CHAR, byteArrayOf(FpmIns.CHAR_BUFFER_ID))
|
|
val start = FpmPackets.cmd(address, FpmIns.GET_CHAR, byteArrayOf(FpmIns.CHAR_BUFFER_ID))
|
|
|
- val plan = TxPlan(
|
|
|
|
|
- listOf(
|
|
|
|
|
- FpmPackets.toMessage(
|
|
|
|
|
- "FPM_GET_CHAR",
|
|
|
|
|
- start,
|
|
|
|
|
- timeoutMs = 15000,
|
|
|
|
|
- gapMs = 60
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ val plan = TxPlan(listOf(FpmPackets.toMessage("FPM_GET_CHAR", start, timeoutMs = 15000, gapMs = 60)))
|
|
|
|
|
+
|
|
|
|
|
+ val chunks = ArrayList<ByteArray>(256)
|
|
|
|
|
+ val policy = buildReadUntilLastPolicy(framer, chunks, readTimeoutMs = 1000, silenceGapMs = 40, hardDeadlineMs = 15000)
|
|
|
|
|
+
|
|
|
proto.sendChain(deviceId, plan, policy)
|
|
proto.sendChain(deviceId, plan, policy)
|
|
|
|
|
+
|
|
|
val total = chunks.sumOf { it.size }
|
|
val total = chunks.sumOf { it.size }
|
|
|
- val out = ByteArray(total)
|
|
|
|
|
- var pos = 0
|
|
|
|
|
- for (c in chunks) {
|
|
|
|
|
- System.arraycopy(c, 0, out, pos, c.size); pos += c.size
|
|
|
|
|
|
|
+ return ByteArray(total).also { out ->
|
|
|
|
|
+ var pos = 0; for (c in chunks) { System.arraycopy(c, 0, out, pos, c.size); pos += c.size }
|
|
|
}
|
|
}
|
|
|
- return out
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
// === 解码自适应 ===
|
|
// === 解码自适应 ===
|
|
|
|
|
|
|
|
private enum class NibbleOrder { HI_LO, LO_HI }
|
|
private enum class NibbleOrder { HI_LO, LO_HI }
|