From 0aea00885e568c1266cb7adf1e7278fa1153d94a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 11 Jul 2016 18:12:23 -0700 Subject: [PATCH] Motor Setup/Testing support --- qgcresources.qrc | 1 + qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 6 +- .../APM/APMAutoPilotPlugin.cc | 8 + src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h | 3 + src/AutoPilotPlugins/Common/MotorComponent.cc | 65 ++++++++ src/AutoPilotPlugins/Common/MotorComponent.h | 41 +++++ .../Common/MotorComponent.qml | 145 ++++++++++++++++ .../Common/MotorComponentIcon.png | Bin 0 -> 684 bytes src/AutoPilotPlugins/Common/SetupPage.qml | 71 ++++++++ .../PX4/PX4AutoPilotPlugin.cc | 28 ++-- src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h | 3 + src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 14 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.h | 2 + .../APM/ArduCopterFirmwarePlugin.cc | 11 ++ .../APM/ArduCopterFirmwarePlugin.h | 2 + src/FirmwarePlugin/FirmwarePlugin.h | 6 + src/FlightMap/Widgets/QGCSlider.qml | 122 -------------- src/QmlControls/MultiRotorMotorDisplay.qml | 156 ++++++++++++++++++ .../MultiRotorMotorDisplayLegend.qml | 71 ++++++++ src/QmlControls/QGCSlider.qml | 56 +++++++ .../QGroundControl.Controls.qmldir | 4 + src/Vehicle/Vehicle.cc | 36 ++++ src/Vehicle/Vehicle.h | 19 ++- src/VehicleSetup/SetupView.qml | 3 + 25 files changed, 739 insertions(+), 136 deletions(-) create mode 100644 src/AutoPilotPlugins/Common/MotorComponent.cc create mode 100644 src/AutoPilotPlugins/Common/MotorComponent.h create mode 100644 src/AutoPilotPlugins/Common/MotorComponent.qml create mode 100644 src/AutoPilotPlugins/Common/MotorComponentIcon.png create mode 100644 src/AutoPilotPlugins/Common/SetupPage.qml delete mode 100644 src/FlightMap/Widgets/QGCSlider.qml create mode 100644 src/QmlControls/MultiRotorMotorDisplay.qml create mode 100644 src/QmlControls/MultiRotorMotorDisplayLegend.qml create mode 100644 src/QmlControls/QGCSlider.qml diff --git a/qgcresources.qrc b/qgcresources.qrc index 2bab64496..db6dafefd 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -63,6 +63,7 @@ src/AutoPilotPlugins/PX4/Images/LandModeCopter.svg src/AutoPilotPlugins/PX4/Images/LowBattery.svg src/AutoPilotPlugins/PX4/Images/LowBatteryLight.svg + src/AutoPilotPlugins/Common/MotorComponentIcon.png src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_01cell.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_02cell.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_03cell.svg diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 181d8c8bc..657073d83 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -638,6 +638,7 @@ HEADERS+= \ src/AutoPilotPlugins/APM/APMSensorsComponent.h \ src/AutoPilotPlugins/APM/APMSensorsComponentController.h \ src/AutoPilotPlugins/APM/APMTuningComponent.h \ + src/AutoPilotPlugins/Common/MotorComponent.h \ src/AutoPilotPlugins/Common/RadioComponentController.h \ src/AutoPilotPlugins/Common/ESP8266ComponentController.h \ src/AutoPilotPlugins/Common/ESP8266Component.h \ @@ -696,6 +697,7 @@ SOURCES += \ src/AutoPilotPlugins/APM/APMSensorsComponent.cc \ src/AutoPilotPlugins/APM/APMSensorsComponentController.cc \ src/AutoPilotPlugins/APM/APMTuningComponent.cc \ + src/AutoPilotPlugins/Common/MotorComponent.cc \ src/AutoPilotPlugins/Common/RadioComponentController.cc \ src/AutoPilotPlugins/Common/ESP8266ComponentController.cc \ src/AutoPilotPlugins/Common/ESP8266Component.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 555e0dda8..0f6a054fd 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -39,6 +39,7 @@ src/ui/MainWindowNative.qml src/ui/MainWindowLeftPanel.qml src/MissionEditor/MissionEditor.qml + src/AutoPilotPlugins/Common/MotorComponent.qml src/AutoPilotPlugins/PX4/PowerComponent.qml src/AutoPilotPlugins/PX4/PowerComponentSummary.qml src/VehicleSetup/PX4FlowSensor.qml @@ -55,6 +56,8 @@ src/QmlControls/MissionItemIndexLabel.qml src/MissionEditor/MissionItemStatus.qml src/QmlControls/MissionCommandDialog.qml + src/QmlControls/MultiRotorMotorDisplay.qml + src/QmlControls/MultiRotorMotorDisplayLegend.qml src/QmlControls/ModeSwitchDisplay.qml src/QmlControls/ParameterEditor.qml src/QmlControls/ParameterEditorDialog.qml @@ -71,6 +74,7 @@ src/QmlControls/QGCMovableItem.qml src/QmlControls/QGCPipable.qml src/QmlControls/QGCRadioButton.qml + src/QmlControls/QGCSlider.qml src/QmlControls/QGCTextField.qml src/QmlControls/QGCToolBarButton.qml src/QmlControls/QGCView.qml @@ -78,6 +82,7 @@ src/QmlControls/QGCViewMessage.qml src/QmlControls/QGCViewPanel.qml src/QmlControls/RoundButton.qml + src/AutoPilotPlugins/Common/SetupPage.qml src/ui/toolbar/SignalStrength.qml src/QmlControls/SliderSwitch.qml src/QmlControls/SubMenuButton.qml @@ -113,7 +118,6 @@ src/FlightMap/Widgets/QGCInstrumentWidget.qml src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml src/FlightMap/Widgets/QGCPitchIndicator.qml - src/FlightMap/Widgets/QGCSlider.qml src/FlightMap/QGCVideoBackground.qml src/FlightMap/Widgets/ValuesWidget.qml src/FlightMap/Widgets/VibrationWidget.qml diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index d9c2fce4f..4c4fb6a57 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -25,6 +25,7 @@ #include "APMTuningComponent.h" #include "APMSensorsComponent.h" #include "APMPowerComponent.h" +#include "MotorComponent.h" #include "APMCameraComponent.h" #include "ESP8266Component.h" @@ -36,6 +37,7 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent) , _cameraComponent(NULL) , _flightModesComponent(NULL) , _powerComponent(NULL) + , _motorComponent(NULL) , _radioComponent(NULL) , _safetyComponent(NULL) , _sensorsComponent(NULL) @@ -77,6 +79,12 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void) _powerComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_powerComponent)); + if (_vehicle->multiRotor() || _vehicle->vtol()) { + _motorComponent = new MotorComponent(_vehicle, this); + _motorComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_motorComponent)); + } + _safetyComponent = new APMSafetyComponent(_vehicle, this); _safetyComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_safetyComponent)); diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h index 752098f79..961d88aee 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h @@ -22,6 +22,7 @@ class APMTuningComponent; class APMSafetyComponent; class APMSensorsComponent; class APMPowerComponent; +class MotorComponent; class APMCameraComponent; class ESP8266Component; @@ -41,6 +42,7 @@ public: APMCameraComponent* cameraComponent (void) const { return _cameraComponent; } APMFlightModesComponent* flightModesComponent(void) const { return _flightModesComponent; } APMPowerComponent* powerComponent (void) const { return _powerComponent; } + MotorComponent* motorComponent (void) const { return _motorComponent; } APMRadioComponent* radioComponent (void) const { return _radioComponent; } APMSafetyComponent* safetyComponent (void) const { return _safetyComponent; } APMSensorsComponent* sensorsComponent (void) const { return _sensorsComponent; } @@ -59,6 +61,7 @@ private: APMCameraComponent* _cameraComponent; APMFlightModesComponent* _flightModesComponent; APMPowerComponent* _powerComponent; + MotorComponent* _motorComponent; APMRadioComponent* _radioComponent; APMSafetyComponent* _safetyComponent; APMSensorsComponent* _sensorsComponent; diff --git a/src/AutoPilotPlugins/Common/MotorComponent.cc b/src/AutoPilotPlugins/Common/MotorComponent.cc new file mode 100644 index 000000000..bca1ed1bd --- /dev/null +++ b/src/AutoPilotPlugins/Common/MotorComponent.cc @@ -0,0 +1,65 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +#include "MotorComponent.h" +#include "APMAutoPilotPlugin.h" +#include "APMAirframeComponent.h" + +MotorComponent::MotorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : + VehicleComponent(vehicle, autopilot, parent), + _name(tr("Motors")) +{ + +} + +QString MotorComponent::name(void) const +{ + return _name; +} + +QString MotorComponent::description(void) const +{ + return tr("Motors Setup is used to manually test motor control and direction."); +} + +QString MotorComponent::iconResource(void) const +{ + return QStringLiteral("/qmlimages/MotorComponentIcon.png"); +} + +bool MotorComponent::requiresSetup(void) const +{ + return false; +} + +bool MotorComponent::setupComplete(void) const +{ + return true; +} + +QStringList MotorComponent::setupCompleteChangedTriggerList(void) const +{ + return QStringList(); +} + +QUrl MotorComponent::setupSource(void) const +{ + return QUrl::fromUserInput(QStringLiteral("qrc:/qml/MotorComponent.qml")); +} + +QUrl MotorComponent::summaryQmlSource(void) const +{ + return QUrl(); +} + +QString MotorComponent::prerequisiteSetup(void) const +{ + return QString(); +} diff --git a/src/AutoPilotPlugins/Common/MotorComponent.h b/src/AutoPilotPlugins/Common/MotorComponent.h new file mode 100644 index 000000000..c44d77c10 --- /dev/null +++ b/src/AutoPilotPlugins/Common/MotorComponent.h @@ -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. + * + ****************************************************************************/ + + +#ifndef MotorComponent_H +#define MotorComponent_H + +#include "VehicleComponent.h" +#include "Fact.h" + +class MotorComponent : public VehicleComponent +{ + Q_OBJECT + +public: + MotorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = NULL); + + // Virtuals from VehicleComponent + QStringList setupCompleteChangedTriggerList(void) const final; + + // Virtuals from VehicleComponent + QString name(void) const final; + QString description(void) const final; + QString iconResource(void) const final; + bool requiresSetup(void) const final; + bool setupComplete(void) const final; + QUrl setupSource(void) const final; + QUrl summaryQmlSource(void) const final; + QString prerequisiteSetup(void) const final; + +private: + const QString _name; +}; + +#endif diff --git a/src/AutoPilotPlugins/Common/MotorComponent.qml b/src/AutoPilotPlugins/Common/MotorComponent.qml new file mode 100644 index 000000000..2c8c3c536 --- /dev/null +++ b/src/AutoPilotPlugins/Common/MotorComponent.qml @@ -0,0 +1,145 @@ +/**************************************************************************** + * + * (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.5 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.ScreenTools 1.0 + +SetupPage { + id: motorPage + pageComponent: pageComponent + + readonly property int _barHeight: 10 + readonly property int _barWidth: 5 + readonly property int _sliderHeight: 10 + + FactPanelController { + id: controller + factPanel: motorPage.viewPanel + } + + Component { + id: pageComponent + + Column { + spacing: 10 + + Row { + id: motorSliders + enabled: safetySwitch.checked + spacing: ScreenTools.defaultFontPixelWidth * 4 + + Repeater { + id: sliderRepeater + model: controller.vehicle.motorCount == -1 ? 8 : controller.vehicle.motorCount + + Column { + property alias motorSlider: slider + + Timer { + interval: 250 + running: true + repeat: true + + property real _lastValue: 0 + + onTriggered: { + if (_lastValue != slider.value) { + controller.vehicle.motorTest(index + 1, slider.value, 1) + } + } + } + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: index + 1 + } + + QGCSlider { + id: slider + height: ScreenTools.defaultFontPixelHeight * _sliderHeight + orientation: Qt.Vertical + maximumValue: 100 + value: 0 + } + } // Column + } // Repeater + + Column { + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("All") + } + + QGCSlider { + id: allSlider + height: ScreenTools.defaultFontPixelHeight * _sliderHeight + orientation: Qt.Vertical + maximumValue: 100 + value: 0 + + onValueChanged: { + for (var sliderIndex=0; sliderIndexv1;I3iNGk1AxW+`ZuuEYff?y;dHnE7p zfn79(O3+>;X2D7u{}Dkg&7q=lpUni$+-`Ea&hF{${K_};-oE+v&0}U+15o*N+{|W^ z1}h2*Py&0tDX%pwno@Ktr`kYT2*!1=m~H-K>1ULCEap> zrhr{t0|hV-Va6rB0xnbvOneBa15+MjR)Ld}HhE0-Ql%gu7bQKAv_;Z5Fb8bR7;8Il z3iu(Z-^^-e_T6K;7I#jGJOf!IUQ0l4ia1+=dElG#A8ga_D(O_O6CXd^>}y{I>zeTn}p2LWW#AUn#(CNe@%H zJq6AJ=KMcMy6<|>iYCo$U#IRqQN}oZz(NB3$TiojI`bFnOI;~FzWRrx#Z~6IvsKOP zrJGlibRtFQx}^PP_M+3b86`;}N{|Oic`V!_(xB152CdJ6RQZhpUdV#yn=PNJ+ z9Bk8fa-GEH0AE#H3>4QtmVl4Ixs1tPPe^Lo^G?XWrvRgp4oEsA>5ZBF%5-crI}a>N znrJeQnAyF5@ylDZ#@x-o4$se@0KNdbb6Q_@^TL8pZ>)}x>mU6XXv%wCrr%DkkS zncZnNmKDkkGaE}9EbHgU$7>D7L1}aufS+lLA#VHZE$N>KrQbdQN`Mle + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 + +import QGroundControl 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controllers 1.0 + +/// Base view control for all Setup pages +QGCView { + id: setupView + viewPanel: setupPanel + + property alias pageComponent: pageLoader.sourceComponent + + property real _margins: ScreenTools.defaultFontPixelHeight / 2 + + QGCPalette { id: qgcPal; colorGroupEnabled: setupPanel.enabled } + + QGCViewPanel { + id: setupPanel + anchors.fill: parent + + QGCFlickable { + anchors.fill: parent + contentWidth: pageLoader.item.x + pageLoader.item.width + contentHeight: pageLoader.item.y + pageLoader.item.height + clip: true + + Column { + id: headingColumn + anchors.left: parent.left + anchors.right: parent.right + spacing: _margins + + QGCLabel { + font.pointSize: ScreenTools.largeFontPointSize + text: vehicleComponent.name + " " + qsTr("Setup") + visible: !ScreenTools.isShortScreen + } + + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: vehicleComponent.description + visible: !ScreenTools.isShortScreen + } + } + + Loader { + anchors.topMargin: _margins + anchors.top: headingColumn.bottom + id: pageLoader + } + } + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index 97be4040f..a2f91ec79 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -22,16 +22,17 @@ /// @brief This is the AutoPilotPlugin implementatin for the MAV_AUTOPILOT_PX4 type. /// @author Don Gagne -PX4AutoPilotPlugin::PX4AutoPilotPlugin(Vehicle* vehicle, QObject* parent) : - AutoPilotPlugin(vehicle, parent), - _airframeComponent(NULL), - _radioComponent(NULL), - _esp8266Component(NULL), - _flightModesComponent(NULL), - _sensorsComponent(NULL), - _safetyComponent(NULL), - _powerComponent(NULL), - _incorrectParameterVersion(false) +PX4AutoPilotPlugin::PX4AutoPilotPlugin(Vehicle* vehicle, QObject* parent) + : AutoPilotPlugin(vehicle, parent) + , _airframeComponent(NULL) + , _radioComponent(NULL) + , _esp8266Component(NULL) + , _flightModesComponent(NULL) + , _sensorsComponent(NULL) + , _safetyComponent(NULL) + , _powerComponent(NULL) + , _motorComponent(NULL) + , _incorrectParameterVersion(false) { Q_ASSERT(vehicle); @@ -74,6 +75,13 @@ const QVariantList& PX4AutoPilotPlugin::vehicleComponents(void) _powerComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_powerComponent)); +#if 0 + // Coming soon + _motorComponent = new MotorComponent(_vehicle, this); + _motorComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_motorComponent)); +#endif + _safetyComponent = new SafetyComponent(_vehicle, this); _safetyComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_safetyComponent)); diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h index a7053943f..9bb2737ad 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h @@ -21,6 +21,7 @@ #include "SafetyComponent.h" #include "CameraComponent.h" #include "PowerComponent.h" +#include "MotorComponent.h" #include "PX4TuningComponent.h" #include "Vehicle.h" @@ -50,6 +51,7 @@ public: SafetyComponent* safetyComponent(void) { return _safetyComponent; } CameraComponent* cameraComponent(void) { return _cameraComponent; } PowerComponent* powerComponent(void) { return _powerComponent; } + MotorComponent* motorComponent(void) { return _motorComponent; } PX4TuningComponent* tuningComponent(void) { return _tuningComponent; } public slots: @@ -67,6 +69,7 @@ private: SafetyComponent* _safetyComponent; CameraComponent* _cameraComponent; PowerComponent* _powerComponent; + MotorComponent* _motorComponent; PX4TuningComponent* _tuningComponent; bool _incorrectParameterVersion; ///< true: parameter version incorrect, setup not allowed }; diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 66e4bc19f..aceb859ad 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -122,7 +122,6 @@ APMCustomMode::APMCustomMode(uint32_t mode, bool settable) : { } - void APMCustomMode::setEnumToStringMapping(const QMap& enumToString) { _enumToString = enumToString; @@ -138,8 +137,10 @@ QString APMCustomMode::modeString() const } APMFirmwarePlugin::APMFirmwarePlugin(void) + : _coaxialMotors(false) + , _textSeverityAdjustmentNeeded(false) { - _textSeverityAdjustmentNeeded = false; + } bool APMFirmwarePlugin::isCapable(FirmwareCapabilities capabilities) @@ -380,6 +381,15 @@ bool APMFirmwarePlugin::_handleStatusText(Vehicle* vehicle, mavlink_message_t* m // Start TCP video handshake with ARTOO _soloVideoHandshake(vehicle); + } else if (messageText.contains(APM_FRAME_REXP)) { + // We need to parse the Frame: message in order to determine whether the motors are coaxial or not + QRegExp frameTypeRegex("^Frame: (\\S*)"); + if (frameTypeRegex.indexIn(messageText) != -1) { + QString frameType = frameTypeRegex.cap(1); + if (!frameType.isEmpty() && (frameType == QStringLiteral("Y6") || frameType == QStringLiteral("OCTA_QUAD") || frameType == QStringLiteral("COAX"))) { + _coaxialMotors = true; + } + } } if (messageText.startsWith("PreArm")) { diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index 04d7f8b49..357189b7d 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -99,6 +99,8 @@ protected: APMFirmwarePlugin(void); void setSupportedModes(QList supportedModes); + bool _coaxialMotors; + private slots: void _artooSocketError(QAbstractSocket::SocketError socketError); diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index 12b72c11b..070cd30ef 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -197,3 +197,14 @@ void ArduCopterFirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) pauseVehicle(vehicle); } } + +bool ArduCopterFirmwarePlugin::multiRotorCoaxialMotors(Vehicle* vehicle) +{ + Q_UNUSED(vehicle); + return _coaxialMotors; +} + +bool ArduCopterFirmwarePlugin::multiRotorXConfig(Vehicle* vehicle) +{ + return vehicle->autopilotPlugin()->getParameterFact(FactSystem::defaultComponentId, "FRAME")->rawValue().toInt() != 0; +} diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h index a43670033..10c7fae92 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h @@ -64,6 +64,8 @@ public: void guidedModeChangeAltitude(Vehicle* vehicle, double altitudeRel) final; const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } virtual int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const final; + virtual bool multiRotorCoaxialMotors(Vehicle* vehicle) final; + virtual bool multiRotorXConfig(Vehicle* vehicle) final; private: static bool _remapParamNameIntialized; diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 393a69029..c64cbdc98 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -181,6 +181,12 @@ public: /// Returns the highest major version number that is known to the remap for this specified major version. virtual int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const; + + /// @return true: Motors are coaxial like an X8 config, false: Quadcopter for example + virtual bool multiRotorCoaxialMotors(Vehicle* vehicle) { Q_UNUSED(vehicle); return false; } + + /// @return true: X confiuration, false: Plus configuration + virtual bool multiRotorXConfig(Vehicle* vehicle) { Q_UNUSED(vehicle); return false; } }; #endif diff --git a/src/FlightMap/Widgets/QGCSlider.qml b/src/FlightMap/Widgets/QGCSlider.qml deleted file mode 100644 index 2a2b42449..000000000 --- a/src/FlightMap/Widgets/QGCSlider.qml +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.1 -import QGroundControl.Controls 1.0 -import QGroundControl.ScreenTools 1.0 - -Item { - id: slider; - height: 12 - property real value // value is read/write. - property real minimum: 0 - property real maximum: 1 - property int length: width - handle.width - - Rectangle { - anchors.fill: parent - radius: ScreenTools.defaultFontPixelHeight * (0.5) - color: Qt.rgba(0,0,0,0.65); - } - - Rectangle { - anchors.left: parent.left - anchors.leftMargin: ScreenTools.defaultFontPixelHeight * (0.33) - radius: ScreenTools.defaultFontPixelHeight * (0.33) - height: ScreenTools.defaultFontPixelHeight * (0.33) - width: handle.x - x - color: "#69bb17" - anchors.verticalCenter: parent.verticalCenter - } - - Rectangle { - id: labelRect - width: label.width - height: label.height + ScreenTools.defaultFontPixelHeight * (0.33) - radius: ScreenTools.defaultFontPixelHeight * (0.33) - smooth: true - color: Qt.rgba(1,1,1,0.75); - border.width: ScreenTools.defaultFontPixelHeight * (0.083) - border.color: Qt.rgba(0,0,0,0.45); - anchors.bottom: handle.top - anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * (0.33) - visible: mouseRegion.pressed - x: Math.max(Math.min(handle.x + (handle.width - width ) / 2, slider.width - width), 0) - QGCLabel{ - id: label - color: "black" - text: slider.value.toFixed(2) - width: font.pointSize * 3.5 - anchors.horizontalCenter: labelRect.horizontalCenter - horizontalAlignment: Text.AlignHCenter - anchors.verticalCenter: labelRect.verticalCenter - } - } - - Rectangle { - id: handle; - smooth: true - width: ScreenTools.defaultFontPixelHeight * (2.16); - y: (slider.height - height) / 2; - x: (slider.value - slider.minimum) * slider.length / (slider.maximum - slider.minimum) - - height: width - radius: width / 2 - gradient: normalGradient - border.width: 2 - border.color: "white" - - Gradient { - id: normalGradient - GradientStop { position: 0.0; color: "#b0b0b0" } - GradientStop { position: 0.66; color: "#909090" } - GradientStop { position: 1.0; color: "#545454" } - } - - MouseArea { - id: mouseRegion - hoverEnabled: false - anchors.fill: parent; drag.target: parent - drag.axis: Drag.XAxis; drag.minimumX: 0; drag.maximumX: slider.length - preventStealing: true - onPositionChanged: { slider.value = (slider.maximum - slider.minimum) * handle.x / slider.length + slider.minimum; } - } - } -} diff --git a/src/QmlControls/MultiRotorMotorDisplay.qml b/src/QmlControls/MultiRotorMotorDisplay.qml new file mode 100644 index 000000000..c1cd09481 --- /dev/null +++ b/src/QmlControls/MultiRotorMotorDisplay.qml @@ -0,0 +1,156 @@ +import QtQuick 2.2 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Item { + id: motorRoot + + property int motorCount: 4 // Number of motors on vehicle + property bool xConfig: true // true: X configuration, false: Plus configuration + property bool coaxial: true // true: motors on top bottom of same arm, false: motors only on top of arm + + property bool _unsupportedConfig: motorCount == 3 || (motorCount == 6 && coaxial) // Tricopters NYI + property var _qgcPal: QGCPalette { colorGroupEnabled: enabled } + property real _rotorRadius: motorRoot.height / 8 + property int _motorsPerSide: motorCount / (coaxial ? 2 : 1) + + readonly property string _cwColor: "green" + readonly property string _ccwColor: "blue" + readonly property var _motorColors4Plus: [ _ccwColor, _cwColor ] + readonly property var _motorColors4X: [ _cwColor, _ccwColor ] + readonly property var _motorColors6: [ _cwColor, _ccwColor ] + readonly property var _motorColors8: [ _cwColor, _ccwColor ] + readonly property var _motorColors4Top: [ _cwColor, _ccwColor ] + readonly property var _motorColors4Bottom: [ _ccwColor, _cwColor ] + + readonly property var _motorNumbers4Plus: [ 1, 4, 2, 3 ] + readonly property var _motorNumbers4X: [ 4, 2, 3, 1 ] + readonly property var _motorNumbers6Plus: [ 6, 2, 3, 5, 1, 4 ] + readonly property var _motorNumbers6X: [ 1, 4, 6, 2, 3, 5 ] + readonly property var _motorNumbers8: [ 8, 4, 2, 6, 7, 5, 1, 3 ] + readonly property var _motorNumbers4Top: [ 4, 3, 2, 1 ] + readonly property var _motorNumbers4Bottom: [ 7, 8, 5, 6 ] + + Component.onCompleted: { + if (coaxial) { + topMotors.motorNumbers = _motorNumbers4Top + bottomMotors.motorNumbers = _motorNumbers4Bottom + } else { + switch (motorCount) { + case 4: + topMotors.motorNumbers = xConfig ? _motorNumbers4X : _motorNumbers4Plus + topMotors.motorColors = xConfig ? _motorColors4X : _motorColors4Plus + break + case 6: + topMotors.motorNumbers = xConfig ? _motorNumbers6X : _motorNumbers6Plus + topMotors.motorColors = _motorColors6 + break + default: + case 8: + topMotors.motorNumbers = _motorNumbers8 + topMotors.motorColors = _motorColors8 + break + } + bottomMotors.motorNumbers = _motorNumbers8 + } + } + + Component { + id: motorDisplayComponent + + Repeater { + id: motorRepeater + model: _motorsPerSide + + Item { + x: motorRepeater.width / 2 + _armXCenter - rotor.radius + y: motorRepeater.height / 2 + _armYCenter - rotor.radius + width: _rotorRadius * 2 + height: _rotorRadius * 2 + + property real _armOffsetRadians: ((2 * Math.PI) / _motorsPerSide) + property real _armOffsetIndexRadians: (_armOffsetRadians * index) + ((xConfig && _motorsPerSide != 6) || (!xConfig && _motorsPerSide == 6) ? _armOffsetRadians / 2 : 0) + property real _armLength: (motorRepeater.height / 2) - (_rotorRadius * (xConfig && _motorsPerSide == 4 ? 0 : 1)) + property real _armXCenter: Math.cos(_armOffsetIndexRadians) * _armLength // adjacent = cos * hypotenuse + property real _armYCenter: Math.sin(_armOffsetIndexRadians) * _armLength // opposite = sin * hypotenuse + + Rectangle {id: rotor + anchors.fill: parent + radius: _rotorRadius + color: motorColors[index & 1] + opacity: topCoaxial ? 0.65 : 1.0 + border.color: topCoaxial ? "black" : color + + readonly property bool topCoaxial: topMotors && coaxial + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + width: radius *2 + height: radius *2 + radius: ScreenTools.defaultFontPixelHeight / 2 + color: "white" + border.color: "black" + + QGCLabel { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: motorNumbers[index] + color: parent.border.color + } + } + } // Item + } // Repeater + } // Component - MotorDisplayComponent + + Item { + anchors.fill: parent + visible: !_unsupportedConfig + + Loader { + id: bottomMotors + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.fill: parent + sourceComponent: motorDisplayComponent + visible: coaxial + + property bool topMotors: false + property var motorNumbers: _motorNumbers8 + property var motorColors: _motorColors4Bottom + } + + Loader { + id: topMotors + anchors.fill: parent + anchors.bottomMargin: coaxial ? ScreenTools.defaultFontPixelHeight : 0 + sourceComponent: motorDisplayComponent + + property bool topMotors: true + property var motorNumbers: _motorNumbers8 + property var motorColors: _motorColors4Top + } + + // Body direction + Canvas { + anchors.fill: parent + + property real _centerX : width / 2 + property real _centerY : height / 2 + + onPaint: { + var ctx = getContext("2d"); + ctx.lineWidth = 3 + ctx.strokeStyle = _qgcPal.text + ctx.translate(_centerX, _centerY) + ctx.moveTo(0, -_rotorRadius / 2); + ctx.lineTo(_rotorRadius / 2, _rotorRadius); + ctx.lineTo(-_rotorRadius / 2, _rotorRadius); + ctx.lineTo(0, -_rotorRadius / 2); + ctx.stroke(); + } + } + } // Item +} // Item diff --git a/src/QmlControls/MultiRotorMotorDisplayLegend.qml b/src/QmlControls/MultiRotorMotorDisplayLegend.qml new file mode 100644 index 000000000..97f186cdc --- /dev/null +++ b/src/QmlControls/MultiRotorMotorDisplayLegend.qml @@ -0,0 +1,71 @@ +import QtQuick 2.2 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Item { + id: legendRoot + + property var _qgcPal: QGCPalette { colorGroupEnabled: enabled } + property real _rotorRadius: legendRoot.height / 16 + + readonly property string _cwColor: "green" + readonly property string _ccwColor: "blue" + + Item { + id: cwItem + anchors.left: parent.left + anchors.right: parent.right + height: legendRoot.height / 2 + + Rectangle { + id: cwRotor + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: _rotorRadius * 2 + height: _rotorRadius * 2 + radius: _rotorRadius + color: _cwColor + } + + QGCLabel { + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.left: cwRotor.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: qsTr("Clockwise rotation, use pusher propellor") + } + } + + Item { + id: ccwItem + anchors.top: cwItem.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + Rectangle { + id: ccwRotor + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: _rotorRadius * 2 + height: _rotorRadius * 2 + radius: _rotorRadius + color: _ccwColor + } + + QGCLabel { + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.left: ccwRotor.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: qsTr("Counter-Clockwise rotation, use normal propellor") + } + } +} // Item diff --git a/src/QmlControls/QGCSlider.qml b/src/QmlControls/QGCSlider.qml new file mode 100644 index 000000000..d08303982 --- /dev/null +++ b/src/QmlControls/QGCSlider.qml @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (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.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 +import QtQuick.Controls.Private 1.0 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Slider { + property var _qgcPal: QGCPalette { colorGroupEnabled: enabled } + + style: SliderStyle { + groove: Item { + property color fillColor: "#49d" + anchors.verticalCenter: parent.verticalCenter + implicitWidth: Math.round(TextSingleton.implicitHeight * 4.5) + implicitHeight: Math.max(6, Math.round(TextSingleton.implicitHeight * 0.3)) + + Rectangle { + radius: height/2 + anchors.fill: parent + border.width: 1 + border.color: "#888" + gradient: Gradient { + GradientStop { color: "#bbb" ; position: 0 } + GradientStop { color: "#ccc" ; position: 0.6 } + GradientStop { color: "#ccc" ; position: 1 } + } + } + + Item { + clip: true + width: styleData.handlePosition + height: parent.height + Rectangle { + anchors.fill: parent + border.color: Qt.darker(fillColor, 1.2) + radius: height/2 + gradient: Gradient { + GradientStop {color: Qt.lighter(fillColor, 1.3) ; position: 0} + GradientStop {color: fillColor ; position: 1.4} + } + } + } + } + } +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 5595c7f19..6a9a1170c 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -13,6 +13,8 @@ MissionItemEditor 1.0 MissionItemEditor.qml MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml MissionItemStatus 1.0 MissionItemStatus.qml ModeSwitchDisplay 1.0 ModeSwitchDisplay.qml +MultiRotorMotorDisplay 1.0 MultiRotorMotorDisplay.qml +MultiRotorMotorDisplayLegend 1.0 MultiRotorMotorDisplayLegend.qml ParameterEditor 1.0 ParameterEditor.qml ParameterEditorDialog 1.0 ParameterEditorDialog.qml RCChannelMonitor 1.0 RCChannelMonitor.qml @@ -26,6 +28,7 @@ QGCMobileFileDialog 1.0 QGCMobileFileDialog.qml QGCMovableItem 1.0 QGCMovableItem.qml QGCPipable 1.0 QGCPipable.qml QGCRadioButton 1.0 QGCRadioButton.qml +QGCSlider 1.0 QGCSlider.qml QGCTextField 1.0 QGCTextField.qml QGCToolBarButton 1.0 QGCToolBarButton.qml QGCView 1.0 QGCView.qml @@ -33,6 +36,7 @@ QGCViewDialog 1.0 QGCViewDialog.qml QGCViewMessage 1.0 QGCViewMessage.qml QGCViewPanel 1.0 QGCViewPanel.qml RoundButton 1.0 RoundButton.qml +SetupPage 1.0 SetupPage.qml SignalStrength 1.0 SignalStrength.qml SliderSwitch 1.0 SliderSwitch.qml SubMenuButton 1.0 SubMenuButton.qml diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 408023f57..230fb67b7 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -916,6 +916,37 @@ void Vehicle::_updateNavigationControllerData(UASInterface *uas, float, float, f } } +int Vehicle::motorCount(void) +{ + switch (_vehicleType) { + case MAV_TYPE_HELICOPTER: + return 1; + case MAV_TYPE_VTOL_DUOROTOR: + return 2; + case MAV_TYPE_TRICOPTER: + return 3; + case MAV_TYPE_QUADROTOR: + case MAV_TYPE_VTOL_QUADROTOR: + return 4; + case MAV_TYPE_HEXAROTOR: + return 6; + case MAV_TYPE_OCTOROTOR: + return 8; + default: + return -1; + } +} + +bool Vehicle::coaxialMotors(void) +{ + return _firmwarePlugin->multiRotorCoaxialMotors(this); +} + +bool Vehicle::xConfigMotors(void) +{ + return _firmwarePlugin->multiRotorXConfig(this); +} + /* * Internal */ @@ -1738,6 +1769,11 @@ void Vehicle::setSoloFirmware(bool soloFirmware) } } +void Vehicle::motorTest(int motor, int percent, int timeoutSecs) +{ + doCommandLong(defaultComponentId(), MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); +} + const char* VehicleGPSFactGroup::_hdopFactName = "hdop"; const char* VehicleGPSFactGroup::_vdopFactName = "vdop"; const char* VehicleGPSFactGroup::_courseOverGroundFactName = "courseOverGround"; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index ebc398bdf..07a8f1bb1 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -277,6 +277,9 @@ public: Q_PROPERTY(bool rover READ rover CONSTANT) Q_PROPERTY(bool autoDisconnect MEMBER _autoDisconnect NOTIFY autoDisconnectChanged) Q_PROPERTY(QString prearmError READ prearmError WRITE setPrearmError NOTIFY prearmErrorChanged) + Q_PROPERTY(int motorCount READ motorCount CONSTANT) + Q_PROPERTY(bool coaxialMotors READ coaxialMotors CONSTANT) + Q_PROPERTY(bool xConfigMotors READ xConfigMotors CONSTANT) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -362,6 +365,12 @@ public: /// Clear Messages Q_INVOKABLE void clearMessages(); + /// Test motor + /// @param motor Motor number, 1-based + /// @param percent 0-no power, 100-full power + /// @param timeoutSecs Number of seconds for motor to run + Q_INVOKABLE void motorTest(int motor, int percent, int timeoutSecs); + bool guidedModeSupported(void) const; bool pauseVehicleSupported(void) const; @@ -438,7 +447,6 @@ public: QString flightMode(void) const; void setFlightMode(const QString& flightMode); - bool hilMode(void); void setHilMode(bool hilMode); @@ -535,6 +543,15 @@ public: int defaultComponentId(void); + /// @return -1 = Unknown, Number of motors on vehicle + int motorCount(void); + + /// @return true: Motors are coaxial like an X8 config, false: Quadcopter for example + bool coaxialMotors(void); + + /// @return true: X confiuration, false: Plus configuration + bool xConfigMotors(void); + public slots: void setLatitude(double latitude); void setLongitude(double longitude); diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index d43369497..4901597da 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -97,6 +97,7 @@ Rectangle { _messagePanelText = vehicleComponent.prerequisiteSetup + " setup must be completed prior to " + vehicleComponent.name + " setup." panelLoader.sourceComponent = messagePanelComponent } else { + panelLoader.vehicleComponent = vehicleComponent panelLoader.source = vehicleComponent.setupSource for(var i = 0; i < componentRepeater.count; i++) { var obj = componentRepeater.itemAt(i); @@ -324,5 +325,7 @@ Rectangle { anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom + + property var vehicleComponent } } -- 2.22.0