Compare commits
4 commits
a1d3b0f457
...
720c560760
| Author | SHA1 | Date | |
|---|---|---|---|
| 720c560760 | |||
| 07c5a22315 | |||
| 64e3684209 | |||
| 3d56a1e31d |
1 changed files with 52 additions and 8 deletions
|
|
@ -157,6 +157,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
private var dxSmartWriteIndex = 0
|
private var dxSmartWriteIndex = 0
|
||||||
private var provisioningMacAddress: String?
|
private var provisioningMacAddress: String?
|
||||||
private var awaitingDeviceInfoForProvisioning = false
|
private var awaitingDeviceInfoForProvisioning = false
|
||||||
|
private var skipDeviceInfoRead = false // set after disconnect during device info — skip MAC read on reconnect
|
||||||
private var isTerminating = false // guards against re-entrant disconnect handling
|
private var isTerminating = false // guards against re-entrant disconnect handling
|
||||||
|
|
||||||
// Read config mode
|
// Read config mode
|
||||||
|
|
@ -177,7 +178,9 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
|
|
||||||
// Connection retry state
|
// Connection retry state
|
||||||
private var connectionRetryCount = 0
|
private var connectionRetryCount = 0
|
||||||
|
private var deviceInfoRetryCount = 0
|
||||||
private static let MAX_CONNECTION_RETRIES = 3
|
private static let MAX_CONNECTION_RETRIES = 3
|
||||||
|
private static let MAX_DEVICE_INFO_RETRIES = 2
|
||||||
private var currentBeacon: DiscoveredBeacon?
|
private var currentBeacon: DiscoveredBeacon?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
|
@ -215,8 +218,10 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
self.dxSmartWriteIndex = 0
|
self.dxSmartWriteIndex = 0
|
||||||
self.provisioningMacAddress = nil
|
self.provisioningMacAddress = nil
|
||||||
self.awaitingDeviceInfoForProvisioning = false
|
self.awaitingDeviceInfoForProvisioning = false
|
||||||
|
self.skipDeviceInfoRead = false
|
||||||
self.isTerminating = false
|
self.isTerminating = false
|
||||||
self.connectionRetryCount = 0
|
self.connectionRetryCount = 0
|
||||||
|
self.deviceInfoRetryCount = 0
|
||||||
self.currentBeacon = beacon
|
self.currentBeacon = beacon
|
||||||
|
|
||||||
state = .connecting
|
state = .connecting
|
||||||
|
|
@ -264,6 +269,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
self.dxReadQueryIndex = 0
|
self.dxReadQueryIndex = 0
|
||||||
self.allDiscoveredServices.removeAll()
|
self.allDiscoveredServices.removeAll()
|
||||||
self.connectionRetryCount = 0
|
self.connectionRetryCount = 0
|
||||||
|
self.deviceInfoRetryCount = 0
|
||||||
self.isTerminating = false
|
self.isTerminating = false
|
||||||
self.currentBeacon = beacon
|
self.currentBeacon = beacon
|
||||||
self.servicesToExplore.removeAll()
|
self.servicesToExplore.removeAll()
|
||||||
|
|
@ -298,8 +304,10 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
dxSmartWriteIndex = 0
|
dxSmartWriteIndex = 0
|
||||||
provisioningMacAddress = nil
|
provisioningMacAddress = nil
|
||||||
awaitingDeviceInfoForProvisioning = false
|
awaitingDeviceInfoForProvisioning = false
|
||||||
|
skipDeviceInfoRead = false
|
||||||
isTerminating = false
|
isTerminating = false
|
||||||
connectionRetryCount = 0
|
connectionRetryCount = 0
|
||||||
|
deviceInfoRetryCount = 0
|
||||||
currentBeacon = nil
|
currentBeacon = nil
|
||||||
state = .idle
|
state = .idle
|
||||||
progress = ""
|
progress = ""
|
||||||
|
|
@ -406,6 +414,14 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
|
|
||||||
/// Read device info (MAC address) before writing config
|
/// Read device info (MAC address) before writing config
|
||||||
private func dxSmartReadDeviceInfoBeforeWrite() {
|
private func dxSmartReadDeviceInfoBeforeWrite() {
|
||||||
|
// If we previously disconnected during device info read, skip it entirely
|
||||||
|
if skipDeviceInfoRead {
|
||||||
|
DebugLog.shared.log("BLE: Skipping device info read (reconnect after previous disconnect)")
|
||||||
|
skipDeviceInfoRead = false
|
||||||
|
dxSmartWriteConfig()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
guard let commandChar = characteristics[BeaconProvisioner.DXSMART_COMMAND_CHAR] else {
|
guard let commandChar = characteristics[BeaconProvisioner.DXSMART_COMMAND_CHAR] else {
|
||||||
DebugLog.shared.log("BLE: FFE2 not found, proceeding without MAC")
|
DebugLog.shared.log("BLE: FFE2 not found, proceeding without MAC")
|
||||||
dxSmartWriteConfig()
|
dxSmartWriteConfig()
|
||||||
|
|
@ -894,6 +910,7 @@ class BeaconProvisioner: NSObject, ObservableObject {
|
||||||
configService = nil
|
configService = nil
|
||||||
characteristics.removeAll()
|
characteristics.removeAll()
|
||||||
connectionRetryCount = 0
|
connectionRetryCount = 0
|
||||||
|
deviceInfoRetryCount = 0
|
||||||
currentBeacon = nil
|
currentBeacon = nil
|
||||||
operationMode = .provisioning
|
operationMode = .provisioning
|
||||||
state = .idle
|
state = .idle
|
||||||
|
|
@ -1012,16 +1029,43 @@ extension BeaconProvisioner: CBCentralManagerDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect during device info read (post-auth, pre-write) — beacon may have
|
// Disconnect during device info read (post-auth, pre-write) — beacon dropped
|
||||||
// dropped the connection during the MAC address query. We authenticated but
|
// connection during the optional MAC address query. Instead of failing, reconnect
|
||||||
// lost connection before writing config, so this is a failure — but a known
|
// and skip the device info step (MAC is nice-to-have, not required).
|
||||||
// one with a clear retry path, not an unexplained "Unexpected disconnect".
|
|
||||||
if state == .authenticating && awaitingDeviceInfoForProvisioning && dxSmartAuthenticated {
|
if state == .authenticating && awaitingDeviceInfoForProvisioning && dxSmartAuthenticated {
|
||||||
DebugLog.shared.log("BLE: Disconnect during device info read (post-auth) — connection lost before config write, failing with retry prompt")
|
|
||||||
awaitingDeviceInfoForProvisioning = false
|
awaitingDeviceInfoForProvisioning = false
|
||||||
// Connection lost — can't write config without it, fail with specific message
|
skipDeviceInfoRead = true // on reconnect, go straight to config write
|
||||||
fail("Disconnected after auth during device info read — please retry", code: .disconnected)
|
|
||||||
|
if deviceInfoRetryCount < BeaconProvisioner.MAX_DEVICE_INFO_RETRIES {
|
||||||
|
deviceInfoRetryCount += 1
|
||||||
|
let delay = Double(deviceInfoRetryCount)
|
||||||
|
progress = "Reconnecting (skip MAC read)..."
|
||||||
|
DebugLog.shared.log("BLE: Disconnect during device info read — reconnecting (\(deviceInfoRetryCount)/\(BeaconProvisioner.MAX_DEVICE_INFO_RETRIES)), will skip MAC read")
|
||||||
|
|
||||||
|
// Reset BLE state for reconnect
|
||||||
|
dxSmartAuthenticated = false
|
||||||
|
dxSmartNotifySubscribed = false
|
||||||
|
dxSmartCommandQueue.removeAll()
|
||||||
|
dxSmartWriteIndex = 0
|
||||||
|
characteristics.removeAll()
|
||||||
|
responseBuffer.removeAll()
|
||||||
|
state = .connecting
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
|
||||||
|
guard let self = self, let beacon = self.currentBeacon else { return }
|
||||||
|
guard self.state == .connecting else { return }
|
||||||
|
|
||||||
|
let resolvedPeripheral = self.resolvePeripheral(beacon)
|
||||||
|
self.peripheral = resolvedPeripheral
|
||||||
|
resolvedPeripheral.delegate = self
|
||||||
|
self.centralManager.connect(resolvedPeripheral, options: nil)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
DebugLog.shared.log("BLE: Disconnect during device info read — max device info retries exhausted (\(deviceInfoRetryCount)/\(BeaconProvisioner.MAX_DEVICE_INFO_RETRIES))")
|
||||||
|
fail("Disconnected while reading device information (retries exhausted)", code: .disconnected)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other disconnects are unexpected
|
// All other disconnects are unexpected
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue