From c6a7a5b32b0cdd699904e8e58b9d4c79ae8b7e63 Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 6 Sep 2024 06:07:37 -0400 Subject: [PATCH] Mavlink: Improve Image Transmission Protocol Handling --- custom-example/qgroundcontrol.qrc | 2 +- qgroundcontrol.qrc | 2 +- src/Comms/LinkInterface.cc | 3 +- src/Comms/LinkInterface.h | 6 +- src/Comms/LinkManager.cc | 16 +-- src/Comms/LinkManager.h | 2 +- .../MockLink/MockLink.Parameter.MetaData.json | 43 ------ src/Comms/QGCSerialPortInfo.cc | 3 - src/Comms/QGCSerialPortInfo.h | 2 - src/Comms/SerialLink.cc | 4 +- src/Comms/SerialLink.h | 2 +- src/Comms/USBBoardInfo.json | 3 - src/MAVLink/ImageProtocolManager.cc | 126 +++++++++++------- src/MAVLink/ImageProtocolManager.h | 29 ++-- src/QGCApplication.cc | 2 +- src/QmlControls/QGCImageProvider.cc | 81 ++++++----- src/QmlControls/QGCImageProvider.h | 15 +-- src/Settings/AutoConnect.SettingsGroup.json | 7 - src/Settings/AutoConnectSettings.cc | 11 -- src/Settings/AutoConnectSettings.h | 1 - src/UI/preferences/LinkSettings.qml | 7 +- .../MainStatusIndicatorOfflinePage.qml | 5 +- .../Components/ComponentInformationManager.cc | 4 +- src/Vehicle/InitialConnectStateMachine.cc | 10 +- src/Vehicle/MultiVehicleManager.cc | 27 ++-- src/Vehicle/Vehicle.cc | 34 +++-- src/Vehicle/Vehicle.h | 25 ++-- src/Vehicle/VehicleLinkManager.cc | 10 -- src/Vehicle/VehicleLinkManager.h | 2 - src/VehicleSetup/Bootloader.h | 1 - src/VehicleSetup/CMakeLists.txt | 2 +- src/VehicleSetup/FirmwareUpgrade.qml | 109 +++++---------- src/VehicleSetup/FirmwareUpgradeController.cc | 33 ----- src/VehicleSetup/FirmwareUpgradeController.h | 6 - ...X4FlowSensor.qml => OpticalFlowSensor.qml} | 16 +-- src/VehicleSetup/SetupView.qml | 9 +- 36 files changed, 265 insertions(+), 395 deletions(-) rename src/VehicleSetup/{PX4FlowSensor.qml => OpticalFlowSensor.qml} (58%) diff --git a/custom-example/qgroundcontrol.qrc b/custom-example/qgroundcontrol.qrc index aaa0b962bbc..0f249c9d16d 100644 --- a/custom-example/qgroundcontrol.qrc +++ b/custom-example/qgroundcontrol.qrc @@ -76,7 +76,7 @@ ../src/UI/preferences/PlanViewSettings.qml ../src/UI/toolbar/PlanViewToolBar.qml ../src/FlightDisplay/PreFlightCheckList.qml - ../src/VehicleSetup/PX4FlowSensor.qml + ../src/VehicleSetup/OpticalFlowSensor.qml ../src/FlightMap/Widgets/VerticalCompassAttitude.qml ../src/FlightMap/Widgets/HorizontalCompassAttitude.qml ../src/AnalyzeView/AnalyzePage.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 26d342e63a7..61516930996 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -76,7 +76,7 @@ src/UI/preferences/PlanViewSettings.qml src/UI/toolbar/PlanViewToolBar.qml src/FlightDisplay/PreFlightCheckList.qml - src/VehicleSetup/PX4FlowSensor.qml + src/VehicleSetup/OpticalFlowSensor.qml src/FlightMap/Widgets/VerticalCompassAttitude.qml src/FlightMap/Widgets/HorizontalCompassAttitude.qml src/AnalyzeView/AnalyzePage.qml diff --git a/src/Comms/LinkInterface.cc b/src/Comms/LinkInterface.cc index 55d651908ea..fb1391c3ab3 100644 --- a/src/Comms/LinkInterface.cc +++ b/src/Comms/LinkInterface.cc @@ -23,10 +23,9 @@ QGC_LOGGING_CATEGORY(LinkInterfaceLog, "LinkInterfaceLog") -LinkInterface::LinkInterface(SharedLinkConfigurationPtr &config, bool isPX4Flow, QObject *parent) +LinkInterface::LinkInterface(SharedLinkConfigurationPtr &config, QObject *parent) : QThread(parent) , _config(config) - , _isPX4Flow(isPX4Flow) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } diff --git a/src/Comms/LinkInterface.h b/src/Comms/LinkInterface.h index 41fce26a01b..9407140ad5c 100644 --- a/src/Comms/LinkInterface.h +++ b/src/Comms/LinkInterface.h @@ -23,8 +23,6 @@ class LinkInterface : public QThread { Q_OBJECT - Q_PROPERTY(bool isPX4Flow READ isPX4Flow CONSTANT) - friend class LinkManager; public: @@ -40,7 +38,6 @@ class LinkInterface : public QThread const SharedLinkConfigurationPtr linkConfiguration() const { return _config; } uint8_t mavlinkChannel() const; bool mavlinkChannelIsSet() const; - bool isPX4Flow() const { return _isPX4Flow; } bool decodedFirstMavlinkPacket(void) const { return _decodedFirstMavlinkPacket; } void setDecodedFirstMavlinkPacket(bool decodedFirstMavlinkPacket) { _decodedFirstMavlinkPacket = decodedFirstMavlinkPacket; } void writeBytesThreadSafe(const char *bytes, int length); @@ -58,7 +55,7 @@ class LinkInterface : public QThread protected: /// Links are only created by LinkManager so constructor is not public - LinkInterface(SharedLinkConfigurationPtr &config, bool isPX4Flow = false, QObject *parent = nullptr); + LinkInterface(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); /// Called by the LinkManager during LinkInterface construction instructing the link to setup channels. /// Default implementation allocates a single channel. But some link types (such as MockLink) need more than one. @@ -80,7 +77,6 @@ private slots: uint8_t _mavlinkChannel = std::numeric_limits::max(); bool _decodedFirstMavlinkPacket = false; - bool _isPX4Flow = false; int _vehicleReferenceCount = 0; bool _signingSignatureFailure = false; }; diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index f0852863f7c..43df54e8d38 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -109,17 +109,15 @@ void LinkManager::createConnectedLink(const LinkConfiguration *config) } } -bool LinkManager::createConnectedLink(SharedLinkConfigurationPtr &config, bool isPX4Flow) +bool LinkManager::createConnectedLink(SharedLinkConfigurationPtr &config) { SharedLinkInterfacePtr link = nullptr; switch(config->type()) { #ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: - link = std::make_shared(config, isPX4Flow); + link = std::make_shared(config); break; -#else - Q_UNUSED(isPX4Flow) #endif case LinkConfiguration::TypeUdp: link = std::make_shared(config); @@ -845,9 +843,6 @@ void LinkManager::_addSerialAutoConnectLink() pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName, portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); break; - case QGCSerialPortInfo::BoardTypePX4Flow: - pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName, portInfo.portName().trimmed())); - break; case QGCSerialPortInfo::BoardTypeSiKRadio: pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName, portInfo.portName().trimmed())); break; @@ -872,7 +867,7 @@ void LinkManager::_addSerialAutoConnectLink() pSerialConfig->setAutoConnect(true); SharedLinkConfigurationPtr sharedConfig(pSerialConfig); - createConnectedLink(sharedConfig, boardType == QGCSerialPortInfo::BoardTypePX4Flow); + createConnectedLink(sharedConfig); } } } @@ -894,11 +889,6 @@ bool LinkManager::_allowAutoConnectToBoard(QGCSerialPortInfo::BoardType_t boardT return true; } break; - case QGCSerialPortInfo::BoardTypePX4Flow: - if (_autoConnectSettings->autoConnectPX4Flow()->rawValue().toBool()) { - return true; - } - break; case QGCSerialPortInfo::BoardTypeSiKRadio: if (_autoConnectSettings->autoConnectSiKRadio()->rawValue().toBool()) { return true; diff --git a/src/Comms/LinkManager.h b/src/Comms/LinkManager.h index 3e67279b7b0..59359d7a69d 100644 --- a/src/Comms/LinkManager.h +++ b/src/Comms/LinkManager.h @@ -87,7 +87,7 @@ class LinkManager : public QGCTool void setConnectionsAllowed() { _connectionsSuspended = false; } /// Creates, connects (and adds) a link based on the given configuration instance. - bool createConnectedLink(SharedLinkConfigurationPtr &config, bool isPX4Flow = false); + bool createConnectedLink(SharedLinkConfigurationPtr &config); /// Returns pointer to the mavlink forwarding link, or nullptr if it does not exist SharedLinkInterfacePtr mavlinkForwardingLink(); diff --git a/src/Comms/MockLink/MockLink.Parameter.MetaData.json b/src/Comms/MockLink/MockLink.Parameter.MetaData.json index a3301a6e374..070777ce995 100644 --- a/src/Comms/MockLink/MockLink.Parameter.MetaData.json +++ b/src/Comms/MockLink/MockLink.Parameter.MetaData.json @@ -18198,49 +18198,6 @@ "shortDesc": "External I2C probe", "longDesc": "Probe for optional external I2C devices." }, - { - "name": "SENS_FLOW_ROT", - "type": "Int32", - "default": 6, - "group": "Sensors", - "shortDesc": "PX4Flow board rotation", - "longDesc": "This parameter defines the yaw rotation of the PX4FLOW board relative to the vehicle body frame. Zero rotation is defined as X on flow board pointing towards front of vehicle. The recommneded installation default for the PX4FLOW board is with the Y axis forward (270 deg yaw).", - "rebootRequired": true, - "values": [ - { - "value": 0, - "description": "No rotation" - }, - { - "value": 1, - "description": "Yaw 45\u00b0" - }, - { - "value": 2, - "description": "Yaw 90\u00b0" - }, - { - "value": 3, - "description": "Yaw 135\u00b0" - }, - { - "value": 4, - "description": "Yaw 180\u00b0" - }, - { - "value": 5, - "description": "Yaw 225\u00b0" - }, - { - "value": 6, - "description": "Yaw 270\u00b0" - }, - { - "value": 7, - "description": "Yaw 315\u00b0" - } - ] - }, { "name": "SENS_GPS_MASK", "type": "Int32", diff --git a/src/Comms/QGCSerialPortInfo.cc b/src/Comms/QGCSerialPortInfo.cc index 5855b250d6f..ac21695ab93 100644 --- a/src/Comms/QGCSerialPortInfo.cc +++ b/src/Comms/QGCSerialPortInfo.cc @@ -237,8 +237,6 @@ QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType) return QStringLiteral("Pixhawk"); case BoardTypeSiKRadio: return QStringLiteral("SiK Radio"); - case BoardTypePX4Flow: - return QStringLiteral("PX4 Flow"); case BoardTypeOpenPilot: return QStringLiteral("OpenPilot"); case BoardTypeRTKGPS: @@ -305,7 +303,6 @@ bool QGCSerialPortInfo::canFlash() const if (getBoardInfo(boardType, name)) { switch(boardType) { case QGCSerialPortInfo::BoardTypePixhawk: - case QGCSerialPortInfo::BoardTypePX4Flow: case QGCSerialPortInfo::BoardTypeSiKRadio: return true; default: diff --git a/src/Comms/QGCSerialPortInfo.h b/src/Comms/QGCSerialPortInfo.h index 9749b14795e..652d6bcae8c 100644 --- a/src/Comms/QGCSerialPortInfo.h +++ b/src/Comms/QGCSerialPortInfo.h @@ -35,7 +35,6 @@ class QGCSerialPortInfo : public QSerialPortInfo enum BoardType_t { BoardTypePixhawk, BoardTypeSiKRadio, - BoardTypePX4Flow, BoardTypeOpenPilot, BoardTypeRTKGPS, BoardTypeUnknown @@ -68,7 +67,6 @@ class QGCSerialPortInfo : public QSerialPortInfo }; static constexpr const BoardClassString2BoardType_t _rgBoardClass2BoardType[BoardTypeUnknown] = { { "Pixhawk", QGCSerialPortInfo::BoardTypePixhawk }, - { "PX4 Flow", QGCSerialPortInfo::BoardTypePX4Flow }, { "RTK GPS", QGCSerialPortInfo::BoardTypeRTKGPS }, { "SiK Radio", QGCSerialPortInfo::BoardTypeSiKRadio }, { "OpenPilot", QGCSerialPortInfo::BoardTypeOpenPilot }, diff --git a/src/Comms/SerialLink.cc b/src/Comms/SerialLink.cc index 8af332f7be7..eecad92fbd9 100644 --- a/src/Comms/SerialLink.cc +++ b/src/Comms/SerialLink.cc @@ -22,8 +22,8 @@ QGC_LOGGING_CATEGORY(SerialLinkLog, "SerialLinkLog") -SerialLink::SerialLink(SharedLinkConfigurationPtr& config, bool isPX4Flow) - : LinkInterface(config, isPX4Flow) +SerialLink::SerialLink(SharedLinkConfigurationPtr& config) + : LinkInterface(config) , _serialConfig(qobject_cast(config.get())) { qRegisterMetaType(); diff --git a/src/Comms/SerialLink.h b/src/Comms/SerialLink.h index b2cd9e1cc1f..3f4e4ef066e 100644 --- a/src/Comms/SerialLink.h +++ b/src/Comms/SerialLink.h @@ -104,7 +104,7 @@ class SerialLink : public LinkInterface Q_OBJECT public: - SerialLink(SharedLinkConfigurationPtr& config, bool isPX4Flow = false); + SerialLink(SharedLinkConfigurationPtr& config); virtual ~SerialLink(); // LinkInterface overrides diff --git a/src/Comms/USBBoardInfo.json b/src/Comms/USBBoardInfo.json index 780b5d9e72a..e3b3925bd2f 100644 --- a/src/Comms/USBBoardInfo.json +++ b/src/Comms/USBBoardInfo.json @@ -45,8 +45,6 @@ { "vendorID": 13735, "productID": 1, "boardClass": "Pixhawk", "name": "ThePeach FCC-K1" }, { "vendorID": 13735, "productID": 2, "boardClass": "Pixhawk", "name": "ThePeach FCC-R1" }, - { "vendorID": 9900, "productID": 21, "boardClass": "PX4 Flow", "name": "PX4 Flow" }, - { "vendorID": 1027, "productID": 24597, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "3DR Radio" }, { "vendorID": 1027, "productID": 24577, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "3DR Radio on FTDI" }, { "vendorID": 4292, "productID": 60000, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "SILabs Radio" }, @@ -96,7 +94,6 @@ { "regExp": "^mRoControlZeroF7", "boardClass": "Pixhawk" }, { "regExp": "^ARK FMU v6X.x$", "boardClass": "Pixhawk" }, { "regExp": "^ARK BL FMU v6X.x$", "boardClass": "Pixhawk" }, - { "regExp": "PX4.*Flow", "boardClass": "PX4 Flow" }, { "regExp": "^FT231X USB UART$", "boardClass": "SiK Radio" }, { "regExp": "USB UART$", "boardClass": "SiK Radio", "androidOnly": true, "comment": "Very broad fallback, too dangerous for non-android" } ], diff --git a/src/MAVLink/ImageProtocolManager.cc b/src/MAVLink/ImageProtocolManager.cc index 19d99a90c2c..08f700139c1 100644 --- a/src/MAVLink/ImageProtocolManager.cc +++ b/src/MAVLink/ImageProtocolManager.cc @@ -10,27 +10,56 @@ #include "ImageProtocolManager.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(ImageProtocolManagerLog, "ImageProtocolManagerLog") +QGC_LOGGING_CATEGORY(ImageProtocolManagerLog, "qgc.mavlink.imageprotocolmanager") -ImageProtocolManager::ImageProtocolManager(void) +ImageProtocolManager::ImageProtocolManager(QObject *parent) + : QObject(parent) { - memset(&_imageHandshake, 0, sizeof(_imageHandshake)); + // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this; } -void ImageProtocolManager::mavlinkMessageReceived(const mavlink_message_t& message) +ImageProtocolManager::~ImageProtocolManager() +{ + // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this; +} + +bool ImageProtocolManager::requestImage(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message) +{ + // Check if there is already an image transmission going on + if (_imageHandshake.packets != 0) { + return false; + } + + constexpr mavlink_data_transmission_handshake_t data = { + 0, 0, 0, 0, + MAVLINK_DATA_STREAM_IMG_JPEG, + 0, + 50 + }; + (void) mavlink_msg_data_transmission_handshake_encode_chan(system_id, component_id, chan, &message, &data); + + return true; +} + +void ImageProtocolManager::cancelRequest(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message) +{ + constexpr mavlink_data_transmission_handshake_t data{0}; + (void) mavlink_msg_data_transmission_handshake_encode_chan(system_id, component_id, chan, &message, &data); +} + +void ImageProtocolManager::mavlinkMessageReceived(const mavlink_message_t &message) { switch (message.msgid) { case MAVLINK_MSG_ID_DATA_TRANSMISSION_HANDSHAKE: { - if (_imageHandshake.packets) { + if (_imageHandshake.packets > 0) { qCWarning(ImageProtocolManagerLog) << "DATA_TRANSMISSION_HANDSHAKE: Previous image transmission incomplete."; } _imageBytes.clear(); mavlink_msg_data_transmission_handshake_decode(&message, &_imageHandshake); qCDebug(ImageProtocolManagerLog) << QStringLiteral("DATA_TRANSMISSION_HANDSHAKE: type(%1) width(%2) height (%3)").arg(_imageHandshake.type).arg(_imageHandshake.width).arg(_imageHandshake.height); - } break; - + } case MAVLINK_MSG_ID_ENCAPSULATED_DATA: { if (_imageHandshake.packets == 0) { @@ -41,71 +70,74 @@ void ImageProtocolManager::mavlinkMessageReceived(const mavlink_message_t& messa mavlink_encapsulated_data_t encapsulatedData; mavlink_msg_encapsulated_data_decode(&message, &encapsulatedData); - int bytePosition = encapsulatedData.seqnr * _imageHandshake.payload; - if (bytePosition >= static_cast(_imageHandshake.size)) { - qCWarning(ImageProtocolManagerLog) << "ENCAPSULATED_DATA: seqnr is past end of image size. seqnr:" << encapsulatedData.seqnr << "_imageHandshake.size" << _imageHandshake.size; + uint32_t bytePosition = encapsulatedData.seqnr * _imageHandshake.payload; + if (bytePosition >= _imageHandshake.size) { + qCWarning(ImageProtocolManagerLog) << "ENCAPSULATED_DATA: seqnr is past end of image size. seqnr:" << encapsulatedData.seqnr << "_imageHandshake.size:" << _imageHandshake.size; break; } - for (uint8_t i=0; i<_imageHandshake.payload; i++) { + for (uint8_t i = 0; i < _imageHandshake.payload; i++) { _imageBytes[bytePosition] = encapsulatedData.data[i]; bytePosition++; } // We use the packets field to track completion _imageHandshake.packets--; - if (_imageHandshake.packets == 0) { // We have all the packets - emit imageReady(); + emit imageReady(_getImage()); + + _flowImageIndex++; + emit flowImageIndexChanged(_flowImageIndex); } - } break; - + } default: break; } } -QImage ImageProtocolManager::getImage(void) +QImage ImageProtocolManager::_getImage() { QImage image; if (_imageBytes.isEmpty()) { - qCWarning(ImageProtocolManagerLog) << "getImage: Called when no image available"; - } else if (_imageHandshake.packets) { - qCWarning(ImageProtocolManagerLog) << "getImage: Called when image is imcomplete. _imageHandshake.packets:" << _imageHandshake.packets; - } else { - switch (_imageHandshake.type) { - case MAVLINK_DATA_STREAM_IMG_RAW8U: - case MAVLINK_DATA_STREAM_IMG_RAW32U: - { - // Construct PGM header - QString header("P5\n%1 %2\n%3\n"); - header = header.arg(_imageHandshake.width).arg(_imageHandshake.height).arg(255 /* image colors */); - - QByteArray tmpImage(header.toStdString().c_str(), header.length()); - tmpImage.append(_imageBytes); - - if (!image.loadFromData(tmpImage, "PGM")) { - qCWarning(ImageProtocolManagerLog) << "getImage: IMG_RAW8U QImage::loadFromData failed"; - } - } - break; + qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Called when no image available"; + return image; + } - case MAVLINK_DATA_STREAM_IMG_BMP: - case MAVLINK_DATA_STREAM_IMG_JPEG: - case MAVLINK_DATA_STREAM_IMG_PGM: - case MAVLINK_DATA_STREAM_IMG_PNG: - if (!image.loadFromData(_imageBytes)) { - qCWarning(ImageProtocolManagerLog) << "getImage: Known header QImage::loadFromData failed"; - } - break; + if (_imageHandshake.packets > 0) { + qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Called when image is imcomplete. _imageHandshake.packets:" << _imageHandshake.packets; + return image; + } - default: - qCWarning(ImageProtocolManagerLog) << "getImage: Unsupported image type:" << _imageHandshake.type; - break; + switch (_imageHandshake.type) { + case MAVLINK_DATA_STREAM_IMG_RAW8U: + case MAVLINK_DATA_STREAM_IMG_RAW32U: + { + // Construct PGM header + const QString header = QStringLiteral("P5\n%1 %2\n255\n").arg(_imageHandshake.width).arg(_imageHandshake.height); + + QByteArray tempImage(header.toStdString().c_str(), header.length()); + (void) tempImage.append(_imageBytes); + + if (!image.loadFromData(tempImage, "PGM")) { + qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "IMG_RAW8U QImage::loadFromData failed"; + } + break; + } + case MAVLINK_DATA_STREAM_IMG_BMP: + case MAVLINK_DATA_STREAM_IMG_JPEG: + case MAVLINK_DATA_STREAM_IMG_PGM: + case MAVLINK_DATA_STREAM_IMG_PNG: + if (!image.loadFromData(_imageBytes)) { + qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Known header QImage::loadFromData failed"; } + break; + + default: + qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Unsupported image type:" << _imageHandshake.type; + break; } return image; diff --git a/src/MAVLink/ImageProtocolManager.h b/src/MAVLink/ImageProtocolManager.h index 26214187d72..1a4a7994bd8 100644 --- a/src/MAVLink/ImageProtocolManager.h +++ b/src/MAVLink/ImageProtocolManager.h @@ -11,30 +11,39 @@ #include "MAVLinkLib.h" -#include #include #include +#include #include Q_DECLARE_LOGGING_CATEGORY(ImageProtocolManagerLog) -// Supports the Mavlink image transmission protocol (https://mavlink.io/en/services/image_transmission.html). -// Mainly used by optical flow cameras. +/// Supports the Mavlink image transmission protocol (https://mavlink.io/en/services/image_transmission.html). +/// Mainly used by optical flow cameras. class ImageProtocolManager : public QObject { Q_OBJECT - + public: - ImageProtocolManager(void); + ImageProtocolManager(QObject *parent = nullptr); + ~ImageProtocolManager(); - void mavlinkMessageReceived (const mavlink_message_t& message); - QImage getImage (void); + uint32_t flowImageIndex() const { return _flowImageIndex; } + + bool requestImage(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message); + void cancelRequest(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message); signals: - void imageReady(void); + void imageReady(const QImage &image); + void flowImageIndexChanged(uint32_t index); + +public slots: + void mavlinkMessageReceived(const mavlink_message_t &message); private: - mavlink_data_transmission_handshake_t _imageHandshake; - QByteArray _imageBytes; + QImage _getImage(); + mavlink_data_transmission_handshake_t _imageHandshake{0}; + QByteArray _imageBytes; + uint32_t _flowImageIndex = 0; }; diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 41fb1467590..fbdb956f99d 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -420,7 +420,7 @@ void QGCApplication::_initForNormalAppBoot() AudioOutput::instance()->init(_toolbox->settingsManager()->appSettings()->audioMuted()); FollowMe::instance()->init(); - // Image provider for PX4 Flow + // Image provider for Optical Flow _qmlAppEngine->addImageProvider(qgcImageProviderId, new QGCImageProvider()); QQuickWindow* rootWindow = mainRootWindow(); diff --git a/src/QmlControls/QGCImageProvider.cc b/src/QmlControls/QGCImageProvider.cc index 86d878e15f6..33afe65eb0b 100644 --- a/src/QmlControls/QGCImageProvider.cc +++ b/src/QmlControls/QGCImageProvider.cc @@ -14,54 +14,63 @@ QGCImageProvider::QGCImageProvider(QQmlImageProviderBase::ImageType imageType) : QQuickImageProvider(imageType) + , _dummy(320, 240, QImage::Format_RGBA8888) { - //-- Dummy temporary image until something comes along - m_image = QImage(320, 240, QImage::Format_RGBA8888); - m_image.fill(Qt::black); - QPainter painter(&m_image); - QFont f = painter.font(); - f.setPixelSize(20); - painter.setFont(f); - painter.setPen(Qt::white); - painter.drawText(QRectF(0, 0, 320, 240), Qt::AlignCenter, "Waiting..."); + // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this; + + Q_ASSERT(imageType == QQmlImageProviderBase::ImageType::Image); + + // Dummy temporary image until something comes along + _dummy.fill(Qt::black); + QPainter painter(&_dummy); + QFont f = painter.font(); + f.setPixelSize(20); + painter.setFont(f); + painter.setPen(Qt::white); + painter.drawText(QRectF(0, 0, _dummy.width(), _dummy.height()), Qt::AlignCenter, QStringLiteral("Waiting...")); + _images[0] = _dummy; } QGCImageProvider::~QGCImageProvider() { - + // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this; } -QImage QGCImageProvider::requestImage(const QString & /* image url with vehicle id*/, QSize *, const QSize &) +QImage QGCImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { -/* - The QML side will request an image using a special URL, which we've registered as QGCImages. - The URL follows this format (or anything you want to make out of it after the "QGCImages" part): + Q_UNUSED(requestedSize); - "image://QGCImages/vvv/iii" + if (id.isEmpty()) { + return _dummy; + } - Where: - vvv: Some vehicle id - iii: An auto incremented index (which forces the Item to reload the image) + if (!id.contains("/")) { + return _dummy; + } - The image index is incremented each time a new image arrives. A signal is emitted and the QML side - updates its contents automatically. + const QStringList url = id.split('/', Qt::SkipEmptyParts); + if (url.size() != 2) { + return _dummy; + } - Image { - source: "image://QGCImages/" + _activeVehicle.id + "/" + _activeVehicle.flowImageIndex - width: parent.width * 0.5 - height: width * 0.75 - cache: false - anchors.centerIn: parent - fillMode: Image.PreserveAspectFit - } + bool ok = false; + const uint8_t vehicleId = url[0].toUInt(&ok); + if (!ok) { + return _dummy; + } - For now, we don't even look at the URL. This will have to be fixed if we're to support multiple - vehicles transmitting flow images. -*/ - return m_image; -} + const uint8_t index = url[1].toUInt(&ok); + if (!ok) { + return _dummy; + } -void QGCImageProvider::setImage(QImage* pImage, int /* vehicle id*/) -{ - m_image = pImage->mirrored(); + if (!_images.contains(vehicleId)) { + return _dummy; + } + + const QImage image = _images[vehicleId]; + // image->scaled(requestedSize); + *size = image.size(); + + return image; } diff --git a/src/QmlControls/QGCImageProvider.h b/src/QmlControls/QGCImageProvider.h index 23ea49f7544..f5feb75dbb1 100644 --- a/src/QmlControls/QGCImageProvider.h +++ b/src/QmlControls/QGCImageProvider.h @@ -10,23 +10,20 @@ #pragma once #include +#include #include -// This is used to expose images from ImageProtocolHandler +/// This is used to expose images from ImageProtocolHandler class QGCImageProvider : public QQuickImageProvider { public: QGCImageProvider(QQmlImageProviderBase::ImageType type = QQmlImageProviderBase::ImageType::Image); ~QGCImageProvider(); - void setImage(QImage* pImage, int id = 0); - - QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) final; + void setImage(const QImage &image, uint8_t vehicleId = 0) { _images[vehicleId] = image.mirrored(); } private: - //-- TODO: For now this is holding a single image. If you happen to have two - // or more vehicles with flow, it will not work. To properly manage that condition - // this should be a map between each vehicle and its image. The URL provided - // for the image request would contain the vehicle identification. - QImage m_image; + QMap _images; + QImage _dummy; }; diff --git a/src/Settings/AutoConnect.SettingsGroup.json b/src/Settings/AutoConnect.SettingsGroup.json index eef97e9ea3a..cb733bf99b7 100644 --- a/src/Settings/AutoConnect.SettingsGroup.json +++ b/src/Settings/AutoConnect.SettingsGroup.json @@ -24,13 +24,6 @@ "type": "bool", "default": true }, -{ - "name": "autoConnectPX4Flow", - "shortDesc": "Automatically connect to a P4 Flow", - "longDesc": "If this option is enabled GroundControl will automatically connect to a PX4 Flow board which is connected via USB.", - "type": "bool", - "default": true -}, { "name": "autoConnectRTKGPS", "shortDesc": "Automatically connect to an RTK GPS", diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index ccd115ea362..0a2aac4c68a 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -45,17 +45,6 @@ DECLARE_SETTINGSFACT_NO_FUNC(AutoConnectSettings, autoConnectSiKRadio) return _autoConnectSiKRadioFact; } -DECLARE_SETTINGSFACT_NO_FUNC(AutoConnectSettings, autoConnectPX4Flow) -{ - if (!_autoConnectPX4FlowFact) { - _autoConnectPX4FlowFact = _createSettingsFact(autoConnectPX4FlowName); -#ifdef Q_OS_IOS - _autoConnectPX4FlowFact->setVisible(false); -#endif - } - return _autoConnectPX4FlowFact; -} - DECLARE_SETTINGSFACT_NO_FUNC(AutoConnectSettings, autoConnectRTKGPS) { if (!_autoConnectRTKGPSFact) { diff --git a/src/Settings/AutoConnectSettings.h b/src/Settings/AutoConnectSettings.h index 9753465f4f5..709d401b097 100644 --- a/src/Settings/AutoConnectSettings.h +++ b/src/Settings/AutoConnectSettings.h @@ -28,7 +28,6 @@ class AutoConnectSettings : public SettingsGroup DEFINE_SETTINGFACT(autoConnectUDP) DEFINE_SETTINGFACT(autoConnectPixhawk) DEFINE_SETTINGFACT(autoConnectSiKRadio) - DEFINE_SETTINGFACT(autoConnectPX4Flow) DEFINE_SETTINGFACT(autoConnectRTKGPS) DEFINE_SETTINGFACT(autoConnectLibrePilot) DEFINE_SETTINGFACT(autoConnectNmeaPort) diff --git a/src/UI/preferences/LinkSettings.qml b/src/UI/preferences/LinkSettings.qml index cc21d3aabc0..d1a0f82f17b 100644 --- a/src/UI/preferences/LinkSettings.qml +++ b/src/UI/preferences/LinkSettings.qml @@ -29,17 +29,16 @@ SettingsPage { Repeater { id: autoConnectRepeater - model: [ + model: [ _autoConnectSettings.autoConnectPixhawk, _autoConnectSettings.autoConnectSiKRadio, - _autoConnectSettings.autoConnectPX4Flow, _autoConnectSettings.autoConnectLibrePilot, _autoConnectSettings.autoConnectUDP, _autoConnectSettings.autoConnectZeroConf, _autoConnectSettings.autoConnectRTKGPS, ] - property var names: [ qsTr("Pixhawk"), qsTr("SiK Radio"), qsTr("PX4 Flow"), qsTr("LibrePilot"), qsTr("UDP"), qsTr("Zero-Conf"), qsTr("RTK") ] + property var names: [ qsTr("Pixhawk"), qsTr("SiK Radio"), qsTr("LibrePilot"), qsTr("UDP"), qsTr("Zero-Conf"), qsTr("RTK") ] FactCheckBoxSlider { Layout.fillWidth: true @@ -223,4 +222,4 @@ SettingsPage { } } } -} \ No newline at end of file +} diff --git a/src/UI/toolbar/MainStatusIndicatorOfflinePage.qml b/src/UI/toolbar/MainStatusIndicatorOfflinePage.qml index 11c937208ff..de6ddb387d3 100644 --- a/src/UI/toolbar/MainStatusIndicatorOfflinePage.qml +++ b/src/UI/toolbar/MainStatusIndicatorOfflinePage.qml @@ -89,17 +89,16 @@ ToolIndicatorPage { Repeater { id: autoConnectRepeater - model: [ + model: [ autoConnectSettings.autoConnectPixhawk, autoConnectSettings.autoConnectSiKRadio, - autoConnectSettings.autoConnectPX4Flow, autoConnectSettings.autoConnectLibrePilot, autoConnectSettings.autoConnectUDP, autoConnectSettings.autoConnectZeroConf, autoConnectSettings.autoConnectRTKGPS, ] - property var names: [ qsTr("Pixhawk"), qsTr("SiK Radio"), qsTr("PX4 Flow"), qsTr("LibrePilot"), qsTr("UDP"), qsTr("Zero-Conf"), qsTr("RTK") ] + property var names: [ qsTr("Pixhawk"), qsTr("SiK Radio"), qsTr("LibrePilot"), qsTr("UDP"), qsTr("Zero-Conf"), qsTr("RTK") ] FactCheckBoxSlider { Layout.fillWidth: true diff --git a/src/Vehicle/Components/ComponentInformationManager.cc b/src/Vehicle/Components/ComponentInformationManager.cc index 21a3eadbdc4..40999d782c9 100644 --- a/src/Vehicle/Components/ComponentInformationManager.cc +++ b/src/Vehicle/Components/ComponentInformationManager.cc @@ -262,7 +262,7 @@ void RequestMetaDataTypeStateMachine::_stateRequestCompInfo(StateMachine* stateM SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock(); if (sharedLink) { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(ComponentInformationManagerLog) << QStringLiteral("_stateRequestCompInfo Skipping component information %1 request due to link type").arg(requestMachine->typeToString()); stateMachine->advance(); } else { @@ -296,7 +296,7 @@ void RequestMetaDataTypeStateMachine::_stateRequestCompInfoDeprecated(StateMachi SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock(); if (sharedLink) { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(ComponentInformationManagerLog) << QStringLiteral("_stateRequestCompInfo Skipping component information %1 request due to link type").arg(requestMachine->typeToString()); stateMachine->advance(); } else { diff --git a/src/Vehicle/InitialConnectStateMachine.cc b/src/Vehicle/InitialConnectStateMachine.cc index 2197263f2da..8751517b465 100644 --- a/src/Vehicle/InitialConnectStateMachine.cc +++ b/src/Vehicle/InitialConnectStateMachine.cc @@ -80,7 +80,7 @@ void InitialConnectStateMachine::_stateRequestAutopilotVersion(StateMachine* sta qCDebug(InitialConnectStateMachineLog) << "Skipping REQUEST_MESSAGE:AUTOPILOT_VERSION request due to no primary link"; connectMachine->advance(); } else { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(InitialConnectStateMachineLog) << "Skipping REQUEST_MESSAGE:AUTOPILOT_VERSION request due to link type"; connectMachine->advance(); } else { @@ -192,7 +192,7 @@ void InitialConnectStateMachine::_stateRequestProtocolVersion(StateMachine* stat qCDebug(InitialConnectStateMachineLog) << "Skipping REQUEST_MESSAGE:PROTOCOL_VERSION request due to no primary link"; connectMachine->advance(); } else { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(InitialConnectStateMachineLog) << "Skipping REQUEST_MESSAGE:PROTOCOL_VERSION request due to link type"; connectMachine->advance(); } else if (vehicle->apmFirmware()) { @@ -312,7 +312,7 @@ void InitialConnectStateMachine::_stateRequestMission(StateMachine* stateMachine qCDebug(InitialConnectStateMachineLog) << "_stateRequestMission: Skipping first mission load request due to no primary link"; connectMachine->advance(); } else { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(InitialConnectStateMachineLog) << "_stateRequestMission: Skipping first mission load request due to link type"; vehicle->_firstMissionLoadComplete(); } else { @@ -337,7 +337,7 @@ void InitialConnectStateMachine::_stateRequestGeoFence(StateMachine* stateMachin qCDebug(InitialConnectStateMachineLog) << "_stateRequestGeoFence: Skipping first geofence load request due to no primary link"; connectMachine->advance(); } else { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(InitialConnectStateMachineLog) << "_stateRequestGeoFence: Skipping first geofence load request due to link type"; vehicle->_firstGeoFenceLoadComplete(); } else { @@ -367,7 +367,7 @@ void InitialConnectStateMachine::_stateRequestRallyPoints(StateMachine* stateMac qCDebug(InitialConnectStateMachineLog) << "_stateRequestRallyPoints: Skipping first rally point load request due to no primary link"; connectMachine->advance(); } else { - if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isPX4Flow() || sharedLink->isLogReplay()) { + if (sharedLink->linkConfiguration()->isHighLatency() || sharedLink->isLogReplay()) { qCDebug(InitialConnectStateMachineLog) << "_stateRequestRallyPoints: Skipping first rally point load request due to link type"; vehicle->_firstRallyPointLoadComplete(); } else { diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index aca33655189..d5bbb22046d 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -69,26 +69,15 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleFirmwareType, int vehicleType) { - // Special case PX4 Flow since depending on firmware it can have different settings. We force to the PX4 Firmware settings. - if (link->isPX4Flow()) { - vehicleId = 81; - componentId = 50;//MAV_COMP_ID_AUTOPILOT1; - vehicleFirmwareType = MAV_AUTOPILOT_GENERIC; - vehicleType = 0; - } - if (componentId != MAV_COMP_ID_AUTOPILOT1) { - // Special case for PX4 Flow - if (vehicleId != 81 || componentId != 50) { - // Don't create vehicles for components other than the autopilot - qCDebug(MultiVehicleManagerLog()) << "Ignoring heartbeat from unknown component port:vehicleId:componentId:fwType:vehicleType" - << link->linkConfiguration()->name() - << vehicleId - << componentId - << vehicleFirmwareType - << vehicleType; - return; - } + // Don't create vehicles for components other than the autopilot + qCDebug(MultiVehicleManagerLog()) << "Ignoring heartbeat from unknown component port:vehicleId:componentId:fwType:vehicleType" + << link->linkConfiguration()->name() + << vehicleId + << componentId + << vehicleFirmwareType + << vehicleType; + return; } #if !defined(NO_ARDUPILOT_DIALECT) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 9920684792f..27145ba2df8 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -293,7 +293,7 @@ void Vehicle::_commonInit() _componentInformationManager = new ComponentInformationManager (this); _initialConnectStateMachine = new InitialConnectStateMachine (this); _ftpManager = new FTPManager (this); - _imageProtocolManager = new ImageProtocolManager (); + _vehicleLinkManager = new VehicleLinkManager (this); connect(_standardModes, &StandardModes::modesUpdated, this, &Vehicle::flightModesChanged); @@ -325,8 +325,7 @@ void Vehicle::_commonInit() // Flight modes can differ based on advanced mode connect(_toolbox->corePlugin(), &QGCCorePlugin::showAdvancedUIChanged, this, &Vehicle::flightModesChanged); - connect(_imageProtocolManager, &ImageProtocolManager::imageReady, this, &Vehicle::_imageProtocolImageReady); - + _createImageProtocolManager(); _createStatusTextHandler(); // _addFactGroup(_vehicleFactGroup, _vehicleFactGroupName); @@ -530,7 +529,7 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes } _ftpManager->_mavlinkMessageReceived(message); _parameterManager->mavlinkMessageReceived(message); - _imageProtocolManager->mavlinkMessageReceived(message); + (void) QMetaObject::invokeMethod(_imageProtocolManager, "mavlinkMessageReceived", Qt::AutoConnection, message); _remoteIDManager->mavlinkMessageReceived(message); _waitForMavlinkMessageMessageReceivedHandler(message); @@ -1879,14 +1878,6 @@ void Vehicle::_sendQGCTimeToVehicle() sendMessageOnLinkThreadSafe(sharedLink.get(), msg); } -void Vehicle::_imageProtocolImageReady(void) -{ - QImage img = _imageProtocolManager->getImage(); - qgcApp()->qgcImageProvider()->setImage(&img, _id); - _flowImageIndex++; - emit flowImageIndexChanged(); -} - void Vehicle::_remoteControlRSSIChanged(uint8_t rssi) { //-- 0 <= rssi <= 100 - 255 means "invalid/unknown" @@ -4099,3 +4090,22 @@ void Vehicle::sendSetupSigning() } /*---------------------------------------------------------------------------*/ +/*===========================================================================*/ +/* Image Protocol Manager */ +/*===========================================================================*/ + +void Vehicle::_createImageProtocolManager() +{ + _imageProtocolManager = new ImageProtocolManager(this); + (void) connect(_imageProtocolManager, &ImageProtocolManager::flowImageIndexChanged, this, &Vehicle::flowImageIndexChanged); + (void) connect(_imageProtocolManager, &ImageProtocolManager::imageReady, this, [this](const QImage &image) { + qgcApp()->qgcImageProvider()->setImage(image, _id); + }); +} + +uint32_t Vehicle::flowImageIndex() const +{ + return (_imageProtocolManager ? _imageProtocolManager->flowImageIndex() : 0); +} + +/*---------------------------------------------------------------------------*/ diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 844470594c4..23f65549eb6 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -158,7 +158,6 @@ class Vehicle : public VehicleFactGroup Q_PROPERTY(float latitude READ latitude NOTIFY coordinateChanged) Q_PROPERTY(float longitude READ longitude NOTIFY coordinateChanged) Q_PROPERTY(bool joystickEnabled READ joystickEnabled WRITE setJoystickEnabled NOTIFY joystickEnabledChanged) - Q_PROPERTY(int flowImageIndex READ flowImageIndex NOTIFY flowImageIndexChanged) Q_PROPERTY(int rcRSSI READ rcRSSI NOTIFY rcRSSIChanged) Q_PROPERTY(bool px4Firmware READ px4Firmware NOTIFY firmwareTypeChanged) Q_PROPERTY(bool apmFirmware READ apmFirmware NOTIFY firmwareTypeChanged) @@ -518,8 +517,6 @@ class Vehicle : public VehicleFactGroup QmlObjectListModel* cameraTriggerPoints () { return &_cameraTriggerPoints; } - int flowImageIndex() const{ return _flowImageIndex; } - //-- Mavlink Logging void startMavlinkLog(); void stopMavlinkLog(); @@ -847,7 +844,6 @@ public slots: void checkListStateChanged (); void longitudeChanged (); void currentConfigChanged (); - void flowImageIndexChanged (); void rcRSSIChanged (int rcRSSI); void telemetryRRSSIChanged (int value); void telemetryLRSSIChanged (int value); @@ -921,7 +917,6 @@ private slots: void _announceArmedChanged (bool armed); void _offlineCruiseSpeedSettingChanged (QVariant value); void _offlineHoverSpeedSettingChanged (QVariant value); - void _imageProtocolImageReady (void); void _prearmErrorTimeout (); void _firstMissionLoadComplete (); void _firstGeoFenceLoadComplete (); @@ -1106,8 +1101,6 @@ private slots: // Toolbox references - int _flowImageIndex = 0; - bool _allLinksRemovedSent = false; ///< true: allLinkRemoved signal already sent one time uint _messagesReceived = 0; @@ -1282,7 +1275,6 @@ private slots: RallyPointManager* _rallyPointManager = nullptr; VehicleLinkManager* _vehicleLinkManager = nullptr; FTPManager* _ftpManager = nullptr; - ImageProtocolManager* _imageProtocolManager = nullptr; InitialConnectStateMachine* _initialConnectStateMachine = nullptr; Actuators* _actuators = nullptr; RemoteIDManager* _remoteIDManager = nullptr; @@ -1368,6 +1360,23 @@ private slots: StatusTextHandler *m_statusTextHandler = nullptr; /*---------------------------------------------------------------------------*/ +/*===========================================================================*/ +/* Image Protocol Manager */ +/*===========================================================================*/ +private: + Q_PROPERTY(uint flowImageIndex READ flowImageIndex NOTIFY flowImageIndexChanged) + +public: + uint32_t flowImageIndex() const; + +signals: + void flowImageIndexChanged(); + +private: + void _createImageProtocolManager(); + + ImageProtocolManager *_imageProtocolManager = nullptr; }; +/*---------------------------------------------------------------------------*/ Q_DECLARE_METATYPE(Vehicle::MavCmdResultFailureCode_t) diff --git a/src/Vehicle/VehicleLinkManager.cc b/src/Vehicle/VehicleLinkManager.cc index 63cf460b95f..1f18e5a4154 100644 --- a/src/Vehicle/VehicleLinkManager.cc +++ b/src/Vehicle/VehicleLinkManager.cc @@ -391,13 +391,3 @@ QStringList VehicleLinkManager::linkStatuses(void) const return rgStatuses; } - -bool VehicleLinkManager::primaryLinkIsPX4Flow(void) const -{ - SharedLinkInterfacePtr sharedLink = _primaryLink.lock(); - if (!sharedLink) { - return false; - } else { - return sharedLink->isPX4Flow(); - } -} diff --git a/src/Vehicle/VehicleLinkManager.h b/src/Vehicle/VehicleLinkManager.h index 1926005d72a..10ff37df23e 100644 --- a/src/Vehicle/VehicleLinkManager.h +++ b/src/Vehicle/VehicleLinkManager.h @@ -33,7 +33,6 @@ class VehicleLinkManager : public QObject public: VehicleLinkManager(Vehicle* vehicle); - Q_PROPERTY(bool primaryLinkIsPX4Flow READ primaryLinkIsPX4Flow NOTIFY primaryLinkChanged) Q_PROPERTY(QString primaryLinkName READ primaryLinkName WRITE setPrimaryLinkByName NOTIFY primaryLinkChanged) Q_PROPERTY(QStringList linkNames READ linkNames NOTIFY linkNamesChanged) Q_PROPERTY(QStringList linkStatuses READ linkStatuses NOTIFY linkStatusesChanged) @@ -41,7 +40,6 @@ class VehicleLinkManager : public QObject Q_PROPERTY(bool communicationLostEnabled READ communicationLostEnabled WRITE setCommunicationLostEnabled NOTIFY communicationLostEnabledChanged) Q_PROPERTY(bool autoDisconnect MEMBER _autoDisconnect NOTIFY autoDisconnectChanged) - bool primaryLinkIsPX4Flow (void) const; void mavlinkMessageReceived (LinkInterface* link, mavlink_message_t message); bool containsLink (LinkInterface* link); WeakLinkInterfacePtr primaryLink (void) { return _primaryLink; } diff --git a/src/VehicleSetup/Bootloader.h b/src/VehicleSetup/Bootloader.h index b1b4897c298..72ba9e1e037 100644 --- a/src/VehicleSetup/Bootloader.h +++ b/src/VehicleSetup/Bootloader.h @@ -36,7 +36,6 @@ class Bootloader : public QObject bool verify (const FirmwareImage* image); bool reboot (void); - static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID static const int boardIDSiKRadio1000 = 78; ///< Original radio based on SI1000 chip static const int boardIDSiKRadio1060 = 80; ///< Newer radio based on SI1060 chip diff --git a/src/VehicleSetup/CMakeLists.txt b/src/VehicleSetup/CMakeLists.txt index 4b3f270d198..2ce3abb4134 100644 --- a/src/VehicleSetup/CMakeLists.txt +++ b/src/VehicleSetup/CMakeLists.txt @@ -58,7 +58,7 @@ endif() # JoystickConfigButtons.qml # JoystickConfigCalibration.qml # JoystickConfigGeneral.qml -# PX4FlowSensor.qml +# OpticalFlowSensor.qml # SetupParameterEditor.qml # SetupView.qml # VehicleSummary.qml diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 6c7287901cd..56ca4143270 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -43,7 +43,7 @@ SetupPage { readonly property string title: qsTr("Firmware Setup") // Popup dialog title readonly property string highlightPrefix: "" readonly property string highlightSuffix: "" - readonly property string welcomeText: qsTr("%1 can upgrade the firmware on Pixhawk devices, SiK Radios and PX4 Flow Smart Cameras.").arg(QGroundControl.appName) + readonly property string welcomeText: qsTr("%1 can upgrade the firmware on Pixhawk devices and SiK Radios.").arg(QGroundControl.appName) readonly property string welcomeTextSingle: qsTr("Update the autopilot firmware to the latest version") readonly property string plugInText: "" + highlightPrefix + qsTr("Plug in your device") + highlightSuffix + qsTr(" via USB to ") + highlightPrefix + qsTr("start") + highlightSuffix + qsTr(" firmware upgrade.") + "" readonly property string flashFailText: qsTr("If upgrade failed, make sure to connect ") + highlightPrefix + qsTr("directly") + highlightSuffix + qsTr(" to a powered USB port on your computer, not through a USB hub. ") + @@ -141,7 +141,6 @@ SetupPage { buttons: Dialog.Ok | Dialog.Cancel property bool showFirmwareTypeSelection: _advanced.checked - property bool px4Flow: controller.px4FlowBoard function firmwareVersionChanged(model) { firmwareWarningMessageVisible = false @@ -187,40 +186,34 @@ SetupPage { if (_singleFirmwareMode) { controller.flashSingleFirmwareMode(controller.selectedFirmwareBuildType) } else { - var stack var firmwareBuildType = firmwareBuildTypeCombo.model.get(firmwareBuildTypeCombo.currentIndex).firmwareType var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware - if (px4Flow) { - stack = px4FlowTypeSelectionCombo.model.get(px4FlowTypeSelectionCombo.currentIndex).stackType - vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware - } else { - stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4 - if (apmFlightStack.checked) { - if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) { - vehicleType = apmVehicleTypeCombo.currentIndex - } else { - if (controller.apmFirmwareNames.length === 0) { - // Not ready yet, or no firmware available - mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("Either firmware list is still downloading, or no firmware is available for current selection.")) - firmwareSelectDialog.preventClose = true - return - } - if (ardupilotFirmwareSelectionCombo.currentIndex == -1) { - mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("You must choose a board type.")) - firmwareSelectDialog.preventClose = true - return - } - - var firmwareUrl = controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex] - if (firmwareUrl == "") { - mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("No firmware was found for the current selection.")) - firmwareSelectDialog.preventClose = true - return - } - controller.flashFirmwareUrl(controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex]) + var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4 + if (apmFlightStack.checked) { + if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) { + vehicleType = apmVehicleTypeCombo.currentIndex + } else { + if (controller.apmFirmwareNames.length === 0) { + // Not ready yet, or no firmware available + mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("Either firmware list is still downloading, or no firmware is available for current selection.")) + firmwareSelectDialog.preventClose = true + return + } + if (ardupilotFirmwareSelectionCombo.currentIndex == -1) { + mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("You must choose a board type.")) + firmwareSelectDialog.preventClose = true return } + + var firmwareUrl = controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex] + if (firmwareUrl == "") { + mainWindow.showMessageDialog(firmwareSelectDialog.title, qsTr("No firmware was found for the current selection.")) + firmwareSelectDialog.preventClose = true + return + } + controller.flashFirmwareUrl(controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex]) + return } } //-- If custom, get file path @@ -260,32 +253,6 @@ SetupPage { } } - ListModel { - id: px4FlowFirmwareList - - ListElement { - text: qsTr("PX4 Pro") - stackType: FirmwareUpgradeController.PX4FlowPX4 - } - ListElement { - text: qsTr("ArduPilot") - stackType: FirmwareUpgradeController.PX4FlowAPM - } - } - - ListModel { - id: px4FlowTypeList - - ListElement { - text: qsTr("Standard Version (stable)") - firmwareType: FirmwareUpgradeController.StableFirmware - } - ListElement { - text: qsTr("Custom firmware file...") - firmwareType: FirmwareUpgradeController.CustomFirmware - } - } - ListModel { id: singleFirmwareModeTypeList @@ -306,9 +273,8 @@ SetupPage { QGCLabel { Layout.fillWidth: true wrapMode: Text.WordWrap - text: (_singleFirmwareMode || !QGroundControl.apmFirmwareSupported) ? _singleFirmwareLabel : (px4Flow ? _px4FlowLabel : _pixhawkLabel) + text: (_singleFirmwareMode || !QGroundControl.apmFirmwareSupported) ? _singleFirmwareLabel : _pixhawkLabel - readonly property string _px4FlowLabel: qsTr("Detected PX4 Flow board. The firmware you use on the PX4 Flow must match the AutoPilot firmware type you are using on the vehicle:") readonly property string _pixhawkLabel: qsTr("Detected Pixhawk board. You can select from the following flight stacks:") readonly property string _singleFirmwareLabel: qsTr("Press Ok to upgrade your vehicle.") } @@ -317,7 +283,7 @@ SetupPage { id: firmwareRadiosColumn spacing: 0 - visible: !_singleFirmwareMode && !px4Flow && QGroundControl.apmFirmwareSupported + visible: !_singleFirmwareMode && QGroundControl.apmFirmwareSupported Component.onCompleted: { if(!QGroundControl.apmFirmwareSupported) { @@ -353,7 +319,7 @@ SetupPage { FactComboBox { Layout.fillWidth: true - visible: !px4Flow && apmFlightStack.checked + visible: apmFlightStack.checked fact: _firmwareUpgradeSettings.apmChibiOS indexModel: false } @@ -361,7 +327,7 @@ SetupPage { FactComboBox { id: apmVehicleTypeCombo Layout.fillWidth: true - visible: !px4Flow && apmFlightStack.checked + visible: apmFlightStack.checked fact: _firmwareUpgradeSettings.apmVehicleType indexModel: false } @@ -369,7 +335,7 @@ SetupPage { QGCComboBox { id: ardupilotFirmwareSelectionCombo Layout.fillWidth: true - visible: !px4Flow && apmFlightStack.checked && !controller.downloadingFirmwareList && controller.apmFirmwareNames.length !== 0 + visible: apmFlightStack.checked && !controller.downloadingFirmwareList && controller.apmFirmwareNames.length !== 0 model: controller.apmFirmwareNames onModelChanged: currentIndex = controller.apmFirmwareNamesBestIndex } @@ -388,20 +354,10 @@ SetupPage { visible: !controller.downloadingFirmwareList && (QGroundControl.apmFirmwareSupported && controller.apmFirmwareNames.length === 0) } - QGCComboBox { - id: px4FlowTypeSelectionCombo - Layout.fillWidth: true - visible: px4Flow - model: px4FlowFirmwareList - textRole: "text" - currentIndex: _defaultFirmwareIsPX4 ? 0 : 1 - } - QGCCheckBox { id: _advanced text: qsTr("Advanced settings") - checked: px4Flow ? true : false - visible: !px4Flow + checked: false onClicked: { firmwareBuildTypeCombo.currentIndex = 0 @@ -415,8 +371,7 @@ SetupPage { wrapMode: Text.WordWrap visible: showFirmwareTypeSelection text: _singleFirmwareMode ? qsTr("Select the standard version or one from the file system (previously downloaded):") : - (px4Flow ? qsTr("Select which version of the firmware you would like to install:") : - qsTr("Select which version of the above flight stack you would like to install:")) + qsTr("Select which version of the above flight stack you would like to install:") } QGCComboBox { @@ -424,7 +379,7 @@ SetupPage { Layout.fillWidth: true visible: showFirmwareTypeSelection textRole: "text" - model: _singleFirmwareMode ? singleFirmwareModeTypeList : (px4Flow ? px4FlowTypeList : firmwareBuildTypeList) + model: _singleFirmwareMode ? singleFirmwareModeTypeList : firmwareBuildTypeList onActivated: (index) => { controller.selectedFirmwareBuildType = model.get(index).firmwareType diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index a3429bee1b8..72fbc50f68f 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -142,7 +142,6 @@ FirmwareUpgradeController::FirmwareUpgradeController(void) connect(_apmVehicleTypeSetting, &Fact::rawValueChanged, this, &FirmwareUpgradeController::_buildAPMFirmwareNames); #endif - _initFirmwareHash(); _determinePX4StableVersion(); #if !defined(NO_ARDUPILOT_DIALECT) @@ -292,31 +291,6 @@ void FirmwareUpgradeController::_foundBoardInfo(int bootloaderVersion, int board } } - -/// @brief intializes the firmware hashes with proper urls. -/// This happens only once for a class instance first time when it is needed. -void FirmwareUpgradeController::_initFirmwareHash() -{ - // indirect check whether this function has been called before or not - // may have to be modified if _rgPX4FMUV2Firmware disappears - if (!_rgPX4FLowFirmware.isEmpty()) { - return; - } - - /////////////////////////////// px4flow firmwares /////////////////////////////////////// - FirmwareToUrlElement_t rgPX4FLowFirmwareArray[] = { - { PX4FlowPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Flow/master/px4flow.px4" }, - #if !defined(NO_ARDUPILOT_DIALECT) - { PX4FlowAPM, StableFirmware, DefaultVehicleFirmware, "http://firmware.ardupilot.org/Tools/PX4Flow/px4flow-klt-latest.px4" }, - #endif - }; - - // We build the maps for PX4 firmwares dynamically using the data below - for (auto& element : rgPX4FLowFirmwareArray) { - _rgPX4FLowFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); - } -} - /// @brief Called when the findBootloader process is unable to sync to the bootloader. Moves the state /// machine to the appropriate error state. void FirmwareUpgradeController::_bootloaderSyncFailed(void) @@ -329,9 +303,6 @@ QHash* FirmwareUpgradeCo _rgFirmwareDynamic.clear(); switch (boardId) { - case Bootloader::boardIDPX4Flow: - _rgFirmwareDynamic = _rgPX4FLowFirmware; - break; case Bootloader::boardIDSiKRadio1000: { FirmwareToUrlElement_t element = { SiKRadio, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/SiK/stable/radio~hm_trp.ihx" }; @@ -550,10 +521,6 @@ void FirmwareUpgradeController::_buildAPMFirmwareNames(void) quint16 boardPID = _boardInfo.productIdentifier(); uint32_t rawBoardId = _bootloaderBoardID == Bootloader::boardIDPX4FMUV3 ? Bootloader::boardIDPX4FMUV2 : _bootloaderBoardID; - if (_boardType == QGCSerialPortInfo::BoardTypePX4Flow) { - return; - } - qCDebug(FirmwareUpgradeLog) << QStringLiteral("_buildAPMFirmwareNames description(%1) vid(%2/0x%3) pid(%4/0x%5)").arg(boardDescription).arg(boardVID).arg(boardVID, 1, 16).arg(boardPID).arg(boardPID, 1, 16); _apmFirmwareNames.clear(); diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index fd5a1763096..f86f67d0c63 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -37,8 +37,6 @@ class FirmwareUpgradeController : public QObject typedef enum { AutoPilotStackPX4 = 0, AutoPilotStackAPM, - PX4FlowPX4, - PX4FlowAPM, SiKRadio, SingleFirmwareMode } AutoPilotStackType_t; @@ -92,7 +90,6 @@ class FirmwareUpgradeController : public QObject Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) Q_PROPERTY(QString boardType MEMBER _boardTypeName NOTIFY boardFound) Q_PROPERTY(bool pixhawkBoard READ pixhawkBoard NOTIFY boardFound) - Q_PROPERTY(bool px4FlowBoard READ px4FlowBoard NOTIFY boardFound) Q_PROPERTY(FirmwareBuildType_t selectedFirmwareBuildType READ selectedFirmwareBuildType WRITE setSelectedFirmwareBuildType NOTIFY selectedFirmwareBuildTypeChanged) Q_PROPERTY(QStringList apmFirmwareNames MEMBER _apmFirmwareNames NOTIFY apmFirmwareNamesChanged) Q_PROPERTY(int apmFirmwareNamesBestIndex MEMBER _apmFirmwareNamesBestIndex NOTIFY apmFirmwareNamesChanged) @@ -146,7 +143,6 @@ class FirmwareUpgradeController : public QObject QString px4BetaVersion (void) { return _px4BetaVersion; } bool pixhawkBoard(void) const { return _boardType == QGCSerialPortInfo::BoardTypePixhawk; } - bool px4FlowBoard(void) const { return _boardType == QGCSerialPortInfo::BoardTypePX4Flow; } /** * @brief Return a human friendly string of available boards @@ -191,7 +187,6 @@ private slots: private: QHash* _firmwareHashForBoardId(int boardId); void _getFirmwareFile (FirmwareIdentifier firmwareId); - void _initFirmwareHash (void); void _downloadFirmware (void); void _appendStatusLog (const QString& text, bool critical = false); void _errorCancel (const QString& msg); @@ -205,7 +200,6 @@ private slots: QString _portDescription; // Firmware hashes - QHash _rgPX4FLowFirmware; QHash _rgSiKRadioFirmware; // Hash map for ArduPilot ChibiOS lookup by board name diff --git a/src/VehicleSetup/PX4FlowSensor.qml b/src/VehicleSetup/OpticalFlowSensor.qml similarity index 58% rename from src/VehicleSetup/PX4FlowSensor.qml rename to src/VehicleSetup/OpticalFlowSensor.qml index f31295fbccd..4ac5b6f2415 100644 --- a/src/VehicleSetup/PX4FlowSensor.qml +++ b/src/VehicleSetup/OpticalFlowSensor.qml @@ -18,16 +18,16 @@ import QGroundControl.ScreenTools Item { QGCLabel { - id: titleLabel - text: qsTr("PX4Flow Camera") - font.bold: true + text: qsTr("Optical Flow Camera") + font.bold: true } + Image { - source: globals.activeVehicle ? "image://QGCImages/" + globals.activeVehicle.id + "/" + globals.activeVehicle.flowImageIndex : "" - width: parent.width * 0.5 - height: width * 0.75 - cache: false - fillMode: Image.PreserveAspectFit + source: globals.activeVehicle ? "image://QGCImages/" + globals.activeVehicle.id + "/" + globals.activeVehicle.flowImageIndex : "" + width: parent.width * 0.5 + height: width * 0.75 + cache: false + fillMode: Image.PreserveAspectFit anchors.centerIn: parent } } diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index 77b3295ba6e..cc5f5d853cc 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -249,13 +249,12 @@ Rectangle { } SubMenuButton { - id: px4FlowButton - buttonGroup: setupButtonGroup - visible: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.primaryLinkIsPX4Flow : false + buttonGroup: setupButtonGroup + visible: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.flowImageIndex > 0 : false setupIndicator: false - text: qsTr("PX4Flow") + text: qsTr("Optical Flow") Layout.fillWidth: true - onClicked: showPanel(this, "PX4FlowSensor.qml") + onClicked: showPanel(this, "OpticalFlowSensor.qml") } SubMenuButton {