Improved scanning results model

This commit is contained in:
2021-08-17 14:03:33 +02:00
parent ea6849fead
commit 5f784b4bdc
9 changed files with 236 additions and 136 deletions

View File

@ -1,33 +1,26 @@
#include "bluetoothbaseclass.h" #include "bluetoothbaseclass.h"
BluetoothBaseClass::BluetoothBaseClass(QObject *parent) : QObject(parent) BluetoothBaseClass::BluetoothBaseClass(QObject *parent) :
QObject{parent}
{ {
} }
QString BluetoothBaseClass::error() const
{
return m_error;
}
QString BluetoothBaseClass::info() const
{
return m_info;
}
void BluetoothBaseClass::setError(const QString &error) void BluetoothBaseClass::setError(const QString &error)
{ {
if (m_error != error) { if (m_error == error)
m_error = error; return;
emit errorChanged();
} m_error = error;
emit errorChanged();
} }
void BluetoothBaseClass::setInfo(const QString &info) void BluetoothBaseClass::setInfo(const QString &info)
{ {
if (m_info != info) { if (m_info == info)
m_info = info; return;
emit infoChanged();
} m_info = info;
emit infoChanged();
} }
void BluetoothBaseClass::clearMessages() void BluetoothBaseClass::clearMessages()

View File

@ -1,6 +1,6 @@
#ifndef BLUETOOTHBASECLASS_H #pragma once
#define BLUETOOTHBASECLASS_H
// Qt includes
#include <QObject> #include <QObject>
class BluetoothBaseClass : public QObject class BluetoothBaseClass : public QObject
@ -12,10 +12,10 @@ class BluetoothBaseClass : public QObject
public: public:
explicit BluetoothBaseClass(QObject *parent = nullptr); explicit BluetoothBaseClass(QObject *parent = nullptr);
QString error() const; QString error() const { return m_error; }
void setError(const QString& error); void setError(const QString& error);
QString info() const; QString info() const { return m_info; }
void setInfo(const QString& info); void setInfo(const QString& info);
void clearMessages(); void clearMessages();
@ -28,5 +28,3 @@ private:
QString m_error; QString m_error;
QString m_info; QString m_info;
}; };
#endif // BLUETOOTHBASECLASS_H

View File

@ -12,7 +12,8 @@ HEADERS += \
bluetoothbaseclass.h \ bluetoothbaseclass.h \
settings.h settings.h
SOURCES += main.cpp \ SOURCES += \
main.cpp \
connectionhandler.cpp \ connectionhandler.cpp \
deviceinfo.cpp \ deviceinfo.cpp \
devicefinder.cpp \ devicefinder.cpp \
@ -20,12 +21,17 @@ SOURCES += main.cpp \
bluetoothbaseclass.cpp \ bluetoothbaseclass.cpp \
settings.cpp settings.cpp
RESOURCES += qml.qrc \ RESOURCES += \
qml.qrc \
images.qrc images.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model # Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = QML_IMPORT_PATH =
CONFIG += qmltypes
QML_IMPORT_NAME = bobbycar
QML_IMPORT_MAJOR_VERSION = 1
DISTFILES += \ DISTFILES += \
android/AndroidManifest.xml \ android/AndroidManifest.xml \
android/build.gradle \ android/build.gradle \

View File

