Compare commits

..

No commits in common. "292821622e35c0b73f4ab98c96feb50e038938a0" and "cabb2825d875fed098c7b1065af07c3c53b53744" have entirely different histories.

2 changed files with 7 additions and 10 deletions

View file

@ -182,7 +182,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
private var disconnectRetryCount = 0 private var disconnectRetryCount = 0
private static let MAX_CONNECTION_RETRIES = 3 private static let MAX_CONNECTION_RETRIES = 3
private static let MAX_DEVICE_INFO_RETRIES = 2 private static let MAX_DEVICE_INFO_RETRIES = 2
private static let MAX_DISCONNECT_RETRIES = 3 private static let MAX_DISCONNECT_RETRIES = 2
private var currentBeacon: DiscoveredBeacon? private var currentBeacon: DiscoveredBeacon?
// Per-write timeout (matches Android's 5-second per-write timeout) // Per-write timeout (matches Android's 5-second per-write timeout)
@ -241,8 +241,8 @@ class BeaconProvisioner: NSObject, ObservableObject {
centralManager.connect(resolvedPeripheral, options: nil) centralManager.connect(resolvedPeripheral, options: nil)
// Timeout after 45 seconds (increased from 30s to accommodate 3 disconnect retries with backoff) // Timeout after 30 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 45) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 30) { [weak self] in
if self?.state != .success && self?.state != .idle { if self?.state != .success && self?.state != .idle {
self?.fail("Connection timeout", code: .connectionTimeout) self?.fail("Connection timeout", code: .connectionTimeout)
} }
@ -1114,9 +1114,6 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
return return
} }
// Cancel any pending write timeout disconnect supersedes it
cancelWriteTimeout()
// Unexpected disconnect during any active provisioning phase retry with full reconnect // Unexpected disconnect during any active provisioning phase retry with full reconnect
let isActivePhase = (state == .discoveringServices || state == .authenticating || state == .writing || state == .verifying) let isActivePhase = (state == .discoveringServices || state == .authenticating || state == .writing || state == .verifying)
if isActivePhase && disconnectRetryCount < BeaconProvisioner.MAX_DISCONNECT_RETRIES { if isActivePhase && disconnectRetryCount < BeaconProvisioner.MAX_DISCONNECT_RETRIES {
@ -1134,7 +1131,7 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
responseBuffer.removeAll() responseBuffer.removeAll()
state = .connecting state = .connecting
let delay = Double(disconnectRetryCount) + 2.0 // 3s, 4s, 5s backoff give BLE time to settle let delay = Double(disconnectRetryCount) + 1.0 // 2s, 3s backoff
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
guard let self = self, let beacon = self.currentBeacon else { return } guard let self = self, let beacon = self.currentBeacon else { return }
guard self.state == .connecting else { return } guard self.state == .connecting else { return }
@ -1148,7 +1145,7 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
// All retries exhausted or disconnect in unexpected state fail // All retries exhausted or disconnect in unexpected state fail
DebugLog.shared.log("BLE: UNEXPECTED disconnect — state=\(state) writeIdx=\(dxSmartWriteIndex) queueCount=\(dxSmartCommandQueue.count) authenticated=\(dxSmartAuthenticated) disconnectRetries=\(disconnectRetryCount)") DebugLog.shared.log("BLE: UNEXPECTED disconnect — state=\(state) writeIdx=\(dxSmartWriteIndex) queueCount=\(dxSmartCommandQueue.count) authenticated=\(dxSmartAuthenticated) disconnectRetries=\(disconnectRetryCount)")
fail("Beacon disconnected \(disconnectRetryCount + 1) times during \(state). Move closer to the beacon and try again.", code: .disconnected) fail("Unexpected disconnect (state: \(state))", code: .disconnected)
} }
} }

View file

@ -385,11 +385,11 @@ struct ScanView: View {
.font(.title3) .font(.title3)
} }
// Provisioning progress show live updates from provisioner // Provisioning progress
if isProvisioning { if isProvisioning {
HStack { HStack {
ProgressView() ProgressView()
Text(provisioner.progress.isEmpty ? provisioningProgress : provisioner.progress) Text(provisioningProgress)
.font(.callout) .font(.callout)
} }
.padding() .padding()