diff --git a/qgcresources.qrc b/qgcresources.qrc index 2bab64496172536bd32116a12e1d6c8a3166844c..db6dafefdb1a905492c039a90a1fd12b2683daba 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 181d8c8bcf6ae907e8d79c27116f94a26176478a..657073d83defa01b2381f4efe714c6b90ae88e58 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 555e0dda80e13feda92f06db61d75314253d4130..0f6a054fd82e742c3fce791c4f65f68d7aa94ee0 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 d9c2fce4f31b5c6c4ce50d951746d661d64abebd..4c4fb6a57f75874bad3b6afb3c3e5e171f07b128 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 752098f79d64aa8d0f7ef7bdc3db6b3f8c5a2713..961d88aee174b31c3429db81db3494b48c9ae9f2 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 0000000000000000000000000000000000000000..bca1ed1bd0e95bec2adf4ab0a63fee8b575c597b --- /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 0000000000000000000000000000000000000000..c44d77c103120bd84c5b02bdda5cadf9f075954b --- /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 0000000000000000000000000000000000000000..2c8c3c536f898ff0b8ed8f869d54b270f6ad96d8 --- /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; sliderIndex + * + * 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 97be4040f2094ae7d801f1255b657f716f5c9a4a..a2f91ec7961e56eb1623fa0198770d1779b0abf3 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 a7053943feeb9118c06308cd07bd1f92c98ccada..9bb2737ad8a0bf3ae1bb08fddfa4288e038750e7 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 66e4bc19f27e686fa63e7befc52f80a1d974d724..aceb859adbcf1b3c13b3c053379fc719583ba39d 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 04d7f8b494e2d11839d57f163c5821548fbe7c47..357189b7d70dcb062496292de2e45b06dee183af 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 12b72c11bce139cb83dcb051150a2da4b43e021d..070cd30ef17dc035d7f96d2d2bfc8b09abb26faa 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 a43670033d24d20ad524d459a08f0de1172fb11d..10c7fae922f95528579338cf49cbfb0d157a7493 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 393a690290d6e90b5177694fedd62eb781d38133..c64cbdc98ae7af9c35b9d7a7510022ca187af62d 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 2a2b4244959c2a980b1e2c2339dbc8ee15f801e8..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..c1cd094816bda9e632c2e78dc83620356eb39bed --- /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 0000000000000000000000000000000000000000..97f186cdc3e5f0de6574d06059789cf9eb0e487d --- /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 0000000000000000000000000000000000000000..d08303982d5c862dd151ab2e9a7619a6ed6d9c1e --- /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 5595c7f195e1680942d7451f754f39feebcd663a..6a9a1170cbdd2b320c370d2c82def990c55416dd 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 408023f57c2ac8482554cc3b5b0f6f04a5eb8211..230fb67b73f446fa11d9c7757afab2792fcd27fb 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 ebc398bdff2ebd250318a9d5de73f62306726850..07a8f1bb118e5f744f680d5406d2430ede302350 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 d433694974dcb51d96270e3b07a2d75807bd7a2c..4901597da6b66bb51ee9d6d0736cb576ee610db1 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 } }