diff --git b/.gitignore a/.gitignore new file mode 100644 index 0000000..0ff047c --- /dev/null +++ a/.gitignore @@ -0,0 +1,2 @@ +build/ +CMakeLists.txt.user diff --git b/CMakeLists.txt a/CMakeLists.txt new file mode 100644 index 0000000..0650364 --- /dev/null +++ a/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0) + +# Check to see where cmake is located. +if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +elseif( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../cmake ) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) +else() + return() +endif() + +# Check to see if there is versioning information available +if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) + include(osdevversion) +endif() + +include(projectheader) +project_header(osdev_qt-bluetooth) + +add_subdirectory(src) +add_subdirectory(tests) + +# include(packaging) +# package_component() diff --git b/README.md a/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ a/README.md diff --git b/src/CMakeLists.txt a/src/CMakeLists.txt new file mode 100644 index 0000000..64d0b8b --- /dev/null +++ a/src/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.0) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake) +include(projectheader) +project_header(bluetooth) + +find_package( Qt5Core REQUIRED ) +find_package( Qt5Bluetooth REQUIRED ) + +include_directories( SYSTEM + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Bluetooth_INCLUDE_DIRS} +) + +include(compiler) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../logutils +) + +set(SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/bluetoothbaseclass.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/connectionhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/devicefinder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/devicehandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/deviceinfo.cpp +) + +include(qtmoc) +create_mocs( SRC_LIST MOC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/bluetoothbaseclass.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionhandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/devicefinder.h + ${CMAKE_CURRENT_SOURCE_DIR}/devicehandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/deviceinfo.h +) + +link_directories( + ${CMAKE_BINARY_DIR}/lib +) + +include(library) +add_libraries( + ${Qt5Core_LIBRARIES} + ${Qt5Bluetooth_LIBRARIES} + logutils +) + +include(installation) +install_component() diff --git b/src/bluetoothbaseclass.cpp a/src/bluetoothbaseclass.cpp new file mode 100644 index 0000000..80229dc --- /dev/null +++ a/src/bluetoothbaseclass.cpp @@ -0,0 +1,72 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +// Qt +#include + +// osdev::components::bluetooth +#include "bluetoothbaseclass.h" + +using namespace osdev::components::bluetooth; + +BluetoothBaseClass::BluetoothBaseClass( QObject *parent ) + : QObject( parent ) + , m_error() + , m_info() +{ + +} + +QString BluetoothBaseClass::error() const +{ + return m_error; +} + +void BluetoothBaseClass::setError( const QString &error ) +{ + if( m_error != error ) + { + m_error = error; + qInfo() << m_error; + emit signalErrorChanged(); + } +} + +QString BluetoothBaseClass::info() const +{ + return m_info; +} + +void BluetoothBaseClass::setInfo( const QString &info ) +{ + if( m_info != info ) + { + m_info = info; + qInfo() << m_info; + emit signalInfoChanged(); + } +} + +void BluetoothBaseClass::clearMessages() +{ + setInfo( "" ); + setError( "" ); +} diff --git b/src/bluetoothbaseclass.h a/src/bluetoothbaseclass.h new file mode 100644 index 0000000..5a0f434 --- /dev/null +++ a/src/bluetoothbaseclass.h @@ -0,0 +1,60 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#ifndef OSDEV_COMPONENTS_BLUETOOTH_BLUETOOTHBASECLASS_H +#define OSDEV_COMPONENTS_BLUETOOTH_BLUETOOTHBASECLASS_H + +#include + +namespace osdev { +namespace components { +namespace bluetooth { + +class BluetoothBaseClass : public QObject +{ + Q_OBJECT + +public: + explicit BluetoothBaseClass( QObject *parent = nullptr ); + + QString error() const; + void setError( const QString &error ); + + QString info() const; + void setInfo( const QString &info ); + + void clearMessages(); + +signals: + void signalErrorChanged(); + void signalInfoChanged(); + +private: + QString m_error; + QString m_info; + +}; + +} /* End namespace bluetooth */ +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_BLUETOOTH_BLUETOOTHBASECLASS_H */ diff --git b/src/connectionhandler.cpp a/src/connectionhandler.cpp new file mode 100644 index 0000000..3eaa198 --- /dev/null +++ a/src/connectionhandler.cpp @@ -0,0 +1,65 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +// osdev::components::bluetooth +#include "connectionhandler.h" +//Qt +#include +#include + +using namespace osdev::components::bluetooth; + +ConnectionHandler::ConnectionHandler( QObject *parent ) + : QObject( parent ) + , m_localDevice() +{ + connect( &m_localDevice, &QBluetoothLocalDevice::hostModeStateChanged, this, &ConnectionHandler::slotHostModeChanged ); +} + +bool ConnectionHandler::alive() const +{ + // Bit hacky but basically just a large 'AND' statement. + return m_localDevice.isValid() && m_localDevice.hostMode() != QBluetoothLocalDevice::HostPoweredOff; +} + +bool ConnectionHandler::requiresAddressType() const +{ +#if QT_CONFIG( bluez ) + return true; +#else + return false; +#endif +} + +QString ConnectionHandler::name() const +{ + return m_localDevice.name(); +} + +QString ConnectionHandler::address() const +{ + return m_localDevice.address().toString(); +} + +void ConnectionHandler::slotHostModeChanged( QBluetoothLocalDevice::HostMode /* mode */ ) +{ + emit signalDeviceChanged(); +} diff --git b/src/connectionhandler.h a/src/connectionhandler.h new file mode 100644 index 0000000..8e07d58 --- /dev/null +++ a/src/connectionhandler.h @@ -0,0 +1,59 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#ifndef OSDEV_COMPONENTS_BLUETOOTH_CONNECTIONHANDLER_H +#define OSDEV_COMPONENTS_BLUETOOTH_CONNECTIONHANDLER_H + +// Qt +#include +#include + +namespace osdev { +namespace components { +namespace bluetooth { + +class ConnectionHandler : public QObject +{ + Q_OBJECT + +public: + explicit ConnectionHandler( QObject *parent = nullptr ); + + bool alive() const; + bool requiresAddressType() const; + QString name() const; + QString address() const; + +signals: + void signalDeviceChanged(); + +private slots: + void slotHostModeChanged( QBluetoothLocalDevice::HostMode mode ); + +private: + QBluetoothLocalDevice m_localDevice; +}; + +} /* End namespace bluetooth */ +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_BLUETOOTH_CONNECTIONHANDLER_H */ diff --git b/src/devicefinder.cpp a/src/devicefinder.cpp new file mode 100644 index 0000000..3c262c5 --- /dev/null +++ a/src/devicefinder.cpp @@ -0,0 +1,156 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +// osdev::components::bluetooth +#include "devicefinder.h" +#include "devicehandler.h" +#include "deviceinfo.h" + +using namespace osdev::components::bluetooth; + +DeviceFinder::DeviceFinder( DeviceHandler *handler, QObject *parent ) + : BluetoothBaseClass( parent ) + , m_pDeviceHandler( handler ) + , m_pDeviceDiscoveryAgent( new QBluetoothDeviceDiscoveryAgent() ) + , m_devices() +{ + //! [devicediscovery-1] + m_pDeviceDiscoveryAgent->setLowEnergyDiscoveryTimeout( 5000 ); + + connect( m_pDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::slotAddDevice ); + connect( m_pDeviceDiscoveryAgent, static_cast(&QBluetoothDeviceDiscoveryAgent::error), this, &DeviceFinder::slotScanError ); + connect( m_pDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::slotScanFinished ); + connect( m_pDeviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::slotScanFinished ); + + connect( m_pDeviceHandler, &DeviceHandler::signalReceivedValue, this, &DeviceFinder::signalSendData ); +} + +DeviceFinder::~DeviceFinder() +{ + qDeleteAll( m_devices ); + m_devices.clear(); +} + +void DeviceFinder::slotStartSearch() +{ + clearMessages(); + m_pDeviceHandler->setDevice( nullptr ); + qDeleteAll( m_devices ); + m_devices.clear(); + + emit signalDevicesChanged(); + + m_pDeviceDiscoveryAgent->start( QBluetoothDeviceDiscoveryAgent::LowEnergyMethod ); + + emit signalScanningChanged(); + setInfo( tr( "Scanning for devices..." ) ); +} + +void DeviceFinder::slotAddDevice( const QBluetoothDeviceInfo &device ) +{ + // If device is LowEnergy-device and its services contain the one we're looking for, add it to the list. + if( device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration ) + { + qInfo() << device.serviceUuids() << " : Looking for : " << m_pDeviceHandler->requestedServiceUuid(); + + m_devices.append( new DeviceInfo( device ) ); + setInfo( tr( "Low Energy Device Found. Scanning for more...." ) ); + emit signalDevicesChanged(); + } +} + +void DeviceFinder::slotScanError( QBluetoothDeviceDiscoveryAgent::Error error ) +{ + if( error == QBluetoothDeviceDiscoveryAgent::PoweredOffError ) + { + setError( tr( "The Bluetooth adapter is powered off." ) ); + } + else if( error == QBluetoothDeviceDiscoveryAgent::InputOutputError ) + { + setError( tr( "Writing or reading from the device resulted in an error." ) ); + } + else + { + setError( tr( "An unknown error has occured." ) ); + } +} + +void DeviceFinder::slotScanFinished() +{ + if( m_devices.isEmpty() ) + setError( tr( "No Low Energy devices found." ) ); + else + setInfo( tr( "Scanning done." ) ); + + emit signalScanningChanged(); + emit signalDevicesChanged(); + emit signalScanFinished(); +} + +void DeviceFinder::slotConnectToService( const QString &address ) +{ + m_pDeviceDiscoveryAgent->stop(); + + DeviceInfo *currentDevice = nullptr; + for( auto entry : qAsConst( m_devices ) ) + { + if( entry && entry->getAddress() == address ) + { + currentDevice = entry; + break; + } + } + + if( currentDevice ) + m_pDeviceHandler->setDevice( currentDevice ); + + clearMessages(); +} + +bool DeviceFinder::scanning() const +{ + return m_pDeviceDiscoveryAgent->isActive(); +} + +QList DeviceFinder::devices() +{ + return m_devices; +} + +QString DeviceFinder::getAddressByName( const QString &name ) +{ + if( name.isEmpty() || name.isNull() ) + return QString(); + + for( const auto &device : qAsConst( m_devices ) ) + { + if( device->getName() == name ) + return device->getAddress(); + } + + return QString(); +} + +void DeviceFinder::slotDisconnect() +{ + m_pDeviceHandler->slotDisconnectService(); +} diff --git b/src/devicefinder.h a/src/devicefinder.h new file mode 100644 index 0000000..b21470c --- /dev/null +++ a/src/devicefinder.h @@ -0,0 +1,84 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#ifndef OSDEV_COMPONENTS_BLUETOOTH_DEVICEFINDER_H +#define OSDEV_COMPONENTS_BLUETOOTH_DEVICEFINDER_H + +// osdev::components::bluetooh +#include "bluetoothbaseclass.h" +#include "deviceinfo.h" +#include "devicehandler.h" +//Qt +#include +#include +#include +#include + +namespace osdev { +namespace components { +namespace bluetooth { + +class DeviceFinder : public BluetoothBaseClass +{ + Q_OBJECT + +public: + DeviceFinder( DeviceHandler *handler, QObject *parent = nullptr ); + ~DeviceFinder(); + + /// Deleted copy-constructor + DeviceFinder( const DeviceFinder& ) = delete; + /// Deleted assignment operator + DeviceFinder& operator=(const DeviceFinder&) = delete; + /// Deleted move-constructor + DeviceFinder( DeviceFinder&& ) = delete; + /// Deleted move operator + DeviceFinder& operator=( DeviceFinder&& ) = delete; + + bool scanning() const; + QList devices(); + QString getAddressByName( const QString &name ); + +public slots: + void slotStartSearch(); + void slotConnectToService( const QString &address ); + void slotAddDevice( const QBluetoothDeviceInfo &device ); + void slotScanError( QBluetoothDeviceDiscoveryAgent::Error error ); + void slotScanFinished(); + void slotDisconnect(); + +signals: + void signalScanningChanged(); + void signalDevicesChanged(); + void signalScanFinished(); + void signalSendData( const QString &data ); + +private: + DeviceHandler *m_pDeviceHandler; + QBluetoothDeviceDiscoveryAgent *m_pDeviceDiscoveryAgent; + QList m_devices; +}; + +} /* End namespace bluetooth */ +} /* End namespace components */ +} /* End namepsace osdev */ + +#endif /* OSDEV_COMPONENTS_BLUETOOTH_DEVICEFINDER_H */ diff --git b/src/devicehandler.cpp a/src/devicehandler.cpp new file mode 100644 index 0000000..aac1a63 --- /dev/null +++ a/src/devicehandler.cpp @@ -0,0 +1,277 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +// osdev::components::bluetooth +#include "devicehandler.h" +#include "deviceinfo.h" +#include +#include +#include + +using namespace osdev::components::bluetooth; + +DeviceHandler::DeviceHandler( QObject *parent ) + : BluetoothBaseClass( parent ) + , m_pControl( nullptr ) + , m_pService( nullptr ) + , m_notificationDescriptor() + , m_pCurrentDevice( nullptr ) + , m_foundRequestedService( false ) + , m_measuring( false ) + , m_measurements() + , m_addressType( QLowEnergyController::PublicAddress ) + , m_requestedServiceUuid() + , m_requestedCharacteristicUuid() +{ + +} + +void DeviceHandler::setAddressType( AddressType type ) +{ + switch( type ) + { + case DeviceHandler::AddressType::PublicAddress: + { + m_addressType = QLowEnergyController::PublicAddress; + break; + } + case DeviceHandler::AddressType::RandomAddress: + { + m_addressType = QLowEnergyController::RandomAddress; + break; + } + } +} + +DeviceHandler::AddressType DeviceHandler::addressType() const +{ + if( m_addressType == QLowEnergyController::RandomAddress ) + return DeviceHandler::AddressType::RandomAddress; + + return DeviceHandler::AddressType::PublicAddress; +} + +void DeviceHandler::setDevice( DeviceInfo *device ) +{ + clearMessages(); + m_pCurrentDevice = device; + + // Disconnect and delete old connection + if( m_pControl ) + { + m_pControl->disconnectFromDevice(); + delete m_pControl; + m_pControl = nullptr; + } + + // Create new controller and connect it if device is available. + if( m_pCurrentDevice ) + { + // Make connections + //! [connect signals-1] + m_pControl = QLowEnergyController::createCentral( m_pCurrentDevice->getDevice(), this ); + m_pControl->setRemoteAddressType( m_addressType ); + + connect( m_pControl, &QLowEnergyController::serviceDiscovered, this, &DeviceHandler::slotServiceDiscovered ); + connect( m_pControl, &QLowEnergyController::discoveryFinished, this, &DeviceHandler::slotServiceScanDone ); + connect( m_pControl, + static_cast(&QLowEnergyController::error), + this, + [this](QLowEnergyController::Error error) + { + Q_UNUSED( error ); + emit signalConnectionStateChanged( false ); + setError( "Cannot connect to remote device." ); + } + ); + connect( m_pControl, &QLowEnergyController::disconnected, this, [this]() + { + emit signalConnectionStateChanged( false ); + setError( "LowEnergy controller disconnected" ); + }); + connect( m_pControl, &QLowEnergyController::connected, this, [this]() + { + emit signalConnectionStateChanged( true ); + setInfo( "LowEnergy controller connected... Search Services" ); + m_pControl->discoverServices(); + }); + + // Connect + m_pControl->connectToDevice(); + } +} + +void DeviceHandler::setRequestedServiceUuid( const QBluetoothUuid &service_uuid ) +{ + m_requestedServiceUuid = service_uuid; +} + +QBluetoothUuid DeviceHandler::requestedServiceUuid() const +{ + return m_requestedServiceUuid; +} + +void DeviceHandler::setRequestedCharacteristicUuid( const QBluetoothUuid &char_uuid ) +{ + m_requestedCharacteristicUuid = char_uuid; +} + +QBluetoothUuid DeviceHandler::requestedCharacteristicUuid() const +{ + return m_requestedCharacteristicUuid; +} + +void DeviceHandler::slotStartMeasurement() +{ + +} + +void DeviceHandler::slotStopMeasurement() +{ + +} + +void DeviceHandler::slotServiceDiscovered( const QBluetoothUuid &gatt ) +{ + if( gatt == m_requestedServiceUuid ) + { + setInfo( "Requested service discovered. Waiting for service scan to be done..." ); + m_foundRequestedService = true; + } +} + +void DeviceHandler::slotServiceScanDone() +{ + setInfo( "Service scan done." ); + + // Delete old service if available. + if( m_pService ) + { + delete m_pService; + m_pService = nullptr; + } + + // If Requested service is found, create a new service. + if( m_foundRequestedService ) + m_pService = m_pControl->createServiceObject( m_requestedServiceUuid, this ); + + if( m_pService ) + { + qInfo() << "Connected to service : " << m_requestedServiceUuid; + connect( m_pService, &QLowEnergyService::stateChanged, this, &DeviceHandler::slotServiceStateChanged ); + connect( m_pService, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::slotUpdateMeasuredValue ); + connect( m_pService, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::slotConfirmedDescriptorWrite ); + m_pService->discoverDetails(); + } + else + { + setError( "Requested Service was not found." ); + } +} + +void DeviceHandler::slotServiceStateChanged( QLowEnergyService::ServiceState state ) +{ + switch( state ) + { + case QLowEnergyService::DiscoveringServices: + setInfo( tr("Discovering Services..." ) ); + break; + case QLowEnergyService::ServiceDiscovered: + { + setInfo( tr( "Service discovered." ) ); + + const QLowEnergyCharacteristic fChar = m_pService->characteristic( m_requestedCharacteristicUuid ); + if( !fChar.isValid() ) + { + setError( "Requasted Data not Found." ); + break; + } + + m_notificationDescriptor = fChar.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration ); + if( m_notificationDescriptor.isValid() ) + { + m_pService->writeDescriptor( m_notificationDescriptor, QByteArray::fromHex( "0100" ) ); + } + break; + } + + default: + // nothing for now + break; + } + + emit signalAliveChanged(); +} + +void DeviceHandler::slotUpdateMeasuredValue( const QLowEnergyCharacteristic &c, const QByteArray &value ) +{ + // Ignore any other characteristic change -> shouldn't really happen, though + if( c.uuid() != m_requestedCharacteristicUuid ) + return; + + qInfo() << "QByteArray() : " << value << " => Size : " << value.size(); + + QString read_value = QString( value ); + emit signalReceivedValue( read_value ); +} + +void DeviceHandler::slotConfirmedDescriptorWrite( const QLowEnergyDescriptor &d, const QByteArray &value ) +{ + if( d.isValid() && d == m_notificationDescriptor && value == QByteArray::fromHex( "0000" ) ) + { + // disabled notifications -> assume disconnect intent + m_pControl->disconnectFromDevice(); + delete m_pService; + m_pService = nullptr; + } +} + +void DeviceHandler::slotDisconnectService() +{ + m_foundRequestedService = false; + + // disable notifications + if( m_notificationDescriptor.isValid() && m_pService && m_notificationDescriptor.value() == QByteArray::fromHex( "0100" ) ) + { + m_pService->writeDescriptor( m_notificationDescriptor, QByteArray::fromHex( "0000" ) ); + } + else + { + if( m_pControl ) + m_pControl->disconnectFromDevice(); + + delete m_pService; + m_pService = nullptr; + } +} + +bool DeviceHandler::measuring() const +{ + return m_measuring; +} + +bool DeviceHandler::alive() const +{ + if( m_pService ) + return m_pService->state() == QLowEnergyService::ServiceDiscovered; + + return false; +} diff --git b/src/devicehandler.h a/src/devicehandler.h new file mode 100644 index 0000000..d7f1b17 --- /dev/null +++ a/src/devicehandler.h @@ -0,0 +1,115 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#ifndef OSDEV_COMPONENTS_BLUETOOTH_DEVICEHANDLER_H +#define OSDEV_COMPONENTS_BLUETOOTH_DEVICEHANDLER_H + +#include "bluetoothbaseclass.h" +#include "deviceinfo.h" + +#include +#include +#include + +#include +#include + +namespace osdev { +namespace components { +namespace bluetooth { + +class DeviceHandler : public BluetoothBaseClass +{ + Q_OBJECT + +public: + enum class AddressType + { + PublicAddress, + RandomAddress + }; + + DeviceHandler( QObject *parent = nullptr ); + + /// Deleted copy-constructor + DeviceHandler( const DeviceHandler& ) = delete; + /// Deleted assignment operator + DeviceHandler& operator=(const DeviceHandler&) = delete; + /// Deleted move-constructor + DeviceHandler( DeviceHandler&& ) = delete; + /// Deleted move operator + DeviceHandler& operator=( DeviceHandler&& ) = delete; + + void setDevice( DeviceInfo *device ); + void setAddressType( AddressType type ); + AddressType addressType() const; + + bool measuring() const; + bool alive() const; + + void setRequestedServiceUuid( const QBluetoothUuid &service_uuid ); + QBluetoothUuid requestedServiceUuid() const; + + void setRequestedCharacteristicUuid( const QBluetoothUuid &char_uuid ); + QBluetoothUuid requestedCharacteristicUuid() const; + +signals: + void signalMeasuringChanged(); + void signalAliveChanged(); + void signalConnectionStateChanged( bool state ); + void signalStatsChanged(); + + void signalReceivedValue( const QString &value ); + +public slots: + void slotStartMeasurement(); + void slotStopMeasurement(); + void slotDisconnectService(); + + // QLowEnergyController + void slotServiceDiscovered( const QBluetoothUuid &gatt ); + void slotServiceScanDone(); + + // QLowEnergyService + void slotServiceStateChanged( QLowEnergyService::ServiceState state ); + void slotUpdateMeasuredValue( const QLowEnergyCharacteristic &characteristic, const QByteArray &value ); + void slotConfirmedDescriptorWrite( const QLowEnergyDescriptor &descriptor, const QByteArray &value ); + +private: + QLowEnergyController *m_pControl; + QLowEnergyService *m_pService; + QLowEnergyDescriptor m_notificationDescriptor; + DeviceInfo *m_pCurrentDevice; + + bool m_foundRequestedService; + bool m_measuring; + + QVector m_measurements; + QLowEnergyController::RemoteAddressType m_addressType; + QBluetoothUuid m_requestedServiceUuid; + QBluetoothUuid m_requestedCharacteristicUuid; +}; + +} /* End namespace bluetooth */ +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_BLUETOOTH_DEVICEHANDLER_H */ diff --git b/src/deviceinfo.cpp a/src/deviceinfo.cpp new file mode 100644 index 0000000..8c81951 --- /dev/null +++ a/src/deviceinfo.cpp @@ -0,0 +1,65 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +// osdev::components::bluetooth +#include "deviceinfo.h" +// Qt +#include +#include + +using namespace osdev::components::bluetooth; + +DeviceInfo::DeviceInfo( const QBluetoothDeviceInfo &info, QObject *parent ) + : QObject( parent ) + , m_device( info ) +{ + +} + +QBluetoothDeviceInfo DeviceInfo::getDevice() const +{ + return m_device; +} + +QString DeviceInfo::getName() const +{ + return m_device.name(); +} + +QString DeviceInfo::getAddress() const +{ +#if defined Q_OS_DARWIN + return m_device.deviceUuid().toString(); +#else + return m_device.address().toString(); +#endif +} + +QString DeviceInfo::getUuid() const +{ + return m_device.deviceUuid().toString(); +} + +void DeviceInfo::setDevice( const QBluetoothDeviceInfo &device ) +{ + m_device = device; + emit signalDeviceChanged(); +} diff --git b/src/deviceinfo.h a/src/deviceinfo.h new file mode 100644 index 0000000..960b334 --- /dev/null +++ a/src/deviceinfo.h @@ -0,0 +1,58 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#ifndef OSDEV_COMPONENTS_BLUETOOTH_DEVICEINFO_H +#define OSDEV_COMPONENTS_BLUETOOTH_DEVICEINFO_H + +#include +#include +#include + +namespace osdev { +namespace components { +namespace bluetooth { + +class DeviceInfo : public QObject +{ + Q_OBJECT + +public: + DeviceInfo( const QBluetoothDeviceInfo &device, QObject *parent = nullptr ); + + void setDevice( const QBluetoothDeviceInfo &device ); + + QString getName() const; + QString getAddress() const; + QString getUuid() const; + QBluetoothDeviceInfo getDevice() const; + +signals: + void signalDeviceChanged(); + +private: + QBluetoothDeviceInfo m_device; +}; + +} /* End namespace bluetooth */ +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_BLUETOOTH_DEVICEINFO_H */ diff --git b/tests/CMakeLists.txt a/tests/CMakeLists.txt new file mode 100644 index 0000000..3635320 --- /dev/null +++ a/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.0) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) + +include(projectheader) +project_header(test_logutils) + +include_directories( SYSTEM + ${CMAKE_CURRENT_SOURCE_DIR}/../../src +) + +include(compiler) +set(SRC_LIST +) + +# add_executable( ${PROJECT_NAME} +# ${SRC_LIST} +# ) + +# target_link_libraries( +# ${PROJECT_NAME} +# ) + +# set_target_properties( ${PROJECT_NAME} PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +# LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib +# ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/archive +# ) + +# include(installation) +# install_application()