Skip to content

Commit

Permalink
Release v0.2.1
Browse files Browse the repository at this point in the history
Merge pull request #188 from OZEO-DOOZ/develop
  • Loading branch information
R0m4in-dooz authored Oct 14, 2021
2 parents 101d633 + d6de419 commit ac51dbe
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 24 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.2.0"
version: "0.2.1"
package_config:
dependency: transitive
description:
Expand Down
37 changes: 22 additions & 15 deletions lib/src/ble/ble_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,21 @@ abstract class BleManager<E extends BleManagerCallbacks> {
connectionTimeout: connectionTimeout,
)
.listen(
(connectionStateUpdate) async {
(ConnectionStateUpdate connectionStateUpdate) async {
switch (connectionStateUpdate.connectionState) {
case DeviceConnectionState.connecting:
_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));
Expand All @@ -138,25 +146,23 @@ abstract class BleManager<E extends BleManagerCallbacks> {
);
break;
case DeviceConnectionState.disconnecting:
// handled by _deviceStatusStream
// handled by _globalStatusListener
break;
case DeviceConnectionState.disconnected:
if (_device != null) {
// 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');
}
}
if (maybeError == null) {
_device = null;
}
} else {
_log('seems that you were already connected to that node..'
'ignoring connection state: $connectionStateUpdate');
Expand Down Expand Up @@ -208,9 +214,6 @@ abstract class BleManager<E extends BleManagerCallbacks> {
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');
Expand All @@ -223,7 +226,7 @@ abstract class BleManager<E extends BleManagerCallbacks> {
/// 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');
Expand Down Expand Up @@ -270,13 +273,17 @@ abstract class BleManager<E extends BleManagerCallbacks> {
// 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);
Expand Down
4 changes: 2 additions & 2 deletions lib/src/ble/ble_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
13 changes: 12 additions & 1 deletion lib/src/contants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ enum ProvisioningFailureCode {
nodeComposition,

/// when `PROVISIONING_FAILED` event is triggered
mesh,
provisioningFailed,

/// 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,

/// unknown error that should be diagnosed
unknown,
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/nrf_mesh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<ProvisionedMeshNode> provisioning(
final MeshManagerApi meshManagerApi,
Expand Down
28 changes: 25 additions & 3 deletions lib/src/utils/provisioning.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ Future<ProvisionedMeshNode> _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 bool isHandlingConnectErrors;
late final ProvisionedMeshNode provisionedMeshNode;

//'Undocumented scan throttle' error caught here
Expand Down Expand Up @@ -116,7 +120,9 @@ Future<ProvisionedMeshNode> _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';
Expand All @@ -131,7 +137,7 @@ Future<ProvisionedMeshNode> _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') {
Expand Down Expand Up @@ -195,7 +201,19 @@ Future<ProvisionedMeshNode> _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);
Expand All @@ -209,14 +227,18 @@ Future<ProvisionedMeshNode> _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();
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;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down

0 comments on commit ac51dbe

Please sign in to comment.