krona-staff/docs/HARDWARE-TEST-SLK20R.md
Ochenstarik a05777c59f style: rebuild formatting without sync behavior changes
Recreate the formatting baseline from f5ac179 using only Prettier output. Leave sync and tenant-isolation files byte-identical to the base so Antigravity and Grok retain ownership. Normalized comparison confirms all 19 changed files are formatting-only; npm run check passes. Full format check retains five upstream warnings in excluded files.
2026-09-26 16:09:55 +07:00

139 lines
8.9 KiB
Markdown

# ZKTeco SLK20R Hardware Integration & Test Report
**Document Version:** 1.0.0
**Test Date:** September 2026
**Hardware Tested:** ZKTeco SLK20R Optical USB Fingerprint Reader
**Driver:** ZKTeco SLK20R USB Driver (slk20r.sys v2.1.0) / ZKFinger SDK 5.3
**Operating Systems:** Windows 10 Pro (22H2 x64), Windows 11 Enterprise (24H2 x64), Windows Server 2022 (Session 0)
---
## 1. Executive Summary
This report documents empirical hardware testing and architectural findings gathered during the integration of the ZKTeco SLK20R fingerprint scanner into the Krona Staff Terminal ecosystem.
### Key Takeaways:
1. **Session 0 Limitation:** The ZKTeco USB driver and native SDK (`libzkfp.dll`) rely on interactive desktop Windows messaging subsystems (`WM_DEVICECHANGE`) and Win32 user session interfaces. Running biometric capture inside a Windows Service (Session 0) consistently enumerates `0` devices.
2. **Hybrid Solution Adopted:** A decoupled dual-process architecture:
- **`KronaStaffTerminal.exe` (Interactive User Session):** Owns hardware USB scanner lifecycle, capture loop, biometric template matching, and multi-finger enrollment.
- **`KronaStaffService.exe` (Windows Service Session 0):** Owns persistent offline sync queue in `%ProgramData%`, draining records to backend HTTPS endpoints even when no user is logged in.
3. **Ghost Device Elimination:** The native ZKFinger SDK frequently enumerates duplicate/phantom device handles when querying `zkfp2.GetDeviceCount()`. Hardware serial number inspection was implemented to guarantee 1:1 hardware device mapping.
4. **USB Disconnect / Hotplug Resilience:** Scanner disconnection mid-session emits negative error codes; an automated reconnection state machine recovers connection within 2.0 seconds of re-plug without data loss or application crash.
---
## 2. Session 0 Investigation & Architectural Analysis
### The Problem
During development of the headless Windows Service (`KronaStaffService`), tests were performed to verify whether `libzkfp.dll` could capture fingerprints in Session 0 (running as `NT AUTHORITY\SYSTEM`).
#### Empirical Observations:
- In Windows Service Session 0, calling `zkfp2.Init()` returns `0` (ZKFP_ERR_OK).
- Calling `zkfp2.GetDeviceCount()` immediately following returns `0`, despite the USB scanner being plugged into the machine and operational in Device Manager.
- Attempting to pass handle `0` to `zkfp2.OpenDevice(0)` returns error code `-1` (device not found).
#### Root Cause:
The ZKTeco SLK20R driver implementation (`slk20r.sys`) registers device interfaces that bind specifically to interactive Windows desktop sessions. Because Session 0 in modern Windows (Vista through Windows 11/Server 2022) is isolated from window stations, the native driver cannot establish the USB pipe without an interactive user station (`WinSta0`).
#### Architectural Resolution:
Instead of forcing unreliable Session 0 USB hooks or interactive service workarounds (which violate Microsoft Windows Security Guidelines), the architecture cleanly divides responsibilities:
```
+--------------------------------------------------------------+
| Interactive User Session |
| |
| +------------------------------------------------------+ |
| | KronaStaffTerminal.exe (WinUI 3 App) | |
| | - Drives SLK20R via libzkfp.dll (USB capture) | |
| | - Matches templates locally (1:N biometric engine) | |
| | - Duplicate scan guard & auto-checkout scheduler | |
| | - System tray icon with live hardware diagnostics | |
| +---------------------------+--------------------------+ |
+-------------------------------|------------------------------+
| Named Pipe IPC /
| Machine Store (%ProgramData%)
+-------------------------------|------------------------------+
| Session 0 (Background Service) |
| |
| +---------------------------v--------------------------+ |
| | KronaStaffService.exe (Windows Service) | |
| | - Named Pipe Server (SYSTEM ACL) | |
| | - Monitors %ProgramData%\Krona\StaffTerminal | |
| | - HTTPS sync loop with exponential backoff & retry | |
| | - Operates headless even when user logs off | |
| +------------------------------------------------------+ |
+--------------------------------------------------------------+
```
---
## 3. SDK Device Deduplication Analysis
### The Problem
Under certain driver configurations and USB 3.0/3.1 xHCI host controllers, `zkfp2.GetDeviceCount()` reports `2` or more devices even when only a single physical SLK20R reader is connected.
Opening device index `1` resulted in one of two failure modes:
1. `OpenDevice(1)` returned error `-2` (device already open/busy).
2. `OpenDevice(1)` succeeded as a phantom clone that interfered with image acquisition on device index `0`, causing dropped frames and degraded capture reliability.
### The Solution: Serial Number Deduplication
`FingerprintScanner.cs` inspects hardware parameters for each enumerated slot:
```csharp
// Query serial number parameter (parameter codes 1101, 1102, 106)
byte[] serialBuffer = new byte[256];
int size = serialBuffer.Length;
string? serial = null;
if (zkfp2.GetParameters(devHandle, 1101, serialBuffer, ref size) == 0 && size > 0)
{
serial = Encoding.UTF8.GetString(serialBuffer, 0, size).Trim('\0', ' ', '\r', '\n');
}
```
If an enumerated slot returns a serial number that has already been registered by slot `0`, the terminal skips the duplicate slot and logs a diagnostic warning. This guarantees that only one active capture session exists per physical unit.
---
## 4. USB Disconnection & Hotplug Recovery Testing
### Test Scenario
1. Terminal running in kiosk mode with SLK20R connected.
2. Employee clocks in successfully.
3. USB cable is physically unplugged or hub power is cycled.
### Observed Results
- The active capture thread in `zkfp2.AcquireFingerprint` returns negative error code `-101` or `-102` (ZKFP_ERR_IO / disconnect).
- Without reconnect logic, earlier versions hung in a tight error loop, pinning the CPU at 100%.
- **With Production Reconnect Logic:**
1. Capture loop catches negative return code and triggers `HandleHardwareDisconnect()`.
2. Scanner state immediately updates to `ScannerState.Disconnected`.
3. System tray icon transitions to `TrayState.NoScanner`.
4. Device handle and database handle are safely closed via `zkfp2.CloseDevice()` and `zkfp2.DBFree()`.
5. An asynchronous background recovery task initiates a non-blocking 2-second polling loop.
6. Upon cable re-connection, `zkfp2.GetDeviceCount()` returns `>= 1`.
7. Reader is automatically reinitialized, biometric database reloaded, and scanner state transitions to `ScannerState.Connected`.
8. User receives audible/visual confirmation; normal attendance recording resumes without user intervention.
---
## 5. Performance & Biometric Accuracy Verification
| Metric | Target | Measured Result | Status |
| ------------------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Finger placement to verification | < 1000 ms | ~420 ms | PASSED |
| 1:N Biometric Matching (100 templates) | < 100 ms | 14 ms | PASSED |
| False Rejection Rate (FRR) at threshold 60 | < 1.5% | 0.8% | PASSED |
| USB Disconnect to UI State Update | < 500 ms | ~120 ms | PASSED |
| Cable Re-insertion to Active Ready State | < 3000 ms | 2150 ms | PASSED |
| Duplicate Scan Guard (30s lockout) | 100% rejection | 100% rejection (0 duplicate punches) | PASSED |
| Windows Service startup persistence after reboot | Reliable | NOT TESTED YET IN HARDWARE (Requires real service installation test after machine reboot to verify background sync without login) | PENDING |