fix: add timeout for characteristic rediscovery to prevent hang
When FFE2 goes missing during writes, the rediscovery path had no timeout — if CoreBluetooth never called back didDiscoverCharacteristics, the app would hang at "Re-discovering characteristics..." indefinitely. Adds a 5-second timeout per rediscovery attempt. If it fires, it either retries (up to MAX_CHAR_REDISCOVERY) or fails with .timeout instead of hanging forever.
This commit is contained in:
parent
813198599a
commit
f0d2b2ae90
1 changed files with 41 additions and 0 deletions
|
|
@ -192,6 +192,8 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
|||
private var writeTimeoutTimer: DispatchWorkItem?
|
||||
private var charRediscoveryCount = 0
|
||||
private static let MAX_CHAR_REDISCOVERY = 2
|
||||
private var charRediscoveryTimer: DispatchWorkItem?
|
||||
private static let CHAR_REDISCOVERY_TIMEOUT: Double = 5.0
|
||||
private static let WRITE_TIMEOUT_SECONDS: Double = 5.0
|
||||
private var writeRetryCount = 0
|
||||
private static let MAX_WRITE_RETRIES = 1 // Retry once per command before failing
|
||||
|
|
@ -331,6 +333,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
|||
private func cleanup() {
|
||||
cancelWriteTimeout()
|
||||
cancelResponseGateTimeout()
|
||||
cancelCharRediscoveryTimeout()
|
||||
awaitingCommandResponse = false
|
||||
peripheral = nil
|
||||
config = nil
|
||||
|
|
@ -594,6 +597,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
|||
DebugLog.shared.log("BLE: FFE2 missing — re-discovering characteristics (attempt \(charRediscoveryCount)/\(BeaconProvisioner.MAX_CHAR_REDISCOVERY))")
|
||||
progress = "Re-discovering characteristics..."
|
||||
state = .discoveringServices
|
||||
scheduleCharRediscoveryTimeout()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
||||
self?.peripheral?.discoverCharacteristics([
|
||||
BeaconProvisioner.DXSMART_NOTIFY_CHAR,
|
||||
|
|
@ -666,6 +670,42 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
|||
writeTimeoutTimer = nil
|
||||
}
|
||||
|
||||
/// Schedule a timeout for characteristic rediscovery.
|
||||
/// If didDiscoverCharacteristicsFor doesn't fire within 5 seconds,
|
||||
/// either retry or fail instead of hanging forever.
|
||||
private func scheduleCharRediscoveryTimeout() {
|
||||
cancelCharRediscoveryTimeout()
|
||||
let timer = DispatchWorkItem { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard self.state == .discoveringServices else { return }
|
||||
|
||||
let attempt = self.charRediscoveryCount
|
||||
DebugLog.shared.log("BLE: Characteristic rediscovery timeout (attempt \(attempt)/\(BeaconProvisioner.MAX_CHAR_REDISCOVERY))")
|
||||
|
||||
if attempt < BeaconProvisioner.MAX_CHAR_REDISCOVERY, let service = self.configService {
|
||||
// Try another rediscovery attempt
|
||||
self.charRediscoveryCount += 1
|
||||
DebugLog.shared.log("BLE: Retrying characteristic rediscovery (attempt \(self.charRediscoveryCount)/\(BeaconProvisioner.MAX_CHAR_REDISCOVERY))")
|
||||
self.scheduleCharRediscoveryTimeout()
|
||||
self.peripheral?.discoverCharacteristics([
|
||||
BeaconProvisioner.DXSMART_NOTIFY_CHAR,
|
||||
BeaconProvisioner.DXSMART_COMMAND_CHAR,
|
||||
BeaconProvisioner.DXSMART_PASSWORD_CHAR
|
||||
], for: service)
|
||||
} else {
|
||||
self.fail("Characteristic rediscovery timed out after \(attempt) attempts", code: .timeout)
|
||||
}
|
||||
}
|
||||
charRediscoveryTimer = timer
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + BeaconProvisioner.CHAR_REDISCOVERY_TIMEOUT, execute: timer)
|
||||
}
|
||||
|
||||
/// Cancel any pending characteristic rediscovery timeout
|
||||
private func cancelCharRediscoveryTimeout() {
|
||||
charRediscoveryTimer?.cancel()
|
||||
charRediscoveryTimer = nil
|
||||
}
|
||||
|
||||
/// Calculate adaptive delay after writing a command.
|
||||
/// Frame selection (0x11-0x16) and type commands (0x61, 0x62) trigger internal
|
||||
/// state changes on the beacon MCU that need extra processing time.
|
||||
|
|
@ -1368,6 +1408,7 @@ extension BeaconProvisioner: CBPeripheralDelegate {
|
|||
exploreNextService()
|
||||
} else if charRediscoveryCount > 0 && dxSmartAuthenticated && !dxSmartCommandQueue.isEmpty {
|
||||
// Rediscovery during active write — resume writing directly (already authenticated)
|
||||
cancelCharRediscoveryTimeout()
|
||||
DebugLog.shared.log("BLE: Characteristics re-discovered after FFE2 miss — resuming write")
|
||||
state = .writing
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue