diff --git a/qgcresources.qrc b/qgcresources.qrc index 88964444ffd829d5925372295df476f99d80cd86..5d2abeb61dfddad3bfc465d76390768e25f01d0c 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -84,6 +84,7 @@ src/AnalyzeView/LogDownloadIcon.svg src/AutoPilotPlugins/PX4/Images/LowBattery.svg src/AutoPilotPlugins/PX4/Images/LowBatteryLight.svg + src/AnalyzeView/MAVLinkInspector.svg src/AutoPilotPlugins/Common/Images/MotorComponentIcon.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_01cell.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_02cell.svg diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index be8c92a12f0d2818ae6cbe4ee355f063c75b31f1..469ed45fc79cf53a5ba323648cda06cf1e8251a7 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -77,6 +77,7 @@ src/PlanView/GeoFenceMapVisuals.qml src/QmlControls/IndicatorButton.qml src/QmlControls/JoystickThumbPad.qml + src/QmlControls/MAVLinkMessageButton.qml src/QmlControls/MissionCommandDialog.qml src/PlanView/MissionItemEditor.qml src/QmlControls/MissionItemIndexLabel.qml diff --git a/src/AnalyzeView/AnalyzeView.qml b/src/AnalyzeView/AnalyzeView.qml index c6cc9a3d181ddb40ecf6532ab9f490b71280a44e..43386fa15c681cfe2dd26fc067d6bc986dde8812 100644 --- a/src/AnalyzeView/AnalyzeView.qml +++ b/src/AnalyzeView/AnalyzeView.qml @@ -108,7 +108,7 @@ Rectangle { pageSource: "MavlinkConsolePage.qml" } ListElement { - buttonImage: "/qmlimages/MavlinkConsoleIcon" + buttonImage: "/qmlimages/MAVLinkInspector" buttonText: qsTr("MAVLink Inspector") pageSource: "MAVLinkInspectorPage.qml" } diff --git a/src/AnalyzeView/MAVLinkInspector.svg b/src/AnalyzeView/MAVLinkInspector.svg new file mode 100644 index 0000000000000000000000000000000000000000..b09244a091967e3e3dc9c0708a1be475adb7d300 --- /dev/null +++ b/src/AnalyzeView/MAVLinkInspector.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/src/AnalyzeView/MAVLinkInspectorController.cc b/src/AnalyzeView/MAVLinkInspectorController.cc index 2992ed0e627fb90bc982abc35340e4237f9ed2c1..4e9284027b095035c25b3c4a226403a6eea529ed 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.cc +++ b/src/AnalyzeView/MAVLinkInspectorController.cc @@ -27,7 +27,6 @@ QGCMAVLinkMessage::QGCMAVLinkMessage(QObject* parent, mavlink_message_t* message : QObject(parent) { _message = *message; - _messageHz = QGC::groundTimeMilliseconds(); const mavlink_message_info_t* msgInfo = mavlink_get_message_info(message); if (!msgInfo) { qWarning() << QStringLiteral("QGCMAVLinkMessage NULL msgInfo msgid(%1)").arg(message->msgid); @@ -55,13 +54,22 @@ QGCMAVLinkMessage::QGCMAVLinkMessage(QObject* parent, mavlink_message_t* message } } +//----------------------------------------------------------------------------- +void +QGCMAVLinkMessage::updateFreq() +{ + quint64 msgCount = _count - _lastCount; + _messageHz = (0.2 * _messageHz) + (0.8 * msgCount); + _lastCount = _count; + emit freqChanged(); +} + //----------------------------------------------------------------------------- void QGCMAVLinkMessage::update(mavlink_message_t* message) { _message = *message; _count++; - _messageHz = QGC::groundTimeMilliseconds(); const mavlink_message_info_t* msgInfo = mavlink_get_message_info(message); if (!msgInfo) { qWarning() << QStringLiteral("QGCMAVLinkMessage::update NULL msgInfo msgid(%1)").arg(message->msgid); @@ -167,7 +175,13 @@ QGCMAVLinkMessage::update(mavlink_message_t* message) } else { // Single value uint32_t n = *(reinterpret_cast(m + msgInfo->fields[i].wire_offset)); - f->updateValue(QString::number(n)); + //-- Special case + if(_message.msgid == MAVLINK_MSG_ID_SYSTEM_TIME) { + QDateTime d = QDateTime::fromMSecsSinceEpoch(static_cast(n),Qt::UTC,0); + f->updateValue(d.toString("HH:mm:ss")); + } else { + f->updateValue(QString::number(n)); + } } break; case MAVLINK_TYPE_INT32_T: @@ -231,7 +245,13 @@ QGCMAVLinkMessage::update(mavlink_message_t* message) } else { // Single value uint64_t n = *(reinterpret_cast(m + msgInfo->fields[i].wire_offset)); - f->updateValue(QString::number(n)); + //-- Special case + if(_message.msgid == MAVLINK_MSG_ID_SYSTEM_TIME) { + QDateTime d = QDateTime::fromMSecsSinceEpoch(n/1000,Qt::UTC,0); + f->updateValue(d.toString("yyyy MM dd HH:mm:ss")); + } else { + f->updateValue(QString::number(n)); + } } break; case MAVLINK_TYPE_INT64_T: @@ -266,12 +286,12 @@ QGCMAVLinkVehicle::QGCMAVLinkVehicle(QObject* parent, quint8 id) //----------------------------------------------------------------------------- QGCMAVLinkMessage* -QGCMAVLinkVehicle::findMessage(uint32_t id) +QGCMAVLinkVehicle::findMessage(uint32_t id, uint8_t cid) { for(int i = 0; i < _messages.count(); i++) { QGCMAVLinkMessage* m = qobject_cast(_messages.get(i)); if(m) { - if(m->id() == id) { + if(m->id() == id && m->cid() == cid) { return m; } } @@ -279,11 +299,32 @@ QGCMAVLinkVehicle::findMessage(uint32_t id) return nullptr; } +//----------------------------------------------------------------------------- +static bool +messages_sort(QObject* a, QObject* b) +{ + QGCMAVLinkMessage* aa = qobject_cast(a); + QGCMAVLinkMessage* bb = qobject_cast(b); + if(!aa || !bb) return false; + if(aa->id() == bb->id()) return aa->cid() < bb->cid(); + return aa->id() < bb->id(); +} + //----------------------------------------------------------------------------- void QGCMAVLinkVehicle::append(QGCMAVLinkMessage* message) { _messages.append(message); + //-- Sort messages by id and then cid + if(_messages.count() > 0) { + std::sort(_messages.objectList()->begin(), _messages.objectList()->end(), messages_sort); + for(int i = 0; i < _messages.count(); i++) { + QGCMAVLinkMessage* m = qobject_cast(_messages.get(i)); + if(m) { + emit m->indexChanged(); + } + } + } emit messagesChanged(); } @@ -295,6 +336,10 @@ MAVLinkInspectorController::MAVLinkInspectorController() connect(multiVehicleManager, &MultiVehicleManager::vehicleRemoved, this, &MAVLinkInspectorController::_vehicleRemoved); MAVLinkProtocol* mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); connect(mavlinkProtocol, &MAVLinkProtocol::messageReceived, this, &MAVLinkInspectorController::_receiveMessage); + connect(&_updateTimer, &QTimer::timeout, this, &MAVLinkInspectorController::_refreshFrequency); + _updateTimer.start(1000); + MultiVehicleManager *manager = qgcApp()->toolbox()->multiVehicleManager(); + connect(manager, &MultiVehicleManager::activeVehicleChanged, this, &MAVLinkInspectorController::_setActiveVehicle); } //----------------------------------------------------------------------------- @@ -303,6 +348,24 @@ MAVLinkInspectorController::~MAVLinkInspectorController() _reset(); } + +//---------------------------------------------------------------------------------------- +void +MAVLinkInspectorController::_setActiveVehicle(Vehicle* vehicle) +{ + if(vehicle) { + QGCMAVLinkVehicle* v = _findVehicle(static_cast(vehicle->id())); + if(v) { + _activeVehicle = v; + } else { + _activeVehicle = nullptr; + } + } else { + _activeVehicle = nullptr; + } + emit activeVehiclesChanged(); +} + //----------------------------------------------------------------------------- QGCMAVLinkVehicle* MAVLinkInspectorController::_findVehicle(uint8_t id) @@ -318,6 +381,23 @@ MAVLinkInspectorController::_findVehicle(uint8_t id) return nullptr; } +//----------------------------------------------------------------------------- +void +MAVLinkInspectorController::_refreshFrequency() +{ + for(int i = 0; i < _vehicles.count(); i++) { + QGCMAVLinkVehicle* v = qobject_cast(_vehicles.get(i)); + if(v) { + for(int i = 0; i < v->messages()->count(); i++) { + QGCMAVLinkMessage* m = qobject_cast(v->messages()->get(i)); + if(m) { + m->updateFreq(); + } + } + } + } +} + //----------------------------------------------------------------------------- void MAVLinkInspectorController::_vehicleAdded(Vehicle* vehicle) @@ -360,8 +440,12 @@ MAVLinkInspectorController::_receiveMessage(LinkInterface* link, mavlink_message _vehicles.append(v); _vehicleNames.append(QString(tr("Vehicle %1")).arg(message.sysid)); emit vehiclesChanged(); + if(!_activeVehicle) { + _activeVehicle = v; + emit activeVehiclesChanged(); + } } else { - m = v->findMessage(message.msgid); + m = v->findMessage(message.msgid, message.compid); } if(!m) { m = new QGCMAVLinkMessage(this, &message); diff --git a/src/AnalyzeView/MAVLinkInspectorController.h b/src/AnalyzeView/MAVLinkInspectorController.h index f73812d508ea1503c5f0344c67122313cbf49ac5..1969a2ccd1addd6a8467b08549fd7aa08d49a639 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.h +++ b/src/AnalyzeView/MAVLinkInspectorController.h @@ -47,51 +47,58 @@ private: //----------------------------------------------------------------------------- class QGCMAVLinkMessage : public QObject { Q_OBJECT - Q_PROPERTY(quint32 id READ id CONSTANT) - Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(quint64 messageHz READ messageHz NOTIFY messageChanged) + Q_PROPERTY(quint32 id READ id NOTIFY indexChanged) + Q_PROPERTY(quint32 cid READ cid NOTIFY indexChanged) + Q_PROPERTY(QString name READ name NOTIFY indexChanged) + Q_PROPERTY(qreal messageHz READ messageHz NOTIFY freqChanged) Q_PROPERTY(quint64 count READ count NOTIFY messageChanged) - Q_PROPERTY(QmlObjectListModel* fields READ fields CONSTANT) + Q_PROPERTY(QmlObjectListModel* fields READ fields NOTIFY indexChanged) public: QGCMAVLinkMessage(QObject* parent, mavlink_message_t* message); - quint32 id () { return _message.msgid; } + quint32 id () { return _message.msgid; } + quint8 cid () { return _message.compid; } QString name () { return _name; } - quint64 messageHz () { return _messageHz; } + qreal messageHz () { return _messageHz; } quint64 count () { return _count; } + quint64 lastCount () { return _lastCount; } QmlObjectListModel* fields () { return &_fields; } void update (mavlink_message_t* message); + void updateFreq (); signals: void messageChanged (); + void freqChanged (); + void indexChanged (); private: QmlObjectListModel _fields; QString _name; - uint64_t _messageHz = 0; + qreal _messageHz = 0.0; uint64_t _count = 0; + uint64_t _lastCount = 0; mavlink_message_t _message; //-- List of QGCMAVLinkMessageField }; //----------------------------------------------------------------------------- class QGCMAVLinkVehicle : public QObject { Q_OBJECT - Q_PROPERTY(quint8 id READ id CONSTANT) - Q_PROPERTY(QmlObjectListModel* messages READ messages NOTIFY messagesChanged) + Q_PROPERTY(quint8 id READ id CONSTANT) + Q_PROPERTY(QmlObjectListModel* messages READ messages NOTIFY messagesChanged) public: QGCMAVLinkVehicle(QObject* parent, quint8 id); - quint8 id () { return _id; } - QmlObjectListModel* messages () { return &_messages; } + quint8 id () { return _id; } + QmlObjectListModel* messages () { return &_messages; } - QGCMAVLinkMessage* findMessage (uint32_t id); - void append (QGCMAVLinkMessage* message); + QGCMAVLinkMessage* findMessage (uint32_t id, uint8_t cid); + void append (QGCMAVLinkMessage* message); signals: - void messagesChanged (); + void messagesChanged (); private: quint8 _id; @@ -108,17 +115,22 @@ public: Q_PROPERTY(QStringList vehicleNames READ vehicleNames NOTIFY vehiclesChanged) Q_PROPERTY(QmlObjectListModel* vehicles READ vehicles NOTIFY vehiclesChanged) + Q_PROPERTY(QGCMAVLinkVehicle* activeVehicle READ activeVehicle NOTIFY activeVehiclesChanged) - QmlObjectListModel* vehicles () { return &_vehicles; } - QStringList vehicleNames () { return _vehicleNames; } + QmlObjectListModel* vehicles () { return &_vehicles; } + QGCMAVLinkVehicle* activeVehicle () { return _activeVehicle; } + QStringList vehicleNames () { return _vehicleNames; } signals: void vehiclesChanged (); + void activeVehiclesChanged (); private slots: void _receiveMessage (LinkInterface* link, mavlink_message_t message); void _vehicleAdded (Vehicle* vehicle); void _vehicleRemoved (Vehicle* vehicle); + void _setActiveVehicle (Vehicle* vehicle); + void _refreshFrequency (); private: void _reset (); @@ -129,6 +141,8 @@ private: int _selectedSystemID = 0; ///< Currently selected system int _selectedComponentID = 0; ///< Currently selected component + QGCMAVLinkVehicle* _activeVehicle = nullptr; + QTimer _updateTimer; QStringList _vehicleNames; QmlObjectListModel _vehicles; //-- List of QGCMAVLinkVehicle }; diff --git a/src/AnalyzeView/MAVLinkInspectorPage.qml b/src/AnalyzeView/MAVLinkInspectorPage.qml index 05820ae5f2f638e5364af3848cfd084f48573000..8f9a7c6d60c27afe6e95b765d705c2cd437117e6 100644 --- a/src/AnalyzeView/MAVLinkInspectorPage.qml +++ b/src/AnalyzeView/MAVLinkInspectorPage.qml @@ -22,10 +22,7 @@ Item { anchors.fill: parent anchors.margins: ScreenTools.defaultFontPixelWidth - readonly property real _butttonWidth: ScreenTools.defaultFontPixelWidth * 30 - - property int curVehicleIndex: 0 - property var curVehicle: controller.vehicles.count > 0 ? controller.vehicles.get(curVehicleIndex) : null + property var curVehicle: controller ? controller.activeVehicle : null property int curMessageIndex: 0 property var curMessage: curVehicle && curVehicle.messages.count ? curVehicle.messages.get(curMessageIndex) : null @@ -38,39 +35,21 @@ Item { } //-- Header - ColumnLayout { - id: header - width: parent.width - spacing: ScreenTools.defaultFontPixelHeight - QGCLabel { - text: qsTr("Analyze real time MAVLink messages.") - } - RowLayout { - Layout.fillWidth: true - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - text: qsTr("Vehicle:") - } - QGCComboBox { - id: vehicleSelector - model: controller.vehicleNames - enabled: controller.vehicles.count > 0 - onActivated: curVehicleIndex = index - Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 16 - } - } - Item { - height: ScreenTools.defaultFontPixelHeight - width: 1 - } + QGCLabel { + id: header + text: qsTr("Inspect real time MAVLink messages.") + anchors.top: parent.top + anchors.left: parent.left } - //-- Messages + + //-- Messages (Buttons) QGCFlickable { id: buttonGrid anchors.top: header.bottom + anchors.topMargin: ScreenTools.defaultFontPixelHeight anchors.bottom: parent.bottom anchors.left: parent.left - width: ScreenTools.defaultFontPixelWidth * 32 + width: buttonCol.width contentWidth: buttonCol.width contentHeight: buttonCol.height ColumnLayout { @@ -78,10 +57,12 @@ Item { spacing: ScreenTools.defaultFontPixelHeight * 0.25 Repeater { model: curVehicle ? curVehicle.messages : [] - delegate: QGCButton { - text: object.name - onClicked: curMessageIndex = index - Layout.minimumWidth: _butttonWidth + delegate: MAVLinkMessageButton { + text: object.name + checked: curMessageIndex === index + messageHz: object.messageHz + onClicked: curMessageIndex = index + Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 36 } } } @@ -89,16 +70,17 @@ Item { //-- Message Data QGCFlickable { id: messageGrid - anchors.top: header.bottom + visible: curMessage !== null + anchors.top: buttonGrid.top anchors.bottom: parent.bottom anchors.left: buttonGrid.right anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 anchors.right: parent.right contentWidth: messageCol.width contentHeight: messageCol.height - Column { + ColumnLayout { id: messageCol - spacing: ScreenTools.defaultFontPixelHeight + spacing: ScreenTools.defaultFontPixelHeight * 0.25 GridLayout { columns: 2 columnSpacing: ScreenTools.defaultFontPixelWidth @@ -109,21 +91,31 @@ Item { } QGCLabel { color: qgcPal.buttonHighlight - text: curMessage ? curMessage.name : "" + text: curMessage ? curMessage.name + ' (' + curMessage.id + ') ' + curMessage.messageHz.toFixed(1) + 'Hz' : "" } QGCLabel { - text: qsTr("Count:") + text: qsTr("Component:") } QGCLabel { - text: curMessage ? curMessage.count : "" + text: curMessage ? curMessage.cid : "" } QGCLabel { - text: qsTr("Frequency:") + text: qsTr("Count:") } QGCLabel { - text: curMessage ? curMessage.messageHz + 'Hz' : "" + text: curMessage ? curMessage.count : "" } } + Item { height: ScreenTools.defaultFontPixelHeight; width: 1 } + QGCLabel { + text: qsTr("Message Fields:") + } + Rectangle { + Layout.fillWidth: true + height: 1 + color: qgcPal.text + } + Item { height: ScreenTools.defaultFontPixelHeight * 0.25; width: 1 } GridLayout { columns: 3 columnSpacing: ScreenTools.defaultFontPixelWidth diff --git a/src/QmlControls/MAVLinkMessageButton.qml b/src/QmlControls/MAVLinkMessageButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..2ed3fc700dc738d31e7c19326234752380ec8ed0 --- /dev/null +++ b/src/QmlControls/MAVLinkMessageButton.qml @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Button { + id: control + height: ScreenTools.defaultFontPixelHeight * 2 + autoExclusive: true + + background: Rectangle { + anchors.fill: parent + color: checked ? qgcPal.buttonHighlight : qgcPal.button + } + + property double messageHz: 0 + + contentItem: RowLayout { + QGCLabel { + text: control.text + color: checked ? qgcPal.buttonHighlightText : qgcPal.buttonText + Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 26 + } + QGCLabel { + color: checked ? qgcPal.buttonHighlightText : qgcPal.buttonText + text: messageHz.toFixed(1) + 'Hz' + Layout.alignment: Qt.AlignRight + } + } +} diff --git a/src/QmlControls/QGroundControl/Controls/qmldir b/src/QmlControls/QGroundControl/Controls/qmldir index 44bb36ff4a0bb4c337c0a0f14edaff5f1d3bc5b4..c4850c1067d53e54e747b75ab13f938301416a45 100644 --- a/src/QmlControls/QGroundControl/Controls/qmldir +++ b/src/QmlControls/QGroundControl/Controls/qmldir @@ -20,6 +20,7 @@ GeoFenceMapVisuals 1.0 GeoFenceMapVisuals.qml HackFileDialog 1.0 HackFileDialog.qml IndicatorButton 1.0 IndicatorButton.qml JoystickThumbPad 1.0 JoystickThumbPad.qml +MAVLinkMessageButton 1.0 MAVLinkMessageButton.qml MissionCommandDialog 1.0 MissionCommandDialog.qml MissionItemEditor 1.0 MissionItemEditor.qml MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml diff --git a/src/ui/MainRootWindow.qml b/src/ui/MainRootWindow.qml index 999bd2d2fff88631eb6960ff11fe373841fd1967..54a6e8fcaee2147e5299a7173bf35fe52989e6fb 100644 --- a/src/ui/MainRootWindow.qml +++ b/src/ui/MainRootWindow.qml @@ -24,8 +24,6 @@ import QGroundControl.FlightMap 1.0 /// Native QML top level window ApplicationWindow { id: mainWindow - width: ScreenTools.isMobile ? Screen.width : Math.min(250 * Screen.pixelDensity, Screen.width) - height: ScreenTools.isMobile ? Screen.height : Math.min(150 * Screen.pixelDensity, Screen.height) minimumWidth: ScreenTools.isMobile ? Screen.width : Math.min(215 * Screen.pixelDensity, Screen.width) minimumHeight: ScreenTools.isMobile ? Screen.height : Math.min(120 * Screen.pixelDensity, Screen.height) visible: true @@ -33,6 +31,9 @@ ApplicationWindow { Component.onCompleted: { if(ScreenTools.isMobile) { mainWindow.showFullScreen() + } else { + width = ScreenTools.isMobile ? Screen.width : Math.min(250 * Screen.pixelDensity, Screen.width) + height = ScreenTools.isMobile ? Screen.height : Math.min(150 * Screen.pixelDensity, Screen.height) } }