@ -1,112 +1,190 @@
#include "devicefinder.h" #include "devicefinder.h"
// system includes
#include <algorithm>
// local includes
#include "devicehandler.h" #include "devicehandler.h"
#include "deviceinfo.h"
DeviceFinder::DeviceFinder(DeviceHandler *handler, QObject *parent): DeviceFinder::DeviceFinder(QObject *parent):
BluetoothBaseClass(parent), QAbstractItemModel{parent},
m_deviceHandler(handler) m_deviceDiscoveryAgent{this}
{ {
//! [devicediscovery-1] m_deviceDiscoveryAgent.setLowEnergyDiscoveryTimeout(5000);
m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(5000);
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice); connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice);
connect(m_deviceDiscoveryAgent, static_cast<void (QBluetoothDeviceDiscoveryAgent::*)(QBluetoothDeviceDiscoveryAgent::Error)>(&QBluetoothDeviceDiscoveryAgent::error), connect(&m_deviceDiscoveryAgent, qOverload<QBluetoothDeviceDiscoveryAgent::Error>(&QBluetoothDeviceDiscoveryAgent::error),
this, &DeviceFinder::scanError); this, &DeviceFinder::scanError);
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished); connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished);
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished); connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished);
//! [devicediscovery-1]
} }
DeviceFinder::~DeviceFinder() QModelIndex DeviceFinder::index(int row, int column, const QModelIndex &parent) const
{ {
qDeleteAll(m_devices); Q_UNUSED(parent)
m_devices.clear(); return createIndex(row, column);
}
QModelIndex DeviceFinder::parent(const QModelIndex &child) const
{
Q_UNUSED(child)
return {};
}
int DeviceFinder::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_devices.size();
}
int DeviceFinder::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant DeviceFinder::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
qWarning() << "invalid index";
return {};
}
if (index.row() < 0 ||
index.row() >= m_devices.size() ||
index.column() != 0)
{
qWarning() << "index out of bounds" << index;
return {};
}
const auto &device = m_devices.at(index.row());
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::UserRole + 1:
return device.name();
case Qt::UserRole + 2:
return device.address().toString();
}
return {};
}
QMap<int, QVariant> DeviceFinder::itemData(const QModelIndex &index) const
{
if (!index.isValid())
{
qWarning() << "invalid index";
return {};
}
if (index.row() < 0 ||
index.row() >= m_devices.size() ||
index.column() != 0)
{
qWarning() << "index out of bounds" << index;
return {};
}
const auto &device = m_devices.at(index.row());
return QMap<int, QVariant> {
{ Qt::UserRole + 1, device.name() },
{ Qt::UserRole + 2, device.address().toString() }
};
}
QHash<int, QByteArray> DeviceFinder::roleNames() const
{
return QHash<int, QByteArray> {
{ Qt::UserRole + 1, QByteArrayLiteral("deviceName") },
{ Qt::UserRole + 2, QByteArrayLiteral("deviceAddress") }
};
} }
void DeviceFinder::startSearch() void DeviceFinder::startSearch()
{ {
clearMessages(); clearMessages();
m_deviceHandler->setDevice(nullptr);
qDeleteAll(m_devices); beginResetModel();
m_devices.clear(); m_devices.clear();
endResetModel();
emit devicesChanged(); m_deviceDiscoveryAgent.start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
//! [devicediscovery-2]
m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
//! [devicediscovery-2]
emit scanningChanged(); emit scanningChanged();
setInfo(tr("Scanning for devices...")); setInfo(tr("Scanning for devices..."));
} }
//! [devicediscovery-3]
void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device) void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device)
{ {
// If device is LowEnergy-device, add it to the list // If device is LowEnergy-device, add it to the list
if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { if (!(device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration))
//m_devices.append(new DeviceInfo(device)); return;
auto info = new DeviceInfo(device);
if(info->getName().contains("bobby")) { // Only add devices with "bobby" in device name to list; (filter) //if (!device.name().contains("bobby"))
m_devices.append(info); // return;
setInfo(tr("Low Energy device found. Scanning more..."));
} beginInsertRows({}, m_devices.size(), m_devices.size());
//! [devicediscovery-3] m_devices.push_back(device);
emit devicesChanged(); endInsertRows();
//! [devicediscovery-4]
} setInfo(tr("Low Energy device found. Scanning more..."));
//...
} }
//! [devicediscovery-4]
void DeviceFinder::scanError(QBluetoothDeviceDiscoveryAgent::Error error) void DeviceFinder::scanError(QBluetoothDeviceDiscoveryAgent::Error error)
{ {
if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError) switch (error)
{
case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
setError(tr("The Bluetooth adaptor is powered off.")); setError(tr("The Bluetooth adaptor is powered off."));
else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError) break;
case QBluetoothDeviceDiscoveryAgent::InputOutputError:
setError(tr("Writing or reading from the device resulted in an error.")); setError(tr("Writing or reading from the device resulted in an error."));
else break;
default:
setError(tr("An unknown error has occurred.")); setError(tr("An unknown error has occurred."));
}
} }
void DeviceFinder::scanFinished() void DeviceFinder::scanFinished()
{ {
if (m_devices.isEmpty()) if (m_devices.empty())
setError(tr("No Low Energy devices found.")); setError(tr("No Low Energy devices found."));
else else
setInfo(tr("Scanning done.")); setInfo(tr("Scanning done."));
emit scanningChanged(); emit scanningChanged();
emit devicesChanged();
} }
void DeviceFinder::connectToService(const QString &address) void DeviceFinder::connectToService(const QString &address)
{ {
m_deviceDiscoveryAgent->stop(); m_deviceDiscoveryAgent.stop();
DeviceInfo *currentDevice = nullptr; auto iter = std::find_if(std::cbegin(m_devices), std::cend(m_devices), [&address](const QBluetoothDeviceInfo &device){
for (QObject *entry : qAsConst(m_devices)) { return device.address().toString() == address;
auto device = qobject_cast<DeviceInfo *>(entry); });
if (device && device->getAddress() == address ) {
currentDevice = device; if (iter == std::cend(m_devices))
break; {
} qWarning() << "could not find address" << address;
setError(tr("could not find address %0").arg(address));
return;
} }
if (currentDevice) if (!m_handler)
m_deviceHandler->setDevice(currentDevice); {
qWarning() << "no valid handler!";
setError(tr("no valid handler!"));
return;
}
m_handler->setDevice(*iter);
clearMessages(); clearMessages();
} }
bool DeviceFinder::scanning() const
{
return m_deviceDiscoveryAgent->isActive();
}
QVariant DeviceFinder::devices()
{
return QVariant::fromValue(m_devices);
}

View File

@ -1,48 +1,74 @@
#ifndef DEVICEFINDER_H #pragma once
#define DEVICEFINDER_H
#include "bluetoothbaseclass.h" // system includes
#include <memory>
#include <vector>
// Qt includes
#include <QAbstractItemModel>
#include <QTimer> #include <QTimer>
#include <QVariant> #include <QVariant>
#include <QBluetoothDeviceDiscoveryAgent> #include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo> #include <QBluetoothDeviceInfo>
#include <QtQml/qqml.h>
// forward declares
class DeviceInfo;
class DeviceHandler; class DeviceHandler;
class DeviceFinder: public BluetoothBaseClass class DeviceFinder : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
Q_PROPERTY(QVariant devices READ devices NOTIFY devicesChanged) Q_PROPERTY(DeviceHandler* handler READ handler WRITE setHandler NOTIFY handlerChanged)
Q_PROPERTY(QString error READ error WRITE setError NOTIFY errorChanged)
Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
QML_ELEMENT
public: public:
DeviceFinder(DeviceHandler *handler, QObject *parent = nullptr); DeviceFinder(QObject *parent = nullptr);
~DeviceFinder();
bool scanning() const; bool scanning() const { return m_deviceDiscoveryAgent.isActive(); }
QVariant devices();
DeviceHandler* handler() { return m_handler; }
const DeviceHandler* handler() const { return m_handler; }
void setHandler(DeviceHandler* handler) { if (m_handler == handler) return; m_handler = handler; emit handlerChanged(); }
QString error() const { return m_error; }
void setError(const QString& error) { if (m_error == error) return; m_error = error; emit errorChanged(); }
QString info() const { return m_info; }
void setInfo(const QString& info) { if (m_info == info) return; m_info = info; emit infoChanged(); }
void clearMessages() { setInfo(""); setError(""); }
// QAbstractItemModel interface
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void scanningChanged();
void handlerChanged();
void errorChanged();
void infoChanged();
public slots: public slots:
void startSearch(); void startSearch();
void connectToService(const QString &address); void connectToService(const QString &address);
private slots: private slots:
void addDevice(const QBluetoothDeviceInfo&); void addDevice(const QBluetoothDeviceInfo &);
void scanError(QBluetoothDeviceDiscoveryAgent::Error error); void scanError(QBluetoothDeviceDiscoveryAgent::Error error);
void scanFinished(); void scanFinished();
signals:
void scanningChanged();
void devicesChanged();
private: private:
DeviceHandler *m_deviceHandler; DeviceHandler *m_handler{};
QBluetoothDeviceDiscoveryAgent *m_deviceDiscoveryAgent; QBluetoothDeviceDiscoveryAgent m_deviceDiscoveryAgent;
QList<QObject*> m_devices; std::vector<QBluetoothDeviceInfo> m_devices;
QString m_error;
QString m_info;
}; };
#endif // DEVICEFINDER_H

View File

@ -27,7 +27,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
{ {
} }
void DeviceHandler::setDevice(DeviceInfo *device) void DeviceHandler::setDevice(const QBluetoothDeviceInfo &device)
{ {
clearMessages(); clearMessages();
m_currentDevice = device; m_currentDevice = device;
@ -41,14 +41,12 @@ void DeviceHandler::setDevice(DeviceInfo *device)
} }
// Create new controller and connect it if device available // Create new controller and connect it if device available
if (m_currentDevice) if (m_currentDevice.isValid())
{ {
// Make connections // Make connections
//! [Connect-Signals-1] m_control = QLowEnergyController::createCentral(m_currentDevice, this);
m_control = QLowEnergyController::createCentral(m_currentDevice->getDevice(), this);
//! [Connect-Signals-1]
m_control->setRemoteAddressType(m_addressType); m_control->setRemoteAddressType(m_addressType);
//! [Connect-Signals-2]
connect(m_control, &QLowEnergyController::serviceDiscovered, connect(m_control, &QLowEnergyController::serviceDiscovered,
this, &DeviceHandler::serviceDiscovered); this, &DeviceHandler::serviceDiscovered);
connect(m_control, &QLowEnergyController::discoveryFinished, connect(m_control, &QLowEnergyController::discoveryFinished,
@ -69,13 +67,13 @@ void DeviceHandler::setDevice(DeviceInfo *device)
// Connect // Connect
m_control->connectToDevice(); m_control->connectToDevice();
//! [Connect-Signals-2]
} }
} }
void DeviceHandler::setAddressType(AddressType type) void DeviceHandler::setAddressType(AddressType type)
{ {
switch (type) { switch (type)
{
case DeviceHandler::AddressType::PublicAddress: case DeviceHandler::AddressType::PublicAddress:
m_addressType = QLowEnergyController::PublicAddress; m_addressType = QLowEnergyController::PublicAddress;
break; break;
@ -351,6 +349,7 @@ void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, cons
void DeviceHandler::confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info, const QByteArray &value) void DeviceHandler::confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info, const QByteArray &value)
{ {
Q_UNUSED(value)
qDebug() << "confirmedCharacteristicWrite"; qDebug() << "confirmedCharacteristicWrite";
if (info == m_remotecontrolCharacteristic) if (info == m_remotecontrolCharacteristic)

View File

@ -1,15 +1,15 @@
#ifndef DEVICEHANDLER_H #pragma once
#define DEVICEHANDLER_H
#include "bluetoothbaseclass.h"
// Qt includes
#include <QDateTime> #include <QDateTime>
#include <QTimer> #include <QTimer>
#include <QVector> #include <QVector>
#include <QLowEnergyController> #include <QLowEnergyController>
#include <QLowEnergyService> #include <QLowEnergyService>
// local includes
#include "bluetoothbaseclass.h"
class DeviceInfo; class DeviceInfo;
class DeviceHandler : public BluetoothBaseClass class DeviceHandler : public BluetoothBaseClass
@ -49,7 +49,7 @@ public:
DeviceHandler(QObject *parent = nullptr); DeviceHandler(QObject *parent = nullptr);
void setDevice(DeviceInfo *device); void setDevice(const QBluetoothDeviceInfo &device);
void setAddressType(AddressType type); void setAddressType(AddressType type);
AddressType addressType() const; AddressType addressType() const;
@ -131,7 +131,7 @@ private:
QLowEnergyService *m_service = nullptr; QLowEnergyService *m_service = nullptr;
QLowEnergyDescriptor m_notificationDescLivestats; QLowEnergyDescriptor m_notificationDescLivestats;
QLowEnergyCharacteristic m_remotecontrolCharacteristic; QLowEnergyCharacteristic m_remotecontrolCharacteristic;
DeviceInfo *m_currentDevice{}; QBluetoothDeviceInfo m_currentDevice;
bool m_foundBobbycarService{}; bool m_foundBobbycarService{};
@ -161,5 +161,3 @@ private:
bool m_waitingForWrite{}; bool m_waitingForWrite{};
}; };
#endif // DEVICEHANDLER_H

View File

@ -15,13 +15,11 @@ int main(int argc, char *argv[])
ConnectionHandler connectionHandler; ConnectionHandler connectionHandler;
DeviceHandler deviceHandler; DeviceHandler deviceHandler;
DeviceFinder deviceFinder(&deviceHandler);
qmlRegisterUncreatableType<DeviceHandler>("Shared", 1, 0, "AddressType", "Enum is not a type"); qmlRegisterUncreatableType<DeviceHandler>("Shared", 1, 0, "AddressType", "Enum is not a type");
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("connectionHandler", &connectionHandler); engine.rootContext()->setContextProperty("connectionHandler", &connectionHandler);
engine.rootContext()->setContextProperty("deviceFinder", &deviceFinder);
engine.rootContext()->setContextProperty("deviceHandler", &deviceHandler); engine.rootContext()->setContextProperty("deviceHandler", &deviceHandler);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));

