fix: add disconnect detection + drop inter-command delay to 50ms
Two changes: 1. DXSmartProvisioner now registers for BLE disconnect callbacks. Previously if the beacon dropped the link mid-write, the provisioner would sit waiting for ACK timeouts (5s × 2 retries = 10s of dead air). Now it fails immediately with a clear error. 2. Inter-command delay reduced from 150ms → 50ms since beacon handles fast writes fine. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a08d3db893
commit
3c41ecb49d
1 changed files with 45 additions and 2 deletions
|
|
@ -41,6 +41,7 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
private(set) var isConnected = false
|
||||
private(set) var isFlashing = false // Beacon LED flashing after trigger
|
||||
private var useNewSDK = true // Prefer new SDK, fallback to old
|
||||
private var disconnected = false // Set true when BLE link drops unexpectedly
|
||||
var diagnosticLog: ProvisionLog?
|
||||
var bleManager: BLEManager?
|
||||
|
||||
|
|
@ -76,6 +77,14 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
await diagnosticLog?.log("auth", "Authenticating (trigger + password)…")
|
||||
try await authenticate()
|
||||
await diagnosticLog?.log("auth", "Auth complete — SDK: \(useNewSDK ? "new (FFE2)" : "old (FFE1)")")
|
||||
|
||||
// Register for unexpected disconnects so we fail fast instead of
|
||||
// waiting for per-command ACK timeouts (5s × 2 = 10s of dead air).
|
||||
bleManager?.onPeripheralDisconnected = { [weak self] disconnectedPeripheral, error in
|
||||
guard disconnectedPeripheral.identifier == self?.peripheral.identifier else { return }
|
||||
self?.handleUnexpectedDisconnect(error: error)
|
||||
}
|
||||
|
||||
isConnected = true
|
||||
isFlashing = true
|
||||
return
|
||||
|
|
@ -125,6 +134,8 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
}
|
||||
|
||||
func disconnect() {
|
||||
// Unregister disconnect handler so intentional disconnect doesn't trigger error path
|
||||
bleManager?.onPeripheralDisconnected = nil
|
||||
if peripheral.state == .connected || peripheral.state == .connecting {
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
}
|
||||
|
|
@ -171,6 +182,12 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
]
|
||||
|
||||
for (index, (name, packet)) in commands.enumerated() {
|
||||
// Bail immediately if BLE link dropped between commands
|
||||
if disconnected {
|
||||
await diagnosticLog?.log("write", "Aborting — BLE disconnected", isError: true)
|
||||
throw ProvisionError.writeFailed("BLE disconnected during write sequence")
|
||||
}
|
||||
|
||||
await diagnosticLog?.log("write", "[\(index + 1)/\(commands.count)] \(name) (\(packet.count) bytes)")
|
||||
|
||||
// SaveConfig (last command) causes beacon MCU to reboot — it never sends an ACK.
|
||||
|
|
@ -205,8 +222,8 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
throw lastError
|
||||
}
|
||||
|
||||
// 150ms between commands — aggressive speedup (was 300ms, originally 500ms)
|
||||
try await Task.sleep(nanoseconds: 150_000_000)
|
||||
// 50ms between commands — beacon handles fast writes fine (was 150ms, 300ms, 500ms)
|
||||
try await Task.sleep(nanoseconds: 50_000_000)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,6 +299,32 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner {
|
|||
return packet
|
||||
}
|
||||
|
||||
// MARK: - Disconnect Detection
|
||||
|
||||
/// Called when BLE link drops unexpectedly during provisioning.
|
||||
/// Immediately resolves any pending continuations so we fail fast
|
||||
/// instead of waiting for the 5s operationTimeout.
|
||||
private func handleUnexpectedDisconnect(error: Error?) {
|
||||
disconnected = true
|
||||
isConnected = false
|
||||
let disconnectError = ProvisionError.writeFailed("BLE disconnected unexpectedly: \(error?.localizedDescription ?? "unknown")")
|
||||
Task { await diagnosticLog?.log("ble", "⚠️ Unexpected disconnect during provisioning", isError: true) }
|
||||
|
||||
// Cancel any pending write/response continuation immediately
|
||||
if let cont = responseContinuation {
|
||||
responseContinuation = nil
|
||||
cont.resume(throwing: disconnectError)
|
||||
}
|
||||
if let cont = writeContinuation {
|
||||
writeContinuation = nil
|
||||
cont.resume(throwing: disconnectError)
|
||||
}
|
||||
if let cont = connectionContinuation {
|
||||
connectionContinuation = nil
|
||||
cont.resume(throwing: disconnectError)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers
|
||||
|
||||
private func connectOnce() async throws {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue