From 242744aaa7fcdf281a5bd68eb6ce63055d4500c4 Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Thu, 8 Oct 2020 14:58:49 -0400 Subject: [PATCH 01/10] Remove wallets --- mobile/lib/src/app.dart | 2 - .../application/wallets/wallets_cubit.dart | 63 ------------------- .../application/wallets/wallets_state.dart | 25 -------- mobile/lib/src/domain/wallet/constants.dart | 1 - .../domain/wallet/i_wallet_repository.dart | 6 -- mobile/lib/src/domain/wallet/money.dart | 57 ----------------- mobile/lib/src/domain/wallet/owner.dart | 32 ---------- mobile/lib/src/domain/wallet/wallet.dart | 35 ----------- .../mock_wallet_repository.dart | 22 ------- mobile/lib/src/locator.dart | 10 --- .../presentation/wallets/wallet_widget.dart | 23 ------- .../presentation/wallets/wallets_route.dart | 62 ------------------ .../presentation/wallets/wallets_widget.dart | 32 ---------- 13 files changed, 370 deletions(-) delete mode 100644 mobile/lib/src/application/wallets/wallets_cubit.dart delete mode 100644 mobile/lib/src/application/wallets/wallets_state.dart delete mode 100644 mobile/lib/src/domain/wallet/constants.dart delete mode 100644 mobile/lib/src/domain/wallet/i_wallet_repository.dart delete mode 100644 mobile/lib/src/domain/wallet/money.dart delete mode 100644 mobile/lib/src/domain/wallet/owner.dart delete mode 100644 mobile/lib/src/domain/wallet/wallet.dart delete mode 100644 mobile/lib/src/infrastructure/mock_wallet_repository.dart delete mode 100644 mobile/lib/src/presentation/wallets/wallet_widget.dart delete mode 100644 mobile/lib/src/presentation/wallets/wallets_route.dart delete mode 100644 mobile/lib/src/presentation/wallets/wallets_widget.dart diff --git a/mobile/lib/src/app.dart b/mobile/lib/src/app.dart index f8056bce..ccc477e3 100644 --- a/mobile/lib/src/app.dart +++ b/mobile/lib/src/app.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:polydodo/src/presentation/bluetooth_route/bluetoothSelector_route.dart'; -import 'package:polydodo/src/presentation/wallets/wallets_route.dart'; import 'locator.dart'; import 'theme.dart'; @@ -21,7 +20,6 @@ class App extends StatelessWidget { theme: theme, initialRoute: BluetoothSelectorRoute.name, routes: { - WalletsRoute.name: (context) => WalletsRoute(), BluetoothSelectorRoute.name: (context) => BluetoothSelectorRoute(), }, ), diff --git a/mobile/lib/src/application/wallets/wallets_cubit.dart b/mobile/lib/src/application/wallets/wallets_cubit.dart deleted file mode 100644 index 6ab7d3e3..00000000 --- a/mobile/lib/src/application/wallets/wallets_cubit.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'dart:math'; - -import 'package:bloc/bloc.dart'; - -import 'package:polydodo/src/application/wallets/wallets_state.dart'; -import 'package:polydodo/src/domain/unique_id.dart'; -import 'package:polydodo/src/domain/wallet/i_wallet_repository.dart'; -import 'package:polydodo/src/domain/wallet/money.dart'; -import 'package:polydodo/src/domain/wallet/owner.dart'; -import 'package:polydodo/src/domain/wallet/wallet.dart'; - -class WalletsCubit extends Cubit { - final IWalletRepository _walletRepository; - - WalletsCubit(this._walletRepository) : super(WalletsInitial()) { - createNewWallets(); - emit(WalletsLoadInProgress()); - _walletRepository - .watch() - .listen((wallets) => emit(WalletsLoadSuccess(wallets))) - .onError((e) => emit(WalletsLoadFailure(e))); - } - - // Does not yield another state - void transfer(Wallet sender, Wallet receiver, Money amount) async { - try { - sender.transfer(amount, receiver); - } catch (e) { - emit(WalletsTransferFailure(e)); - } - await Future.wait([ - _walletRepository.store(sender), - _walletRepository.store(receiver), - ]).catchError((e) => emit(WalletsTransferFailure(e))); - } - - Future> createNewWallets() => Future.wait([ - _walletRepository.store( - Wallet( - UniqueId(), - Owner( - id: UniqueId.from('1'), - firstName: 'Bob', - lastName: 'Skiridovsky', - age: 25, - ), - Money(Random().nextDouble() * 10, Currency.cad), - ), - ), - _walletRepository.store( - Wallet( - UniqueId(), - Owner( - id: UniqueId.from('2'), - firstName: 'Alice', - lastName: 'Pogo', - age: 35, - ), - Money(Random().nextDouble() * 1000, Currency.cad), - ), - ) - ]); -} diff --git a/mobile/lib/src/application/wallets/wallets_state.dart b/mobile/lib/src/application/wallets/wallets_state.dart deleted file mode 100644 index 01788a39..00000000 --- a/mobile/lib/src/application/wallets/wallets_state.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:polydodo/src/domain/wallet/wallet.dart'; - -abstract class WalletsState {} - -class WalletsInitial extends WalletsState {} - -class WalletsLoadInProgress extends WalletsState {} - -class WalletsLoadSuccess extends WalletsState { - final List wallets; - - WalletsLoadSuccess(this.wallets); -} - -class WalletsLoadFailure extends WalletsState { - final Exception cause; - - WalletsLoadFailure(this.cause); -} - -class WalletsTransferFailure extends WalletsState { - final Exception cause; - - WalletsTransferFailure(this.cause); -} diff --git a/mobile/lib/src/domain/wallet/constants.dart b/mobile/lib/src/domain/wallet/constants.dart deleted file mode 100644 index 595ccd29..00000000 --- a/mobile/lib/src/domain/wallet/constants.dart +++ /dev/null @@ -1 +0,0 @@ -const cadToUsdRate = 1.32; diff --git a/mobile/lib/src/domain/wallet/i_wallet_repository.dart b/mobile/lib/src/domain/wallet/i_wallet_repository.dart deleted file mode 100644 index d1f87cff..00000000 --- a/mobile/lib/src/domain/wallet/i_wallet_repository.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'wallet.dart'; - -abstract class IWalletRepository { - Future store(Wallet wallet); - Stream> watch(); -} diff --git a/mobile/lib/src/domain/wallet/money.dart b/mobile/lib/src/domain/wallet/money.dart deleted file mode 100644 index 1f760cea..00000000 --- a/mobile/lib/src/domain/wallet/money.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:equatable/equatable.dart'; - -import 'constants.dart'; - -enum Currency { usd, cad } - -Map currencyToString = { - Currency.usd: 'USD', - Currency.cad: 'CAD', -}; - -// Value object usually extends equatable -class Money extends Equatable { - static const _min = 0; - - final double value; - final Currency currency; - - Money(this.value, this.currency) - : assert(value != null), - assert(currency != null) { - // Validation that money cannot be in an impossible state - if (value <= _min) { - Exception('Money amount cannot be inferior to $_min'); - } - } - - double get usd { - if (currency == Currency.cad) { - return value / cadToUsdRate; - } - return value; - } - - double get cad { - if (currency == Currency.usd) { - return value * cadToUsdRate; - } - return value; - } - - Money convertTo(Currency currency) { - if (currency == Currency.usd) { - return Money(usd, currency); - } - return Money(cad, currency); - } - - /// Gives back result into left operand's currency - Money operator +(Money other) => - Money(this.usd + other.usd, Currency.usd).convertTo(this.currency); - Money operator -(Money other) => - Money(this.usd - other.usd, Currency.usd).convertTo(this.currency); - - @override - List get props => [usd]; -} diff --git a/mobile/lib/src/domain/wallet/owner.dart b/mobile/lib/src/domain/wallet/owner.dart deleted file mode 100644 index a1360dbf..00000000 --- a/mobile/lib/src/domain/wallet/owner.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:meta/meta.dart'; - -import 'package:polydodo/src/domain/unique_id.dart'; - -class Owner { - static const _legalAge = 16; - - final UniqueId id; - final String firstName; - final String lastName; - final int age; - - Owner({ - @required this.id, - @required this.firstName, - @required this.lastName, - @required this.age, - }) : assert(id != null), - assert(firstName != null), - assert(lastName != null) { - if (firstName.length <= 0 || lastName.length <= 0) { - throw Exception('First and last name cannot be empty'); - } - if (!_hasLegalAge()) { - throw Exception('This owner does not have the legal age'); - } - } - - bool _hasLegalAge() => age >= _legalAge; - - // In a good model entity, we should have behavorial method... -} diff --git a/mobile/lib/src/domain/wallet/wallet.dart b/mobile/lib/src/domain/wallet/wallet.dart deleted file mode 100644 index 0c1dffcf..00000000 --- a/mobile/lib/src/domain/wallet/wallet.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:polydodo/src/domain/unique_id.dart'; - -import 'money.dart'; -import 'owner.dart'; - -// We call Wallet an aggregate root because it own other entities -class Wallet { - final UniqueId id; - final Owner owner; - Money money; - - Wallet(this.id, this.owner, this.money); - - // Wallet is an entity and therefore it must have a behavior. It is here that - // business logic appears. - void transfer(Money amount, Wallet receiver) { - if (money.usd - amount.usd < 0) { - throw Exception( - 'Not enough money in this wallet to perform this transaction', - ); - } - money -= amount; - receiver._receive(amount); - } - - void _receive(Money amount) { - money += amount; - } - - @override - bool operator ==(other) => this.id == other.id; - - @override - int get hashCode => super.hashCode; -} diff --git a/mobile/lib/src/infrastructure/mock_wallet_repository.dart b/mobile/lib/src/infrastructure/mock_wallet_repository.dart deleted file mode 100644 index 3e8cc516..00000000 --- a/mobile/lib/src/infrastructure/mock_wallet_repository.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:async'; - -import 'package:polydodo/src/domain/wallet/i_wallet_repository.dart'; -import 'package:polydodo/src/domain/wallet/wallet.dart'; - -class MockWalletRepository implements IWalletRepository { - static List walletsMockPersistency = []; - final streamController = StreamController>(); - - @override - Future store(Wallet wallet) async { - await Future.delayed(Duration(milliseconds: 400)); - final idx = walletsMockPersistency.indexOf(wallet); - idx == -1 - ? walletsMockPersistency.add(wallet) - : walletsMockPersistency[idx] = wallet; - streamController.add(walletsMockPersistency); - } - - @override - Stream> watch() => streamController.stream; -} diff --git a/mobile/lib/src/locator.dart b/mobile/lib/src/locator.dart index 560237f3..8d521dae 100644 --- a/mobile/lib/src/locator.dart +++ b/mobile/lib/src/locator.dart @@ -1,14 +1,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_blue/flutter_blue.dart'; import 'package:get_it/get_it.dart'; import 'package:polydodo/src/application/device/device_selector_cubit.dart'; import 'package:polydodo/src/application/eeg_data/data_cubit.dart'; import 'package:polydodo/src/domain/acquisition_device/i_acquisition_device_repository.dart'; -import 'package:polydodo/src/infrastructure/mock_wallet_repository.dart'; -import 'application/wallets/wallets_cubit.dart'; import 'domain/eeg_data/i_eeg_data_repository.dart'; -import 'domain/wallet/i_wallet_repository.dart'; import 'infrastructure/bluetooth_repository.dart'; import 'infrastructure/eeg_data_repository.dart'; @@ -16,7 +12,6 @@ import 'infrastructure/eeg_data_repository.dart'; final _serviceLocator = GetIt.asNewInstance(); void registerServices() { - _serviceLocator.registerSingleton(MockWalletRepository()); _serviceLocator .registerSingleton(BluetoothRepository()); _serviceLocator.registerSingleton(EEGDataRepository()); @@ -24,11 +19,6 @@ void registerServices() { /// This function creates all the BlocProviders used in this app List createBlocProviders() => [ - BlocProvider( - create: (context) => WalletsCubit( - _serviceLocator.get(), - ), - ), BlocProvider( create: (context) => DeviceSelectorCubit( _serviceLocator.get(), diff --git a/mobile/lib/src/presentation/wallets/wallet_widget.dart b/mobile/lib/src/presentation/wallets/wallet_widget.dart deleted file mode 100644 index 426e3dc6..00000000 --- a/mobile/lib/src/presentation/wallets/wallet_widget.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:polydodo/src/domain/wallet/money.dart'; -import 'package:polydodo/src/domain/wallet/wallet.dart'; - -class WalletWidget extends StatelessWidget { - final Wallet wallet; - - const WalletWidget({Key key, @required this.wallet}) : super(key: key); - - @override - Widget build(BuildContext context) { - final currency = currencyToString[wallet.money.currency]; - return Container( - child: Column( - children: [ - Text('${wallet.owner.firstName} ${wallet.owner.lastName}'), - Text('ID: ${wallet.owner.id.get()}'), - Text('${wallet.money.value.toStringAsFixed(2)} $currency'), - ], - ), - ); - } -} diff --git a/mobile/lib/src/presentation/wallets/wallets_route.dart b/mobile/lib/src/presentation/wallets/wallets_route.dart deleted file mode 100644 index 842784d5..00000000 --- a/mobile/lib/src/presentation/wallets/wallets_route.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:polydodo/src/application/wallets/wallets_cubit.dart'; -import 'package:polydodo/src/application/wallets/wallets_state.dart'; -import 'package:polydodo/src/domain/wallet/constants.dart'; -import 'package:polydodo/src/domain/wallet/money.dart'; - -import 'wallets_widget.dart'; - -class WalletsRoute extends StatelessWidget { - static const name = 'walletsRoute'; - - WalletsRoute({Key key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text('Polydodo')), - body: BlocConsumer( - listener: (context, state) { - print(state.runtimeType); - if (state is WalletsLoadFailure) { - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('Unable to load Wallets because ${state.cause}'), - )); - } else if (state is WalletsTransferFailure) { - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('Unable to transfer money because ${state.cause}'), - )); - } - }, - builder: (context, state) { - return Center( - child: Column( - children: [ - Text('CAD to USD Rate: $cadToUsdRate'), - WalletsWidget(state: state), - if (state is WalletsLoadSuccess) - _buildTransferButton(context, state), - ], - ), - ); - }, - ), - ); - } - - Widget _buildTransferButton(BuildContext context, WalletsLoadSuccess state) { - final sender = state.wallets[0]; - final receiver = state.wallets[1]; - return RaisedButton( - child: Text( - 'Send 1 USD from ${sender.owner.firstName} to ${receiver.owner.firstName}', - ), - onPressed: () => BlocProvider.of(context).transfer( - sender, - receiver, - Money(1, Currency.usd), - ), - ); - } -} diff --git a/mobile/lib/src/presentation/wallets/wallets_widget.dart b/mobile/lib/src/presentation/wallets/wallets_widget.dart deleted file mode 100644 index 3e8b643d..00000000 --- a/mobile/lib/src/presentation/wallets/wallets_widget.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:polydodo/src/application/wallets/wallets_state.dart'; - -import 'wallet_widget.dart'; - -class WalletsWidget extends StatelessWidget { - final WalletsState state; - - const WalletsWidget({Key key, @required this.state}) : super(key: key); - - @override - Widget build(BuildContext context) { - if (state is WalletsLoadSuccess) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: (state as WalletsLoadSuccess) - .wallets - .map((wallet) => Container( - child: WalletWidget(wallet: wallet), - margin: EdgeInsets.all(24), - )) - .toList(), - ); - } - - return SizedBox( - child: CircularProgressIndicator(), - height: 50, - width: 50, - ); - } -} From 0d135c2e5890a7fdd7551399fad4ff2b511bff91 Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Thu, 8 Oct 2020 15:12:46 -0400 Subject: [PATCH 02/10] inherit from entity --- .../domain/acquisition_device/acquisition_device.dart | 3 --- mobile/lib/src/domain/eeg_data/eeg_data.dart | 11 +++++------ mobile/lib/src/domain/entity.dart | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/mobile/lib/src/domain/acquisition_device/acquisition_device.dart b/mobile/lib/src/domain/acquisition_device/acquisition_device.dart index f15efc14..9721ec3b 100644 --- a/mobile/lib/src/domain/acquisition_device/acquisition_device.dart +++ b/mobile/lib/src/domain/acquisition_device/acquisition_device.dart @@ -1,8 +1,5 @@ -import 'package:equatable/equatable.dart'; import 'package:polydodo/src/domain/entity.dart'; -import '../unique_id.dart'; - class AcquisitionDevice extends Entity { final String name; diff --git a/mobile/lib/src/domain/eeg_data/eeg_data.dart b/mobile/lib/src/domain/eeg_data/eeg_data.dart index 4f6b38ab..6ba58bbf 100644 --- a/mobile/lib/src/domain/eeg_data/eeg_data.dart +++ b/mobile/lib/src/domain/eeg_data/eeg_data.dart @@ -1,14 +1,13 @@ // EEGData can be extended later to add our metrics -import '../unique_id.dart'; +import 'package:polydodo/src/domain/entity.dart'; -class EEGData { - UniqueId id; +class EEGData extends Entity { List _values; int sampleCounter = 0; - EEGData(this.id, this._values) - : assert(id != null), - assert(_values != null); + EEGData(id, this._values) + : assert(_values != null), + super(id); List get values => _values; diff --git a/mobile/lib/src/domain/entity.dart b/mobile/lib/src/domain/entity.dart index 844514b4..6b250817 100644 --- a/mobile/lib/src/domain/entity.dart +++ b/mobile/lib/src/domain/entity.dart @@ -4,7 +4,7 @@ import 'package:polydodo/src/domain/unique_id.dart'; abstract class Entity extends Equatable { final UniqueId id; - Entity(this.id); + Entity(this.id) : assert(id != null); @override List get props => [id]; From 1a3036dedc7eb4c518be8e2bea4654299b6b9831 Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Thu, 8 Oct 2020 16:14:07 -0400 Subject: [PATCH 03/10] Normalize await usage --- .../device/device_selector_cubit.dart | 16 +++--- .../src/application/eeg_data/data_cubit.dart | 7 ++- .../infrastructure/bluetooth_repository.dart | 50 +++++++++---------- .../infrastructure/eeg_data_repository.dart | 4 +- .../src/infrastructure/serial_repository.dart | 1 + 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/mobile/lib/src/application/device/device_selector_cubit.dart b/mobile/lib/src/application/device/device_selector_cubit.dart index 6d876880..02a657c2 100644 --- a/mobile/lib/src/application/device/device_selector_cubit.dart +++ b/mobile/lib/src/application/device/device_selector_cubit.dart @@ -27,15 +27,17 @@ class DeviceSelectorCubit extends Cubit { } } - void connect(AcquisitionDevice device) async { + Future connect(AcquisitionDevice device) async { emit(DeviceConnectionInProgress()); - _deviceRepository.connect(device).then( - (value) => { - _acquisitionDeviceStream.cancel(), - emit(DeviceConnectionSuccess()) - }, - onError: (e) => {emit(DeviceConnectionFailure(e)), resetSearch()}); + try { + await _deviceRepository.connect(device); + _acquisitionDeviceStream.cancel(); + emit(DeviceConnectionSuccess()); + } catch (e) { + emit(DeviceConnectionFailure(e)); + resetSearch(); + } } void resetSearch() { diff --git a/mobile/lib/src/application/eeg_data/data_cubit.dart b/mobile/lib/src/application/eeg_data/data_cubit.dart index 5f06fa8f..3f69dbce 100644 --- a/mobile/lib/src/application/eeg_data/data_cubit.dart +++ b/mobile/lib/src/application/eeg_data/data_cubit.dart @@ -12,11 +12,10 @@ class DataCubit extends Cubit { DataCubit(this._deviceRepository, this._eegDataRepository) : super(DataStateInitial()); - void startStreaming() { + Future startStreaming() async { emit(DataStateRecording()); - _deviceRepository - .startDataStream() - .then((stream) => _eegDataRepository.createRecordingFromStream(stream)); + _eegDataRepository + .createRecordingFromStream(await _deviceRepository.startDataStream()); } void stopStreaming() { diff --git a/mobile/lib/src/infrastructure/bluetooth_repository.dart b/mobile/lib/src/infrastructure/bluetooth_repository.dart index cc087ab3..371a2670 100644 --- a/mobile/lib/src/infrastructure/bluetooth_repository.dart +++ b/mobile/lib/src/infrastructure/bluetooth_repository.dart @@ -76,46 +76,42 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { flutterBlue.stopScan(); try { - await _selectedDevice - .connect() - .then((value) => findRelevantCharacteristics()) - .timeout(Duration(seconds: 6), - onTimeout: () => - {disconnect(), throw Exception("Connection Timed out")}); + await _selectedDevice.connect().timeout(Duration(seconds: 6), + onTimeout: () => + {disconnect(), throw Exception("Connection Timed out")}); + + await findRelevantCharacteristics(); } catch (e) { if (e is PlatformException) { if (e.code != "already_connected") throw Exception(e.details); } else throw e; } - - return; } - void disconnect() async { + Future disconnect() async { if (_selectedDevice != null) { await _selectedDevice.disconnect(); _selectedDevice = null; } } - void findRelevantCharacteristics() { - _selectedDevice.discoverServices().then((services) => { - for (BluetoothCharacteristic characteristic in (services.where( - (service) => service.uuid.toString().contains(BLE_SERVICE))) - .first - .characteristics) - { - if (characteristic.uuid.toString().contains(BLE_RECEIVE)) - {_receiveCharacteristic = characteristic} - else if (characteristic.uuid.toString().contains(BLE_SEND)) - {_sendCharacteristic = characteristic} - }, - if (_receiveCharacteristic == null) - throw Exception('Device is missing receive Characteristic'), - if (_sendCharacteristic == null) - throw Exception('Device is missing send Characteristic') - }); + Future findRelevantCharacteristics() async { + var characteristics = (await _selectedDevice.discoverServices()) + .firstWhere((service) => service.uuid.toString().contains(BLE_SERVICE)) + .characteristics; + for (BluetoothCharacteristic characteristic in characteristics) { + if (characteristic.uuid.toString().contains(BLE_RECEIVE)) { + _receiveCharacteristic = characteristic; + } else if (characteristic.uuid.toString().contains(BLE_SEND)) { + _sendCharacteristic = characteristic; + } + } + + if (_receiveCharacteristic == null) + throw Exception('Device is missing receive Characteristic'); + if (_sendCharacteristic == null) + throw Exception('Device is missing send Characteristic'); } Future>> startDataStream() async { @@ -125,7 +121,7 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { return _receiveCharacteristic.value; } - void stopDataStream() async { + Future stopDataStream() async { await _receiveCharacteristic.setNotifyValue(false); await _sendCharacteristic.write(stopStreamChar.codeUnits); } diff --git a/mobile/lib/src/infrastructure/eeg_data_repository.dart b/mobile/lib/src/infrastructure/eeg_data_repository.dart index 0eabb1bd..7e29ca01 100644 --- a/mobile/lib/src/infrastructure/eeg_data_repository.dart +++ b/mobile/lib/src/infrastructure/eeg_data_repository.dart @@ -28,7 +28,7 @@ class EEGDataRepository implements IEEGDataRepository { } } - void stopRecordingFromStream() async { + Future stopRecordingFromStream() async { // todo: move save future to another file final directory = await getExternalStorageDirectory(); final pathOfTheFileToWrite = @@ -45,7 +45,7 @@ class EEGDataRepository implements IEEGDataRepository { void importData() {} void exportData() {} - void addData(List event) async { + Future addData(List event) async { //print("Lost packets: " + packetLoss.toString()); //print("Total packets: " + totalPackets.toString()); //print("Lost percentage: " + diff --git a/mobile/lib/src/infrastructure/serial_repository.dart b/mobile/lib/src/infrastructure/serial_repository.dart index e69de29b..8b137891 100644 --- a/mobile/lib/src/infrastructure/serial_repository.dart +++ b/mobile/lib/src/infrastructure/serial_repository.dart @@ -0,0 +1 @@ + From 3d62b65eaee675690465f5805ae7eac37512a403 Mon Sep 17 00:00:00 2001 From: MouradLachhab Date: Sat, 10 Oct 2020 17:12:11 -0400 Subject: [PATCH 04/10] Switched to flutter_reactive_ble for less packet loss (clean needs to be cleaned) --- mobile/android/app/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 2 +- mobile/lib/src/domain/eeg_data/eeg_data.dart | 3 +- .../infrastructure/bluetooth_repository.dart | 111 +++++++++--------- .../infrastructure/eeg_data_repository.dart | 70 +++++------ mobile/pubspec.lock | 34 ++++-- mobile/pubspec.yaml | 2 +- 7 files changed, 119 insertions(+), 105 deletions(-) diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 4e62f489..6795bb69 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -38,7 +38,7 @@ android { defaultConfig { applicationId "com.PolyCortex.Polydodo" - minSdkVersion 19 + minSdkVersion 24 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 98c8e964..064e835c 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -46,6 +46,6 @@ - + diff --git a/mobile/lib/src/domain/eeg_data/eeg_data.dart b/mobile/lib/src/domain/eeg_data/eeg_data.dart index 6ba58bbf..b90928c7 100644 --- a/mobile/lib/src/domain/eeg_data/eeg_data.dart +++ b/mobile/lib/src/domain/eeg_data/eeg_data.dart @@ -2,8 +2,7 @@ import 'package:polydodo/src/domain/entity.dart'; class EEGData extends Entity { - List _values; - int sampleCounter = 0; + final List _values; EEGData(id, this._values) : assert(_values != null), diff --git a/mobile/lib/src/infrastructure/bluetooth_repository.dart b/mobile/lib/src/infrastructure/bluetooth_repository.dart index 371a2670..c8b137ff 100644 --- a/mobile/lib/src/infrastructure/bluetooth_repository.dart +++ b/mobile/lib/src/infrastructure/bluetooth_repository.dart @@ -1,55 +1,44 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:flutter_blue/flutter_blue.dart'; +import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:polydodo/src/domain/acquisition_device/acquisition_device.dart'; import 'package:polydodo/src/domain/acquisition_device/i_acquisition_device_repository.dart'; import 'package:polydodo/src/domain/unique_id.dart'; class BluetoothRepository implements IAcquisitionDeviceRepository { - static const String BLE_SERVICE = "fe84"; - static const String BLE_RECEIVE = "2d30c082"; - static const String BLE_SEND = "2d30c083"; + static const String BLE_SERVICE = "0000fe84-0000-1000-8000-00805f9b34fb"; + static const String BLE_RECEIVE = "2d30c082-f39f-4ce6-923f-3484ea480596"; + static const String BLE_SEND = "2d30c083-f39f-4ce6-923f-3484ea480596"; static const startStreamChar = 'b'; static const stopStreamChar = 's'; - BluetoothDevice _selectedDevice; - BluetoothCharacteristic _sendCharacteristic; - BluetoothCharacteristic _receiveCharacteristic; + DiscoveredDevice _selectedDevice; + QualifiedCharacteristic _sendCharacteristic; + QualifiedCharacteristic _receiveCharacteristic; - FlutterBlue flutterBlue; - StreamSubscription> _bluetoothScanSubscription; + FlutterReactiveBle flutterReactiveBle; + StreamSubscription _bluetoothScanSubscription; List _acquisitionDevicePersistency = []; - List _bluetoothDevices = []; + List _bluetoothDevices = []; final streamController = StreamController>(); BluetoothRepository(); void initializeRepository() { if (_bluetoothScanSubscription == null) { - flutterBlue = FlutterBlue.instance; - - flutterBlue.connectedDevices - .asStream() - .asBroadcastStream() - .listen((List devices) { - for (BluetoothDevice device in devices) { - addDevice(device); - } - }); - _bluetoothScanSubscription = - flutterBlue.scanResults.listen((List results) { - for (ScanResult result in results) { - addDevice(result.device); - } - }); + flutterReactiveBle = FlutterReactiveBle(); + + _bluetoothScanSubscription = flutterReactiveBle + .scanForDevices() + .listen((device) => addDevice(device)); } else { _bluetoothScanSubscription.resume(); } - flutterBlue.startScan(); } - void addDevice(BluetoothDevice bluetoothDevice) { + void addDevice(DiscoveredDevice bluetoothDevice) { + print(bluetoothDevice); AcquisitionDevice device = AcquisitionDevice( UniqueId.from(bluetoothDevice.id.toString()), bluetoothDevice.name); @@ -69,19 +58,20 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { Future connect(AcquisitionDevice device) async { _selectedDevice = _bluetoothDevices[_acquisitionDevicePersistency.indexOf(device)]; - + print("haha"); + print(_selectedDevice); _acquisitionDevicePersistency.clear(); _bluetoothDevices.clear(); _bluetoothScanSubscription.pause(); - flutterBlue.stopScan(); + _bluetoothScanSubscription.cancel(); try { - await _selectedDevice.connect().timeout(Duration(seconds: 6), - onTimeout: () => - {disconnect(), throw Exception("Connection Timed out")}); - - await findRelevantCharacteristics(); + flutterReactiveBle + .connectToDevice(id: _selectedDevice.id) + .timeout(Duration(seconds: 10)); } catch (e) { + print(e); + // onTimeout: () => {disconnect(), throw Exception("Connection Timed out")}; if (e is PlatformException) { if (e.code != "already_connected") throw Exception(e.details); } else @@ -91,39 +81,48 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { Future disconnect() async { if (_selectedDevice != null) { - await _selectedDevice.disconnect(); _selectedDevice = null; } } Future findRelevantCharacteristics() async { - var characteristics = (await _selectedDevice.discoverServices()) - .firstWhere((service) => service.uuid.toString().contains(BLE_SERVICE)) - .characteristics; - for (BluetoothCharacteristic characteristic in characteristics) { - if (characteristic.uuid.toString().contains(BLE_RECEIVE)) { - _receiveCharacteristic = characteristic; - } else if (characteristic.uuid.toString().contains(BLE_SEND)) { - _sendCharacteristic = characteristic; - } - } - - if (_receiveCharacteristic == null) - throw Exception('Device is missing receive Characteristic'); - if (_sendCharacteristic == null) - throw Exception('Device is missing send Characteristic'); + _sendCharacteristic = QualifiedCharacteristic( + characteristicId: Uuid.parse(BLE_SEND), + serviceId: Uuid.parse(BLE_SERVICE), + deviceId: _selectedDevice.id); + _receiveCharacteristic = QualifiedCharacteristic( + characteristicId: Uuid.parse(BLE_RECEIVE), + serviceId: Uuid.parse(BLE_SERVICE), + deviceId: _selectedDevice.id); + + print(_sendCharacteristic); + print(_receiveCharacteristic); } Future>> startDataStream() async { - await _receiveCharacteristic.setNotifyValue(true); + findRelevantCharacteristics(); + try { + await flutterReactiveBle.requestConnectionPriority( + deviceId: _selectedDevice.id, + priority: ConnectionPriority.highPerformance); + flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, + value: startStreamChar.codeUnits); + } catch (e) { + print(e); + } - await _sendCharacteristic.write(startStreamChar.codeUnits); - return _receiveCharacteristic.value; + return flutterReactiveBle.subscribeToCharacteristic(_receiveCharacteristic); } Future stopDataStream() async { - await _receiveCharacteristic.setNotifyValue(false); - await _sendCharacteristic.write(stopStreamChar.codeUnits); + try { + await flutterReactiveBle.requestConnectionPriority( + deviceId: _selectedDevice.id, priority: ConnectionPriority.balanced); + flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, + value: stopStreamChar.codeUnits); + } catch (e) { + print(e); + } } @override diff --git a/mobile/lib/src/infrastructure/eeg_data_repository.dart b/mobile/lib/src/infrastructure/eeg_data_repository.dart index 7e29ca01..2770df88 100644 --- a/mobile/lib/src/infrastructure/eeg_data_repository.dart +++ b/mobile/lib/src/infrastructure/eeg_data_repository.dart @@ -8,20 +8,21 @@ import 'package:polydodo/src/domain/unique_id.dart'; import 'constants.dart'; class EEGDataRepository implements IEEGDataRepository { - bool initializeStream = false; - EEGData recordingData; - List lastSampleData = [0, 0, 0, 0, 0]; - int packetLoss; - int totalPackets; - int nextPacketId; - + bool _streamInitialized = false; + EEGData _recordingData; + List _lastSampleData = [0, 0, 0, 0, 0]; + int _packetLoss; + int _totalPackets; + int _nextPacketId; + int _sampleCounter; void createRecordingFromStream(Stream> stream) { - recordingData = - new EEGData(UniqueId.from(DateTime.now().toString()), new List()); - packetLoss = 0; - totalPackets = 0; - if (!initializeStream) { - initializeStream = true; + _recordingData = + EEGData(UniqueId.from(DateTime.now().toString()), List()); + _packetLoss = 0; + _totalPackets = 0; + _sampleCounter = 0; + if (!_streamInitialized) { + _streamInitialized = true; stream.listen((value) { addData(value); }); @@ -30,13 +31,14 @@ class EEGDataRepository implements IEEGDataRepository { Future stopRecordingFromStream() async { // todo: move save future to another file + final directory = await getExternalStorageDirectory(); final pathOfTheFileToWrite = - directory.path + '/' + recordingData.fileName + ".txt"; + directory.path + '/' + _recordingData.fileName + ".txt"; File file = File(pathOfTheFileToWrite); - List fileContent = new List(); + List fileContent = List(); fileContent.addAll(OPEN_BCI_HEADER); - fileContent.addAll(recordingData.values); + fileContent.addAll(_recordingData.values); String csv = const ListToCsvConverter().convert(fileContent); await file.writeAsString(csv); } @@ -46,42 +48,42 @@ class EEGDataRepository implements IEEGDataRepository { void exportData() {} Future addData(List event) async { - //print("Lost packets: " + packetLoss.toString()); - //print("Total packets: " + totalPackets.toString()); - //print("Lost percentage: " + - // (packetLoss.toDouble() / totalPackets.toDouble()).toString()); + print("Lost packets: " + _packetLoss.toString()); + print("Total packets: " + _totalPackets.toString()); + print("Lost percentage: " + + (_packetLoss.toDouble() / _totalPackets.toDouble()).toString()); if (event.length != 20) { print("Invalid Event"); return; } - totalPackets++; + _totalPackets++; int packetID = event[0]; // todo: handle packet id 0 (raw data) and possibly impedence for signal validation if (packetID == 0) { - nextPacketId = 101; + _nextPacketId = 101; List data = parseRaw(event); data = convertToMicrovolts(data, false); - recordingData.values.add(data.sublist(0, 15)); + _recordingData.values.add(data.sublist(0, 15)); } else if (packetID >= 101 && packetID <= 200) { // print(packetID); - // print(nextPacketId); - packetLoss += packetID - nextPacketId; - nextPacketId = packetID + 1; + // print(_nextPacketId); + _packetLoss += packetID - _nextPacketId; + _nextPacketId = packetID + 1; List data = parse19Bit(event); data = convertToMicrovolts(data, true); - recordingData.values.add(data.sublist(0, 15)); - recordingData.values.add(data.sublist(15, 30)); + _recordingData.values.add(data.sublist(0, 15)); + _recordingData.values.add(data.sublist(15, 30)); } } List parseRaw(event) { List data = getListForCSV(); - data[0] = recordingData.sampleCounter++; + data[0] = _sampleCounter++; data[1] = (event[1] << 16) | (event[2] << 8) | event[3]; data[2] = (event[4] << 16) | (event[5] << 8) | event[6]; data[3] = (event[7] << 16) | (event[8] << 8) | event[9]; @@ -99,7 +101,7 @@ class EEGDataRepository implements IEEGDataRepository { List data = getListForCSV(); - data[0] = recordingData.sampleCounter; + data[0] = _sampleCounter; data[1] = (event[1] << 11) | (event[2] << 3) | (event[3] >> 5); data[2] = ((event[3] & 31) << 14) | (event[4] << 6) | (event[5] >> 2); data[3] = ((event[5] & 3) << 17) | @@ -107,7 +109,7 @@ class EEGDataRepository implements IEEGDataRepository { (event[7] << 1) | (event[8] >> 7); data[4] = ((event[8] & 127) << 12) | (event[9] << 4) | (event[10] >> 4); - data[15] = recordingData.sampleCounter++; + data[15] = _sampleCounter++; data[16] = ((event[10] & 15) << 15) | (event[11] << 7) | (event[12] >> 1); data[17] = ((event[12] & 1) << 18) | (event[13] << 10) | @@ -120,7 +122,7 @@ class EEGDataRepository implements IEEGDataRepository { } List getListForCSV() { - List data = new List(30); + List data = List(30); for (int i = 5; i < 15; ++i) { data[i] = 0; @@ -147,9 +149,9 @@ class EEGDataRepository implements IEEGDataRepository { data[i + offset].toDouble() * (1200000 / (8388607.0 * 1.5 * 51.0)); // Convert delta - if (isDelta) data[i + offset] = lastSampleData[i] - data[i + offset]; + if (isDelta) data[i + offset] = _lastSampleData[i] - data[i + offset]; - lastSampleData[i] = data[i + offset]; + _lastSampleData[i] = data[i + offset]; } } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 920435e6..5c26b06c 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -160,18 +160,25 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.6" - flutter_blue: + flutter_reactive_ble: dependency: "direct main" description: - name: flutter_blue + name: flutter_reactive_ble url: "https://pub.dartlang.org" source: hosted - version: "0.7.2" + version: "2.5.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + functional_data: + dependency: transitive + description: + name: functional_data + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" get_it: dependency: "direct main" description: @@ -263,6 +270,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.4+1" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + plain_optional: + dependency: transitive + description: + name: plain_optional + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" platform: dependency: transitive description: @@ -298,13 +319,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.3.2+2" - rxdart: - dependency: transitive - description: - name: rxdart - url: "https://pub.dartlang.org" - source: hosted - version: "0.24.1" share: dependency: "direct main" description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index d082d4d9..ea9fcbdd 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -32,7 +32,6 @@ dependencies: cupertino_icons: ^0.1.3 equatable: ^1.2.5 flutter_bloc: ^6.0.5 - flutter_blue: ^0.7.2 get_it: ^4.0.4 hive: ^1.4.4 meta: ^1.1.8 @@ -42,6 +41,7 @@ dependencies: binary: ^2.0.0 csv: ^4.0.3 path_provider: ^1.6.16 + flutter_reactive_ble : ^2.5.2 dev_dependencies: flutter_test: From c1cb9cc7f397fffc0b21e9f547af4c5d5fb5debe Mon Sep 17 00:00:00 2001 From: MouradLachhab Date: Sun, 11 Oct 2020 15:31:32 -0400 Subject: [PATCH 05/10] Handling errors and removed packet loss variables --- .../device/device_selector_cubit.dart | 13 +-- .../i_acquisition_device_repository.dart | 3 +- .../infrastructure/bluetooth_repository.dart | 90 ++++++++----------- .../infrastructure/eeg_data_repository.dart | 17 +--- 4 files changed, 48 insertions(+), 75 deletions(-) diff --git a/mobile/lib/src/application/device/device_selector_cubit.dart b/mobile/lib/src/application/device/device_selector_cubit.dart index 02a657c2..90412b90 100644 --- a/mobile/lib/src/application/device/device_selector_cubit.dart +++ b/mobile/lib/src/application/device/device_selector_cubit.dart @@ -30,13 +30,16 @@ class DeviceSelectorCubit extends Cubit { Future connect(AcquisitionDevice device) async { emit(DeviceConnectionInProgress()); - try { - await _deviceRepository.connect(device); - _acquisitionDeviceStream.cancel(); - emit(DeviceConnectionSuccess()); - } catch (e) { + _deviceRepository.connect(device, connectionCallback); + } + + void connectionCallback(bool connected, Exception e) { + if (e != null) { emit(DeviceConnectionFailure(e)); resetSearch(); + } else if (connected) { + _acquisitionDeviceStream.cancel(); + emit(DeviceConnectionSuccess()); } } diff --git a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart index 04067b8e..25909304 100644 --- a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart +++ b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart @@ -3,7 +3,8 @@ import 'acquisition_device.dart'; abstract class IAcquisitionDeviceRepository { void initializeRepository(); - Future connect(AcquisitionDevice device); + Future connect( + AcquisitionDevice device, Function(bool, Exception) callback); void disconnect(); Future>> startDataStream(); diff --git a/mobile/lib/src/infrastructure/bluetooth_repository.dart b/mobile/lib/src/infrastructure/bluetooth_repository.dart index c8b137ff..7f03f149 100644 --- a/mobile/lib/src/infrastructure/bluetooth_repository.dart +++ b/mobile/lib/src/infrastructure/bluetooth_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:polydodo/src/domain/acquisition_device/acquisition_device.dart'; import 'package:polydodo/src/domain/acquisition_device/i_acquisition_device_repository.dart'; @@ -13,14 +12,13 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { static const startStreamChar = 'b'; static const stopStreamChar = 's'; - DiscoveredDevice _selectedDevice; + AcquisitionDevice _selectedDevice; QualifiedCharacteristic _sendCharacteristic; QualifiedCharacteristic _receiveCharacteristic; FlutterReactiveBle flutterReactiveBle; StreamSubscription _bluetoothScanSubscription; List _acquisitionDevicePersistency = []; - List _bluetoothDevices = []; final streamController = StreamController>(); BluetoothRepository(); @@ -29,16 +27,14 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { if (_bluetoothScanSubscription == null) { flutterReactiveBle = FlutterReactiveBle(); - _bluetoothScanSubscription = flutterReactiveBle - .scanForDevices() - .listen((device) => addDevice(device)); + _bluetoothScanSubscription = flutterReactiveBle.scanForDevices( + withServices: []).listen((device) => addDevice(device)); } else { _bluetoothScanSubscription.resume(); } } void addDevice(DiscoveredDevice bluetoothDevice) { - print(bluetoothDevice); AcquisitionDevice device = AcquisitionDevice( UniqueId.from(bluetoothDevice.id.toString()), bluetoothDevice.name); @@ -46,37 +42,34 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { if (idx == -1) { _acquisitionDevicePersistency.add(device); - _bluetoothDevices.add(bluetoothDevice); } else { _acquisitionDevicePersistency[idx] = device; - _bluetoothDevices[idx] = bluetoothDevice; } streamController.add(_acquisitionDevicePersistency); } - Future connect(AcquisitionDevice device) async { - _selectedDevice = - _bluetoothDevices[_acquisitionDevicePersistency.indexOf(device)]; - print("haha"); - print(_selectedDevice); + Future connect( + AcquisitionDevice device, Function(bool, Exception) callback) async { + StreamSubscription stream; + + _selectedDevice = device; _acquisitionDevicePersistency.clear(); - _bluetoothDevices.clear(); _bluetoothScanSubscription.pause(); - _bluetoothScanSubscription.cancel(); - - try { - flutterReactiveBle - .connectToDevice(id: _selectedDevice.id) - .timeout(Duration(seconds: 10)); - } catch (e) { - print(e); - // onTimeout: () => {disconnect(), throw Exception("Connection Timed out")}; - if (e is PlatformException) { - if (e.code != "already_connected") throw Exception(e.details); - } else - throw e; - } + + stream = flutterReactiveBle + .connectToDevice(id: _selectedDevice.id.toString()) + .timeout(Duration(seconds: 10), onTimeout: (sink) { + sink.close(); + callback(false, Exception("Connection Timed out")); + }).listen((event) { + if (event.connectionState == DeviceConnectionState.connected) { + setupCharacteristics(); + stream.cancel(); + callback(true, null); + } else if (event.connectionState == DeviceConnectionState.disconnected) + callback(false, Exception("Failed to connect to device")); + }); } Future disconnect() async { @@ -85,44 +78,35 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { } } - Future findRelevantCharacteristics() async { + Future setupCharacteristics() async { _sendCharacteristic = QualifiedCharacteristic( characteristicId: Uuid.parse(BLE_SEND), serviceId: Uuid.parse(BLE_SERVICE), - deviceId: _selectedDevice.id); + deviceId: _selectedDevice.id.toString()); + _receiveCharacteristic = QualifiedCharacteristic( characteristicId: Uuid.parse(BLE_RECEIVE), serviceId: Uuid.parse(BLE_SERVICE), - deviceId: _selectedDevice.id); - - print(_sendCharacteristic); - print(_receiveCharacteristic); + deviceId: _selectedDevice.id.toString()); } Future>> startDataStream() async { - findRelevantCharacteristics(); - try { - await flutterReactiveBle.requestConnectionPriority( - deviceId: _selectedDevice.id, - priority: ConnectionPriority.highPerformance); - flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, - value: startStreamChar.codeUnits); - } catch (e) { - print(e); - } + await flutterReactiveBle.requestConnectionPriority( + deviceId: _selectedDevice.id.toString(), + priority: ConnectionPriority.highPerformance); + + flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, + value: startStreamChar.codeUnits); return flutterReactiveBle.subscribeToCharacteristic(_receiveCharacteristic); } Future stopDataStream() async { - try { - await flutterReactiveBle.requestConnectionPriority( - deviceId: _selectedDevice.id, priority: ConnectionPriority.balanced); - flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, - value: stopStreamChar.codeUnits); - } catch (e) { - print(e); - } + await flutterReactiveBle.requestConnectionPriority( + deviceId: _selectedDevice.id.toString(), + priority: ConnectionPriority.balanced); + flutterReactiveBle.writeCharacteristicWithoutResponse(_sendCharacteristic, + value: stopStreamChar.codeUnits); } @override diff --git a/mobile/lib/src/infrastructure/eeg_data_repository.dart b/mobile/lib/src/infrastructure/eeg_data_repository.dart index 2770df88..06af14b6 100644 --- a/mobile/lib/src/infrastructure/eeg_data_repository.dart +++ b/mobile/lib/src/infrastructure/eeg_data_repository.dart @@ -11,15 +11,11 @@ class EEGDataRepository implements IEEGDataRepository { bool _streamInitialized = false; EEGData _recordingData; List _lastSampleData = [0, 0, 0, 0, 0]; - int _packetLoss; - int _totalPackets; - int _nextPacketId; int _sampleCounter; + void createRecordingFromStream(Stream> stream) { _recordingData = EEGData(UniqueId.from(DateTime.now().toString()), List()); - _packetLoss = 0; - _totalPackets = 0; _sampleCounter = 0; if (!_streamInitialized) { _streamInitialized = true; @@ -48,30 +44,19 @@ class EEGDataRepository implements IEEGDataRepository { void exportData() {} Future addData(List event) async { - print("Lost packets: " + _packetLoss.toString()); - print("Total packets: " + _totalPackets.toString()); - print("Lost percentage: " + - (_packetLoss.toDouble() / _totalPackets.toDouble()).toString()); if (event.length != 20) { print("Invalid Event"); return; } - _totalPackets++; int packetID = event[0]; // todo: handle packet id 0 (raw data) and possibly impedence for signal validation if (packetID == 0) { - _nextPacketId = 101; - List data = parseRaw(event); data = convertToMicrovolts(data, false); _recordingData.values.add(data.sublist(0, 15)); } else if (packetID >= 101 && packetID <= 200) { - // print(packetID); - // print(_nextPacketId); - _packetLoss += packetID - _nextPacketId; - _nextPacketId = packetID + 1; List data = parse19Bit(event); data = convertToMicrovolts(data, true); From 60d500931799cba35f05e004539cd26a34d225b5 Mon Sep 17 00:00:00 2001 From: MouradLachhab Date: Sun, 11 Oct 2020 18:33:22 -0400 Subject: [PATCH 06/10] Rearranged pubspec, changed future void to void, and fixed cancel stream order --- .../i_acquisition_device_repository.dart | 3 +-- .../infrastructure/bluetooth_repository.dart | 26 +++++++++---------- .../infrastructure/eeg_data_repository.dart | 2 +- mobile/pubspec.yaml | 8 +++--- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart index 25909304..4c6148a7 100644 --- a/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart +++ b/mobile/lib/src/domain/acquisition_device/i_acquisition_device_repository.dart @@ -3,8 +3,7 @@ import 'acquisition_device.dart'; abstract class IAcquisitionDeviceRepository { void initializeRepository(); - Future connect( - AcquisitionDevice device, Function(bool, Exception) callback); + void connect(AcquisitionDevice device, Function(bool, Exception) callback); void disconnect(); Future>> startDataStream(); diff --git a/mobile/lib/src/infrastructure/bluetooth_repository.dart b/mobile/lib/src/infrastructure/bluetooth_repository.dart index 7f03f149..57e3f812 100644 --- a/mobile/lib/src/infrastructure/bluetooth_repository.dart +++ b/mobile/lib/src/infrastructure/bluetooth_repository.dart @@ -17,6 +17,7 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { QualifiedCharacteristic _receiveCharacteristic; FlutterReactiveBle flutterReactiveBle; + StreamSubscription _connectedDeviceStream; StreamSubscription _bluetoothScanSubscription; List _acquisitionDevicePersistency = []; final streamController = StreamController>(); @@ -49,36 +50,35 @@ class BluetoothRepository implements IAcquisitionDeviceRepository { streamController.add(_acquisitionDevicePersistency); } - Future connect( + void connect( AcquisitionDevice device, Function(bool, Exception) callback) async { - StreamSubscription stream; - _selectedDevice = device; _acquisitionDevicePersistency.clear(); _bluetoothScanSubscription.pause(); - stream = flutterReactiveBle - .connectToDevice(id: _selectedDevice.id.toString()) - .timeout(Duration(seconds: 10), onTimeout: (sink) { - sink.close(); - callback(false, Exception("Connection Timed out")); - }).listen((event) { + _connectedDeviceStream = flutterReactiveBle + .connectToDevice( + id: _selectedDevice.id.toString(), + connectionTimeout: Duration(seconds: 10)) + .listen((event) { if (event.connectionState == DeviceConnectionState.connected) { setupCharacteristics(); - stream.cancel(); callback(true, null); - } else if (event.connectionState == DeviceConnectionState.disconnected) + } else if (event.connectionState == DeviceConnectionState.disconnected) { + disconnect(); callback(false, Exception("Failed to connect to device")); + } }); } - Future disconnect() async { + void disconnect() async { if (_selectedDevice != null) { _selectedDevice = null; + _connectedDeviceStream.cancel(); } } - Future setupCharacteristics() async { + void setupCharacteristics() async { _sendCharacteristic = QualifiedCharacteristic( characteristicId: Uuid.parse(BLE_SEND), serviceId: Uuid.parse(BLE_SERVICE), diff --git a/mobile/lib/src/infrastructure/eeg_data_repository.dart b/mobile/lib/src/infrastructure/eeg_data_repository.dart index 06af14b6..c24e2a26 100644 --- a/mobile/lib/src/infrastructure/eeg_data_repository.dart +++ b/mobile/lib/src/infrastructure/eeg_data_repository.dart @@ -32,7 +32,7 @@ class EEGDataRepository implements IEEGDataRepository { final pathOfTheFileToWrite = directory.path + '/' + _recordingData.fileName + ".txt"; File file = File(pathOfTheFileToWrite); - List fileContent = List(); + var fileContent = []; fileContent.addAll(OPEN_BCI_HEADER); fileContent.addAll(_recordingData.values); String csv = const ListToCsvConverter().convert(fileContent); diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index ea9fcbdd..10bc9657 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -28,20 +28,20 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. archive: ^2.0.13 battery: ^1.0.5 + binary: ^2.0.0 bloc: ^6.0.3 + csv: ^4.0.3 cupertino_icons: ^0.1.3 equatable: ^1.2.5 flutter_bloc: ^6.0.5 + flutter_reactive_ble : ^2.5.2 get_it: ^4.0.4 hive: ^1.4.4 meta: ^1.1.8 + path_provider: ^1.6.16 share: ^0.6.5 uuid: ^2.2.2 usb_serial: ^0.2.4 - binary: ^2.0.0 - csv: ^4.0.3 - path_provider: ^1.6.16 - flutter_reactive_ble : ^2.5.2 dev_dependencies: flutter_test: From 2056cc81b2331b77495cacded9301f9c6addac74 Mon Sep 17 00:00:00 2001 From: MouradLachhab Date: Sun, 11 Oct 2020 18:52:45 -0400 Subject: [PATCH 07/10] Changed for ios build and removed unecessary addins in android manifest --- mobile/android/app/src/main/AndroidManifest.xml | 3 --- mobile/ios/Flutter/AppFrameworkInfo.plist | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 064e835c..22862eb9 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -44,8 +44,5 @@ android:name="flutterEmbedding" android:value="2" /> - - - diff --git a/mobile/ios/Flutter/AppFrameworkInfo.plist b/mobile/ios/Flutter/AppFrameworkInfo.plist index f2872cf4..beffb28c 100644 --- a/mobile/ios/Flutter/AppFrameworkInfo.plist +++ b/mobile/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 10.0 From 5bcce2931714819804f85632a066e854eeb58016 Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Sun, 11 Oct 2020 19:24:01 -0400 Subject: [PATCH 08/10] Add podfile --- mobile/ios/Podfile | 1 + 1 file changed, 1 insertion(+) create mode 100644 mobile/ios/Podfile diff --git a/mobile/ios/Podfile b/mobile/ios/Podfile new file mode 100644 index 00000000..f6f16d0e --- /dev/null +++ b/mobile/ios/Podfile @@ -0,0 +1 @@ +platform :ios, '10.0' From 5a3016a7637b0f2917059fb9584ed440a990f5d2 Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Sun, 11 Oct 2020 19:38:21 -0400 Subject: [PATCH 09/10] Revert "Add podfile" This reverts commit 5bcce2931714819804f85632a066e854eeb58016. --- mobile/ios/Podfile | 1 - 1 file changed, 1 deletion(-) delete mode 100644 mobile/ios/Podfile diff --git a/mobile/ios/Podfile b/mobile/ios/Podfile deleted file mode 100644 index f6f16d0e..00000000 --- a/mobile/ios/Podfile +++ /dev/null @@ -1 +0,0 @@ -platform :ios, '10.0' From e29cefa2fb51df2a481fdd2f3eed1204b82952be Mon Sep 17 00:00:00 2001 From: Anes Belfodil Date: Sun, 11 Oct 2020 19:39:41 -0400 Subject: [PATCH 10/10] try something else --- mobile/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index bbd0e334..a81b9d8b 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -272,7 +272,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -354,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -403,7 +403,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos;