diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 98e0bd5f31c18fc86534079c2388c2cd26e5d850..83ee73aa55ce9e596d6932232a85bf4a6ca9130b 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -69,11 +69,16 @@ QT += \ xml \ !MobileBuild { - QT += \ +QT += \ printsupport \ serialport \ } +!WindowsBuild { +QT += \ + bluetooth \ +} + contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) { QT += multimedia } @@ -304,6 +309,9 @@ HEADERS += \ WindowsBuild { PRECOMPILED_HEADER += src/stable_headers.h HEADERS += src/stable_headers.h +} else { + HEADERS += \ + src/comm/BluetoothLink.h \ } !iOSBuild { @@ -425,6 +433,11 @@ SOURCES += \ src/ui/SerialConfigurationWindow.cc \ } +!WindowsBuild { + SOURCES += \ + src/comm/BluetoothLink.cc \ +} + !MobileBuild { SOURCES += \ src/comm/LogReplayLink.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 230f97571c99818a2f3ee45bb12a5a2419882ee1..9a447a30b7e59ebede2b4e98f98ca59e22807d51 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -16,6 +16,7 @@ src/AutoPilotPlugins/PX4/FlightModesComponent.qml src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml + src/ui/preferences/BluetoothSettings.qml src/ui/preferences/DebugWindow.qml src/ui/preferences/GeneralSettings.qml src/ui/preferences/LinkSettings.qml diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc new file mode 100644 index 0000000000000000000000000000000000000000..6fb2a0eb8671f1b449c762337373cde1d119e3e7 --- /dev/null +++ b/src/comm/BluetoothLink.cc @@ -0,0 +1,420 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009 - 2015 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Definition of Bluetooth connection for unmanned vehicles + * @author Gus Grubba + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "BluetoothLink.h" +#include "QGC.h" + +static void print_device_info(QBluetoothDeviceInfo info) +{ + qDebug() << "Bluetooth: " << info.name(); + qDebug() << " Services:" << info.serviceClasses(); + qDebug() << " Major Classs:" << info.majorDeviceClass(); + qDebug() << " Minor Classs:" << info.minorDeviceClass(); + qDebug() << " RSSI:" << info.rssi(); + qDebug() << " UUID:" << info.deviceUuid(); + qDebug() << " Service UUID:" << info.serviceUuids(); + qDebug() << " Core Config:" << info.coreConfigurations(); + qDebug() << " Valid:" << info.isValid(); +} + +BluetoothLink::BluetoothLink(BluetoothConfiguration* config) + : _connectState(false) + , _targetSocket(NULL) + , _targetDevice(NULL) + , _deviceDiscover(NULL) + , _running(false) +{ + Q_ASSERT(config != NULL); + _config = config; + _config->setLink(this); + // We're doing it wrong - because the Qt folks got the API wrong: + // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ + //moveToThread(this); +} + +BluetoothLink::~BluetoothLink() +{ + // Disconnect link from configuration + _config->setLink(NULL); + _disconnect(); + // Tell the thread to exit + _running = false; + quit(); + // Wait for it to exit + wait(); + this->deleteLater(); +} + +void BluetoothLink::run() +{ + while (!_targetDevice && _running) + this->msleep(50); + if(_running && _hardwareConnect()) { + exec(); + } +} + +void BluetoothLink::deviceDiscovered(QBluetoothDeviceInfo info) +{ + if(info.name() == _config->device()) + { + if(!_targetDevice) + { + print_device_info(info); + _targetDevice = new QBluetoothDeviceInfo(info); + //-- Start Thread + _running = true; + start(NormalPriority); + } + } +} + +void BluetoothLink::doneScanning() +{ + if(!_targetDevice) + { + _connectState = false; + qWarning() << "Bluetooth scanning did not find device" << _config->device(); + emit communicationError("Bluetooth Link Error", "Device Not Found"); + _running = false; + } +} + +void BluetoothLink::_restartConnection() +{ + if(this->isConnected()) + { + _disconnect(); + _connect(); + } +} + +QString BluetoothLink::getName() const +{ + return _config->name(); +} + +void BluetoothLink::writeBytes(const char* data, qint64 size) +{ + _sendBytes(data, size); +} + +void BluetoothLink::_sendBytes(const char* data, qint64 size) +{ + if(_targetSocket) + { + if(_targetSocket->isWritable()) + { + if(_targetSocket->write(data, size) > 0) { + _logOutputDataRate(size, QDateTime::currentMSecsSinceEpoch()); + } + else + qWarning() << "Bluetooth write error"; + } + else + qWarning() << "Bluetooth not writable error"; + } +} + +void BluetoothLink::readBytes() +{ + while (_targetSocket->bytesAvailable() > 0) + { + QByteArray datagram; + datagram.resize(_targetSocket->bytesAvailable()); + _targetSocket->read(datagram.data(), datagram.size()); + emit bytesReceived(this, datagram); + _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); + } +} + +void BluetoothLink::_disconnect(void) +{ + _running = false; + quit(); + wait(); + if(_targetDevice) + { + delete _targetDevice; + _targetDevice = NULL; + } + if(_targetSocket) + { + _targetSocket->deleteLater(); + _targetSocket = NULL; + emit disconnected(); + } + if(_deviceDiscover) + { + _deviceDiscover->stop(); + delete _deviceDiscover; + _deviceDiscover = NULL; + } + _connectState = false; +} + +bool BluetoothLink::_connect(void) +{ + if(this->isRunning() || _running) + { + _running = false; + quit(); + wait(); + } + if(_deviceDiscover) + { + _deviceDiscover->stop(); + delete _deviceDiscover; + _deviceDiscover = NULL; + } + if(_targetDevice) + { + delete _targetDevice; + _targetDevice = NULL; + } + //-- Scan devices + _deviceDiscover = new QBluetoothDeviceDiscoveryAgent(); + QObject::connect(_deviceDiscover, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); + QObject::connect(_deviceDiscover, SIGNAL(finished()), this, SLOT(doneScanning())); + _deviceDiscover->setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); + _deviceDiscover->start(); + return true; +} + +bool BluetoothLink::_hardwareConnect() +{ + if(_targetSocket) + { + delete _targetSocket; + _targetSocket = NULL; + } + // Build Device Class Descriptor + /* + quint32 device_class = 0; + device_class |= ((0x20 | 0x100) << 13); // Service Class + device_class |= (0x04 << 8); // Audio Device (CLASS MAJOR) The Crossfire presents itself as an "Audio Service" + device_class |= (0x01 << 2); // WearableHeadsetDevice (CLASS MINOR) + qDebug() << _config->address() << _config->device() << device_class; + _targetDevice = new QBluetoothDeviceInfo(QBluetoothAddress(_config->address()), _config->device(), device_class); + _targetDevice->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration); + */ + + _targetSocket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); + _targetSocket->moveToThread(this); + + qDebug() << "Connecting..."; + print_device_info(*_targetDevice); + + QObject::connect(_targetSocket, SIGNAL(connected()), this, SLOT(deviceConnected())); + QObject::connect(_targetSocket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(deviceError(QBluetoothSocket::SocketError))); + QObject::connect(_targetSocket, SIGNAL(readyRead()), this, SLOT(readBytes())); + QObject::connect(_targetSocket, SIGNAL(disconnected()), this, SLOT(deviceDisconnected())); + QObject::connect(_targetSocket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), this, SLOT(stateChanged(QBluetoothSocket::SocketState))); + _targetSocket->connectToService(_targetDevice->address(), QBluetoothUuid(QBluetoothUuid::Rfcomm)); + return true; +} + +void BluetoothLink::stateChanged(QBluetoothSocket::SocketState state) +{ + qDebug() << "Bluetooth state:" << state; +} + +void BluetoothLink::deviceConnected() +{ + _connectState = true; + emit connected(); +} + +void BluetoothLink::deviceDisconnected() +{ + _connectState = false; + qWarning() << "Bluetooth disconnected"; +} + +void BluetoothLink::deviceError(QBluetoothSocket::SocketError error) +{ + _connectState = false; + qWarning() << "Bluetooth error" << error; + emit communicationError("Bluetooth Link Error", _targetSocket->errorString()); +} + +bool BluetoothLink::isConnected() const +{ + return _connectState; +} + +qint64 BluetoothLink::getConnectionSpeed() const +{ + return 1000000; // 1 Mbit +} + +qint64 BluetoothLink::getCurrentInDataRate() const +{ + return 0; +} + +qint64 BluetoothLink::getCurrentOutDataRate() const +{ + return 0; +} + +//-------------------------------------------------------------------------- +//-- BluetoothConfiguration + +BluetoothConfiguration::BluetoothConfiguration(const QString& name) + : LinkConfiguration(name) + , _deviceDiscover(NULL) +{ + +} + +BluetoothConfiguration::BluetoothConfiguration(BluetoothConfiguration* source) + : LinkConfiguration(source) + , _deviceDiscover(NULL) +{ + _address = source->address(); + _device = source->device(); +} + +BluetoothConfiguration::~BluetoothConfiguration() +{ + if(_deviceDiscover) + { + _deviceDiscover->stop(); + delete _deviceDiscover; + } +} + +void BluetoothConfiguration::copyFrom(LinkConfiguration *source) +{ + LinkConfiguration::copyFrom(source); + BluetoothConfiguration* usource = dynamic_cast(source); + Q_ASSERT(usource != NULL); + _address = usource->address(); + _device = usource->device(); +} + +void BluetoothConfiguration::saveSettings(QSettings& settings, const QString& root) +{ + settings.beginGroup(root); + settings.setValue("device", _device); + settings.setValue("address", _address); + settings.endGroup(); +} + +void BluetoothConfiguration::loadSettings(QSettings& settings, const QString& root) +{ + settings.beginGroup(root); + QString device = settings.value("device", _device).toString(); + QString address = settings.value("address", _address).toString(); + _device = device; + _address = address; + settings.endGroup(); +} + +void BluetoothConfiguration::updateSettings() +{ + if(_link) { + BluetoothLink* ulink = dynamic_cast(_link); + if(ulink) { + ulink->_restartConnection(); + } + } +} + +void BluetoothConfiguration::stopScan() +{ + if(_deviceDiscover) + { + + } +} + +void BluetoothConfiguration::startScan() +{ + if(!_deviceDiscover) + { + _deviceDiscover = new QBluetoothDeviceDiscoveryAgent(this); + connect(_deviceDiscover, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothConfiguration::deviceDiscovered); + connect(_deviceDiscover, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothConfiguration::doneScanning); + emit scanningChanged(); + } + else + { + _deviceDiscover->stop(); + } + _deviceList.clear(); + _addressList.clear(); + emit deviceListChanged(); + _deviceDiscover->setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); + _deviceDiscover->start(); +} + +void BluetoothConfiguration::deviceDiscovered(QBluetoothDeviceInfo info) +{ + _deviceList += info.name(); + _addressList += info.address().toString(); + print_device_info(info); + emit deviceListChanged(); +} + +void BluetoothConfiguration::doneScanning() +{ + if(_deviceDiscover) + { + _deviceDiscover->deleteLater(); + _deviceDiscover = NULL; + emit scanningChanged(); + } +} + +void BluetoothConfiguration::setDevice(QString device) +{ + int idx = _deviceList.indexOf(device); + if(idx >= 0) + { + _address = _addressList.at(idx); + _device = device; + emit deviceChanged(); + emit addressChanged(); + } +} + diff --git a/src/comm/BluetoothLink.h b/src/comm/BluetoothLink.h new file mode 100644 index 0000000000000000000000000000000000000000..ee4993c41e0e782391e0896f2263b3aff8b3b18f --- /dev/null +++ b/src/comm/BluetoothLink.h @@ -0,0 +1,166 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009 - 2015 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/*! + * @file + * @brief Bluetooth connection for unmanned vehicles + * @author Gus Grubba + * + */ + +#ifndef BTLINK_H +#define BTLINK_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QGCConfig.h" +#include "LinkManager.h" + +class QBluetoothDeviceDiscoveryAgent; + +class BluetoothConfiguration : public LinkConfiguration +{ + Q_OBJECT + +public: + + BluetoothConfiguration(const QString& name); + BluetoothConfiguration(BluetoothConfiguration* source); + ~BluetoothConfiguration(); + + Q_PROPERTY(QString device READ device WRITE setDevice NOTIFY deviceChanged) + Q_PROPERTY(QString address READ address WRITE setAddress NOTIFY addressChanged) + Q_PROPERTY(QStringList deviceList READ deviceList NOTIFY deviceListChanged) + Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) + + Q_INVOKABLE void startScan (); + Q_INVOKABLE void stopScan (); + + QString device () { return _device; } + QString address () { return _address; } + QStringList deviceList () { return _deviceList; } + bool scanning () { return _deviceDiscover != NULL; } + + void setDevice (QString device); + void setAddress (QString address) { _address = address; emit addressChanged(); } + + /// From LinkConfiguration + LinkType type () { return LinkConfiguration::TypeBluetooth; } + void copyFrom (LinkConfiguration* source); + void loadSettings (QSettings& settings, const QString& root); + void saveSettings (QSettings& settings, const QString& root); + void updateSettings (); + QString settingsURL () { return "BluetoothSettings.qml"; } + +public slots: + void deviceDiscovered (QBluetoothDeviceInfo info); + void doneScanning (); + +signals: + void newDevice (QBluetoothDeviceInfo info); + void deviceChanged (); + void addressChanged (); + void deviceListChanged (); + void scanningChanged (); + +private: + +private: + QBluetoothDeviceDiscoveryAgent* _deviceDiscover; + QString _device; + QString _address; + QStringList _deviceList; + QStringList _addressList; +}; + +class BluetoothLink : public LinkInterface +{ + Q_OBJECT + + friend class BluetoothConfiguration; + friend class LinkManager; + +public: + void requestReset () { } + bool isConnected () const; + QString getName () const; + + // Extensive statistics for scientific purposes + qint64 getConnectionSpeed () const; + qint64 getCurrentInDataRate () const; + qint64 getCurrentOutDataRate () const; + + void run(); + + // These are left unimplemented in order to cause linker errors which indicate incorrect usage of + // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. + bool connect (void); + bool disconnect (void); + + LinkConfiguration* getLinkConfiguration() { return _config; } + +public slots: + + void readBytes (); + void writeBytes (const char* data, qint64 length); + void deviceConnected (); + void deviceDisconnected (); + void deviceError (QBluetoothSocket::SocketError error); + void stateChanged (QBluetoothSocket::SocketState state); + void deviceDiscovered (QBluetoothDeviceInfo info); + void doneScanning (); + +protected: + + BluetoothConfiguration* _config; + bool _connectState; + +private: + // Links are only created/destroyed by LinkManager so constructor/destructor is not public + BluetoothLink(BluetoothConfiguration* config); + ~BluetoothLink(); + + // From LinkInterface + bool _connect (void); + void _disconnect (void); + + bool _hardwareConnect (); + void _restartConnection (); + void _sendBytes (const char* data, qint64 size); + +private: + + QBluetoothSocket* _targetSocket; + QBluetoothDeviceInfo* _targetDevice; + QBluetoothDeviceDiscoveryAgent* _deviceDiscover; + bool _running; +}; + +#endif // BTLINK_H diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc index 1656ff5f622f9ee1c6844bb8a95e45764859db9d..9af87788f2658a56c6c3a9f6d1f2e95da610c56e 100644 --- a/src/comm/LinkConfiguration.cc +++ b/src/comm/LinkConfiguration.cc @@ -36,7 +36,9 @@ This file is part of the QGROUNDCONTROL project #ifndef __mobile__ #include "LogReplayLink.h" #endif - +#ifndef Q_OS_WIN +#include "BluetoothLink.h" +#endif #ifdef QT_DEBUG #include "MockLink.h" #endif @@ -101,6 +103,11 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na case LinkConfiguration::TypeTcp: config = new TCPConfiguration(name); break; +#ifndef Q_OS_WIN + case LinkConfiguration::TypeBluetooth: + config = new BluetoothConfiguration(name); + break; +#endif #ifndef __mobile__ case LinkConfiguration::TypeLogReplay: config = new LogReplayLinkConfiguration(name); @@ -134,6 +141,11 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc case TypeTcp: dupe = new TCPConfiguration(dynamic_cast(source)); break; +#ifndef Q_OS_WIN + case TypeBluetooth: + dupe = new BluetoothConfiguration(dynamic_cast(source)); + break; +#endif #ifndef __mobile__ case TypeLogReplay: dupe = new LogReplayLinkConfiguration(dynamic_cast(source)); diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index d569d4f9e495bddee12f4453238fac942c896f27..082f0961842cb4ef5f87f86a6a03162eae70b9d2 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -57,12 +57,16 @@ public: void setLink(LinkInterface* link); /// The link types supported by QGC + /// Any changes here MUST be reflected in LinkManager::linkTypeStrings() enum LinkType { #ifndef __ios__ TypeSerial, ///< Serial Link #endif TypeUdp, ///< UDP Link TypeTcp, ///< TCP Link +#ifndef Q_OS_WIN + TypeBluetooth, ///< Bluetooth Link +#endif #if 0 // TODO Below is not yet implemented TypeForwarding, ///< Forwarding Link diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 55d0a36b99abe46c912a9347eec1c3a670be66a4..93b5e5c2a1c46fe10a5d99237b6e8f28bbec2e57 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -44,6 +44,9 @@ This file is part of the QGROUNDCONTROL project #include "QGCApplication.h" #include "UDPLink.h" #include "TCPLink.h" +#ifndef Q_OS_WIN +#include "BluetoothLink.h" +#endif QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog") QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog") @@ -124,6 +127,11 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) case LinkConfiguration::TypeTcp: pLink = new TCPLink(dynamic_cast(config)); break; +#ifndef Q_OS_WIN + case LinkConfiguration::TypeBluetooth: + pLink = new BluetoothLink(dynamic_cast(config)); + break; +#endif #ifndef __mobile__ case LinkConfiguration::TypeLogReplay: pLink = new LogReplayLink(dynamic_cast(config)); @@ -359,6 +367,11 @@ void LinkManager::loadLinkConfigurationList() case LinkConfiguration::TypeTcp: pLink = (LinkConfiguration*)new TCPConfiguration(name); break; +#ifndef Q_OS_WIN + case LinkConfiguration::TypeBluetooth: + pLink = (LinkConfiguration*)new BluetoothConfiguration(name); + break; +#endif #ifndef __mobile__ case LinkConfiguration::TypeLogReplay: pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name); @@ -701,12 +714,16 @@ QStringList LinkManager::linkTypeStrings(void) const #endif list += "UDP"; list += "TCP"; +#ifndef Q_OS_WIN + list += "Bluetooth"; +#endif #ifdef QT_DEBUG list += "Mock Link"; #endif #ifndef __mobile__ list += "Log Replay"; #endif + Q_ASSERT(list.size() == (int)LinkConfiguration::TypeLast); } return list; } @@ -828,6 +845,15 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config) } } break; +#ifndef Q_OS_WIN + case LinkConfiguration::TypeBluetooth: { + BluetoothConfiguration* tconfig = dynamic_cast(config); + if(tconfig) { + config->setName(QString("Bluetooth Device on %1").arg(tconfig->device())); + } + } + break; +#endif #ifndef __mobile__ case LinkConfiguration::TypeLogReplay: { LogReplayLinkConfiguration* tconfig = dynamic_cast(config); diff --git a/src/main.cc b/src/main.cc index 2de8233a8047c6f6b9a1785c0ed718c4b6a5ad32..03a524fce84ac470a20777f08fb4029a2fa6bbf5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -50,6 +50,10 @@ This file is part of the QGROUNDCONTROL project #endif #endif +#ifndef Q_OS_WIN +#include +#endif + #include /* SDL does ugly things to main() */ @@ -135,6 +139,10 @@ int main(int argc, char *argv[]) // anyway to silence the debug output. #ifndef __ios__ qRegisterMetaType(); +#endif +#ifndef Q_OS_WIN + qRegisterMetaType(); + qRegisterMetaType(); #endif qRegisterMetaType(); #ifndef __mobile__ diff --git a/src/ui/preferences/BluetoothSettings.qml b/src/ui/preferences/BluetoothSettings.qml new file mode 100644 index 0000000000000000000000000000000000000000..b6a10e4ec81be622c4311009675018e5eeb5a347 --- /dev/null +++ b/src/ui/preferences/BluetoothSettings.qml @@ -0,0 +1,179 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.1 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Item { + id: _btSettings + width: parent ? parent.width : 0 + height: btColumn.height + + function saveSettings() { + // No need + } + + property var _currentDevice: "" + + Column { + id: btColumn + spacing: ScreenTools.defaultFontPixelHeight / 2 + + ExclusiveGroup { id: linkGroup } + + QGCPalette { + id: qgcPal + colorGroupEnabled: enabled + } + + QGCLabel { + id: btLabel + text: "Bluetooth Link Settings" + } + Rectangle { + height: 1 + width: btLabel.width + color: qgcPal.button + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: "Device:" + width: _firstColumn + } + QGCLabel { + id: deviceField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.device : "" + } + } + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: "Address:" + width: _firstColumn + } + QGCLabel { + id: addressField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.address : "" + } + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + QGCLabel { + text: "Bluetooth Devices:" + } + Item { + width: hostRow.width + height: hostRow.height + Row { + id: hostRow + spacing: ScreenTools.defaultFontPixelWidth + Item { + height: 1 + width: _firstColumn + } + Column { + id: hostColumn + spacing: ScreenTools.defaultFontPixelHeight / 2 + Rectangle { + height: 1 + width: _secondColumn + color: qgcPal.button + visible: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && subEditConfig.deviceList.length > 0 + } + Repeater { + model: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth ? subEditConfig.deviceList : "" + delegate: + QGCButton { + text: modelData + width: _secondColumn + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 + exclusiveGroup: linkGroup + onClicked: { + checked = true + _btSettings._currentDevice = modelData + } + } + } + Rectangle { + height: 1 + width: _secondColumn + color: qgcPal.button + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + Item { + width: _secondColumn + height: udpButtonRow.height + Row { + id: udpButtonRow + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 10 + text: "Scan" + enabled: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && !subEditConfig.scanning + onClicked: { + if(subEditConfig) + subEditConfig.startScan() + } + } + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 10 + text: "Stop" + enabled: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeBluetooth && subEditConfig.scanning + onClicked: { + if(subEditConfig) + subEditConfig.stopScan() + } + } + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 10 + enabled: _btSettings._currentDevice && _btSettings._currentDevice !== "" + text: "Select" + onClicked: { + if(subEditConfig) + subEditConfig.device = _btSettings._currentDevice + } + } + } + } + } + } + } + } +}