Index: sources/bluetooth/BluetoothInterface.cpp =================================================================== diff -u -r2dd767833cf0cf706c457951c2d78e7e20aff768 -r028cb1403e8fcb2d2e9ab2aa1562f660124015b1 --- sources/bluetooth/BluetoothInterface.cpp (.../BluetoothInterface.cpp) (revision 2dd767833cf0cf706c457951c2d78e7e20aff768) +++ sources/bluetooth/BluetoothInterface.cpp (.../BluetoothInterface.cpp) (revision 028cb1403e8fcb2d2e9ab2aa1562f660124015b1) @@ -80,13 +80,10 @@ // coco begin validated: Application termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. quitThread(); // validated -} -void BluetoothInterface::onDestroy() -{ - qDebug() << "Destroy"; - delete _local; - delete _agent; + if ( _local ) _local ->deleteLater(); + if ( _agent ) _agent ->deleteLater(); + if ( _device ) _device ->deleteLater(); } // coco end @@ -97,9 +94,6 @@ */ void BluetoothInterface::initConnections() { - connect(this , &BluetoothInterface :: destroyed , - this , &BluetoothInterface ::onDestroy ); - // Local connections connect(_local , &QBluetoothLocalDevice :: pairingFinished , this , &BluetoothInterface ::onLocalPairingFinish ); @@ -135,7 +129,7 @@ void BluetoothInterface::initThread(QThread &vThread) { // runs in main thread - Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); + Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread"); _thread = &vThread; _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); @@ -160,7 +154,13 @@ } // coco end +#define NOTIFY_IDLE notifyStateChange(MBluetooth(MBluetooth::eIS_Idle )); +#define NOTIFY_CLOSE notifyStateChange(MBluetooth(MBluetooth::eIS_Close )); + // ~~~~~~~~~~~ Local +#define NOTIFY_LOCAL_INIT notifyStateChange(MBluetooth(MBluetooth::eIS_Local_Init , \ + _local->address().toString() , \ + _local->name() )); #define NOTIFY_LOCAL_CONNECT notifyStateChange(MBluetooth(MBluetooth::eIS_Local_Connect , vAddress.toString()) ); #define NOTIFY_LOCAL_ERROR notifyStateChange(MBluetooth(MBluetooth::eIS_Local_Error_Unknown )); #define NOTIFY_LOCAL_ERROR_IO notifyStateChange(MBluetooth(MBluetooth::eIS_Local_Error_IO )); @@ -191,7 +191,7 @@ notifyStateChange(_model ); #define NOTIFY_SCAN_START notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_Start )); #define NOTIFY_SCAN_REJECT notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_Reject )); -#define NOTIFY_SCAN_NOTFOUND notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_NotFound ,"","" )); +#define NOTIFY_SCAN_NOTFOUND notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_NotFound ,"", "" )); #define NOTIFY_SCAN_STOP notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_Stop )); #define NOTIFY_SCAN_DONE notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_Done )); #define NOTIFY_SCAN_DETAIL notifyStateChange(MBluetooth(MBluetooth::eIS_Scan_Detail , \ @@ -208,6 +208,9 @@ #define NOTIFY_DEVICE_CONNECT notifyStateChange(MBluetooth(MBluetooth::eIS_Device_Connect , \ _info.address().toString() , \ _info.name() )); +#define NOTIFY_DEVICE_DONE notifyStateChange(MBluetooth(MBluetooth::eIS_Device_Done , \ + _info.address().toString() , \ + _info.name() )); #define NOTIFY_DEVICE_ERROR notifyStateChange(MBluetooth(MBluetooth::eIS_Device_Error , \ _info.address().toString() , \ _info.name() , \ @@ -216,48 +219,137 @@ _info.address().toString() , \ _info.name() )); -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#define NOTIFY_SERVICE_START notifyStateChange(MBluetooth(MBluetooth::eIS_Service_Start )); +/* +#define NOTIFY_SERVICE_DISCOVER notifyStateChange(MBluetooth(MBluetooth::eIS_Service_Discover , \ + _service->serviceUuid().toString() , \ + _service->serviceName() )); +*/ + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~ Public interface to initialize and start the scan +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/*! + * \brief BluetoothInterface::onstart + * \details the thread safe slot to be called when the public function start is called. + * initializes the local Bluetooth device _local and setups up the interface. + */ +void BluetoothInterface::onstart() +{ + _local->powerOn(); + if (! _local->isValid() ) { + NOTIFY_LOCAL_ERROR_INVALID + return; + } + + resetDevice(); + + _agent->setLowEnergyDiscoveryTimeout(5000); + NOTIFY_LOCAL_INIT + NOTIFY_IDLE +} + +/*! + * \brief BluetoothInterface::ondoScan + * \details the thread safe slot to be called when the public function doScan is called. + * initializes/validates the discovery agent _agent and setups it up to start the discovery. + */ +void BluetoothInterface::ondoScan() +{ + if (_agent && _agent->isActive()) { + NOTIFY_SCAN_REJECT + return; + } + + NOTIFY_SCAN_START + _agent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~ Local Device Slots -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -void BluetoothInterface::onLocalDeviceConnect(const QBluetoothAddress &vAddress) { +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Set of notifications on each event +/*! + * \brief BluetoothInterface::onLocalDeviceConnect + * \details Notifies the observers (view: VBluetooth) that the Local Device Connected + * \param vAddress + */ +void BluetoothInterface::onLocalDeviceConnect (const QBluetoothAddress &vAddress ) { NOTIFY_LOCAL_CONNECT } -void BluetoothInterface::onLocalDeviceDisconnect(const QBluetoothAddress &vAddress) { +/*! + * \brief BluetoothInterface::onLocalDeviceDisconnect + * \details Notifies the observers (view: VBluetooth) that the Local Device Disconnected + * \param vAddress + */ +void BluetoothInterface::onLocalDeviceDisconnect (const QBluetoothAddress &vAddress ) { NOTIFY_LOCAL_DISCONNECT } -void BluetoothInterface::onLocalPairingFinish(const QBluetoothAddress &vAddress, QBluetoothLocalDevice::Pairing vPairing) { - NOTIFY_PAIR_DONE - //_local->pairingConfirmation(true); - _device->discoverServices(); -} - -void BluetoothInterface::onLocalPairingDisplayConfirmation(const QBluetoothAddress &vAddress, const QString &vPin) { +/*! + * \brief BluetoothInterface::onLocalPairingDisplayConfirmation + * \details Notifies the observers (view: VBluetooth) that the user needs to confirm paring. + * \param vAddress - remote device address + * \param vPin - remote device requested pin code + * \note Not used for now just logged for later use. + */ +void BluetoothInterface::onLocalPairingDisplayConfirmation (const QBluetoothAddress &vAddress, const QString &vPin ) { NOTIFY_PAIR_CONFIRM - //_local->pairingConfirmation(true); } -void BluetoothInterface::onLocalPairingDisplayPinCode(const QBluetoothAddress &vAddress, const QString &vPin) { +/*! + * \brief BluetoothInterface::onLocalPairingDisplayPinCode + * \details Notifies the observers (view: VBluetooth) the pin code to make sure this is the device it intends to connect. + * Signal by some platforms to display the pin to the user for address. + * The pin is automatically generated, and does not need to be confirmed. + * \param vAddress + * \param vPin + * \note Not used for now just logged for later use. + */ +void BluetoothInterface::onLocalPairingDisplayPinCode (const QBluetoothAddress &vAddress, const QString &vPin ) { NOTIFY_PAIR_PINCODE - //_local->pairingConfirmation(true); } -void BluetoothInterface::onLocalError(QBluetoothLocalDevice::Error vError) { +/*! + * \brief BluetoothInterface::onLocalPairingFinish + * \details Notifies the observers (view: VBluetooth) that the paring is successfully done + * \param vAddress - Paired remote address + * \param vPairing - The Paring status/Type + */ +void BluetoothInterface::onLocalPairingFinish (const QBluetoothAddress &vAddress, QBluetoothLocalDevice::Pairing vPairing ) { + NOTIFY_PAIR_DONE +} + +/*! + * \brief BluetoothInterface::onLocalError + * \details Notifies the observers (view: VBluetooth) that the local adapter has error + * \param vError - The error + */ +void BluetoothInterface::onLocalError ( QBluetoothLocalDevice::Error vError) { switch (vError) { case QBluetoothLocalDevice::PairingError: NOTIFY_PAIR_ERROR break; default : NOTIFY_LOCAL_ERROR break; } + if ( _device ) _device->disconnectFromDevice(); } -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~ Discovery Agent Slots -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/*! + * \brief BluetoothInterface::onAgentDiscoverDevice + * \details The slot to handle each discovered Bluetooth Low Energy device. + * if the discovered BLE device , which its information provided in vInfo, has any identified keyword, the scan/discovery stops if it is still active, and + * notifies the observers (view: VBluetooth) that a device found. + * \param vInfo + * \note If multiple supported devices are near, the first one will be identified as found. + */ void BluetoothInterface::onAgentDiscoverDevice(const QBluetoothDeviceInfo &vInfo) { NOTIFY_SCAN_DISCOVER if (vInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { - if ( vInfo.name().startsWith("BP7000") || vInfo.name().startsWith("BLEsmart") || vInfo.name().startsWith("BLESmart") ) { + if ( isDeviceSupported( vInfo.name() ) ) { _info = QBluetoothDeviceInfo(vInfo.address(), vInfo.name(), QBluetoothDeviceInfo::HealthBloodPressureMonitor); NOTIFY_SCAN_FOUND if (_agent->isActive()) @@ -266,119 +358,238 @@ } } +/*! + * \brief BluetoothInterface::onAgentDiscoverError + * \details Notifies the observers (view: VBluetooth) that the discovery agent has error, during discovery. + * \param vError - The discovery agent error + */ +void BluetoothInterface::onAgentDiscoverError(QBluetoothDeviceDiscoveryAgent::Error vError) +{ + switch (vError) { + case QBluetoothDeviceDiscoveryAgent::PoweredOffError : NOTIFY_LOCAL_ERROR_OFF break; + case QBluetoothDeviceDiscoveryAgent::InputOutputError : NOTIFY_LOCAL_ERROR_IO break; + default : NOTIFY_LOCAL_ERROR break; + } + if ( _device ) _device->disconnectFromDevice(); +} + +/*! + * \brief BluetoothInterface::onAgentDiscoverFinish + * \details Notifies the observers (view: VBluetooth) that the discovery agent has done discovery, and + * starts gathering details and setup the discovered device. + */ void BluetoothInterface::onAgentDiscoverFinish() { NOTIFY_SCAN_DONE - discoverFinish(); + connectToDevice(); } +/*! + * \brief BluetoothInterface::onAgentDiscoverCancel + * \details Notifies the observers (view: VBluetooth) that the discovery agent has stopped discovery, and + * starts gathering details and setup the discovered device. + * \note this slot is called when the discovery stopped before it is finished (currently by first supported found device). + */ void BluetoothInterface::onAgentDiscoverCancel() { NOTIFY_SCAN_STOP - discoverFinish(); + connectToDevice(); } -void BluetoothInterface::onAgentDiscoverError(QBluetoothDeviceDiscoveryAgent::Error vError) +/*! + * \brief BluetoothInterface::connectToDevice + * \return false if + * the local Bluetooth device _local is not valid + * the device info _info is not valid + * + */ +bool BluetoothInterface::connectToDevice() { - switch (vError) { - case QBluetoothDeviceDiscoveryAgent::PoweredOffError : NOTIFY_LOCAL_ERROR_OFF break; - case QBluetoothDeviceDiscoveryAgent::InputOutputError : NOTIFY_LOCAL_ERROR_IO break; - default : NOTIFY_LOCAL_ERROR break; - } + if ( ! isLocalValid() ) return false; + if ( ! isInfoValid () ) return false; + if ( ! initDevice () ) return false; + + NOTIFY_DEVICE_START + _device->connectToDevice(); + + return true; } -void BluetoothInterface::doScan() +void BluetoothInterface::onDeviceConnect() { + NOTIFY_DEVICE_CONNECT + _device->discoverServices(); + NOTIFY_SERVICE_START +} + +void BluetoothInterface::onDeviceDisconnect() { - if (_agent && _agent->isActive()) { - NOTIFY_SCAN_REJECT - return; - } - emit didstart(); + NOTIFY_DEVICE_DISCONNECT } -void BluetoothInterface::onstart() +void BluetoothInterface::onDeviceError(QLowEnergyController::Error vError) { - _info = QBluetoothDeviceInfo(); // reset the device + NOTIFY_DEVICE_ERROR +} - _local->powerOn(); - if (! _local->isValid() ) { - NOTIFY_LOCAL_ERROR_INVALID - return; - } +void BluetoothInterface::onDeviceStateChanged(QLowEnergyController::ControllerState vState) +{ + // qDebug() << "Device State Changed" << vState; +} - qDebug() << _local->address(); - qDebug() << _local->connectedDevices(); - qDebug() << _local->hostMode(); - qDebug() << _local->isValid(); - qDebug() << _local->name(); - - _agent->setLowEnergyDiscoveryTimeout(5000); - NOTIFY_SCAN_START - _agent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); +void BluetoothInterface::onDeviceConnectionUpdated(const QLowEnergyConnectionParameters &/*vParameters*/) +{ + // qDebug() << "Device Connection Changed"; } -bool BluetoothInterface::discoverFinish() +bool BluetoothInterface::initDevice() { - if (! _local->isValid() ) { - NOTIFY_LOCAL_ERROR_INVALID - return false; + if ( _device ) { + _device->disconnectFromDevice(); + delete _device; + _device = nullptr; } - if ( ! _info.isValid() ) { - NOTIFY_SCAN_NOTFOUND - return false; - } - - NOTIFY_SCAN_DETAIL - _device = QLowEnergyController::createCentral(_info, this); - if (_device) NOTIFY_DEVICE_INIT - else { NOTIFY_DEVICE_INIT_ERROR + if ( _device ) NOTIFY_DEVICE_INIT + else { NOTIFY_DEVICE_INIT_ERROR return false; } - QObject::connect(_device, &QLowEnergyController::connected, [=](){ - NOTIFY_DEVICE_CONNECT - // TODO: If the device is paired it can't be connected again !!!!!!!!!! - // -------------------------------------------------------------------- - // if ( _model.devicePair == QBluetoothLocalDevice::Unpaired ) { - // _local->requestPairing(_device->remoteAddress(), QBluetoothLocalDevice::AuthorizedPaired); - // qDebug() << " ~~~~~ Connected Request Paring"; - // NOTIFY_PAIR_START - // } - // else { - // qDebug() << " ~~~~~ Connected Discover Services"; - _device->discoverServices(); - // } + // Device controller + connect( _device, SIGNAL( connected ( )), + this , SLOT(onDeviceConnect ( ))); + connect( _device, SIGNAL( error (QLowEnergyController::Error )), + this , SLOT(onDeviceError (QLowEnergyController::Error ))); + connect( _device, SIGNAL( disconnected ( )), + this , SLOT(onDeviceDisconnect ( ))); + connect( _device, SIGNAL( stateChanged(QLowEnergyController::ControllerState )), + this , SLOT(onDeviceStateChanged(QLowEnergyController::ControllerState ))); + connect( _device, SIGNAL( connectionUpdated(QLowEnergyConnectionParameters )), + this , SLOT(onDeviceConnectionUpdated(QLowEnergyConnectionParameters ))); + // Device Services + connect( _device, &QLowEnergyController::serviceDiscovered, [=](const QBluetoothUuid &vService){ + if (vService == QBluetoothUuid (QBluetoothUuid::DeviceInformation )) { + if ( _serviceDeviceInformation )S + delete _serviceDeviceInformation ; + _serviceDeviceInformation = _device->createServiceObject(vService, this); + qDebug() << " ~~~~~ Inf Service Found" << vService; + return; + } + if (vService == QBluetoothUuid (QBluetoothUuid::CurrentTimeService )) { + if ( _serviceCurrentTime ) + delete _serviceCurrentTime ; + _serviceCurrentTime = _device->createServiceObject(vService, this); + qDebug() << " ~~~~~ TDt Service Found" << vService; + return; + } + if (vService == QBluetoothUuid (QBluetoothUuid::BloodPressure )) { + if ( _serviceBloodPressure ) + delete _serviceBloodPressure ; + _serviceBloodPressure = _device->createServiceObject(vService, this); + qDebug() << " ~~~~~ BPr Service Found" << vService; + return; + } + if (vService == QBluetoothUuid (QBluetoothUuid::BatteryService )) { + if ( _serviceBattery ) + delete _serviceBattery ; + _serviceBattery = _device->createServiceObject(vService, this); + qDebug() << " ~~~~~ Bat Service Found" << vService; + return; + } }); - QObject::connect(_device, &QLowEnergyController::serviceDiscovered, [=](const QBluetoothUuid &service){ - QLowEnergyService *m_service = _device->createServiceObject(service, this); - qDebug() << " ~~~~~ Controller Service : " << m_service->serviceUuid() << m_service->serviceName(); - }); - QObject::connect(_device, &QLowEnergyController::discoveryFinished, [=](){ - qDebug() << " ~~~~~ Controller Discovery Finished"; + connect(_device, &QLowEnergyController::discoveryFinished, [=](){ + // _serviceInformation->discoverDetails(); + NOTIFY_DEVICE_DONE _device->disconnectFromDevice(); }); - QObject::connect(_device, &QLowEnergyController::disconnected, [=](){ - NOTIFY_DEVICE_DISCONNECT - }); - QObject::connect(_device, QOverload::of(&QLowEnergyController::error),[=](QLowEnergyController::Error vError){ - NOTIFY_DEVICE_ERROR - }); - - _device->connectToDevice(); - NOTIFY_DEVICE_START return true; } -void BluetoothInterface::notifyStateChange(const MBluetooth &vData) +/*! + * \brief BluetoothInterface::notifyStateChange + * \details sends a notification signal to the observers (view: VBluetooth) on any reported state changes. + * \param vData - the Bluetooth data model containing information about the current state and device. + */ +void BluetoothInterface::notifyStateChange(const BluetoothData &vData) { _state = vData.state; emit didStateChange(vData); } +/*! + * \brief BluetoothInterface::timerEvent + * \details The main QObject's timer which has been set with the interval of the _interval (1s) + * The main purpose is to reconnect to the device on each second interval to get any new measurement data. + */ void BluetoothInterface::timerEvent(QTimerEvent *) { - if ( _bpRead ) { + if ( _bpRead ) { } +} +/*! + * \brief BluetoothInterface::isDeviceSupported + * \param vName - remote device name + * \return true if the name provided starts with one of the supported keywords mentioned in the Omron documentation + */ +bool BluetoothInterface::isDeviceSupported( const QString &vName ) { + for (const QString &name : _supportedDeviceKeywords ) + if ( vName.startsWith(name) ) + return true; + return false; +} + +/*! + * \brief BluetoothInterface::isLocalValid + * \details checks if the local Bluetooth device is valid, and + * notifies the observers (view: VBluetooth) that local is not valid if fails. + * \return false if the local Bluetooth device _local is not valid + */ +bool BluetoothInterface::isLocalValid() { + if (! _local->isValid() ) { + NOTIFY_LOCAL_ERROR_INVALID + return false; } + return true; } + +/*! + * \brief BluetoothInterface::isInfoValid + * \details checks if the device info _info is valid. + * notifies the observers (view: VBluetooth) that the device info is not valid. + * \return false if the device info _local is not valid + * \note the device info _info is set to a default QBluetoothDeviceInfo which is invalid by default, + * when the interface initialized (onStart), and + * when a scan started (ondoScan) + */ +bool BluetoothInterface::isInfoValid() { + if ( ! _info.isValid() ) { + NOTIFY_SCAN_NOTFOUND // not a valid device found + return false; + } + return true; +} + +/*! + * \brief BluetoothInterface::isDeviceValid + * \details checks if the remote Bluetooth device has been setup. + * \return returns false if no device is setup. + */ +bool BluetoothInterface::isDeviceValid() { + if ( _device ) { NOTIFY_DEVICE_INIT } + else { NOTIFY_DEVICE_INIT_ERROR + return false; + } + return true; +} + +/*! + * \brief BluetoothInterface::resetDevice + * \details resets the remote device _device and the device info _info. + */ +void BluetoothInterface::resetDevice() { + _info = QBluetoothDeviceInfo(); // reset the found info + if ( _device ) { + _device->disconnectFromDevice(); + delete _device; + _device = nullptr; + } +}