diff --git a/CodingStyle.h b/CodingStyle.h index 2fe6dbb2566..b25ee40d758 100644 --- a/CodingStyle.h +++ b/CodingStyle.h @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT + * (c) 2009-2022 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. @@ -31,7 +31,7 @@ Q_DECLARE_LOGGING_CATEGORY(CodingStyleLog) /// Here is the class documentation. Class names are PascalCase. If you override any of the Qt base classes to provide /// generic base implementations for widespread use prefix the class name with QGC. For example: -/// QGCMessageBox - is a QGC special vesion of Qt MessageBox +/// QGCMessageBox - is a QGC special version of Qt MessageBox /// QGCPalette - is a QGC special version of Qt Palette /// For normal single use classes do no prefix them name with QGC. diff --git a/custom-example/custom.cmake b/custom-example/custom.cmake new file mode 100644 index 00000000000..53312e68352 --- /dev/null +++ b/custom-example/custom.cmake @@ -0,0 +1,42 @@ +message(STATUS "Adding Custom Plugin") + +# you can disable usb camera +set(QGC_DISABLE_UVC OFF) + +# disable video streaming +set(DISABLE_VIDEOSTREAMING OFF) +# disable video streaming +set(QGC_DISABLE_MAVLINK_INSPECTOR OFF ) + +# disable airmap +set(DISABLE_AIRMAP OFF) + +# disable Ardupilot mavlink dialect +set(QGC_DISABLE_APM_MAVLINK ON) +# disable Ardupilot firmware plugin +set(QGC_DISABLE_APM_PLUGIN ON) +# disable PX4 firmware plugin +# set(QGC_DISABLE_PX4_PLUGIN ON) +# disable Ardupilot firmware plugin factory +set(QGC_DISABLE_APM_PLUGIN_FACTORY ON) +# disable PX4 firmware plugin factory +set(QGC_DISABLE_PX4_PLUGIN_FACTORY ON) + +set(CUSTOMCLASS "CustomPlugin") +set(CUSTOMHEADER \"CustomPlugin.h\") + +set(CUSTOM_SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/custom/src) + +include_directories(${CUSTOM_SRC_PATH}) +include_directories(${CUSTOM_SRC_PATH}/FirmwarePlugin) +include_directories(${CUSTOM_SRC_PATH}/AutoPilotPlugin) + +set(CUSTOM_SRC + ${CUSTOM_SRC_PATH}/CustomPlugin.cc + ${CUSTOM_SRC_PATH}/AutoPilotPlugin/CustomAutoPilotPlugin.cc + ${CUSTOM_SRC_PATH}/FirmwarePlugin/CustomFirmwarePlugin.cc + ${CUSTOM_SRC_PATH}/FirmwarePlugin/CustomFirmwarePluginFactory.cc + ) + + +set(CUSTOM_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/custom/custom.qrc) \ No newline at end of file diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 27e34fd89c7..1f3e45389d4 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -153,7 +153,7 @@ iOSBuild { # disable both the Plugin and PluginFactory entries. To include custom support # for an existing plugin type disable PluginFactory only. Then provide you own # implementation of FirmwarePluginFactory and use the FirmwarePlugin and -# AutoPilotPlugin classes as the base clase for your derived plugin +# AutoPilotPlugin classes as the base class for your derived plugin # implementation. contains (CONFIG, QGC_DISABLE_APM_PLUGIN) { @@ -1376,6 +1376,9 @@ contains (CONFIG, DISABLE_VIDEOSTREAMING) { } !VideoEnabled { +# TODO bzd here propably should go: +# DEFINES -= QGC_GST_STREAMING + INCLUDEPATH += \ src/VideoReceiver diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index f07d6a356e5..b1b9d3fc644 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -185,7 +185,7 @@ src/PlanView/TerrainStatus.qml src/PlanView/TakeoffItemMapVisual.qml src/QmlControls/ToolStrip.qml - src/QmlControls/ToolStripHoverButton.qml + src/QmlControls/ToolStripHoverButton.qml src/PlanView/TransectStyleComplexItemEditor.qml src/PlanView/TransectStyleComplexItemStats.qml src/PlanView/TransectStyleComplexItemTabBar.qml diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index ad410bb3853..96c165062b1 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -7,6 +7,15 @@ if(BUILD_TESTING) ) endif() +if (NOT QGC_DISABLE_MAVLINK_INSPECTOR) + set(MAVLINK_INSPECTOR_SRC + MAVLinkInspectorController.cc + MAVLinkInspectorController.h + ) + set(MAVLINK_INSPECTOR_QML + MAVLinkInspectorPage.qml) +endif() + add_library(AnalyzeView ExifParser.cc ExifParser.h @@ -16,8 +25,7 @@ add_library(AnalyzeView LogDownloadController.h MavlinkConsoleController.cc MavlinkConsoleController.h - MAVLinkInspectorController.cc - MAVLinkInspectorController.h + ${MAVLINK_INSPECTOR_SRC} PX4LogParser.cc PX4LogParser.h ULogParser.cc @@ -33,7 +41,7 @@ add_custom_target(AnalyzeViewQml GeoTagPage.qml LogDownloadPage.qml MavlinkConsolePage.qml - MAVLinkInspectorPage.qml + ${MAVLINK_INSPECTOR_QML} VibrationPage.qml ) diff --git a/src/AnalyzeView/MAVLinkInspectorController.h b/src/AnalyzeView/MAVLinkInspectorController.h index 83736d842eb..dd6c251e8ef 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.h +++ b/src/AnalyzeView/MAVLinkInspectorController.h @@ -95,6 +95,7 @@ class QGCMAVLinkMessage : public QObject { Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(qreal messageHz READ messageHz NOTIFY freqChanged) Q_PROPERTY(quint64 count READ count NOTIFY countChanged) + Q_PROPERTY(int mavlinkVer READ mavlinkVer CONSTANT) Q_PROPERTY(QmlObjectListModel* fields READ fields CONSTANT) Q_PROPERTY(bool fieldSelected READ fieldSelected NOTIFY fieldSelectedChanged) Q_PROPERTY(bool selected READ selected NOTIFY selectedChanged) @@ -107,6 +108,7 @@ class QGCMAVLinkMessage : public QObject { QString name () { return _name; } qreal messageHz () const{ return _messageHz; } quint64 count () const{ return _count; } + int mavlinkVer () const{ return _message.magic == 0xFE ? 1 : 2; } quint64 lastCount () const{ return _lastCount; } QmlObjectListModel* fields () { return &_fields; } bool fieldSelected () const{ return _fieldSelected; } diff --git a/src/AnalyzeView/MAVLinkInspectorPage.qml b/src/AnalyzeView/MAVLinkInspectorPage.qml index a0b0ee03efb..e5ad1bae9c9 100644 --- a/src/AnalyzeView/MAVLinkInspectorPage.qml +++ b/src/AnalyzeView/MAVLinkInspectorPage.qml @@ -158,6 +158,12 @@ AnalyzePage { QGCLabel { text: curMessage ? curMessage.count : "" } + QGCLabel { + text: qsTr("Mavlink ver:") + } + QGCLabel { + text: curMessage ? curMessage.mavlinkVer : "" + } } Item { height: ScreenTools.defaultFontPixelHeight; width: 1 } //--------------------------------------------------------- diff --git a/src/AutoPilotPlugins/CMakeLists.txt b/src/AutoPilotPlugins/CMakeLists.txt index e737b81d8cb..774c383423e 100644 --- a/src/AutoPilotPlugins/CMakeLists.txt +++ b/src/AutoPilotPlugins/CMakeLists.txt @@ -1,30 +1,75 @@ - -add_subdirectory(APM) add_subdirectory(Common) -add_subdirectory(PX4) -add_library(AutoPilotPlugins - APM/APMAirframeComponent.cc - APM/APMAirframeComponentController.cc - APM/APMAutoPilotPlugin.cc - APM/APMCameraComponent.cc - APM/APMCompassCal.cc - APM/APMFlightModesComponent.cc - APM/APMFlightModesComponentController.cc - APM/APMFollowComponent.cc - APM/APMFollowComponentController.cc - APM/APMHeliComponent.cc - APM/APMLightsComponent.cc - APM/APMMotorComponent.cc - APM/APMPowerComponent.cc - APM/APMRadioComponent.cc - APM/APMSafetyComponent.cc - APM/APMSensorsComponent.cc - APM/APMSensorsComponentController.cc - APM/APMSubFrameComponent.cc - APM/APMSubMotorComponentController.cc - APM/APMTuningComponent.cc +if (NOT QGC_DISABLE_PX4_PLUGIN) + add_subdirectory(PX4) + + set(PX4FirmwarePlugin + PX4/ActuatorComponent.cc + PX4/ActuatorComponent.h + PX4/AirframeComponentAirframes.cc + PX4/AirframeComponentAirframes.h + PX4/AirframeComponent.cc + PX4/AirframeComponentController.cc + PX4/AirframeComponentController.h + PX4/AirframeComponent.h + PX4/CameraComponent.cc + PX4/CameraComponent.h + PX4/FlightModesComponent.cc + PX4/FlightModesComponent.h + PX4/PowerComponent.cc + PX4/PowerComponentController.cc + PX4/PowerComponentController.h + PX4/PowerComponent.h + PX4/PX4AirframeLoader.cc + PX4/PX4AirframeLoader.h + PX4/PX4AutoPilotPlugin.cc + PX4/PX4AutoPilotPlugin.h + PX4/PX4FlightBehavior.cc + PX4/PX4FlightBehavior.h + PX4/PX4RadioComponent.cc + PX4/PX4RadioComponent.h + PX4/PX4SimpleFlightModesController.cc + PX4/PX4SimpleFlightModesController.h + PX4/PX4TuningComponent.cc + PX4/PX4TuningComponent.h + PX4/SafetyComponent.cc + PX4/SafetyComponent.h + PX4/SensorsComponent.cc + PX4/SensorsComponentController.cc + PX4/SensorsComponentController.h + PX4/SensorsComponent.h + ) +endif() + +if (NOT QGC_DISABLE_APM_PLUGIN) + add_subdirectory(APM) + set(APMFirmwarePlugin + APM/APMAirframeComponent.cc + APM/APMAirframeComponentController.cc + APM/APMAutoPilotPlugin.cc + APM/APMCameraComponent.cc + APM/APMCompassCal.cc + APM/APMFlightModesComponent.cc + APM/APMFlightModesComponentController.cc + APM/APMFollowComponent.cc + APM/APMFollowComponentController.cc + APM/APMHeliComponent.cc + APM/APMLightsComponent.cc + APM/APMMotorComponent.cc + APM/APMPowerComponent.cc + APM/APMRadioComponent.cc + APM/APMSafetyComponent.cc + APM/APMSensorsComponent.cc + APM/APMSensorsComponentController.cc + APM/APMSubFrameComponent.cc + APM/APMSubMotorComponentController.cc + APM/APMTuningComponent.cc + APM/APMRemoteSupportComponent.cc + ) +endif() + +add_library(AutoPilotPlugins Common/ESP8266Component.cc Common/ESP8266ComponentController.cc Common/MotorComponent.cc @@ -34,42 +79,9 @@ add_library(AutoPilotPlugins Generic/GenericAutoPilotPlugin.cc - PX4/ActuatorComponent.cc - PX4/ActuatorComponent.h - PX4/AirframeComponentAirframes.cc - PX4/AirframeComponentAirframes.h - PX4/AirframeComponent.cc - PX4/AirframeComponentController.cc - PX4/AirframeComponentController.h - PX4/AirframeComponent.h - PX4/CameraComponent.cc - PX4/CameraComponent.h - PX4/FlightModesComponent.cc - PX4/FlightModesComponent.h - PX4/PowerComponent.cc - PX4/PowerComponentController.cc - PX4/PowerComponentController.h - PX4/PowerComponent.h - PX4/PX4AirframeLoader.cc - PX4/PX4AirframeLoader.h - PX4/PX4AutoPilotPlugin.cc - PX4/PX4AutoPilotPlugin.h - PX4/PX4FlightBehavior.cc - PX4/PX4FlightBehavior.h - PX4/PX4RadioComponent.cc - PX4/PX4RadioComponent.h - PX4/PX4SimpleFlightModesController.cc - PX4/PX4SimpleFlightModesController.h - PX4/PX4TuningComponent.cc - PX4/PX4TuningComponent.h - PX4/SafetyComponent.cc - PX4/SafetyComponent.h - PX4/SensorsComponent.cc - PX4/SensorsComponentController.cc - PX4/SensorsComponentController.h - PX4/SensorsComponent.h - AutoPilotPlugin.cc + ${APMFirmwarePlugin} + ${PX4FirmwarePlugin} ) target_link_libraries(AutoPilotPlugins @@ -81,7 +93,23 @@ target_include_directories(AutoPilotPlugins INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC - APM Common - PX4 ) + +if (NOT QGC_DISABLE_APM_PLUGIN) + target_include_directories(AutoPilotPlugins + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + APM + ) +endif() + +if (NOT QGC_DISABLE_PX4_PLUGIN) + target_include_directories(AutoPilotPlugins + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + PX4 + ) +endif() diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index d404c70b7f9..c3a9683cbfb 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -260,6 +260,7 @@ bool APMFirmwarePlugin::_handleIncomingStatusText(Vehicle* /*vehicle*/, mavlink_ // and lower the severity on them so that they don't pop in the users face. QString messageText = _getMessageText(message); + qDebug(APMFirmwarePluginLog) << "StatusText: " << messageText; if (messageText.contains("Place vehicle") || messageText.contains("Calibration successful")) { _adjustCalibrationMessageSeverity(message); return true; @@ -1073,3 +1074,8 @@ QMutex& APMFirmwarePlugin::_reencodeMavlinkChannelMutex() static QMutex _mutex{}; return _mutex; } + +bool APMFirmwarePlugin::supportsRcChannelOverride(void) +{ + return true; +} diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index f4587f94137..c7ae3734720 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -77,9 +77,10 @@ class APMFirmwarePlugin : public FirmwarePlugin QObject* _loadParameterMetaData (const QString& metaDataFile) override; QString brandImageIndoor (const Vehicle* vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } QString brandImageOutdoor (const Vehicle* vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } - QString getHobbsMeter (Vehicle* vehicle) override; + QString getHobbsMeter (Vehicle* vehicle) override; bool hasGripper (const Vehicle* vehicle) const override; const QVariantList& toolIndicators (const Vehicle* vehicle) override; + bool supportsRcChannelOverride(void) override; protected: /// All access to singleton is through stack specific implementation diff --git a/src/FirmwarePlugin/CMakeLists.txt b/src/FirmwarePlugin/CMakeLists.txt index c2f51adae42..5f9a588178d 100644 --- a/src/FirmwarePlugin/CMakeLists.txt +++ b/src/FirmwarePlugin/CMakeLists.txt @@ -1,23 +1,48 @@ add_subdirectory(APM) +if (NOT QGC_DISABLE_PX4_PLUGIN_FACTORY) + set(PX4FirmwarePluginFactory + PX4/PX4FirmwarePluginFactory.cc + ) +endif() + +if (NOT QGC_DISABLE_PX4_PLUGIN) + + set(PX4FirmwarePlugin + PX4/PX4FirmwarePlugin.cc + PX4/PX4ParameterMetaData.cc + PX4/PX4Resources.qrc + ) +endif() + +if (NOT QGC_DISABLE_APM_PLUGIN_FACTORY) + set(APMFirmwarePluginFactory + APM/APMFirmwarePluginFactory.cc + ) +endif() + +if (NOT QGC_DISABLE_APM_PLUGIN) + set(APMFirmwarePlugin + APM/APMFirmwarePlugin.cc + APM/APMParameterMetaData.cc + APM/ArduCopterFirmwarePlugin.cc + APM/ArduPlaneFirmwarePlugin.cc + APM/ArduRoverFirmwarePlugin.cc + APM/ArduSubFirmwarePlugin.cc + APM/APMResources.qrc + ) +endif() + add_library(FirmwarePlugin CameraMetaData.cc FirmwarePlugin.cc FirmwarePluginManager.cc - APM/APMFirmwarePlugin.cc - APM/APMFirmwarePluginFactory.cc - APM/APMParameterMetaData.cc - APM/ArduCopterFirmwarePlugin.cc - APM/ArduPlaneFirmwarePlugin.cc - APM/ArduRoverFirmwarePlugin.cc - APM/ArduSubFirmwarePlugin.cc - APM/APMResources.qrc - - PX4/PX4FirmwarePlugin.cc - PX4/PX4FirmwarePluginFactory.cc - PX4/PX4ParameterMetaData.cc - PX4/PX4Resources.qrc + ${APMFirmwarePlugin} + ${APMFirmwarePluginFactory} + + ${PX4FirmwarePluginFactory} + ${PX4FirmwarePlugin} ) target_link_libraries(FirmwarePlugin @@ -28,6 +53,17 @@ target_link_libraries(FirmwarePlugin target_include_directories(FirmwarePlugin INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - APM - ) + ) +if (NOT QGC_DISABLE_APM_PLUGIN) + target_include_directories(FirmwarePlugin + INTERFACE + APM) +endif() + +if (NOT QGC_DISABLE_PX4_PLUGIN) + target_include_directories(FirmwarePlugin + INTERFACE + PX4 + ) +endif() diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index 574f2618f9a..80cd2add873 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -154,6 +154,11 @@ bool FirmwarePlugin::supportsJSButton(void) return false; } +bool FirmwarePlugin::supportsRcChannelOverride(void) +{ + return false; +} + bool FirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message) { Q_UNUSED(vehicle); diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index a3956747263..4d2a2be1122 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -211,6 +211,10 @@ class FirmwarePlugin : public QObject /// to be assigned via parameters in firmware. Default is false. virtual bool supportsJSButton(void); + /// Returns true if the firmware supports the mavlink RC_CHANNELS_OVERRIDE message + /// Default is false. + virtual bool supportsRcChannelOverride(void); + /// Returns true if the firmware supports calibrating motor interference offsets for the compass /// (CompassMot). Default is true. virtual bool supportsMotorInterference(void); diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 1d75579c5c9..b0b17801b56 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -736,4 +736,9 @@ bool PX4FirmwarePlugin::hasGripper(const Vehicle* vehicle) const return _hasGripper; } return false; +} + +bool PX4FirmwarePlugin::supportsRcChannelOverride(void) +{ + return false; } \ No newline at end of file diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index ff8e9eb8c02..6f261ff8578 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -73,6 +73,7 @@ class PX4FirmwarePlugin : public FirmwarePlugin bool supportsNegativeThrust (Vehicle* vehicle) override; QString getHobbsMeter (Vehicle* vehicle) override; bool hasGripper (const Vehicle* vehicle) const override; + bool supportsRcChannelOverride (void) override; protected: typedef struct { diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index 83b1065fdca..84f2f9a47b1 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -1,4 +1,3 @@ - set(EXTRA_SRC) if (ANDROID) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 767d2081812..d4d446bc13d 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -10,7 +10,6 @@ #include "Joystick.h" #include "QGC.h" -#include "AutoPilotPlugin.h" #include "UAS.h" #include "QGCApplication.h" #include "VideoManager.h" @@ -26,6 +25,9 @@ const char* Joystick::_settingsGroup = "Joysticks"; const char* Joystick::_calibratedSettingsKey = "Calibrated4"; // Increment number to force recalibration const char* Joystick::_buttonActionNameKey = "ButtonActionName%1"; const char* Joystick::_buttonActionRepeatKey = "ButtonActionRepeat%1"; +const char* Joystick::_buttonActionLowPwmValueKey = "ButtonActionLowPwm%1"; +const char* Joystick::_buttonActionHighPwmValueKey = "ButtonActionHighPwm%1"; +const char* Joystick::_buttonActionLatchPwmValueKey = "ButtonActionLatchPwm%1"; const char* Joystick::_throttleModeSettingsKey = "ThrottleMode"; const char* Joystick::_negativeThrustSettingsKey = "NegativeThrust"; const char* Joystick::_exponentialSettingsKey = "Exponential"; @@ -85,13 +87,62 @@ const float Joystick::_minAxisFrequencyHz = 0.25f; const float Joystick::_maxAxisFrequencyHz = 200.0f; const float Joystick::_minButtonFrequencyHz = 0.25f; const float Joystick::_maxButtonFrequencyHz = 50.0f; +const float Joystick::_rcOverrideFrequencyHz = 0.5f; -AssignedButtonAction::AssignedButtonAction(QObject* parent, const QString name) +AssignedButtonAction::AssignedButtonAction(QObject* parent, const QString action) : QObject(parent) - , action(name) + , _action(action) + , _isPwmOverrideAction(false) + , _pwmRcChannel(0) { } +AssignedButtonAction::AssignedButtonAction( + QObject *parent, const QString action, const uint16_t loPwmValue, const uint16_t hiPwmValue, bool latch) + : QObject(parent) + , _action(action) + , _isPwmOverrideAction(true) + , _loPwmValue(loPwmValue) + , _hiPwmValue(hiPwmValue) + , _pwmLatchMode(latch) + , _pwmRcChannel(getRcChannelFromAction(action)) +{ +} + +int16_t AssignedButtonAction::calculatePwm(bool buttonDown) +{ + if (!_isPwmOverrideAction) { + qCWarning(JoystickLog) << "send called on non-pwm action"; + return -1; + } + + uint16_t pwmValue = buttonDown ? _hiPwmValue : _loPwmValue; + if (_pwmLatchMode) { + qCDebug(JoystickLog) << "Latch mode, current saved button state " << (_pwmLatchButtonDown ? "down" : "up"); + + if (buttonDown) { + // altering latch state + _pwmLatchButtonDown = !_pwmLatchButtonDown; + } + pwmValue = _pwmLatchButtonDown ? _hiPwmValue : _loPwmValue; + } + qCDebug(JoystickLog) << " Calculated PWM Value " << pwmValue << " for action " << _action; + return pwmValue; +} + +uint8_t AssignedButtonAction::getRcChannelFromAction(const QString action) +{ + QRegularExpression re("^Channel (\\d{1,2}) direct PWM"); + QRegularExpressionMatch match = re.match(action); + if (match.hasMatch()) { + QString channelNoStr = match.captured(1); + qCDebug(JoystickLog) << "RC override matched for channel " << channelNoStr; + uint8_t channelNo = channelNoStr.toInt(); + return channelNo; + } + return 0; +} + AssignableButtonAction::AssignableButtonAction(QObject* parent, QString action_, bool canRepeat_) : QObject(parent) , _action(action_) @@ -120,6 +171,7 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC _rgButtonValues[i] = BUTTON_UP; _buttonActionArray.append(nullptr); } + _buildActionList(_multiVehicleManager->activeVehicle()); _updateTXModeSettingsKey(_multiVehicleManager->activeVehicle()); _loadSettings(); @@ -220,6 +272,7 @@ void Joystick::_activeVehicleChanged(Vehicle* activeVehicle) int mode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt(); setTXMode(mode); } + _clearRcOverrideButtonActions(); } void Joystick::_loadSettings() @@ -298,26 +351,48 @@ void Joystick::_loadSettings() // Remap to stored TX mode in settings _remapAxes(2, _transmitterMode, _rgFunctionAxis); + // Preparing pwm sections visibility map + while (_pwmSettingsVisibilities.length() < _totalButtonCount) { + _pwmSettingsVisibilities.push_back(false); + } + for (int button = 0; button < _totalButtonCount; button++) { - QString a = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString(); - if(!a.isEmpty() && a != _buttonActionNone) { - if(_buttonActionArray[button]) { + _pwmSettingsVisibilities[button] = false; + QString action = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString(); + if (!action.isEmpty() && action != _buttonActionNone) { + if (_buttonActionArray[button]) { _buttonActionArray[button]->deleteLater(); } - AssignedButtonAction* ap = new AssignedButtonAction(this, a); - ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool(); + + AssignedButtonAction *ap; + if (assignableActionIsPwm(action)) { + _pwmSettingsVisibilities[button] = true; + int lowPwm = settings.value(QString(_buttonActionLowPwmValueKey).arg(button), -1).toInt(); + int highPwm = settings.value(QString(_buttonActionHighPwmValueKey).arg(button), -1).toInt(); + bool latch = settings.value(QString(_buttonActionLatchPwmValueKey).arg(button), false).toBool(); + qCDebug(JoystickLog) << "_loadSettings button:action:pwm" << button << action << "L:" << lowPwm + << " H:" << highPwm << " Latch:" << latch; + ap = new AssignedButtonAction(this, action, lowPwm, highPwm, latch); + } else { + ap = new AssignedButtonAction(this, action); + } _buttonActionArray[button] = ap; - _buttonActionArray[button]->buttonTime.start(); - qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat; + _buttonActionArray[button]->buttonTime().start(); + + bool savedRepeatState = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool(); + _setButtonRepeatIfAvailable(button, savedRepeatState); + + qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action() + << _buttonActionArray[button]->repeat(); } } - if (badSettings) { _calibrated = false; settings.setValue(_calibratedSettingsKey, false); } } + void Joystick::_saveButtonSettings() { QSettings settings; @@ -325,9 +400,14 @@ void Joystick::_saveButtonSettings() settings.beginGroup(_name); for (int button = 0; button < _totalButtonCount; button++) { if(_buttonActionArray[button]) { - settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action); - settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat); - qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat; + settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action()); + settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat()); + qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button << _buttonActionArray[button]->action() << _buttonActionArray[button]->repeat(); + if (_buttonActionArray[button]->isPwmOverrideAction()) { + settings.setValue(QString(_buttonActionLowPwmValueKey).arg(button), _buttonActionArray[button]->lowPwm()); + settings.setValue(QString(_buttonActionHighPwmValueKey).arg(button), _buttonActionArray[button]->highPwm()); + settings.setValue(QString(_buttonActionLatchPwmValueKey).arg(button), _buttonActionArray[button]->pwmLatchMode()); + } } } } @@ -482,15 +562,17 @@ void Joystick::run() _open(); //-- Reset timers _axisTime.start(); + _rcOverrideTimer.start(); for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) { if(_buttonActionArray[buttonIndex]) { - _buttonActionArray[buttonIndex]->buttonTime.start(); + _buttonActionArray[buttonIndex]->buttonTime().start(); } } while (!_exitThread) { _update(); _handleButtons(); _handleAxis(); + _handleRcOverride(); QGC::SLEEP::msleep(qMin(static_cast(1000.0f / _maxAxisFrequencyHz), static_cast(1000.0f / _maxButtonFrequencyHz)) / 2); } _close(); @@ -535,18 +617,22 @@ void Joystick::_handleButtons() for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) { if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) { if(_buttonActionArray[buttonIndex]) { - QString buttonAction = _buttonActionArray[buttonIndex]->action; + QString buttonAction = _buttonActionArray[buttonIndex]->action(); if(buttonAction.isEmpty() || buttonAction == _buttonActionNone) continue; - if(!_buttonActionArray[buttonIndex]->repeat) { + if(!_buttonActionArray[buttonIndex]->repeat()) { //-- This button just went down if(_rgButtonValues[buttonIndex] == BUTTON_DOWN) { - // Check for a multi-button action + // Check for a multi-button action except for PWM actions. + // PWM buttons can produce different PWM values on same channel. + // Therefore low value must be the same for all buttons at the same channel. QList rgButtons = { buttonIndex }; bool executeButtonAction = true; for (int multiIndex = 0; multiIndex < _totalButtonCount; multiIndex++) { if (multiIndex != buttonIndex) { - if (_buttonActionArray[multiIndex] && _buttonActionArray[multiIndex]->action == buttonAction) { + if (_buttonActionArray[multiIndex] && _buttonActionArray[multiIndex]->action() == buttonAction + && !_buttonActionArray[multiIndex]->isPwmOverrideAction()) { + qCDebug(JoystickLog) << "Multi-button action:" << buttonAction; // We found a multi-button action if (_rgButtonValues[multiIndex] == BUTTON_DOWN || _rgButtonValues[multiIndex] == BUTTON_REPEAT) { // So far so good @@ -562,16 +648,16 @@ void Joystick::_handleButtons() } if (executeButtonAction) { qCDebug(JoystickLog) << "Action triggered" << rgButtons << buttonAction; - _executeButtonAction(buttonAction, true); + _executeButtonAction(buttonAction, buttonIndex, true); } } } else { //-- Process repeat buttons int buttonDelay = static_cast(1000.0f / _buttonFrequencyHz); - if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) { - _buttonActionArray[buttonIndex]->buttonTime.start(); + if(_buttonActionArray[buttonIndex]->buttonTime().elapsed() > buttonDelay) { + _buttonActionArray[buttonIndex]->buttonTime().start(); qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction; - _executeButtonAction(buttonAction, true); + _executeButtonAction(buttonAction, buttonIndex, true); } } } @@ -582,11 +668,11 @@ void Joystick::_handleButtons() if(buttonIndex < 256) { if(lastBbuttonValues[buttonIndex] == BUTTON_DOWN || lastBbuttonValues[buttonIndex] == BUTTON_REPEAT) { if(_buttonActionArray[buttonIndex]) { - QString buttonAction = _buttonActionArray[buttonIndex]->action; + QString buttonAction = _buttonActionArray[buttonIndex]->action(); if(buttonAction.isEmpty() || buttonAction == _buttonActionNone) continue; qCDebug(JoystickLog) << "Button up" << buttonIndex << buttonAction; - _executeButtonAction(buttonAction, false); + _executeButtonAction(buttonAction, buttonIndex, false); } } } @@ -690,6 +776,26 @@ void Joystick::_handleAxis() } } +void Joystick::_handleRcOverride() +{ + if (!_activeVehicle || !_activeVehicle->supportsRcChannelOverride()) { + return; + } + const int rcOverrideInterval = static_cast(1000.0f / _rcOverrideFrequencyHz); + + if (_rcOverrideActive || _rcOverrideTimer.elapsed() > rcOverrideInterval) { + _rcOverrideActive = false; + _rcOverrideTimer.start(); + uint16_t overrideData[18] = {UINT16_MAX}; + overrideData[16] = _mapRcOverrideToRelease(17, 0); + overrideData[17] = _mapRcOverrideToRelease(18, 0); + for (int i = 0; i < MAX_RC_CHANNELS; i++) { + overrideData[i] = _rcOverride[i] == -1 ? _mapRcOverrideToRelease(i + 1, 0) : _rcOverride[i]; + } + _activeVehicle->rcChannelsOverride(overrideData); + } +} + void Joystick::startPolling(Vehicle* vehicle) { if (vehicle) { @@ -795,13 +901,13 @@ void Joystick::setButtonRepeat(int button, bool repeat) if (!_validButton(button) || !_buttonActionArray[button]) { return; } - _buttonActionArray[button]->repeat = repeat; - _buttonActionArray[button]->buttonTime.start(); + _buttonActionArray[button]->repeat(repeat); + _buttonActionArray[button]->buttonTime().start(); //-- Save to settings QSettings settings; settings.beginGroup(_settingsGroup); settings.beginGroup(_name); - settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat); + settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat()); } bool Joystick::getButtonRepeat(int button) @@ -809,7 +915,7 @@ bool Joystick::getButtonRepeat(int button) if (!_validButton(button) || !_buttonActionArray[button]) { return false; } - return _buttonActionArray[button]->repeat; + return _buttonActionArray[button]->repeat(); } void Joystick::setButtonAction(int button, const QString& action) @@ -818,48 +924,171 @@ void Joystick::setButtonAction(int button, const QString& action) return; } qCWarning(JoystickLog) << "setButtonAction:" << button << action; - QSettings settings; - settings.beginGroup(_settingsGroup); - settings.beginGroup(_name); - if(action.isEmpty() || action == _buttonActionNone) { - if(_buttonActionArray[button]) { + + if (action.isEmpty() || action == _buttonActionNone) { + _pwmSettingsVisibilities[button] = false; + if (_buttonActionArray[button]) { + _removeButtonSettings(button); _buttonActionArray[button]->deleteLater(); _buttonActionArray[button] = nullptr; - //-- Clear from settings - settings.remove(QString(_buttonActionNameKey).arg(button)); - settings.remove(QString(_buttonActionRepeatKey).arg(button)); } } else { - if(!_buttonActionArray[button]) { - _buttonActionArray[button] = new AssignedButtonAction(this, action); + bool isPwmAction = assignableActionIsPwm(action); + _pwmSettingsVisibilities[button] = isPwmAction; + qCDebug(JoystickLog) << "setButtonAction: isPwmAction " << isPwmAction; + + if (!_buttonActionArray[button]) { + _buttonActionArray[button] = isPwmAction ? new AssignedButtonAction(this, action, 1500, 1500, false) + : new AssignedButtonAction(this, action); } else { - _buttonActionArray[button]->action = action; - //-- Make sure repeat is off if this action doesn't support repeats - int idx = _findAssignableButtonAction(action); - if(idx >= 0) { - AssignableButtonAction* p = qobject_cast(_assignableButtonActions[idx]); - if(!p->canRepeat()) { - _buttonActionArray[button]->repeat = false; + if (isPwmAction) { + // PWM Actions are not reusable + _buttonActionArray[button]->deleteLater(); + _buttonActionArray[button] = new AssignedButtonAction(this, action, 0, 0, false); + } else { + if (_buttonActionArray[button]->isPwmOverrideAction()) { + _buttonActionArray[button]->deleteLater(); + _buttonActionArray[button] = new AssignedButtonAction(this, action); + } else { + _buttonActionArray[button]->action(action); } } } - //-- Save to settings - settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action); - settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat); + + //-- Make sure repeat is off if this action doesn't support repeats + _setButtonRepeatIfAvailable(button, false); + _saveButtonSettings(button); } + emit pwmVisibilitiesChanged(); emit buttonActionsChanged(); } +void Joystick::_setButtonRepeatIfAvailable(int button, bool repeat) +{ + int idx = _findAssignableButtonAction(_buttonActionArray[button]->action()); + if (idx >= 0) { + AssignableButtonAction *p = qobject_cast(_assignableButtonActions[idx]); + if (p->canRepeat()) { + _buttonActionArray[button]->repeat(repeat); + return; + } + } + _buttonActionArray[button]->repeat(false); +} + +void Joystick::_saveButtonSettings(int button) +{ + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + //-- Save to settings + settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action()); + settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat()); + if (_buttonActionArray[button]->isPwmOverrideAction()) { + settings.setValue(QString(_buttonActionLowPwmValueKey).arg(button), _buttonActionArray[button]->lowPwm()); + settings.setValue(QString(_buttonActionHighPwmValueKey).arg(button), _buttonActionArray[button]->highPwm()); + settings.setValue(QString(_buttonActionLatchPwmValueKey).arg(button), _buttonActionArray[button]->pwmLatchMode()); + } +} + +void Joystick::_removeButtonSettings(int button) +{ + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + settings.remove(QString(_buttonActionNameKey).arg(button)); + settings.remove(QString(_buttonActionRepeatKey).arg(button)); + if (assignableButtonActionIsPwm(button)) { + settings.remove(QString(_buttonActionHighPwmValueKey).arg(button)); + settings.remove(QString(_buttonActionLowPwmValueKey).arg(button)); + settings.remove(QString(_buttonActionLatchPwmValueKey).arg(button)); + } +} + QString Joystick::getButtonAction(int button) { if (_validButton(button)) { if(_buttonActionArray[button]) { - return _buttonActionArray[button]->action; + return _buttonActionArray[button]->action(); } } return QString(_buttonActionNone); } +bool Joystick::assignableButtonActionIsPwm(int button) { + return _validButton(button) && _buttonActionArray[button] && _buttonActionArray[button]->isPwmOverrideAction(); +} + +bool Joystick::assignableActionIsPwm(QString action) { + return action.contains("PWM"); +} + +int Joystick::setButtonPwm(int button, bool lowPwm, int value) { + qDebug(JoystickLog) << "setButtonPwm: " << button << (lowPwm ? "LOW " : "HIGH ") << value; + if (assignableButtonActionIsPwm(button)) { + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + if (lowPwm) { + /// finds first other button with same action and sets low value to same value, emits error + int anyOtherButtonWithSameAction = _getOtherMultiButtonPWMOverrideButtonIndex(button); + if (anyOtherButtonWithSameAction != -1) { + if (value != _buttonActionArray[anyOtherButtonWithSameAction]->lowPwm()) { + value = _buttonActionArray[anyOtherButtonWithSameAction]->lowPwm(); + qCDebug(JoystickLog) << "setButtonPwm: " << button << " has same action as " << anyOtherButtonWithSameAction << " setting low pwm to " << value; + //TODO(bzd) emit error + } + } + _buttonActionArray[button]->lowPwm(value); + settings.setValue(QString(_buttonActionLowPwmValueKey).arg(button), value); + } else { + _buttonActionArray[button]->highPwm(value); + settings.setValue(QString(_buttonActionHighPwmValueKey).arg(button), value); + } + return value; + } + + return -1; +} + +int Joystick::getButtonPwm(int button, bool lowPwm) { + if (_validButton(button) && assignableButtonActionIsPwm(button)) { + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + if (lowPwm) { + return settings.value(QString(_buttonActionLowPwmValueKey).arg(button), -1).toInt(); + } else { + return settings.value(QString(_buttonActionHighPwmValueKey).arg(button), -1).toInt(); + } + } + + return -1; +} + +void Joystick::setButtonPwmLatch(int button, bool latch) +{ + if (!_validButton(button) || !_buttonActionArray[button]) { + return; + } + qCDebug(JoystickLog) << "PWM Latch mode for button " << button << (latch ? " enabled" : " disabled"); + auto action = _buttonActionArray[button]; + action->pwmLatchMode(latch); + + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + settings.setValue(QString(_buttonActionLatchPwmValueKey).arg(button), action->pwmLatchMode()); +} + +bool Joystick::getButtonPwmLatch(int button) { + if (!_validButton(button) || !_buttonActionArray[button]) { + return false; + } + auto action = (_buttonActionArray[button]); + return _buttonActionArray[button]->isPwmOverrideAction() ? action->pwmLatchMode() : false; +} + QStringList Joystick::buttonActions() { QStringList list; @@ -982,8 +1211,7 @@ void Joystick::setCalibrationMode(bool calibrating) } } - -void Joystick::_executeButtonAction(const QString& action, bool buttonDown) +void Joystick::_executeButtonAction(const QString &action, int buttonIndex, bool buttonDown) { if (!_activeVehicle || !_activeVehicle->joystickEnabled() || action == _buttonActionNone) { return; @@ -1045,13 +1273,22 @@ void Joystick::_executeButtonAction(const QString& action, bool buttonDown) emit gripperAction(GRIPPER_ACTION_RELEASE); } } else { - if (buttonDown && _activeVehicle) { - for (auto& item : _customMavCommands) { - if (action == item.name()) { - item.send(_activeVehicle); - return; + qCDebug(JoystickLog) << "Action " << action << " button " << (buttonDown ? "down" : "up"); + if (_activeVehicle) { + bool actionServed = false; + if (assignableActionIsPwm(action)) { + actionServed = _executeRcOverrideButtonAction(buttonIndex, buttonDown); + } + if (buttonDown && !actionServed) { + for (auto& item : _customMavCommands) { + if (action == item.name()) { + item.send(_activeVehicle); + return; + } } } + } else { + qCDebug(JoystickLog) << "_buttonAction unknown action:" << action; } } } @@ -1143,6 +1380,12 @@ void Joystick::_buildActionList(Vehicle* activeVehicle) _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGripperGrab)); _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGripperRelease)); + for (int ch = 8; ch <= MAX_RC_CHANNELS; ch++) { + _assignableButtonActions.append(new AssignableButtonAction(this, QString("Channel %1 direct PWM").arg(ch))); + } + _clearRcOverrideButtonActions(); + + _assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionEmergencyStop)); for (auto& item : _customMavCommands) { _assignableButtonActions.append(new AssignableButtonAction(this, item.name())); } @@ -1153,3 +1396,44 @@ void Joystick::_buildActionList(Vehicle* activeVehicle) } emit assignableActionsChanged(); } + +bool Joystick::_executeRcOverrideButtonAction(int buttonIndex, bool buttonDown) +{ + if (_buttonActionArray[buttonIndex]) { + uint8_t channel = _buttonActionArray[buttonIndex]->rcChannel(); + auto pwm = _buttonActionArray[buttonIndex]->calculatePwm(buttonDown); + _rcOverride[channel-1] = pwm; + _rcOverrideActive = true; + return true; + } + return false; +} + +void Joystick::_clearRcOverrideButtonActions() { + qCDebug(JoystickLog) << "Clearing RC override button actions"; + for (int i = 0; i < MAX_RC_CHANNELS; i++) { + _rcOverride[i] = -1; + } +} + +uint16_t Joystick::_mapRcOverrideToRelease(uint8_t rcChannel, uint16_t value) { + // UINT16_MAX - ignore + // 0 Release + if (value == 0) { + return rcChannel < 9 ? 0 : UINT16_MAX - 1; + } + return value; +} + +int Joystick::_getOtherMultiButtonPWMOverrideButtonIndex(int button) { + if (_buttonActionArray[button] && _buttonActionArray[button]->isPwmOverrideAction()) { + auto action = _buttonActionArray[button]->action(); + // check if there is another button with the same action + for (int i = 0; i < _buttonActionArray.count(); i++) { + if (i != button && _buttonActionArray[i] && _buttonActionArray[i]->action() == action) { + return i; + } + } + } + return -1; +} \ No newline at end of file diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index fcdc5bf8d82..f32dbd3b28b 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -16,23 +16,57 @@ #include #include +#include "JoystickMavCommand.h" +#include "MultiVehicleManager.h" #include "QGCLoggingCategory.h" #include "Vehicle.h" -#include "MultiVehicleManager.h" -#include "JoystickMavCommand.h" Q_DECLARE_LOGGING_CATEGORY(JoystickLog) Q_DECLARE_LOGGING_CATEGORY(JoystickValuesLog) Q_DECLARE_METATYPE(GRIPPER_ACTIONS) +static const int MAX_RC_CHANNELS = 16; + /// Action assigned to button class AssignedButtonAction : public QObject { Q_OBJECT + public: - AssignedButtonAction(QObject* parent, const QString name); - QString action; - QElapsedTimer buttonTime; - bool repeat = false; + AssignedButtonAction(QObject* parent, const QString action); + + AssignedButtonAction(QObject* parent, const QString action, + const uint16_t loPwmValue, + const uint16_t hiPwmValue, + bool latch); + QString action() const { return _action; } + void action(const QString& action) { _action = action; } + QElapsedTimer buttonTime() const { return _buttonTime; } + bool repeat() const { return _repeat; } + void repeat(bool repeat) { _repeat = repeat; } + + uint16_t lowPwm() const { return _loPwmValue; } + uint16_t highPwm() const { return _hiPwmValue; } + void lowPwm(uint16_t value) { _loPwmValue = value; } + void highPwm(uint16_t value) { _hiPwmValue = value; } + void pwmLatchMode(bool latch) { _pwmLatchMode = latch; } + bool pwmLatchMode() const { return _pwmLatchMode; } + bool isPwmOverrideAction() const { return _isPwmOverrideAction; } + int16_t calculatePwm(bool buttonDown); + uint8_t rcChannel() const { return _pwmRcChannel; } + +private: + static uint8_t getRcChannelFromAction(const QString action); + + QString _action; + QElapsedTimer _buttonTime; + bool _repeat = false; +/// PWM override related fields + const bool _isPwmOverrideAction; + uint16_t _loPwmValue; + uint16_t _hiPwmValue; + bool _pwmLatchMode; + const uint8_t _pwmRcChannel; + bool _pwmLatchButtonDown = false; }; /// Assignable Button Action @@ -101,6 +135,7 @@ class Joystick : public QThread Q_PROPERTY(QmlObjectListModel* assignableActions READ assignableActions NOTIFY assignableActionsChanged) Q_PROPERTY(QStringList assignableActionTitles READ assignableActionTitles NOTIFY assignableActionsChanged) Q_PROPERTY(QString disabledActionName READ disabledActionName CONSTANT) + Q_PROPERTY(QList pwmVisibilities READ pwmVisibilities NOTIFY pwmVisibilitiesChanged) Q_PROPERTY(int throttleMode READ throttleMode WRITE setThrottleMode NOTIFY throttleModeChanged) Q_PROPERTY(float axisFrequencyHz READ axisFrequencyHz WRITE setAxisFrequency NOTIFY axisFrequencyHzChanged) @@ -116,11 +151,16 @@ class Joystick : public QThread Q_INVOKABLE void setButtonRepeat (int button, bool repeat); Q_INVOKABLE bool getButtonRepeat (int button); + Q_INVOKABLE void setButtonPwmLatch (int button, bool latch); + Q_INVOKABLE bool getButtonPwmLatch (int button); Q_INVOKABLE void setButtonAction (int button, const QString& action); Q_INVOKABLE QString getButtonAction (int button); + Q_INVOKABLE bool assignableButtonActionIsPwm (int button); + Q_INVOKABLE bool assignableActionIsPwm (QString action); + Q_INVOKABLE int setButtonPwm (int button, bool lowPwm, int value); + Q_INVOKABLE int getButtonPwm (int button, bool lowPwm); // Property accessors - QString name () { return _name; } int totalButtonCount () const{ return _totalButtonCount; } int axisCount () const{ return _axisCount; } @@ -129,6 +169,7 @@ class Joystick : public QThread QmlObjectListModel* assignableActions () { return &_assignableButtonActions; } QStringList assignableActionTitles () { return _availableActionTitles; } QString disabledActionName () { return _buttonActionNone; } + QList pwmVisibilities () { return _pwmSettingsVisibilities; } /// Start the polling thread which will in turn emit joystick signals void startPolling(Vehicle* vehicle); @@ -142,12 +183,6 @@ class Joystick : public QThread void stop(); -/* - // Joystick index used by sdl library - // Settable because sdl library remaps indices after certain events - virtual int index(void) = 0; - virtual void setIndex(int index) = 0; -*/ virtual bool requiresCalibration(void) { return true; } int throttleMode (); @@ -191,6 +226,7 @@ class Joystick : public QThread void calibratedChanged (bool calibrated); void buttonActionsChanged (); void assignableActionsChanged (); + void pwmVisibilitiesChanged (); void throttleModeChanged (int mode); void negativeThrustChanged (bool allowNegative); void exponentialChanged (float exponential); @@ -231,12 +267,13 @@ class Joystick : public QThread void _saveButtonSettings (); void _loadSettings (); float _adjustRange (int value, Calibration_t calibration, bool withDeadbands); - void _executeButtonAction (const QString& action, bool buttonDown); + void _executeButtonAction (const QString &action, int buttonIndex, bool buttonDown); int _findAssignableButtonAction(const QString& action); bool _validAxis (int axis) const; bool _validButton (int button) const; void _handleAxis (); void _handleButtons (); + void _handleRcOverride (); void _buildActionList (Vehicle* activeVehicle); void _pitchStep (int direction); @@ -257,6 +294,27 @@ class Joystick : public QThread int _mapFunctionMode(int mode, int function); void _remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]); + void _removeButtonSettings(int button); + void _saveButtonSettings(int button); + /// if repeat is available for action under button, sets passed repeat flag + /// otherwise sets false as safe value + void _setButtonRepeatIfAvailable(int button, bool repeat); + bool _executeRcOverrideButtonAction(int buttonIndex, bool buttonDown); + void _clearRcOverrideButtonActions(); + uint16_t _mapRcOverrideToRelease(uint8_t rcChannel, uint16_t value); +// bool _isButtonOfMultiButtonPWMOverride(int button); + /** + * @brief Checks if the button is a multi-button PWM override, returns any other button if it is + * + * Searches for other buttons that are part of the same multi-button PWM override Action and returns the first one found that is different from the passed button. + * If the passed button is not part of a multi-button PWM override, returns -1. + * + * @param button + * @return + */ + int _getOtherMultiButtonPWMOverrideButtonIndex(int button); + bool _isActionMultiButtonPWMOverride(const QString& action); + // Override from QThread virtual void run(); @@ -286,6 +344,7 @@ class Joystick : public QThread float _axisFrequencyHz = _defaultAxisFrequencyHz; float _buttonFrequencyHz = _defaultButtonFrequencyHz; Vehicle* _activeVehicle = nullptr; + int16_t _rcOverride[MAX_RC_CHANNELS]; bool _pollingStartedForCalibration = false; @@ -300,18 +359,22 @@ class Joystick : public QThread static int _transmitterMode; int _rgFunctionAxis[maxFunction] = {}; QElapsedTimer _axisTime; + QElapsedTimer _rcOverrideTimer; + bool _rcOverrideActive = false; QmlObjectListModel _assignableButtonActions; QList _buttonActionArray; QStringList _availableActionTitles; + QList _pwmSettingsVisibilities; MultiVehicleManager* _multiVehicleManager = nullptr; - QList _customMavCommands; + QList _customMavCommands; static const float _minAxisFrequencyHz; static const float _maxAxisFrequencyHz; static const float _minButtonFrequencyHz; static const float _maxButtonFrequencyHz; + static const float _rcOverrideFrequencyHz; private: static const char* _rgFunctionSettingsKey[maxFunction]; @@ -320,6 +383,9 @@ class Joystick : public QThread static const char* _calibratedSettingsKey; static const char* _buttonActionNameKey; static const char* _buttonActionRepeatKey; + static const char* _buttonActionLowPwmValueKey; + static const char* _buttonActionHighPwmValueKey; + static const char* _buttonActionLatchPwmValueKey; static const char* _throttleModeSettingsKey; static const char* _negativeThrustSettingsKey; static const char* _exponentialSettingsKey; @@ -365,4 +431,5 @@ class Joystick : public QThread private slots: void _activeVehicleChanged(Vehicle* activeVehicle); + }; diff --git a/src/QGCLoggingCategory.h b/src/QGCLoggingCategory.h index 7b549118426..50f93b0802e 100644 --- a/src/QGCLoggingCategory.h +++ b/src/QGCLoggingCategory.h @@ -27,7 +27,7 @@ Q_DECLARE_LOGGING_CATEGORY(VideoAllLog) // turns on all individual QGC video log /// @def QGC_LOGGING_CATEGORY /// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a -/// global list. It's usage is the same as Q_LOGGING_CATEOGRY. +/// global list. It's usage is the same as Q_LOGGING_CATEGORY. #define QGC_LOGGING_CATEGORY(name, ...) \ static QGCLoggingCategory qgcCategory ## name (__VA_ARGS__); \ Q_LOGGING_CATEGORY(name, __VA_ARGS__) diff --git a/src/Settings/CMakeLists.txt b/src/Settings/CMakeLists.txt index c06f941d3ed..acb9d515386 100644 --- a/src/Settings/CMakeLists.txt +++ b/src/Settings/CMakeLists.txt @@ -1,9 +1,15 @@ +if (NOT QGC_DISABLE_APM_MAVLINK) + set(APM_MAVLINK_SR_SETTINGS_SRC + APMMavlinkStreamRateSettings.cc + APMMavlinkStreamRateSettings.h + + ) +endif() add_library(Settings ADSBVehicleManagerSettings.cc ADSBVehicleManagerSettings.h - APMMavlinkStreamRateSettings.cc - APMMavlinkStreamRateSettings.h + ${APM_MAVLINK_SR_SETTINGS_SRC} AppSettings.cc AppSettings.h AutoConnectSettings.cc @@ -37,8 +43,12 @@ add_library(Settings target_link_libraries(Settings PUBLIC qgc - Qt5::Multimedia ) - +if (NOT QGC_DISABLE_UVC) + target_link_libraries(Settings + PUBLIC + Qt5::Multimedia + ) +endif () target_include_directories(Settings PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 8ae6b6b9d27..f8b44dbf8c4 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2274,7 +2274,8 @@ void Vehicle::requestDataStream(MAV_DATA_STREAM stream, uint16_t rate, bool send void Vehicle::_sendMessageMultipleNext() { if (_nextSendMessageMultipleIndex < _sendMessageMultipleList.count()) { - qCDebug(VehicleLog) << "_sendMessageMultipleNext:" << _sendMessageMultipleList[_nextSendMessageMultipleIndex].message.msgid; +//bzd to be enabled but it spams too much + //qCDebug(VehicleLog) << "_sendMessageMultipleNext: " << _nextSendMessageMultipleIndex << ", mesg id: " << _sendMessageMultipleList[_nextSendMessageMultipleIndex].message.msgid; SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock(); if (sharedLink) { @@ -2533,6 +2534,11 @@ bool Vehicle::supportsTerrainFrame() const return !px4Firmware(); } +bool Vehicle::supportsRcChannelOverride() const +{ + return _firmwarePlugin->supportsRcChannelOverride(); +} + QString Vehicle::vehicleTypeName() const { static QMap typeNames = { { MAV_TYPE_GENERIC, tr("Generic micro air vehicle" )}, @@ -2688,8 +2694,8 @@ double Vehicle::minimumEquivalentAirspeed() return _firmwarePlugin->minimumEquivalentAirspeed(this); } -bool Vehicle::hasGripper() const -{ +bool Vehicle::hasGripper() const +{ return _firmwarePlugin->hasGripper(this); } @@ -3800,7 +3806,7 @@ void Vehicle::_handleRawImuTemp(mavlink_message_t& message) { // This is used by compass calibration emit mavlinkRawImu(message); - + mavlink_raw_imu_t imuRaw; mavlink_msg_raw_imu_decode(&message, &imuRaw); @@ -4053,8 +4059,8 @@ void Vehicle::doSetHome(const QGeoCoordinate& coord) disconnect(_currentDoSetHomeTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_doSetHomeTerrainReceived); _currentDoSetHomeTerrainAtCoordinateQuery = nullptr; } - // Save the coord for using when our terrain data arrives. If there was a pending terrain query paired with an older coordinate it is safe to - // Override now, as we just disconnected the signal that would trigger the command sending + // Save the coord for using when our terrain data arrives. If there was a pending terrain query paired with an older coordinate it is safe to + // Override now, as we just disconnected the signal that would trigger the command sending _doSetHomeCoordinate = coord; // Now setup and trigger the new terrain query _currentDoSetHomeTerrainAtCoordinateQuery = new TerrainAtCoordinateQuery(true /* autoDelet */); @@ -4297,16 +4303,89 @@ void Vehicle::setGripperAction(GRIPPER_ACTIONS gripperAction) void Vehicle::sendGripperAction(GRIPPER_OPTIONS gripperOption) { switch(gripperOption) { - case Gripper_release: + case Gripper_release: setGripperAction(GRIPPER_ACTION_RELEASE); break; - case Gripper_grab: + case Gripper_grab: setGripperAction(GRIPPER_ACTION_GRAB); break; case Invalid_option: qDebug("unknown function"); break; - default: + default: break; } } + +void Vehicle::rcChannelOverride(uint8_t rcChannel, uint16_t pwmValue) +{ + // TODO(bzd) take from joystick settings or from RC settings + const int maxRcChannels = 16; + if (rcChannel > maxRcChannels) { + qCWarning(VehicleLog) << "Unsupported rc channel " << rcChannel << " to override"; + return; + } + if (pwmValue > 2000 || pwmValue < 1000) { + qCWarning(VehicleLog) << "Bad PWM override value " << pwmValue << " for channel " << rcChannel; + return; + } + + qCDebug(VehicleLog) << "Sending RC channel " << rcChannel << " PWM override to " << pwmValue; + + uint16_t override_data[18] = {}; + for (int i = 0; i < 18; i++) { + override_data[i] = UINT16_MAX; + } + override_data[rcChannel - 1] = pwmValue; + + rcChannelsOverride(override_data); +} + +void Vehicle::rcChannelsOverride(uint16_t *override_data) { + SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock(); + if (!sharedLink) { + qCDebug(VehicleLog)<< "rcChannelOverride: primary link gone!"; + return; + } + + if (sharedLink->linkConfiguration()->isHighLatency()) { + return; + } + + if (VehicleLog().isDebugEnabled()) { + for (int i = 0; i < 18; i++) { + if (override_data[i] > 0 && override_data[i] < UINT16_MAX - 1) { + qCDebug(VehicleLog) << "RC Override ch " << i << " => " << override_data[i]; + } + } + } + + mavlink_message_t message; + + mavlink_msg_rc_channels_override_pack_chan( + static_cast(_mavlink->getSystemId()), + static_cast(_mavlink->getComponentId()), + sharedLink->mavlinkChannel(), + &message, + static_cast(_id), + 0, + override_data[0], + override_data[1], + override_data[2], + override_data[3], + override_data[4], + override_data[5], + override_data[6], + override_data[7], + override_data[8], + override_data[9], + override_data[10], + override_data[11], + override_data[12], + override_data[13], + override_data[14], + override_data[15], + override_data[16], + override_data[17]); + sendMessageOnLinkThreadSafe(sharedLink.get(), message); +} diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index a2ebc5a26c9..b4122facbe2 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -485,6 +485,15 @@ class Vehicle : public FactGroup void setJoystickEnabled (bool enabled); void sendJoystickDataThreadSafe (float roll, float pitch, float yaw, float thrust, quint16 buttons); + /// Sends Channel override value + /// @param rcChannel channel number 1-16 + /// @param pwmValue direct value 1000 - 2000 + void rcChannelOverride(uint8_t rcChannel, uint16_t pwmValue); + void rcChannelsOverride(uint16_t* rcChannels); + /// Sends disabling of channel override + /// @param rcChannel channel number 1-16 + void disableChannelOverride(uint8_t rcChannel); + // Property accesors int id() const{ return _id; } int compId() const{ return _compID; } @@ -533,14 +542,14 @@ class Vehicle : public FactGroup enum GRIPPER_OPTIONS { - Gripper_release = GRIPPER_ACTION_RELEASE, + Gripper_release = GRIPPER_ACTION_RELEASE, Gripper_grab = GRIPPER_ACTION_GRAB, Invalid_option = GRIPPER_ACTIONS_ENUM_END, - }; + }; Q_ENUM(GRIPPER_OPTIONS) void setGripperAction(GRIPPER_ACTIONS gripperAction); - Q_INVOKABLE void sendGripperAction(GRIPPER_OPTIONS gripperOption); + Q_INVOKABLE void sendGripperAction(GRIPPER_OPTIONS gripperOption); bool fixedWing() const; bool multiRotor() const; @@ -554,6 +563,7 @@ class Vehicle : public FactGroup bool supportsJSButton () const; bool supportsMotorInterference () const; bool supportsTerrainFrame () const; + bool supportsRcChannelOverride () const; void setGuidedMode(bool guidedMode); diff --git a/src/VehicleSetup/JoystickConfigButtons.qml b/src/VehicleSetup/JoystickConfigButtons.qml index 7fd3cb143ed..e26141785a0 100644 --- a/src/VehicleSetup/JoystickConfigButtons.qml +++ b/src/VehicleSetup/JoystickConfigButtons.qml @@ -52,63 +52,190 @@ ColumnLayout { Repeater { id: buttonActionRepeater model: _activeJoystick ? Math.min(_activeJoystick.totalButtonCount, _maxButtons) : [] - Row { - spacing: ScreenTools.defaultFontPixelWidth + + Column { + spacing: ScreenTools.defaultFontPixelWidth property bool pressed - property var currentAssignableAction: _activeJoystick ? _activeJoystick.assignableActions.get(buttonActionCombo.currentIndex) : null - Rectangle { - anchors.verticalCenter: parent.verticalCenter - width: ScreenTools.defaultFontPixelHeight * 1.5 - height: width - border.width: 1 - border.color: qgcPal.text - color: pressed ? qgcPal.buttonHighlight : qgcPal.button - QGCLabel { - anchors.fill: parent - color: pressed ? qgcPal.buttonHighlightText : qgcPal.buttonText - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: modelData + + Row { + spacing: ScreenTools.defaultFontPixelWidth + property var currentAssignableAction: _activeJoystick ? _activeJoystick.assignableActions.get(buttonActionCombo.currentIndex) : null + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: ScreenTools.defaultFontPixelHeight * 1.5 + height: width + border.width: 1 + border.color: qgcPal.text + color: parent.parent.pressed ? qgcPal.buttonHighlight : qgcPal.button + QGCLabel { + anchors.fill: parent + color: parent.parent.parent.pressed ? qgcPal.buttonHighlightText : qgcPal.buttonText + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: modelData + } + } + QGCComboBox { + id: buttonActionCombo + width: ScreenTools.defaultFontPixelWidth * 26 + model: _activeJoystick ? _activeJoystick.assignableActionTitles : [] + sizeToContents: true + + function _findCurrentButtonAction() { + if(_activeJoystick) { + var i = find(_activeJoystick.buttonActions[modelData]) + if(i < 0) i = 0 + currentIndex = i + } + } + + Component.onCompleted: _findCurrentButtonAction() + onModelChanged: _findCurrentButtonAction() + onActivated: _activeJoystick.setButtonAction(modelData, textAt(index)) + } + QGCCheckBox { + id: repeatCheck + text: qsTr("Repeat") + enabled: parent.currentAssignableAction && _activeJoystick.calibrated && parent.currentAssignableAction.canRepeat + onClicked: { + _activeJoystick.setButtonRepeat(modelData, checked) + } + Component.onCompleted: { + if(_activeJoystick) { + checked = _activeJoystick.getButtonRepeat(modelData) + } + } + anchors.verticalCenter: parent.verticalCenter + } + Item { + width: ScreenTools.defaultFontPixelWidth * 2 + height: 1 } } - QGCComboBox { - id: buttonActionCombo - width: ScreenTools.defaultFontPixelWidth * 26 - model: _activeJoystick ? _activeJoystick.assignableActionTitles : [] - sizeToContents: true - function _findCurrentButtonAction() { + Row { + id: pwmSettings + spacing: ScreenTools.defaultFontPixelWidth + visible: _activeJoystick ? _activeJoystick.pwmVisibilities[modelData] : false + + function _setButtonPwm(button, isLow, pwm) { + var pwmValue = -1; if(_activeJoystick) { - var i = find(_activeJoystick.buttonActions[modelData]) - if(i < 0) i = 0 - currentIndex = i + if (pwm < 1000) { + pwm = 1000; + } + if (pwm > 2000) { + pwm = 2000; + } + pwmValue = _activeJoystick.setButtonPwm(modelData, isLow, pwm) } + return pwmValue == -1 ? "" : pwmValue; } - Component.onCompleted: _findCurrentButtonAction() - onModelChanged: _findCurrentButtonAction() - onActivated: _activeJoystick.setButtonAction(modelData, textAt(index)) - } - QGCCheckBox { - id: repeatCheck - text: qsTr("Repeat") - enabled: currentAssignableAction && _activeJoystick.calibrated && currentAssignableAction.canRepeat - onClicked: { - _activeJoystick.setButtonRepeat(modelData, checked) - } - Component.onCompleted: { + function _getButtonPwm(button, isLow) { + var pwmValue = -1; if(_activeJoystick) { - checked = _activeJoystick.getButtonRepeat(modelData) + pwmValue = _activeJoystick.getButtonPwm(modelData, isLow) + } + return pwmValue == -1 ? "" : pwmValue; + } + + QGCLabel { + id: lowPwmLabel + text: qsTr("Low") + anchors.verticalCenter: parent.verticalCenter + } + + QGCTextField { + id: lowPwmValue + width: ScreenTools.defaultFontPixelWidth * 10 + implicitHeight: ScreenTools.implicitTextFieldHeight + visible: true + validator: IntValidator { bottom:1000; top: 2000} + + Connections { + target: buttonActionCombo + onCurrentIndexChanged: { + if (_activeJoystick) { + console.log("index changed, ", buttonActionCombo.currentIndex) + console.log("index changed, ", modelData) + console.log("index changed, ", target) + var pwm = pwmSettings._getButtonPwm(modelData, true) + console.log("pwm ", pwm) + lowPwmValue.text = pwm; + } + } + } + + Component.onCompleted: { + if(_activeJoystick) { + text = pwmSettings._getButtonPwm(modelData, true) + } + } + onEditingFinished: { + // setButtonPwm calculates proper value and we set it back + var pwm = pwmSettings._setButtonPwm(modelData, true, text) + lowPwmValue.text = pwm; + } + + } + QGCLabel { + id: highPwmLabel + text: qsTr("High") + anchors.verticalCenter: parent.verticalCenter + } + QGCTextField { + id: highPwmValue + width: ScreenTools.defaultFontPixelWidth * 10 + implicitHeight: ScreenTools.implicitTextFieldHeight + visible: true + validator: IntValidator { bottom:1000; top: 2000} + + Connections { + target: buttonActionCombo + function onCurrentIndexChanged(index) { + if(_activeJoystick) { + console.log("index changed, ", buttonActionCombo.currentIndex) + console.log("index changed, ", modelData) + console.log("index changed, ", target) + console.log("text ", target.text) + var pwm = pwmSettings._getButtonPwm(modelData, false) + console.log("pwm ", pwm) + highPwmValue.text = pwm; + } + } + } + + Component.onCompleted: { + if(_activeJoystick) { + text = pwmSettings._getButtonPwm(modelData, false) + } + } + onEditingFinished: { + // setButtonPwm calculates proper value and we set it back + var pwm = pwmSettings._setButtonPwm(modelData, false, text) + highPwmValue.text = pwm; + } + } + + QGCCheckBox { + id: latchCheck + text: qsTr("Latch") + anchors.verticalCenter: parent.verticalCenter + enabled: pwmSettings._latchEnabled(modelData) + + onClicked: { + _activeJoystick.setButtonPwmLatch(modelData, checked) + } + Component.onCompleted: { + if(_activeJoystick) { + checked = _activeJoystick.getButtonPwmLatch(modelData) + } } } - anchors.verticalCenter: parent.verticalCenter - } - Item { - width: ScreenTools.defaultFontPixelWidth * 2 - height: 1 } } - } + } } } Column { diff --git a/src/VideoManager/CMakeLists.txt b/src/VideoManager/CMakeLists.txt index 4ffc81714de..4634b2ff2a6 100644 --- a/src/VideoManager/CMakeLists.txt +++ b/src/VideoManager/CMakeLists.txt @@ -10,9 +10,21 @@ add_library(VideoManager target_link_libraries(VideoManager PUBLIC qgc - Qt5::Multimedia Qt5::OpenGL VideoReceiver ) +if (NOT QGC_DISABLE_UVC) + target_link_libraries(VideoManager + PUBLIC + Qt5::Multimedia + ) +endif () + target_include_directories(VideoManager INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +if (QGC_DISABLE_UVC) +# add_compile_definitions(QGC_DISABLE_UVC) + message(STATUS "VideoManager: UVC disabled") + +endif () \ No newline at end of file diff --git a/src/VideoReceiver/CMakeLists.txt b/src/VideoReceiver/CMakeLists.txt index 243ff62d90e..1fcbbdda46b 100644 --- a/src/VideoReceiver/CMakeLists.txt +++ b/src/VideoReceiver/CMakeLists.txt @@ -21,14 +21,21 @@ add_library(VideoReceiver target_link_libraries(VideoReceiver PUBLIC - Qt5::Multimedia Qt5::OpenGL Qt5::Quick ${EXTRA_LIBRARIES} Settings ) +if (NOT QGC_DISABLE_UVC) + target_link_libraries(VideoReceiver + PUBLIC + Qt5::Multimedia + ) +endif () + target_include_directories(VideoReceiver INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) if (GST_FOUND) target_include_directories(VideoReceiver PUBLIC ${GST_INCLUDE_DIRS}) + set(VideoEnabled TRUE PARENT_SCOPE) endif() diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 93f17ba8703..e3220d712b4 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -208,14 +208,23 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) for (int position = 0; position < b.size(); position++) { if (mavlink_parse_char(mavlinkChannel, static_cast(b[position]), &_message, &_status)) { // Got a valid message - if (!link->decodedFirstMavlinkPacket()) { + if (!link->decodedFirstMavlinkPacket() && !_messageIsFromAutopilot(_message)) { + qCritical(MAVLinkProtocolLog) << "First message from GCS is not from autopilot!!!" + << " component " << _message.compid << " system " + << _message.sysid << " msgid " << _message.msgid; + } + if (!link->decodedFirstMavlinkPacket() && _messageIsFromAutopilot(_message)) { link->setDecodedFirstMavlinkPacket(true); + qCDebug(MAVLinkProtocolLog) << "proto " << mavlink_get_proto_version(mavlinkChannel) + << " component " << _message.compid << " system " << _message.sysid << " msgid " << _message.msgid; mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); - if (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { + if (true || (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1))) { qCDebug(MAVLinkProtocolLog) << "Switching outbound to mavlink 2.0 due to incoming mavlink 2.0 packet:" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; // Set all links to v2 setVersion(200); + } else { + qCDebug(MAVLinkProtocolLog) << "Outbound connection is mavlink 1.0 due to incoming mavlink 1.0 packet:" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; } } @@ -536,3 +545,6 @@ void MAVLinkProtocol::deleteTempLogFiles(void) } } +bool MAVLinkProtocol::_messageIsFromAutopilot(mavlink_message_t& message) { + return message.compid == MAV_COMP_ID_AUTOPILOT1; +} \ No newline at end of file diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 65d333eaefe..709d9a7c42c 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -159,6 +159,7 @@ private slots: bool _closeLogFile(void); void _startLogging(void); void _stopLogging(void); + bool _messageIsFromAutopilot(mavlink_message_t& message); bool _logSuspendError; ///< true: Logging suspended due to error bool _logSuspendReplay; ///< true: Logging suspended due to replay diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index d932bdaf663..4c9121e30e8 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -50,7 +50,7 @@ void TCPLink::_writeDebugBytes(const QByteArray data) ascii.append(219); } } - qDebug() << "Sent" << size << "bytes to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "data:"; + qDebug() << "Sent" << data.size() << "bytes to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "data:"; qDebug() << bytes; qDebug() << "ASCII:" << ascii; }