From 48bb5d591a8b54df27fa5bbe5f74e28c27718168 Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:25:04 +0200 Subject: [PATCH 1/7] [BleManager] fix Future already completed error when an error is thrown in data out stream sub before the end of _negociateAndInitGatt (#182) --- example/pubspec.lock | 2 +- lib/src/ble/ble_manager.dart | 13 +++++++++---- pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 514125aaa..aea266e54 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -326,7 +326,7 @@ packages: path: ".." relative: true source: path - version: "0.2.0" + version: "0.2.1" package_config: dependency: transitive description: diff --git a/lib/src/ble/ble_manager.dart b/lib/src/ble/ble_manager.dart index d8301e800..02014a0e3 100644 --- a/lib/src/ble/ble_manager.dart +++ b/lib/src/ble/ble_manager.dart @@ -122,7 +122,15 @@ abstract class BleManager { _device = discoveredDevice; break; case DeviceConnectionState.connected: - _negotiateAndInitGatt().then((_) => _connectCompleter.complete()).catchError( + _negotiateAndInitGatt().then((_) { + if (!_connectCompleter.isCompleted) { + _connectCompleter.complete(); + if (!_callbacks.onDeviceReadyController.isClosed && + _callbacks.onDeviceReadyController.hasListener) { + _callbacks.onDeviceReadyController.add(_device!); + } + } + }).catchError( (e, s) { if (!_callbacks.onErrorController.isClosed && _callbacks.onErrorController.hasListener) { _callbacks.onErrorController.add(BleManagerCallbacksError(_device, 'GATT error', e)); @@ -208,9 +216,6 @@ abstract class BleManager { mtuSize = negotiatedMtu; } await _callbacks.sendMtuToMeshManagerApi(mtuSize); - if (!_callbacks.onDeviceReadyController.isClosed && _callbacks.onDeviceReadyController.hasListener) { - _callbacks.onDeviceReadyController.add(_device!); - } } catch (e) { _log('caught error during negociation : $e'); throw BleManagerException(BleManagerFailureCode.negociation, '$e'); diff --git a/pubspec.yaml b/pubspec.yaml index 99e4bc51d..575c85b01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: nordic_nrf_mesh description: A Flutter plugin to enable mesh network management and communication using Nordic's SDKs. It also provides the ability to open BLE connection with mesh nodes using some other flutter package. -version: 0.2.0 +version: 0.2.1 environment: sdk: ">=2.12.0 <3.0.0" From bd4c47c314b65b9a020f02254150a6a3c68da5fc Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Mon, 4 Oct 2021 10:32:53 +0200 Subject: [PATCH 2/7] more error codes for provisioning + docs (#183) --- lib/src/ble/ble_scanner.dart | 4 ++-- lib/src/contants.dart | 10 +++++++++- lib/src/nrf_mesh.dart | 2 +- lib/src/utils/provisioning.dart | 9 +++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/src/ble/ble_scanner.dart b/lib/src/ble/ble_scanner.dart index 827b06989..d0a972769 100644 --- a/lib/src/ble/ble_scanner.dart +++ b/lib/src/ble/ble_scanner.dart @@ -96,9 +96,9 @@ class BleScanner { withServices: [forProxy ? meshProxyUuid : meshProvisioningUuid], ).firstWhere((s) => validScanResult(s, uid)).timeout(scanTimeout); } on StateError catch (e) { - debugPrint('[BleScanner] StateError -- no device found with UUID : $uid\n$e\n${e.message}'); + debugPrint('[NordicNrfMesh] StateError -- no device found with UUID : $uid\n$e\n${e.message}'); } on TimeoutException catch (e) { - debugPrint('[BleScanner] TimeoutException -- no device found with UUID : $uid\n$e\n${e.message}'); + debugPrint('[NordicNrfMesh] TimeoutException -- no device found with UUID : $uid\n$e\n${e.message}'); } return result; } diff --git a/lib/src/contants.dart b/lib/src/contants.dart index 16ffe1b33..e9170ffc6 100644 --- a/lib/src/contants.dart +++ b/lib/src/contants.dart @@ -18,7 +18,15 @@ enum ProvisioningFailureCode { nodeComposition, /// when `PROVISIONING_FAILED` event is triggered - mesh, + provisioningFailed, + + /// when the configuration of the n/w is invalid (null network ?) + meshConfiguration, + + /// when provisioning goes timeout + timeout, + + /// unknown error that should be diagnosed unknown, } diff --git a/lib/src/nrf_mesh.dart b/lib/src/nrf_mesh.dart index f4f2d3829..c096a4098 100644 --- a/lib/src/nrf_mesh.dart +++ b/lib/src/nrf_mesh.dart @@ -44,7 +44,7 @@ class NordicNrfMesh { /// /// Returns a [ProvisionedMeshNode] if success. /// - /// Throws an [Exception] if provisioning failed + /// Throws an [NrfMeshProvisioningException] if provisioning failed /// or an [UnsupportedError] if the current OS is not supported. Future provisioning( final MeshManagerApi meshManagerApi, diff --git a/lib/src/utils/provisioning.dart b/lib/src/utils/provisioning.dart index 6658b21ff..a81a55f4d 100644 --- a/lib/src/utils/provisioning.dart +++ b/lib/src/utils/provisioning.dart @@ -79,7 +79,10 @@ Future _provisioning( DiscoveredDevice deviceToProvision, String serviceDataUuid, ProvisioningEvent? events) async { - assert(meshManagerApi.meshNetwork != null, 'You need to load a meshNetwork before being able to provision a device'); + if (meshManagerApi.meshNetwork == null) { + throw NrfMeshProvisioningException(ProvisioningFailureCode.meshConfiguration, + 'You need to load a meshNetwork before being able to provision a device'); + } final completer = Completer(); late final ProvisionedMeshNode provisionedMeshNode; @@ -131,7 +134,7 @@ Future _provisioning( }); onProvisioningFailedSubscription = meshManagerApi.onProvisioningFailed.listen((event) async { completer.completeError(NrfMeshProvisioningException( - ProvisioningFailureCode.mesh, 'Failed to provision device ${deviceToProvision.id}')); + ProvisioningFailureCode.provisioningFailed, 'Failed to provision device ${deviceToProvision.id}')); }); onProvisioningStateChangedSubscription = meshManagerApi.onProvisioningStateChanged.listen((event) async { if (event.state == 'PROVISIONING_CAPABILITIES') { @@ -215,8 +218,10 @@ Future _provisioning( await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); cancelProvisioningCallbackSubscription(bleMeshManager); + _log('provisioning success !'); return provisionedMeshNode; } catch (e) { + _log('caught error during provisioning... $e'); await cancelProvisioning(meshManagerApi, bleScanner, bleMeshManager); if (e is NrfMeshProvisioningException) { rethrow; From 3e34f247072fd956667a94392907253df362b24a Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Tue, 5 Oct 2021 10:30:32 +0200 Subject: [PATCH 3/7] Bug : prov error (#184) * allow to only have onError callback * check if has device disconnected callback --- lib/src/ble/ble_manager.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/ble/ble_manager.dart b/lib/src/ble/ble_manager.dart index 02014a0e3..52105dce5 100644 --- a/lib/src/ble/ble_manager.dart +++ b/lib/src/ble/ble_manager.dart @@ -275,13 +275,17 @@ abstract class BleManager { // error may have been caught upon connection initialization or during connection GenericFailure? maybeError = connectionStateUpdate.failure; // determine if callbacks are set - if (_hasDeviceDisconnectedCallback) { + if (_hasDeviceDisconnectedCallback || _hasErrorCallback) { if (_hasErrorCallback) { // if all callbacks are available, prioritize error callback if any error is given in the event if (maybeError != null) { _callbacks.onErrorController.add(BleManagerCallbacksError(_device, maybeError.message, maybeError)); } else { - _callbacks.onDeviceDisconnectedController.add(connectionStateUpdate); + if (_hasDeviceDisconnectedCallback) { + _callbacks.onDeviceDisconnectedController.add(connectionStateUpdate); + } else { + _log('device has been disconnected ! $connectionStateUpdate'); + } } } else { _callbacks.onDeviceDisconnectedController.add(connectionStateUpdate); From 8bc3e7b7640b57430aaa97c31bd73edd2aeea05e Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Tue, 5 Oct 2021 11:03:13 +0200 Subject: [PATCH 4/7] Feature : prov error code (#185) * add one more error code for provisioning : handling unexpected gatt error out of connection phases * check if completer is completed before propagating gatt error --- lib/src/contants.dart | 3 +++ lib/src/utils/provisioning.dart | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/src/contants.dart b/lib/src/contants.dart index e9170ffc6..d68c3ce12 100644 --- a/lib/src/contants.dart +++ b/lib/src/contants.dart @@ -23,6 +23,9 @@ enum ProvisioningFailureCode { /// when the configuration of the n/w is invalid (null network ?) meshConfiguration, + /// when an unexpected disconnectione event is received with an error + unexpectedGattError, + /// when provisioning goes timeout timeout, diff --git a/lib/src/utils/provisioning.dart b/lib/src/utils/provisioning.dart index a81a55f4d..721562e51 100644 --- a/lib/src/utils/provisioning.dart +++ b/lib/src/utils/provisioning.dart @@ -84,6 +84,7 @@ Future _provisioning( 'You need to load a meshNetwork before being able to provision a device'); } final completer = Completer(); + late bool isHandlingConnectErrors; late final ProvisionedMeshNode provisionedMeshNode; //'Undocumented scan throttle' error caught here @@ -119,7 +120,9 @@ Future _provisioning( events?._provisioningReconnectController.add(null); try { _connectRetryCount = 0; + isHandlingConnectErrors = true; await _connect(bleMeshManager, device!); + isHandlingConnectErrors = false; provisionedMeshNode = ProvisionedMeshNode(event.meshNode!.uuid); } catch (e) { const _msg = 'Error in connection during provisioning process'; @@ -198,7 +201,19 @@ Future _provisioning( await meshManagerApi.handleNotifications(event.mtu, event.pdu); }); onGattErrorSubscription = bleMeshManager.callbacks!.onError.listen((event) { - events?._provisioningGattErrorController.add(event); + _log('received error event : $event'); + if (!isHandlingConnectErrors) { + // if not in a connection phase where auto retry are implemented, we should notify gatt errors + events?._provisioningGattErrorController.add(event); + if (!completer.isCompleted) { + completer.completeError( + NrfMeshProvisioningException( + ProvisioningFailureCode.unexpectedGattError, + 'received a gatt error event outside connection phases', + ), + ); + } + } }); onConfigCompositionDataStatusSubscription = meshManagerApi.onConfigCompositionDataStatus.listen((event) async { events?._onConfigCompositionDataStatusController.add(null); @@ -212,7 +227,9 @@ Future _provisioning( await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); _connectRetryCount = 0; + isHandlingConnectErrors = true; await _connect(bleMeshManager, deviceToProvision); + isHandlingConnectErrors = false; await completer.future; await meshManagerApi.cleanProvisioningData(); await bleMeshManager.refreshDeviceCache(); From 40e2746c5a275f9de58a2a285e46f5aa037100b6 Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Wed, 6 Oct 2021 09:22:52 +0200 Subject: [PATCH 5/7] [BleManager] fix disconnected event tagged as unexpected in global status listener (#186) --- lib/src/ble/ble_manager.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/src/ble/ble_manager.dart b/lib/src/ble/ble_manager.dart index 52105dce5..d04c17f21 100644 --- a/lib/src/ble/ble_manager.dart +++ b/lib/src/ble/ble_manager.dart @@ -146,7 +146,7 @@ abstract class BleManager { ); break; case DeviceConnectionState.disconnecting: - // handled by _deviceStatusStream + // handled by _globalStatusListener break; case DeviceConnectionState.disconnected: if (_device != null) { @@ -162,9 +162,6 @@ abstract class BleManager { _connectCompleter.complete(); } } - if (maybeError == null) { - _device = null; - } } else { _log('seems that you were already connected to that node..' 'ignoring connection state: $connectionStateUpdate'); From 8dbf8c1c4f7153a8ca8daf43a6818393dbfd2668 Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Thu, 14 Oct 2021 12:14:09 +0200 Subject: [PATCH 6/7] Bug : uncaught disconnect (#187) * [BleManager] now always complete error upon disconnected event if _connect completer is not completed yet (init gatt success) * [BleManager] explicit typ for event listener param --- lib/src/ble/ble_manager.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/ble/ble_manager.dart b/lib/src/ble/ble_manager.dart index d04c17f21..3ec100ec9 100644 --- a/lib/src/ble/ble_manager.dart +++ b/lib/src/ble/ble_manager.dart @@ -116,7 +116,7 @@ abstract class BleManager { connectionTimeout: connectionTimeout, ) .listen( - (connectionStateUpdate) async { + (ConnectionStateUpdate connectionStateUpdate) async { switch (connectionStateUpdate.connectionState) { case DeviceConnectionState.connecting: _device = discoveredDevice; @@ -153,13 +153,14 @@ abstract class BleManager { // error may have been caught upon connection initialization or during connection GenericFailure? maybeError = connectionStateUpdate.failure; if (!_connectCompleter.isCompleted) { + // will notify for error as the connection could not be properly established + _log('connect failed after ${watch.elapsedMilliseconds}ms'); if (maybeError != null) { - // will notify for error as the connection could not be properly established - _log('connect failed after ${watch.elapsedMilliseconds}ms'); connectTimeout.cancel(); _connectCompleter.completeError(maybeError); } else { - _connectCompleter.complete(); + connectTimeout.cancel(); + _connectCompleter.completeError('disconnect event before device is ready'); } } } else { @@ -225,7 +226,7 @@ abstract class BleManager { /// This handler will propagate events to any existing callback (if the update is expected). /// /// On confirmed disconnection event, it will reset the [_device] to `null` reflecting the current state of [BleManager] that manage one device at a time. - void _onGlobalStateUpdate(connectionStateUpdate) { + void _onGlobalStateUpdate(ConnectionStateUpdate connectionStateUpdate) { final _callbacks = callbacks; if (_callbacks == null) { _log('no callbacks set...received $connectionStateUpdate'); From d6de41903328e1a76baff0affd529e71a70d6ea5 Mon Sep 17 00:00:00 2001 From: R0m4in-dooz <72380352+R0m4in-dooz@users.noreply.github.com> Date: Thu, 14 Oct 2021 12:29:07 +0200 Subject: [PATCH 7/7] update CHANGELOG.md (#189) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5e712891..60828c1a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.2.1 + +- fix Completer error if error occurs during gatt data out stream subscription +- add more error codes for provisioning failure + update docs +- fix unhandled disconnection event during provisioning +- fix bug in disconnection event handlers + ## 0.2.0 - Work on error handling for BleManager and BleMeshManager. Implement two ways to handle errors for the consumer : either by asynchronous errors, or with dedicated StreamControllers that, if defined, would propagate any error \ No newline at end of file