View File

@ -1,8 +1,12 @@
import QtQuick 2.15 import QtQuick 2.15
import Shared 1.0 import Shared 1.0
import bobbycar 1.0
GamePage { GamePage {
DeviceFinder {
id: deviceFinder
handler: deviceHandler
}
function init() { function init() {
deviceFinder.startSearch() deviceFinder.startSearch()
@ -49,7 +53,7 @@ GamePage {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.top: title.bottom anchors.top: title.bottom
model: deviceFinder.devices model: deviceFinder
clip: true clip: true
delegate: Rectangle { delegate: Rectangle {
@ -61,15 +65,15 @@ GamePage {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
deviceFinder.connectToService(modelData.deviceAddress); deviceFinder.connectToService(deviceAddress);
app.showPage("Livedata.qml") app.showPage("Livedata.qml")
} }
} }
Text { Text {
id: device //id: device
font.pixelSize: GameSettings.smallFontSize font.pixelSize: GameSettings.smallFontSize
text: modelData.deviceName text: deviceName
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: parent.height * 0.1 anchors.topMargin: parent.height * 0.1
anchors.leftMargin: parent.height * 0.1 anchors.leftMargin: parent.height * 0.1
@ -78,9 +82,9 @@ GamePage {
} }
Text { Text {
id: deviceAddress //id: deviceAddress
font.pixelSize: GameSettings.smallFontSize font.pixelSize: GameSettings.smallFontSize
text: modelData.deviceAddress text: deviceAddress
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: parent.height * 0.1 anchors.bottomMargin: parent.height * 0.1
anchors.rightMargin: parent.height * 0.1 anchors.rightMargin: parent.height * 0.1