devicehandler.cpp 9.09 KB
/* ****************************************************************************
 * 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 <QtEndian>
#include <QRandomGenerator>
#include <QScopedPointer>

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<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&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;
}