diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 3f5ce0841474c9692100cf9368c3144657afdd22..54d414fb80ab5bfcdda0d966968fad40a116d77c 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -1,626 +1,639 @@ -# ------------------------------------------------- -# QGroundControl - Micro Air Vehicle Groundstation -# Please see our website at -# Maintainer: -# Lorenz Meier -# (c) 2009-2011 QGroundControl Developers -# This file is part of the open groundstation project -# QGroundControl is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# QGroundControl is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with QGroundControl. If not, see . -# ------------------------------------------------- - - -# Qt configuration -CONFIG += qt \ - thread -QT += network \ - opengl \ - svg \ - xml \ - phonon \ - webkit \ - sql - -TEMPLATE = app -TARGET = qgroundcontrol -BASEDIR = $${IN_PWD} -linux-g++|linux-g++-64{ - debug { - TARGETDIR = $${OUT_PWD}/debug - BUILDDIR = $${OUT_PWD}/build-debug - } - release { - TARGETDIR = $${OUT_PWD}/release - BUILDDIR = $${OUT_PWD}/build-release - } -} else { - TARGETDIR = $${OUT_PWD} - BUILDDIR = $${OUT_PWD}/build -} -LANGUAGE = C++ -OBJECTS_DIR = $${BUILDDIR}/obj -MOC_DIR = $${BUILDDIR}/moc -UI_DIR = $${BUILDDIR}/ui -RCC_DIR = $${BUILDDIR}/rcc -MAVLINK_CONF = "pixhawk" -MAVLINKPATH = $$BASEDIR/libs/mavlink/include/mavlink/v1.0 -DEFINES += MAVLINK_NO_DATA - -win32 { - QMAKE_INCDIR_QT = $$(QTDIR)/include - QMAKE_LIBDIR_QT = $$(QTDIR)/lib - QMAKE_UIC = "$$(QTDIR)/bin/uic.exe" - QMAKE_MOC = "$$(QTDIR)/bin/moc.exe" - QMAKE_RCC = "$$(QTDIR)/bin/rcc.exe" - QMAKE_QMAKE = "$$(QTDIR)/bin/qmake.exe" - - # Build QAX for GoogleEarth API access - !exists( $(QTDIR)/src/activeqt/Makefile ) { - message( Making QAx (ONE TIME) ) - system( cd $$(QTDIR)\\src\\activeqt && $$(QTDIR)\\bin\\qmake.exe ) - system( cd $$(QTDIR)\\src\\activeqt\\container && $$(QTDIR)\\bin\\qmake.exe ) - system( cd $$(QTDIR)\\src\\activeqt\\control && $$(QTDIR)\\bin\\qmake.exe ) - } -} - - - -################################################################# -# EXTERNAL LIBRARY CONFIGURATION - -# EIGEN matrix library (header-only) -INCLUDEPATH += libs/eigen - -# OPMapControl library (from OpenPilot) -include(libs/utils/utils_external.pri) -include(libs/opmapcontrol/opmapcontrol_external.pri) -DEPENDPATH += \ - libs/utils \ - libs/utils/src \ - libs/opmapcontrol \ - libs/opmapcontrol/src \ - libs/opmapcontrol/src/mapwidget - -INCLUDEPATH += \ - libs/utils \ - libs \ - libs/opmapcontrol - -# If the user config file exists, it will be included. -# if the variable MAVLINK_CONF contains the name of an -# additional project, QGroundControl includes the support -# of custom MAVLink messages of this project. It will also -# create a QGC_USE_{AUTOPILOT_NAME}_MESSAGES macro for use -# within the actual code. -exists(user_config.pri) { - include(user_config.pri) - message("----- USING CUSTOM USER QGROUNDCONTROL CONFIG FROM user_config.pri -----") - message("Adding support for additional MAVLink messages for: " $$MAVLINK_CONF) - message("------------------------------------------------------------------------") -} -INCLUDEPATH += $$MAVLINKPATH -isEmpty(MAVLINK_CONF) { - INCLUDEPATH += $$MAVLINKPATH/common -} else { - INCLUDEPATH += $$MAVLINKPATH/$$MAVLINK_CONF - #DEFINES += 'MAVLINK_CONF="$${MAVLINK_CONF}.h"' - DEFINES += $$sprintf('QGC_USE_%1_MESSAGES', $$upper($$MAVLINK_CONF)) -} - -# Include general settings for QGroundControl -# necessary as last include to override any non-acceptable settings -# done by the plugins above -include(qgroundcontrol.pri) - -# Include MAVLink generator -# has been deprecated -DEPENDPATH += \ - src/apps/mavlinkgen - -INCLUDEPATH += \ - src/apps/mavlinkgen \ - src/apps/mavlinkgen/ui \ - src/apps/mavlinkgen/generator - -include(src/apps/mavlinkgen/mavlinkgen.pri) - - - -# Include QWT plotting library -include(libs/qwt/qwt.pri) -DEPENDPATH += . \ - plugins \ - libs/thirdParty/qserialport/include \ - libs/thirdParty/qserialport/include/QtSerialPort \ - libs/thirdParty/qserialport \ - libs/qextserialport - -INCLUDEPATH += . \ - libs/thirdParty/qserialport/include \ - libs/thirdParty/qserialport/include/QtSerialPort \ - libs/thirdParty/qserialport/src \ - libs/qextserialport - -# Include serial port library (QSerial) -include(qserialport.pri) - -# Serial port detection (ripped-off from qextserialport library) -macx|macx-g++|macx-g++42::SOURCES += libs/qextserialport/qextserialenumerator_osx.cpp -linux-g++::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp -linux-g++-64::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp -win32::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp -win32-msvc2008|win32-msvc2010::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp - -# Input -FORMS += src/ui/MainWindow.ui \ - src/ui/CommSettings.ui \ - src/ui/SerialSettings.ui \ - src/ui/UASControl.ui \ - src/ui/UASList.ui \ - src/ui/UASInfo.ui \ - src/ui/Linechart.ui \ - src/ui/UASView.ui \ - src/ui/ParameterInterface.ui \ - src/ui/WaypointList.ui \ - src/ui/ObjectDetectionView.ui \ - src/ui/JoystickWidget.ui \ - src/ui/DebugConsole.ui \ - src/ui/HDDisplay.ui \ - src/ui/MAVLinkSettingsWidget.ui \ - src/ui/AudioOutputWidget.ui \ - src/ui/QGCSensorSettingsWidget.ui \ - src/ui/watchdog/WatchdogControl.ui \ - src/ui/watchdog/WatchdogProcessView.ui \ - src/ui/watchdog/WatchdogView.ui \ - src/ui/QGCFirmwareUpdate.ui \ - src/ui/QGCPxImuFirmwareUpdate.ui \ - src/ui/QGCDataPlot2D.ui \ - src/ui/QGCRemoteControlView.ui \ - src/ui/QMap3D.ui \ - src/ui/QGCWebView.ui \ - src/ui/map3D/QGCGoogleEarthView.ui \ - src/ui/SlugsDataSensorView.ui \ - src/ui/SlugsHilSim.ui \ - src/ui/SlugsPadCameraControl.ui \ - src/ui/uas/QGCUnconnectedInfoWidget.ui \ - src/ui/designer/QGCToolWidget.ui \ - src/ui/designer/QGCParamSlider.ui \ - src/ui/designer/QGCActionButton.ui \ - src/ui/designer/QGCCommandButton.ui \ - src/ui/QGCMAVLinkLogPlayer.ui \ - src/ui/QGCWaypointListMulti.ui \ - src/ui/QGCUDPLinkConfiguration.ui \ - src/ui/QGCSettingsWidget.ui \ - src/ui/UASControlParameters.ui \ - src/ui/map/QGCMapTool.ui \ - src/ui/map/QGCMapToolBar.ui \ - src/ui/QGCMAVLinkInspector.ui \ - src/ui/WaypointViewOnlyView.ui \ - src/ui/WaypointEditableView.ui \ - src/ui/UnconnectedUASInfoWidget.ui \ - src/ui/mavlink/QGCMAVLinkMessageSender.ui \ - src/ui/firmwareupdate/QGCFirmwareUpdateWidget.ui \ - src/ui/QGCPluginHost.ui \ - src/ui/firmwareupdate/QGCPX4FirmwareUpdate.ui \ - src/ui/mission/QGCMissionOther.ui \ - src/ui/mission/QGCMissionNavWaypoint.ui \ - src/ui/mission/QGCMissionDoJump.ui \ - src/ui/mission/QGCMissionConditionDelay.ui \ - src/ui/mission/QGCMissionNavLoiterUnlim.ui \ - src/ui/mission/QGCMissionNavLoiterTurns.ui \ - src/ui/mission/QGCMissionNavLoiterTime.ui \ - src/ui/mission/QGCMissionNavReturnToLaunch.ui \ - src/ui/mission/QGCMissionNavLand.ui \ - src/ui/mission/QGCMissionNavTakeoff.ui \ - src/ui/mission/QGCMissionNavSweep.ui \ - src/ui/mission/QGCMissionDoStartSearch.ui \ - src/ui/mission/QGCMissionDoFinishSearch.ui \ - src/ui/QGCVehicleConfig.ui \ - src/ui/QGCHilConfiguration.ui \ - src/ui/QGCHilFlightGearConfiguration.ui \ - src/ui/QGCHilXPlaneConfiguration.ui -INCLUDEPATH += src \ - src/ui \ - src/ui/linechart \ - src/ui/uas \ - src/ui/map \ - src/uas \ - src/comm \ - include/ui \ - src/input \ - src/lib/qmapcontrol \ - src/ui/mavlink \ - src/ui/param \ - src/ui/watchdog \ - src/ui/map3D \ - src/ui/mission \ - src/ui/designer -HEADERS += src/MG.h \ - src/QGCCore.h \ - src/uas/UASInterface.h \ - src/uas/UAS.h \ - src/uas/UASManager.h \ - src/comm/LinkManager.h \ - src/comm/LinkInterface.h \ - src/comm/SerialLinkInterface.h \ - src/comm/SerialLink.h \ - src/comm/ProtocolInterface.h \ - src/comm/MAVLinkProtocol.h \ - src/comm/QGCFlightGearLink.h \ - src/comm/QGCXPlaneLink.h \ - src/ui/CommConfigurationWindow.h \ - src/ui/SerialConfigurationWindow.h \ - src/ui/MainWindow.h \ - src/ui/uas/UASControlWidget.h \ - src/ui/uas/UASListWidget.h \ - src/ui/uas/UASInfoWidget.h \ - src/ui/HUD.h \ - src/ui/linechart/LinechartWidget.h \ - src/ui/linechart/LinechartPlot.h \ - src/ui/linechart/Scrollbar.h \ - src/ui/linechart/ScrollZoomer.h \ - src/configuration.h \ - src/ui/uas/UASView.h \ - src/ui/CameraView.h \ - src/comm/MAVLinkSimulationLink.h \ - src/comm/UDPLink.h \ - src/ui/ParameterInterface.h \ - src/ui/WaypointList.h \ - src/Waypoint.h \ - src/ui/ObjectDetectionView.h \ - src/input/JoystickInput.h \ - src/ui/JoystickWidget.h \ - src/ui/DebugConsole.h \ - src/ui/HDDisplay.h \ - src/ui/MAVLinkSettingsWidget.h \ - src/ui/AudioOutputWidget.h \ - src/GAudioOutput.h \ - src/LogCompressor.h \ - src/ui/QGCParamWidget.h \ - src/ui/QGCSensorSettingsWidget.h \ - src/ui/linechart/Linecharts.h \ - src/uas/SlugsMAV.h \ - src/uas/PxQuadMAV.h \ - src/uas/ArduPilotMegaMAV.h \ - src/uas/senseSoarMAV.h \ - src/ui/watchdog/WatchdogControl.h \ - src/ui/watchdog/WatchdogProcessView.h \ - src/ui/watchdog/WatchdogView.h \ - src/uas/UASWaypointManager.h \ - src/ui/HSIDisplay.h \ - src/QGC.h \ - src/ui/QGCFirmwareUpdate.h \ - src/ui/QGCPxImuFirmwareUpdate.h \ - src/ui/QGCDataPlot2D.h \ - src/ui/linechart/IncrementalPlot.h \ - src/ui/QGCRemoteControlView.h \ - src/ui/RadioCalibration/RadioCalibrationData.h \ - src/ui/RadioCalibration/RadioCalibrationWindow.h \ - src/ui/RadioCalibration/AirfoilServoCalibrator.h \ - src/ui/RadioCalibration/SwitchCalibrator.h \ - src/ui/RadioCalibration/CurveCalibrator.h \ - src/ui/RadioCalibration/AbstractCalibrator.h \ - src/comm/QGCMAVLink.h \ - src/ui/QGCWebView.h \ - src/ui/map3D/QGCWebPage.h \ - src/ui/SlugsDataSensorView.h \ - src/ui/SlugsHilSim.h \ - src/ui/SlugsPadCameraControl.h \ - src/ui/QGCMainWindowAPConfigurator.h \ - src/comm/MAVLinkSwarmSimulationLink.h \ - src/ui/uas/QGCUnconnectedInfoWidget.h \ - src/ui/designer/QGCToolWidget.h \ - src/ui/designer/QGCParamSlider.h \ - src/ui/designer/QGCCommandButton.h \ - src/ui/designer/QGCToolWidgetItem.h \ - src/ui/QGCMAVLinkLogPlayer.h \ - src/comm/MAVLinkSimulationWaypointPlanner.h \ - src/comm/MAVLinkSimulationMAV.h \ - src/uas/QGCMAVLinkUASFactory.h \ - src/ui/QGCWaypointListMulti.h \ - src/ui/QGCUDPLinkConfiguration.h \ - src/ui/QGCSettingsWidget.h \ - src/ui/uas/UASControlParameters.h \ - src/uas/QGCUASParamManager.h \ - src/ui/map/QGCMapWidget.h \ - src/ui/map/MAV2DIcon.h \ - src/ui/map/Waypoint2DIcon.h \ - src/ui/map/QGCMapTool.h \ - src/ui/map/QGCMapToolBar.h \ - libs/qextserialport/qextserialenumerator.h \ - src/QGCGeo.h \ - src/ui/QGCToolBar.h \ - src/ui/QGCMAVLinkInspector.h \ - src/ui/MAVLinkDecoder.h \ - src/ui/WaypointViewOnlyView.h \ - src/ui/WaypointViewOnlyView.h \ - src/ui/WaypointEditableView.h \ - src/ui/UnconnectedUASInfoWidget.h \ - src/ui/QGCRGBDView.h \ - src/ui/mavlink/QGCMAVLinkMessageSender.h \ - src/ui/firmwareupdate/QGCFirmwareUpdateWidget.h \ - src/ui/QGCPluginHost.h \ - src/ui/firmwareupdate/QGCPX4FirmwareUpdate.h \ - src/ui/mission/QGCMissionOther.h \ - src/ui/mission/QGCMissionNavWaypoint.h \ - src/ui/mission/QGCMissionDoJump.h \ - src/ui/mission/QGCMissionConditionDelay.h \ - src/ui/mission/QGCMissionNavLoiterUnlim.h \ - src/ui/mission/QGCMissionNavLoiterTurns.h \ - src/ui/mission/QGCMissionNavLoiterTime.h \ - src/ui/mission/QGCMissionNavReturnToLaunch.h \ - src/ui/mission/QGCMissionNavLand.h \ - src/ui/mission/QGCMissionNavTakeoff.h \ - src/ui/mission/QGCMissionNavSweep.h \ - src/ui/mission/QGCMissionDoStartSearch.h \ - src/ui/mission/QGCMissionDoFinishSearch.h \ - src/ui/QGCVehicleConfig.h \ - src/comm/QGCHilLink.h \ - src/ui/QGCHilConfiguration.h \ - src/ui/QGCHilFlightGearConfiguration.h \ - src/ui/QGCHilXPlaneConfiguration.h - -# Google Earth is only supported on Mac OS and Windows with Visual Studio Compiler -macx|macx-g++|macx-g++42|win32-msvc2008|win32-msvc2010::HEADERS += src/ui/map3D/QGCGoogleEarthView.h -contains(DEPENDENCIES_PRESENT, osg) { - message("Including headers for OpenSceneGraph") - - # Enable only if OpenSceneGraph is available - HEADERS += src/ui/map3D/gpl.h \ - src/ui/map3D/CameraParams.h \ - src/ui/map3D/ViewParamWidget.h \ - src/ui/map3D/SystemContainer.h \ - src/ui/map3D/SystemViewParams.h \ - src/ui/map3D/GlobalViewParams.h \ - src/ui/map3D/SystemGroupNode.h \ - src/ui/map3D/Q3DWidget.h \ - src/ui/map3D/GCManipulator.h \ - src/ui/map3D/ImageWindowGeode.h \ - src/ui/map3D/PixhawkCheetahNode.h \ - src/ui/map3D/Pixhawk3DWidget.h \ - src/ui/map3D/Q3DWidgetFactory.h \ - src/ui/map3D/WebImageCache.h \ - src/ui/map3D/WebImage.h \ - src/ui/map3D/TextureCache.h \ - src/ui/map3D/Texture.h \ - src/ui/map3D/Imagery.h \ - src/ui/map3D/HUDScaleGeode.h \ - src/ui/map3D/WaypointGroupNode.h \ - src/ui/map3D/TerrainParamDialog.h \ - src/ui/map3D/ImageryParamDialog.h -} -contains(DEPENDENCIES_PRESENT, protobuf):contains(MAVLINK_CONF, pixhawk) { - message("Including headers for Protocol Buffers") - - # Enable only if protobuf is available - HEADERS += libs/mavlink/include/mavlink/v1.0/pixhawk/pixhawk.pb.h \ - src/ui/map3D/ObstacleGroupNode.h \ - src/ui/map3D/GLOverlayGeode.h -} -contains(DEPENDENCIES_PRESENT, libfreenect) { - message("Including headers for libfreenect") - - # Enable only if libfreenect is available - HEADERS += src/input/Freenect.h -} -SOURCES += src/main.cc \ - src/QGCCore.cc \ - src/uas/UASManager.cc \ - src/uas/UAS.cc \ - src/comm/LinkManager.cc \ - src/comm/LinkInterface.cpp \ - src/comm/SerialLink.cc \ - src/comm/MAVLinkProtocol.cc \ - src/comm/QGCFlightGearLink.cc \ - src/comm/QGCXPlaneLink.cc \ - src/ui/CommConfigurationWindow.cc \ - src/ui/SerialConfigurationWindow.cc \ - src/ui/MainWindow.cc \ - src/ui/uas/UASControlWidget.cc \ - src/ui/uas/UASListWidget.cc \ - src/ui/uas/UASInfoWidget.cc \ - src/ui/HUD.cc \ - src/ui/linechart/LinechartWidget.cc \ - src/ui/linechart/LinechartPlot.cc \ - src/ui/linechart/Scrollbar.cc \ - src/ui/linechart/ScrollZoomer.cc \ - src/ui/uas/UASView.cc \ - src/ui/CameraView.cc \ - src/comm/MAVLinkSimulationLink.cc \ - src/comm/UDPLink.cc \ - src/ui/ParameterInterface.cc \ - src/ui/WaypointList.cc \ - src/Waypoint.cc \ - src/ui/ObjectDetectionView.cc \ - src/input/JoystickInput.cc \ - src/ui/JoystickWidget.cc \ - src/ui/DebugConsole.cc \ - src/ui/HDDisplay.cc \ - src/ui/MAVLinkSettingsWidget.cc \ - src/ui/AudioOutputWidget.cc \ - src/GAudioOutput.cc \ - src/LogCompressor.cc \ - src/ui/QGCParamWidget.cc \ - src/ui/QGCSensorSettingsWidget.cc \ - src/ui/linechart/Linecharts.cc \ - src/uas/SlugsMAV.cc \ - src/uas/PxQuadMAV.cc \ - src/uas/ArduPilotMegaMAV.cc \ - src/uas/senseSoarMAV.cpp \ - src/ui/watchdog/WatchdogControl.cc \ - src/ui/watchdog/WatchdogProcessView.cc \ - src/ui/watchdog/WatchdogView.cc \ - src/uas/UASWaypointManager.cc \ - src/ui/HSIDisplay.cc \ - src/QGC.cc \ - src/ui/QGCFirmwareUpdate.cc \ - src/ui/QGCPxImuFirmwareUpdate.cc \ - src/ui/QGCDataPlot2D.cc \ - src/ui/linechart/IncrementalPlot.cc \ - src/ui/QGCRemoteControlView.cc \ - src/ui/RadioCalibration/RadioCalibrationWindow.cc \ - src/ui/RadioCalibration/AirfoilServoCalibrator.cc \ - src/ui/RadioCalibration/SwitchCalibrator.cc \ - src/ui/RadioCalibration/CurveCalibrator.cc \ - src/ui/RadioCalibration/AbstractCalibrator.cc \ - src/ui/RadioCalibration/RadioCalibrationData.cc \ - src/ui/QGCWebView.cc \ - src/ui/map3D/QGCWebPage.cc \ - src/ui/SlugsDataSensorView.cc \ - src/ui/SlugsHilSim.cc \ - src/ui/SlugsPadCameraControl.cpp \ - src/ui/QGCMainWindowAPConfigurator.cc \ - src/comm/MAVLinkSwarmSimulationLink.cc \ - src/ui/uas/QGCUnconnectedInfoWidget.cc \ - src/ui/designer/QGCToolWidget.cc \ - src/ui/designer/QGCParamSlider.cc \ - src/ui/designer/QGCCommandButton.cc \ - src/ui/designer/QGCToolWidgetItem.cc \ - src/ui/QGCMAVLinkLogPlayer.cc \ - src/comm/MAVLinkSimulationWaypointPlanner.cc \ - src/comm/MAVLinkSimulationMAV.cc \ - src/uas/QGCMAVLinkUASFactory.cc \ - src/ui/QGCWaypointListMulti.cc \ - src/ui/QGCUDPLinkConfiguration.cc \ - src/ui/QGCSettingsWidget.cc \ - src/ui/uas/UASControlParameters.cpp \ - src/uas/QGCUASParamManager.cc \ - src/ui/map/QGCMapWidget.cc \ - src/ui/map/MAV2DIcon.cc \ - src/ui/map/Waypoint2DIcon.cc \ - src/ui/map/QGCMapTool.cc \ - src/ui/map/QGCMapToolBar.cc \ - src/ui/QGCToolBar.cc \ - src/ui/QGCMAVLinkInspector.cc \ - src/ui/MAVLinkDecoder.cc \ - src/ui/WaypointViewOnlyView.cc \ - src/ui/WaypointEditableView.cc \ - src/ui/UnconnectedUASInfoWidget.cc \ - src/ui/QGCRGBDView.cc \ - src/ui/mavlink/QGCMAVLinkMessageSender.cc \ - src/ui/firmwareupdate/QGCFirmwareUpdateWidget.cc \ - src/ui/QGCPluginHost.cc \ - src/ui/firmwareupdate/QGCPX4FirmwareUpdate.cc \ - src/ui/mission/QGCMissionOther.cc \ - src/ui/mission/QGCMissionNavWaypoint.cc \ - src/ui/mission/QGCMissionDoJump.cc \ - src/ui/mission/QGCMissionConditionDelay.cc \ - src/ui/mission/QGCMissionNavLoiterUnlim.cc \ - src/ui/mission/QGCMissionNavLoiterTurns.cc \ - src/ui/mission/QGCMissionNavLoiterTime.cc \ - src/ui/mission/QGCMissionNavReturnToLaunch.cc \ - src/ui/mission/QGCMissionNavLand.cc \ - src/ui/mission/QGCMissionNavTakeoff.cc \ - src/ui/mission/QGCMissionNavSweep.cc \ - src/ui/mission/QGCMissionDoStartSearch.cc \ - src/ui/mission/QGCMissionDoFinishSearch.cc \ - src/ui/QGCVehicleConfig.cc \ - src/ui/QGCHilConfiguration.cc \ - src/ui/QGCHilFlightGearConfiguration.cc \ - src/ui/QGCHilXPlaneConfiguration.cc - -# Enable Google Earth only on Mac OS and Windows with Visual Studio compiler -macx|macx-g++|macx-g++42|win32-msvc2008|win32-msvc2010::SOURCES += src/ui/map3D/QGCGoogleEarthView.cc - -# Enable OSG only if it has been found -contains(DEPENDENCIES_PRESENT, osg) { - message("Including sources for OpenSceneGraph") - - # Enable only if OpenSceneGraph is available - SOURCES += src/ui/map3D/gpl.cc \ - src/ui/map3D/CameraParams.cc \ - src/ui/map3D/ViewParamWidget.cc \ - src/ui/map3D/SystemContainer.cc \ - src/ui/map3D/SystemViewParams.cc \ - src/ui/map3D/GlobalViewParams.cc \ - src/ui/map3D/SystemGroupNode.cc \ - src/ui/map3D/Q3DWidget.cc \ - src/ui/map3D/ImageWindowGeode.cc \ - src/ui/map3D/GCManipulator.cc \ - src/ui/map3D/PixhawkCheetahNode.cc \ - src/ui/map3D/Pixhawk3DWidget.cc \ - src/ui/map3D/Q3DWidgetFactory.cc \ - src/ui/map3D/WebImageCache.cc \ - src/ui/map3D/WebImage.cc \ - src/ui/map3D/TextureCache.cc \ - src/ui/map3D/Texture.cc \ - src/ui/map3D/Imagery.cc \ - src/ui/map3D/HUDScaleGeode.cc \ - src/ui/map3D/WaypointGroupNode.cc \ - src/ui/map3D/TerrainParamDialog.cc \ - src/ui/map3D/ImageryParamDialog.cc - - contains(DEPENDENCIES_PRESENT, osgearth) { - message("Including sources for osgEarth") - - # Enable only if OpenSceneGraph is available - SOURCES += - } -} -contains(DEPENDENCIES_PRESENT, protobuf):contains(MAVLINK_CONF, pixhawk) { - message("Including sources for Protocol Buffers") - - # Enable only if protobuf is available - SOURCES += libs/mavlink/share/mavlink/src/v1.0/pixhawk/pixhawk.pb.cc \ - src/ui/map3D/ObstacleGroupNode.cc \ - src/ui/map3D/GLOverlayGeode.cc -} -contains(DEPENDENCIES_PRESENT, libfreenect) { - message("Including sources for libfreenect") - - # Enable only if libfreenect is available - SOURCES += src/input/Freenect.cc -} - -# Add icons and other resources -RESOURCES += qgroundcontrol.qrc - -# Include RT-LAB Library -win32:exists(src/lib/opalrt/OpalApi.h):exists(C:/OPAL-RT/RT-LAB7.2.4/Common/bin) { - message("Building support for Opal-RT") - LIBS += -LC:/OPAL-RT/RT-LAB7.2.4/Common/bin \ - -lOpalApi - INCLUDEPATH += src/lib/opalrt - HEADERS += src/comm/OpalRT.h \ - src/comm/OpalLink.h \ - src/comm/Parameter.h \ - src/comm/QGCParamID.h \ - src/comm/ParameterList.h \ - src/ui/OpalLinkConfigurationWindow.h - SOURCES += src/comm/OpalRT.cc \ - src/comm/OpalLink.cc \ - src/comm/Parameter.cc \ - src/comm/QGCParamID.cc \ - src/comm/ParameterList.cc \ - src/ui/OpalLinkConfigurationWindow.cc - FORMS += src/ui/OpalLinkSettings.ui - DEFINES += OPAL_RT -} -TRANSLATIONS += es-MX.ts \ - en-US.ts - -# xbee support -# libxbee only supported by linux and windows systems -win32-msvc2008|win32-msvc2010|linux { - HEADERS += src/comm/XbeeLinkInterface.h \ - src/comm/XbeeLink.h \ - src/comm/HexSpinBox.h \ - src/ui/XbeeConfigurationWindow.h \ - src/comm/CallConv.h - SOURCES += src/comm/XbeeLink.cpp \ - src/comm/HexSpinBox.cpp \ - src/ui/XbeeConfigurationWindow.cpp - DEFINES += XBEELINK - INCLUDEPATH += libs/thirdParty/libxbee -# TO DO: build library when it does not exist already - LIBS += -Llibs/thirdParty/libxbee/lib \ - -llibxbee -} +# ------------------------------------------------- +# QGroundControl - Micro Air Vehicle Groundstation +# Please see our website at +# Maintainer: +# Lorenz Meier +# (c) 2009-2011 QGroundControl Developers +# This file is part of the open groundstation project +# QGroundControl is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# QGroundControl is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with QGroundControl. If not, see . +# ------------------------------------------------- + + +# Qt configuration +CONFIG += qt \ + thread +QT += network \ + opengl \ + svg \ + xml \ + phonon \ + webkit \ + sql + +TEMPLATE = app +TARGET = qgroundcontrol +BASEDIR = $${IN_PWD} +linux-g++|linux-g++-64{ + debug { + TARGETDIR = $${OUT_PWD}/debug + BUILDDIR = $${OUT_PWD}/build-debug + } + release { + TARGETDIR = $${OUT_PWD}/release + BUILDDIR = $${OUT_PWD}/build-release + } +} else { + TARGETDIR = $${OUT_PWD} + BUILDDIR = $${OUT_PWD}/build +} +LANGUAGE = C++ +OBJECTS_DIR = $${BUILDDIR}/obj +MOC_DIR = $${BUILDDIR}/moc +UI_DIR = $${BUILDDIR}/ui +RCC_DIR = $${BUILDDIR}/rcc +MAVLINK_CONF = "pixhawk" +MAVLINKPATH = $$BASEDIR/libs/mavlink/include/mavlink/v1.0 +DEFINES += MAVLINK_NO_DATA + +win32 { + QMAKE_INCDIR_QT = $$(QTDIR)/include + QMAKE_LIBDIR_QT = $$(QTDIR)/lib + QMAKE_UIC = "$$(QTDIR)/bin/uic.exe" + QMAKE_MOC = "$$(QTDIR)/bin/moc.exe" + QMAKE_RCC = "$$(QTDIR)/bin/rcc.exe" + QMAKE_QMAKE = "$$(QTDIR)/bin/qmake.exe" + + # Build QAX for GoogleEarth API access + !exists( $(QTDIR)/src/activeqt/Makefile ) { + message( Making QAx (ONE TIME) ) + system( cd $$(QTDIR)\\src\\activeqt && $$(QTDIR)\\bin\\qmake.exe ) + system( cd $$(QTDIR)\\src\\activeqt\\container && $$(QTDIR)\\bin\\qmake.exe ) + system( cd $$(QTDIR)\\src\\activeqt\\control && $$(QTDIR)\\bin\\qmake.exe ) + } +} + + + +################################################################# +# EXTERNAL LIBRARY CONFIGURATION + +# EIGEN matrix library (header-only) +INCLUDEPATH += libs/eigen + +# OPMapControl library (from OpenPilot) +include(libs/utils/utils_external.pri) +include(libs/opmapcontrol/opmapcontrol_external.pri) +DEPENDPATH += \ + libs/utils \ + libs/utils/src \ + libs/opmapcontrol \ + libs/opmapcontrol/src \ + libs/opmapcontrol/src/mapwidget + +INCLUDEPATH += \ + libs/utils \ + libs \ + libs/opmapcontrol + +# If the user config file exists, it will be included. +# if the variable MAVLINK_CONF contains the name of an +# additional project, QGroundControl includes the support +# of custom MAVLink messages of this project. It will also +# create a QGC_USE_{AUTOPILOT_NAME}_MESSAGES macro for use +# within the actual code. +exists(user_config.pri) { + include(user_config.pri) + message("----- USING CUSTOM USER QGROUNDCONTROL CONFIG FROM user_config.pri -----") + message("Adding support for additional MAVLink messages for: " $$MAVLINK_CONF) + message("------------------------------------------------------------------------") +} +INCLUDEPATH += $$MAVLINKPATH +isEmpty(MAVLINK_CONF) { + INCLUDEPATH += $$MAVLINKPATH/common +} else { + INCLUDEPATH += $$MAVLINKPATH/$$MAVLINK_CONF + #DEFINES += 'MAVLINK_CONF="$${MAVLINK_CONF}.h"' + DEFINES += $$sprintf('QGC_USE_%1_MESSAGES', $$upper($$MAVLINK_CONF)) +} + +# Include general settings for QGroundControl +# necessary as last include to override any non-acceptable settings +# done by the plugins above +include(qgroundcontrol.pri) + +# Include MAVLink generator +# has been deprecated +DEPENDPATH += \ + src/apps/mavlinkgen + +INCLUDEPATH += \ + src/apps/mavlinkgen \ + src/apps/mavlinkgen/ui \ + src/apps/mavlinkgen/generator + +include(src/apps/mavlinkgen/mavlinkgen.pri) + + + +# Include QWT plotting library +include(libs/qwt/qwt.pri) +DEPENDPATH += . \ + plugins \ + libs/thirdParty/qserialport/include \ + libs/thirdParty/qserialport/include/QtSerialPort \ + libs/thirdParty/qserialport \ + libs/qextserialport + +INCLUDEPATH += . \ + libs/thirdParty/qserialport/include \ + libs/thirdParty/qserialport/include/QtSerialPort \ + libs/thirdParty/qserialport/src \ + libs/qextserialport + +# Include serial port library (QSerial) +include(qserialport.pri) + +# Serial port detection (ripped-off from qextserialport library) +macx|macx-g++|macx-g++42::SOURCES += libs/qextserialport/qextserialenumerator_osx.cpp +linux-g++::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp +linux-g++-64::SOURCES += libs/qextserialport/qextserialenumerator_unix.cpp +win32::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp +win32-msvc2008|win32-msvc2010::SOURCES += libs/qextserialport/qextserialenumerator_win.cpp + +# Input +FORMS += src/ui/MainWindow.ui \ + src/ui/CommSettings.ui \ + src/ui/SerialSettings.ui \ + src/ui/UASControl.ui \ + src/ui/UASList.ui \ + src/ui/UASInfo.ui \ + src/ui/Linechart.ui \ + src/ui/UASView.ui \ + src/ui/ParameterInterface.ui \ + src/ui/WaypointList.ui \ + src/ui/ObjectDetectionView.ui \ + src/ui/JoystickWidget.ui \ + src/ui/DebugConsole.ui \ + src/ui/HDDisplay.ui \ + src/ui/MAVLinkSettingsWidget.ui \ + src/ui/AudioOutputWidget.ui \ + src/ui/QGCSensorSettingsWidget.ui \ + src/ui/watchdog/WatchdogControl.ui \ + src/ui/watchdog/WatchdogProcessView.ui \ + src/ui/watchdog/WatchdogView.ui \ + src/ui/QGCFirmwareUpdate.ui \ + src/ui/QGCPxImuFirmwareUpdate.ui \ + src/ui/QGCDataPlot2D.ui \ + src/ui/QGCRemoteControlView.ui \ + src/ui/QMap3D.ui \ + src/ui/QGCWebView.ui \ + src/ui/map3D/QGCGoogleEarthView.ui \ + src/ui/SlugsDataSensorView.ui \ + src/ui/SlugsHilSim.ui \ + src/ui/SlugsPadCameraControl.ui \ + src/ui/uas/QGCUnconnectedInfoWidget.ui \ + src/ui/designer/QGCToolWidget.ui \ + src/ui/designer/QGCParamSlider.ui \ + src/ui/designer/QGCActionButton.ui \ + src/ui/designer/QGCCommandButton.ui \ + src/ui/QGCMAVLinkLogPlayer.ui \ + src/ui/QGCWaypointListMulti.ui \ + src/ui/QGCUDPLinkConfiguration.ui \ + src/ui/QGCSettingsWidget.ui \ + src/ui/UASControlParameters.ui \ + src/ui/map/QGCMapTool.ui \ + src/ui/map/QGCMapToolBar.ui \ + src/ui/QGCMAVLinkInspector.ui \ + src/ui/WaypointViewOnlyView.ui \ + src/ui/WaypointEditableView.ui \ + src/ui/UnconnectedUASInfoWidget.ui \ + src/ui/mavlink/QGCMAVLinkMessageSender.ui \ + src/ui/firmwareupdate/QGCFirmwareUpdateWidget.ui \ + src/ui/QGCPluginHost.ui \ + src/ui/firmwareupdate/QGCPX4FirmwareUpdate.ui \ + src/ui/mission/QGCMissionOther.ui \ + src/ui/mission/QGCMissionNavWaypoint.ui \ + src/ui/mission/QGCMissionDoJump.ui \ + src/ui/mission/QGCMissionConditionDelay.ui \ + src/ui/mission/QGCMissionNavLoiterUnlim.ui \ + src/ui/mission/QGCMissionNavLoiterTurns.ui \ + src/ui/mission/QGCMissionNavLoiterTime.ui \ + src/ui/mission/QGCMissionNavReturnToLaunch.ui \ + src/ui/mission/QGCMissionNavLand.ui \ + src/ui/mission/QGCMissionNavTakeoff.ui \ + src/ui/mission/QGCMissionNavSweep.ui \ + src/ui/mission/QGCMissionDoStartSearch.ui \ + src/ui/mission/QGCMissionDoFinishSearch.ui \ + src/ui/QGCVehicleConfig.ui \ + src/ui/QGCHilConfiguration.ui \ + src/ui/QGCHilFlightGearConfiguration.ui \ + src/ui/QGCHilXPlaneConfiguration.ui +INCLUDEPATH += src \ + src/ui \ + src/ui/linechart \ + src/ui/uas \ + src/ui/map \ + src/uas \ + src/comm \ + include/ui \ + src/input \ + src/lib/qmapcontrol \ + src/ui/mavlink \ + src/ui/param \ + src/ui/watchdog \ + src/ui/map3D \ + src/ui/mission \ + src/ui/designer +HEADERS += src/MG.h \ + src/QGCCore.h \ + src/uas/UASInterface.h \ + src/uas/UAS.h \ + src/uas/UASManager.h \ + src/comm/LinkManager.h \ + src/comm/LinkInterface.h \ + src/comm/SerialLinkInterface.h \ + src/comm/SerialLink.h \ + src/comm/ProtocolInterface.h \ + src/comm/MAVLinkProtocol.h \ + src/comm/QGCFlightGearLink.h \ + src/comm/QGCXPlaneLink.h \ + src/ui/CommConfigurationWindow.h \ + src/ui/SerialConfigurationWindow.h \ + src/ui/MainWindow.h \ + src/ui/uas/UASControlWidget.h \ + src/ui/uas/UASListWidget.h \ + src/ui/uas/UASInfoWidget.h \ + src/ui/HUD.h \ + src/ui/linechart/LinechartWidget.h \ + src/ui/linechart/LinechartPlot.h \ + src/ui/linechart/Scrollbar.h \ + src/ui/linechart/ScrollZoomer.h \ + src/configuration.h \ + src/ui/uas/UASView.h \ + src/ui/CameraView.h \ + src/comm/MAVLinkSimulationLink.h \ + src/comm/UDPLink.h \ + src/ui/ParameterInterface.h \ + src/ui/WaypointList.h \ + src/Waypoint.h \ + src/ui/ObjectDetectionView.h \ + src/input/JoystickInput.h \ + src/ui/JoystickWidget.h \ + src/ui/DebugConsole.h \ + src/ui/HDDisplay.h \ + src/ui/MAVLinkSettingsWidget.h \ + src/ui/AudioOutputWidget.h \ + src/GAudioOutput.h \ + src/LogCompressor.h \ + src/ui/QGCParamWidget.h \ + src/ui/QGCSensorSettingsWidget.h \ + src/ui/linechart/Linecharts.h \ + src/uas/SlugsMAV.h \ + src/uas/PxQuadMAV.h \ + src/uas/ArduPilotMegaMAV.h \ + src/uas/senseSoarMAV.h \ + src/ui/watchdog/WatchdogControl.h \ + src/ui/watchdog/WatchdogProcessView.h \ + src/ui/watchdog/WatchdogView.h \ + src/uas/UASWaypointManager.h \ + src/ui/HSIDisplay.h \ + src/QGC.h \ + src/ui/QGCFirmwareUpdate.h \ + src/ui/QGCPxImuFirmwareUpdate.h \ + src/ui/QGCDataPlot2D.h \ + src/ui/linechart/IncrementalPlot.h \ + src/ui/QGCRemoteControlView.h \ + src/ui/RadioCalibration/RadioCalibrationData.h \ + src/ui/RadioCalibration/RadioCalibrationWindow.h \ + src/ui/RadioCalibration/AirfoilServoCalibrator.h \ + src/ui/RadioCalibration/SwitchCalibrator.h \ + src/ui/RadioCalibration/CurveCalibrator.h \ + src/ui/RadioCalibration/AbstractCalibrator.h \ + src/comm/QGCMAVLink.h \ + src/ui/QGCWebView.h \ + src/ui/map3D/QGCWebPage.h \ + src/ui/SlugsDataSensorView.h \ + src/ui/SlugsHilSim.h \ + src/ui/SlugsPadCameraControl.h \ + src/ui/QGCMainWindowAPConfigurator.h \ + src/comm/MAVLinkSwarmSimulationLink.h \ + src/ui/uas/QGCUnconnectedInfoWidget.h \ + src/ui/designer/QGCToolWidget.h \ + src/ui/designer/QGCParamSlider.h \ + src/ui/designer/QGCCommandButton.h \ + src/ui/designer/QGCToolWidgetItem.h \ + src/ui/QGCMAVLinkLogPlayer.h \ + src/comm/MAVLinkSimulationWaypointPlanner.h \ + src/comm/MAVLinkSimulationMAV.h \ + src/uas/QGCMAVLinkUASFactory.h \ + src/ui/QGCWaypointListMulti.h \ + src/ui/QGCUDPLinkConfiguration.h \ + src/ui/QGCSettingsWidget.h \ + src/ui/uas/UASControlParameters.h \ + src/uas/QGCUASParamManager.h \ + src/ui/map/QGCMapWidget.h \ + src/ui/map/MAV2DIcon.h \ + src/ui/map/Waypoint2DIcon.h \ + src/ui/map/QGCMapTool.h \ + src/ui/map/QGCMapToolBar.h \ + libs/qextserialport/qextserialenumerator.h \ + src/QGCGeo.h \ + src/ui/QGCToolBar.h \ + src/ui/QGCMAVLinkInspector.h \ + src/ui/MAVLinkDecoder.h \ + src/ui/WaypointViewOnlyView.h \ + src/ui/WaypointViewOnlyView.h \ + src/ui/WaypointEditableView.h \ + src/ui/UnconnectedUASInfoWidget.h \ + src/ui/QGCRGBDView.h \ + src/ui/mavlink/QGCMAVLinkMessageSender.h \ + src/ui/firmwareupdate/QGCFirmwareUpdateWidget.h \ + src/ui/QGCPluginHost.h \ + src/ui/firmwareupdate/QGCPX4FirmwareUpdate.h \ + src/ui/mission/QGCMissionOther.h \ + src/ui/mission/QGCMissionNavWaypoint.h \ + src/ui/mission/QGCMissionDoJump.h \ + src/ui/mission/QGCMissionConditionDelay.h \ + src/ui/mission/QGCMissionNavLoiterUnlim.h \ + src/ui/mission/QGCMissionNavLoiterTurns.h \ + src/ui/mission/QGCMissionNavLoiterTime.h \ + src/ui/mission/QGCMissionNavReturnToLaunch.h \ + src/ui/mission/QGCMissionNavLand.h \ + src/ui/mission/QGCMissionNavTakeoff.h \ + src/ui/mission/QGCMissionNavSweep.h \ + src/ui/mission/QGCMissionDoStartSearch.h \ + src/ui/mission/QGCMissionDoFinishSearch.h \ + src/ui/QGCVehicleConfig.h \ + src/comm/QGCHilLink.h \ + src/ui/QGCHilConfiguration.h \ + src/ui/QGCHilFlightGearConfiguration.h \ + src/ui/QGCHilXPlaneConfiguration.h + +# Google Earth is only supported on Mac OS and Windows with Visual Studio Compiler +macx|macx-g++|macx-g++42|win32-msvc2008|win32-msvc2010::HEADERS += src/ui/map3D/QGCGoogleEarthView.h +contains(DEPENDENCIES_PRESENT, osg) { + message("Including headers for OpenSceneGraph") + + # Enable only if OpenSceneGraph is available + HEADERS += src/ui/map3D/gpl.h \ + src/ui/map3D/CameraParams.h \ + src/ui/map3D/ViewParamWidget.h \ + src/ui/map3D/SystemContainer.h \ + src/ui/map3D/SystemViewParams.h \ + src/ui/map3D/GlobalViewParams.h \ + src/ui/map3D/SystemGroupNode.h \ + src/ui/map3D/Q3DWidget.h \ + src/ui/map3D/GCManipulator.h \ + src/ui/map3D/ImageWindowGeode.h \ + src/ui/map3D/PixhawkCheetahNode.h \ + src/ui/map3D/Pixhawk3DWidget.h \ + src/ui/map3D/Q3DWidgetFactory.h \ + src/ui/map3D/WebImageCache.h \ + src/ui/map3D/WebImage.h \ + src/ui/map3D/TextureCache.h \ + src/ui/map3D/Texture.h \ + src/ui/map3D/Imagery.h \ + src/ui/map3D/HUDScaleGeode.h \ + src/ui/map3D/WaypointGroupNode.h \ + src/ui/map3D/TerrainParamDialog.h \ + src/ui/map3D/ImageryParamDialog.h +} +contains(DEPENDENCIES_PRESENT, protobuf):contains(MAVLINK_CONF, pixhawk) { + message("Including headers for Protocol Buffers") + + # Enable only if protobuf is available + HEADERS += libs/mavlink/include/mavlink/v1.0/pixhawk/pixhawk.pb.h \ + src/ui/map3D/ObstacleGroupNode.h \ + src/ui/map3D/GLOverlayGeode.h +} +contains(DEPENDENCIES_PRESENT, libfreenect) { + message("Including headers for libfreenect") + + # Enable only if libfreenect is available + HEADERS += src/input/Freenect.h +} +SOURCES += src/main.cc \ + src/QGCCore.cc \ + src/uas/UASManager.cc \ + src/uas/UAS.cc \ + src/comm/LinkManager.cc \ + src/comm/LinkInterface.cpp \ + src/comm/SerialLink.cc \ + src/comm/MAVLinkProtocol.cc \ + src/comm/QGCFlightGearLink.cc \ + src/comm/QGCXPlaneLink.cc \ + src/ui/CommConfigurationWindow.cc \ + src/ui/SerialConfigurationWindow.cc \ + src/ui/MainWindow.cc \ + src/ui/uas/UASControlWidget.cc \ + src/ui/uas/UASListWidget.cc \ + src/ui/uas/UASInfoWidget.cc \ + src/ui/HUD.cc \ + src/ui/linechart/LinechartWidget.cc \ + src/ui/linechart/LinechartPlot.cc \ + src/ui/linechart/Scrollbar.cc \ + src/ui/linechart/ScrollZoomer.cc \ + src/ui/uas/UASView.cc \ + src/ui/CameraView.cc \ + src/comm/MAVLinkSimulationLink.cc \ + src/comm/UDPLink.cc \ + src/ui/ParameterInterface.cc \ + src/ui/WaypointList.cc \ + src/Waypoint.cc \ + src/ui/ObjectDetectionView.cc \ + src/input/JoystickInput.cc \ + src/ui/JoystickWidget.cc \ + src/ui/DebugConsole.cc \ + src/ui/HDDisplay.cc \ + src/ui/MAVLinkSettingsWidget.cc \ + src/ui/AudioOutputWidget.cc \ + src/GAudioOutput.cc \ + src/LogCompressor.cc \ + src/ui/QGCParamWidget.cc \ + src/ui/QGCSensorSettingsWidget.cc \ + src/ui/linechart/Linecharts.cc \ + src/uas/SlugsMAV.cc \ + src/uas/PxQuadMAV.cc \ + src/uas/ArduPilotMegaMAV.cc \ + src/uas/senseSoarMAV.cpp \ + src/ui/watchdog/WatchdogControl.cc \ + src/ui/watchdog/WatchdogProcessView.cc \ + src/ui/watchdog/WatchdogView.cc \ + src/uas/UASWaypointManager.cc \ + src/ui/HSIDisplay.cc \ + src/QGC.cc \ + src/ui/QGCFirmwareUpdate.cc \ + src/ui/QGCPxImuFirmwareUpdate.cc \ + src/ui/QGCDataPlot2D.cc \ + src/ui/linechart/IncrementalPlot.cc \ + src/ui/QGCRemoteControlView.cc \ + src/ui/RadioCalibration/RadioCalibrationWindow.cc \ + src/ui/RadioCalibration/AirfoilServoCalibrator.cc \ + src/ui/RadioCalibration/SwitchCalibrator.cc \ + src/ui/RadioCalibration/CurveCalibrator.cc \ + src/ui/RadioCalibration/AbstractCalibrator.cc \ + src/ui/RadioCalibration/RadioCalibrationData.cc \ + src/ui/QGCWebView.cc \ + src/ui/map3D/QGCWebPage.cc \ + src/ui/SlugsDataSensorView.cc \ + src/ui/SlugsHilSim.cc \ + src/ui/SlugsPadCameraControl.cpp \ + src/ui/QGCMainWindowAPConfigurator.cc \ + src/comm/MAVLinkSwarmSimulationLink.cc \ + src/ui/uas/QGCUnconnectedInfoWidget.cc \ + src/ui/designer/QGCToolWidget.cc \ + src/ui/designer/QGCParamSlider.cc \ + src/ui/designer/QGCCommandButton.cc \ + src/ui/designer/QGCToolWidgetItem.cc \ + src/ui/QGCMAVLinkLogPlayer.cc \ + src/comm/MAVLinkSimulationWaypointPlanner.cc \ + src/comm/MAVLinkSimulationMAV.cc \ + src/uas/QGCMAVLinkUASFactory.cc \ + src/ui/QGCWaypointListMulti.cc \ + src/ui/QGCUDPLinkConfiguration.cc \ + src/ui/QGCSettingsWidget.cc \ + src/ui/uas/UASControlParameters.cpp \ + src/uas/QGCUASParamManager.cc \ + src/ui/map/QGCMapWidget.cc \ + src/ui/map/MAV2DIcon.cc \ + src/ui/map/Waypoint2DIcon.cc \ + src/ui/map/QGCMapTool.cc \ + src/ui/map/QGCMapToolBar.cc \ + src/ui/QGCToolBar.cc \ + src/ui/QGCMAVLinkInspector.cc \ + src/ui/MAVLinkDecoder.cc \ + src/ui/WaypointViewOnlyView.cc \ + src/ui/WaypointEditableView.cc \ + src/ui/UnconnectedUASInfoWidget.cc \ + src/ui/QGCRGBDView.cc \ + src/ui/mavlink/QGCMAVLinkMessageSender.cc \ + src/ui/firmwareupdate/QGCFirmwareUpdateWidget.cc \ + src/ui/QGCPluginHost.cc \ + src/ui/firmwareupdate/QGCPX4FirmwareUpdate.cc \ + src/ui/mission/QGCMissionOther.cc \ + src/ui/mission/QGCMissionNavWaypoint.cc \ + src/ui/mission/QGCMissionDoJump.cc \ + src/ui/mission/QGCMissionConditionDelay.cc \ + src/ui/mission/QGCMissionNavLoiterUnlim.cc \ + src/ui/mission/QGCMissionNavLoiterTurns.cc \ + src/ui/mission/QGCMissionNavLoiterTime.cc \ + src/ui/mission/QGCMissionNavReturnToLaunch.cc \ + src/ui/mission/QGCMissionNavLand.cc \ + src/ui/mission/QGCMissionNavTakeoff.cc \ + src/ui/mission/QGCMissionNavSweep.cc \ + src/ui/mission/QGCMissionDoStartSearch.cc \ + src/ui/mission/QGCMissionDoFinishSearch.cc \ + src/ui/QGCVehicleConfig.cc \ + src/ui/QGCHilConfiguration.cc \ + src/ui/QGCHilFlightGearConfiguration.cc \ + src/ui/QGCHilXPlaneConfiguration.cc + +# Enable Google Earth only on Mac OS and Windows with Visual Studio compiler +macx|macx-g++|macx-g++42|win32-msvc2008|win32-msvc2010::SOURCES += src/ui/map3D/QGCGoogleEarthView.cc + +# Enable OSG only if it has been found +contains(DEPENDENCIES_PRESENT, osg) { + message("Including sources for OpenSceneGraph") + + # Enable only if OpenSceneGraph is available + SOURCES += src/ui/map3D/gpl.cc \ + src/ui/map3D/CameraParams.cc \ + src/ui/map3D/ViewParamWidget.cc \ + src/ui/map3D/SystemContainer.cc \ + src/ui/map3D/SystemViewParams.cc \ + src/ui/map3D/GlobalViewParams.cc \ + src/ui/map3D/SystemGroupNode.cc \ + src/ui/map3D/Q3DWidget.cc \ + src/ui/map3D/ImageWindowGeode.cc \ + src/ui/map3D/GCManipulator.cc \ + src/ui/map3D/PixhawkCheetahNode.cc \ + src/ui/map3D/Pixhawk3DWidget.cc \ + src/ui/map3D/Q3DWidgetFactory.cc \ + src/ui/map3D/WebImageCache.cc \ + src/ui/map3D/WebImage.cc \ + src/ui/map3D/TextureCache.cc \ + src/ui/map3D/Texture.cc \ + src/ui/map3D/Imagery.cc \ + src/ui/map3D/HUDScaleGeode.cc \ + src/ui/map3D/WaypointGroupNode.cc \ + src/ui/map3D/TerrainParamDialog.cc \ + src/ui/map3D/ImageryParamDialog.cc + + contains(DEPENDENCIES_PRESENT, osgearth) { + message("Including sources for osgEarth") + + # Enable only if OpenSceneGraph is available + SOURCES += + } +} +contains(DEPENDENCIES_PRESENT, protobuf):contains(MAVLINK_CONF, pixhawk) { + message("Including sources for Protocol Buffers") + + # Enable only if protobuf is available + SOURCES += libs/mavlink/share/mavlink/src/v1.0/pixhawk/pixhawk.pb.cc \ + src/ui/map3D/ObstacleGroupNode.cc \ + src/ui/map3D/GLOverlayGeode.cc +} +contains(DEPENDENCIES_PRESENT, libfreenect) { + message("Including sources for libfreenect") + + # Enable only if libfreenect is available + SOURCES += src/input/Freenect.cc +} + +# Add icons and other resources +RESOURCES += qgroundcontrol.qrc + +# Include RT-LAB Library +win32:exists(src/lib/opalrt/OpalApi.h):exists(C:/OPAL-RT/RT-LAB7.2.4/Common/bin) { + message("Building support for Opal-RT") + LIBS += -LC:/OPAL-RT/RT-LAB7.2.4/Common/bin \ + -lOpalApi + INCLUDEPATH += src/lib/opalrt + HEADERS += src/comm/OpalRT.h \ + src/comm/OpalLink.h \ + src/comm/Parameter.h \ + src/comm/QGCParamID.h \ + src/comm/ParameterList.h \ + src/ui/OpalLinkConfigurationWindow.h + SOURCES += src/comm/OpalRT.cc \ + src/comm/OpalLink.cc \ + src/comm/Parameter.cc \ + src/comm/QGCParamID.cc \ + src/comm/ParameterList.cc \ + src/ui/OpalLinkConfigurationWindow.cc + FORMS += src/ui/OpalLinkSettings.ui + DEFINES += OPAL_RT +} +TRANSLATIONS += es-MX.ts \ + en-US.ts + +# xbee support +# libxbee only supported by linux and windows systems +win32-msvc2008|win32-msvc2010|linux { + HEADERS += src/comm/XbeeLinkInterface.h \ + src/comm/XbeeLink.h \ + src/comm/HexSpinBox.h \ + src/ui/XbeeConfigurationWindow.h \ + src/comm/CallConv.h + SOURCES += src/comm/XbeeLink.cpp \ + src/comm/HexSpinBox.cpp \ + src/ui/XbeeConfigurationWindow.cpp + DEFINES += XBEELINK + INCLUDEPATH += libs/thirdParty/libxbee +# TO DO: build library when it does not exist already + LIBS += -Llibs/thirdParty/libxbee/lib \ + -llibxbee +} + +#win32-msvc2008|win32-msvc2010 { + message("Including support for 3DxWare for Windows system.") + SOURCES += libs/thirdParty/3DMouse/win/MouseParameters.cpp \ + libs/thirdParty/3DMouse/win/Mouse3DInput.cpp \ + src/input/Mouse6dofInput.cpp + HEADERS += libs/thirdParty/3DMouse/win/I3dMouseParams.h \ + libs/thirdParty/3DMouse/win/MouseParameters.h \ + libs/thirdParty/3DMouse/win/Mouse3DInput.h \ + src/input/Mouse6dofInput.h + INCLUDEPATH += libs/thirdParty/3DMouse/win + DEFINES += MOUSE_ENABLED_WIN +#} diff --git a/src/input/Mouse6dofInput.cpp b/src/input/Mouse6dofInput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc5ed9ed4efcc0135a0222a2aee0ca75d32af810 --- /dev/null +++ b/src/input/Mouse6dofInput.cpp @@ -0,0 +1,119 @@ +/** + * @file + * @brief 3dConnexion 3dMouse interface for QGroundControl + * + * @author Matthias Krebs + * + */ + +#include "Mouse6dofInput.h" +#include "UAS.h" +#include "UASManager.h" + +Mouse6dofInput::Mouse6dofInput(Mouse3DInput* mouseInput) : + mouse3DMax(0.01), // TODO: check maximum value fot plugged device + uas(NULL), + done(false), + xValue(0.0), + yValue(0.0), + zValue(0.0), + aValue(0.0), + bValue(0.0), + cValue(0.0) +{ + connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*))); + // Connect 3DxWare SDK MotionEvent + connect(mouseInput, SIGNAL(Move3d(std::vector&)), this, SLOT(motion3DMouse(std::vector&))); + // TODO: Connect button mapping + //connect(mouseInput, SIGNAL(On3dmouseKeyDown(int)), this, SLOT); + //connect(mouseInput, SIGNAL(On3dmouseKeyUp(int)), this, SLOT); + +} + +Mouse6dofInput::~Mouse6dofInput() +{ + done = true; +} + +void Mouse6dofInput::setActiveUAS(UASInterface* uas) +{ + // Only connect / disconnect is the UAS is of a controllable UAS class + UAS* tmp = 0; + if (this->uas) + { + tmp = dynamic_cast(this->uas); + if(tmp) + { + disconnect(this, SIGNAL(mouse6dofChanged(double,double,double,double,double,double)), tmp, SLOT(setManual6DOFControlCommands(double,double,double,double,double,double))); + // Todo: disconnect button mapping + } + } + + this->uas = uas; + + tmp = dynamic_cast(this->uas); + if(tmp) { + connect(this, SIGNAL(mouse6dofChanged(double,double,double,double,double,double)), tmp, SLOT(setManual6DOFControlCommands(double,double,double,double,double,double))); + // Todo: disconnect button mapping + } + if (!isRunning()) + { + start(); + } +} + +void Mouse6dofInput::init() +{ + // Make sure active UAS is set + setActiveUAS(UASManager::instance()->getActiveUAS()); +} + +void Mouse6dofInput::run() +{ + init(); + + forever + { + if (done) + { + done = false; + exit(); + } + // Bound x value + if (xValue > 1.0) xValue = 1.0; + if (xValue < -1.0) xValue = -1.0; + // Bound x value + if (yValue > 1.0) yValue = 1.0; + if (yValue < -1.0) yValue = -1.0; + // Bound x value + if (zValue > 1.0) zValue = 1.0; + if (zValue < -1.0) zValue = -1.0; + // Bound x value + if (aValue > 1.0) aValue = 1.0; + if (aValue < -1.0) aValue = -1.0; + // Bound x value + if (bValue > 1.0) bValue = 1.0; + if (bValue < -1.0) bValue = -1.0; + // Bound x value + if (cValue > 1.0) cValue = 1.0; + if (cValue < -1.0) cValue = -1.0; + + emit mouse6dofChanged(xValue, yValue, zValue, aValue, bValue, cValue); + + // Sleep, update rate of 3d mouse is approx. 50 Hz (1000 ms / 50 = 20 ms) + QGC::SLEEP::msleep(20); + } +} + +void Mouse6dofInput::motion3DMouse(std::vector &motionData) +{ + if (motionData.size() < 6) return; + xValue = (double)1.0e2f*motionData[ 1 ] / mouse3DMax; + yValue = (double)1.0e2f*motionData[ 0 ] / mouse3DMax; + zValue = (double)1.0e2f*motionData[ 2 ] / mouse3DMax; + aValue = (double)1.0e2f*motionData[ 4 ] / mouse3DMax; + bValue = (double)1.0e2f*motionData[ 3 ] / mouse3DMax; + cValue = (double)1.0e2f*motionData[ 5 ] / mouse3DMax; + + qDebug() << "NEW 3D MOUSE VALUES -- X" << xValue << " -- Y" << yValue << " -- Z" << zValue << " -- A" << aValue << " -- B" << bValue << " -- C" << cValue; +} diff --git a/src/input/Mouse6dofInput.h b/src/input/Mouse6dofInput.h new file mode 100644 index 0000000000000000000000000000000000000000..a765a31b18397c53cf1576e4c6823c4e3dc8dda4 --- /dev/null +++ b/src/input/Mouse6dofInput.h @@ -0,0 +1,61 @@ +/** + * @file + * @brief Definition of 3dConnexion 3dMouse interface for QGroundControl + * + * @author Matthias Krebs + * + */ + +#ifndef MOUSE6DOFINPUT_H +#define MOUSE6DOFINPUT_H + +#include +#include "Mouse3DInput.h" + +#include "UASInterface.h" + +class Mouse6dofInput : public QThread +{ + Q_OBJECT + +public: + Mouse6dofInput(Mouse3DInput* mouseInput); + ~Mouse6dofInput(); + void run(); + + const double mouse3DMax; + +protected: + void init(); + + Mouse3DInput* mouse3D; + UASInterface* uas; + bool done; + + double xValue; + double yValue; + double zValue; + double aValue; + double bValue; + double cValue; + +signals: + /** + * @brief Input of the 3d mouse has changed + * + * @param x Input x direction, range [-1, 1] + * @param y Input y direction, range [-1, 1] + * @param z Input z direction, range [-1, 1] + * @param a Input x rotation, range [-1, 1] + * @param b Input y rotation, range [-1, 1] + * @param c Input z rotation, range [-1, 1] + */ + void mouse6dofChanged(double x, double y, double z, double a, double b, double c); + +public slots: + void setActiveUAS(UASInterface* uas); + void motion3DMouse(std::vector &motionData); + +}; + +#endif // MOUSE6DOFINPUT_H diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 124c4f0260d3a9996d8df125df8b02d9e7bf0620..95aff3d615c2784848ed05b16bef3c5cf360dcd2 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -1,3066 +1,3071 @@ -/*=================================================================== -======================================================================*/ - -/** - * @file - * @brief Represents one unmanned aerial vehicle - * - * @author Lorenz Meier - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "UAS.h" -#include "LinkInterface.h" -#include "UASManager.h" -#include "QGC.h" -#include "GAudioOutput.h" -#include "MAVLinkProtocol.h" -#include "QGCMAVLink.h" -#include "LinkManager.h" -#include "SerialLink.h" - -#ifdef QGC_PROTOBUF_ENABLED -#include -#endif - -/** -* Gets the settings from the previous UAS (name, airframe, autopilot, battery specs) -* by calling readSettings. This means the new UAS will have the same settings -* as the previous one created unless one calls deleteSettings in the code after -* creating the UAS. -*/ -UAS::UAS(MAVLinkProtocol* protocol, int id) : UASInterface(), - uasId(id), - startTime(QGC::groundTimeMilliseconds()), - commStatus(COMM_DISCONNECTED), - name(""), - autopilot(-1), - links(new QList()), - unknownPackets(), - mavlink(protocol), - waypointManager(this), - thrustSum(0), - thrustMax(10), - startVoltage(-1.0f), - tickVoltage(10.5f), - lastTickVoltageValue(13.0f), - tickLowpassVoltage(12.0f), - warnVoltage(9.5f), - warnLevelPercent(20.0f), - currentVoltage(12.6f), - lpVoltage(12.0f), - batteryRemainingEstimateEnabled(true), - mode(-1), - status(-1), - navMode(-1), - onboardTimeOffset(0), - controlRollManual(true), - controlPitchManual(true), - controlYawManual(true), - controlThrustManual(true), - manualRollAngle(0), - manualPitchAngle(0), - manualYawAngle(0), - manualThrust(0), - receiveDropRate(0), - sendDropRate(0), - lowBattAlarm(false), - positionLock(false), - localX(0.0), - localY(0.0), - localZ(0.0), - latitude(0.0), - longitude(0.0), - altitude(0.0), - roll(0.0), - pitch(0.0), - yaw(0.0), - statusTimeout(new QTimer(this)), - #if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) - receivedOverlayTimestamp(0.0), - receivedObstacleListTimestamp(0.0), - receivedPathTimestamp(0.0), - receivedPointCloudTimestamp(0.0), - receivedRGBDImageTimestamp(0.0), - #endif - paramsOnceRequested(false), - airframe(QGC_AIRFRAME_GENERIC), - attitudeKnown(false), - paramManager(NULL), - attitudeStamped(false), - lastAttitude(0), - simulation(0), - isLocalPositionKnown(false), - isGlobalPositionKnown(false), - systemIsArmed(false), - nedPosGlobalOffset(0,0,0), - nedAttGlobalOffset(0,0,0), - connectionLost(false), - lastVoltageWarning(0), - lastNonNullTime(0), - onboardTimeOffsetInvalidCount(0), - hilEnabled(false) - -{ - for (unsigned int i = 0; i<255;++i) - { - componentID[i] = -1; - componentMulti[i] = false; - } - - color = UASInterface::getNextColor(); - setBatterySpecs(QString("9V,9.5V,12.6V")); - connect(statusTimeout, SIGNAL(timeout()), this, SLOT(updateState())); - connect(this, SIGNAL(systemSpecsChanged(int)), this, SLOT(writeSettings())); - statusTimeout->start(500); - readSettings(); - type = MAV_TYPE_GENERIC; - // Initial signals - emit disarmed(); - emit armingChanged(false); -} - -/** -* Saves the settings of name, airframe, autopilot type and battery specifications -* by calling writeSettings. -*/ -UAS::~UAS() -{ - writeSettings(); - delete links; - delete statusTimeout; - delete simulation; -} - -/** -* Saves the settings of name, airframe, autopilot type and battery specifications -* for the next instantiation of UAS. -*/ -void UAS::writeSettings() -{ - QSettings settings; - settings.beginGroup(QString("MAV%1").arg(uasId)); - settings.setValue("NAME", this->name); - settings.setValue("AIRFRAME", this->airframe); - settings.setValue("AP_TYPE", this->autopilot); - settings.setValue("BATTERY_SPECS", getBatterySpecs()); - settings.endGroup(); - settings.sync(); -} - -/** -* Reads in the settings: name, airframe, autopilot type, and battery specifications -* for the new UAS. -*/ -void UAS::readSettings() -{ - QSettings settings; - settings.beginGroup(QString("MAV%1").arg(uasId)); - this->name = settings.value("NAME", this->name).toString(); - this->airframe = settings.value("AIRFRAME", this->airframe).toInt(); - this->autopilot = settings.value("AP_TYPE", this->autopilot).toInt(); - if (settings.contains("BATTERY_SPECS")) - { - setBatterySpecs(settings.value("BATTERY_SPECS").toString()); - } - settings.endGroup(); -} - -/** -* Deletes the settings origianally read into the UAS by readSettings. -* This is in case one does not want the old values but would rather -* start with the values assigned by the constructor. -*/ -void UAS::deleteSettings() -{ - this->name = ""; - this->airframe = QGC_AIRFRAME_GENERIC; - this->autopilot = -1; - setBatterySpecs(QString("9V,9.5V,12.6V")); -} - -/** -* @ return the id of the uas -*/ -int UAS::getUASID() const -{ - return uasId; -} - -/** -* Update the heartbeat. -*/ -void UAS::updateState() -{ - // Check if heartbeat timed out - quint64 heartbeatInterval = QGC::groundTimeUsecs() - lastHeartbeat; - if (!connectionLost && (heartbeatInterval > timeoutIntervalHeartbeat)) - { - connectionLost = true; - QString audiostring = QString("Link lost to system %1").arg(this->getUASID()); - GAudioOutput::instance()->say(audiostring.toLower()); - } - - // Update connection loss time on each iteration - if (connectionLost && (heartbeatInterval > timeoutIntervalHeartbeat)) - { - connectionLossTime = heartbeatInterval; - emit heartbeatTimeout(true, heartbeatInterval/1000); - } - - // Connection gained - if (connectionLost && (heartbeatInterval < timeoutIntervalHeartbeat)) - { - QString audiostring = QString("Link regained to system %1 after %2 seconds").arg(this->getUASID()).arg((int)(connectionLossTime/1000000)); - GAudioOutput::instance()->say(audiostring.toLower()); - connectionLost = false; - connectionLossTime = 0; - emit heartbeatTimeout(false, 0); - } - - // Position lock is set by the MAVLink message handler - // if no position lock is available, indicate an error - if (positionLock) - { - positionLock = false; - } - else - { - if (((mode&MAV_MODE_FLAG_DECODE_POSITION_AUTO) || (mode&MAV_MODE_FLAG_DECODE_POSITION_GUIDED)) && positionLock) - { - GAudioOutput::instance()->notifyNegative(); - } - } - -//#define MAVLINK_OFFBOARD_CONTROL_MODE_NONE 0 -//#define MAVLINK_OFFBOARD_CONTROL_MODE_RATES 1 -//#define MAVLINK_OFFBOARD_CONTROL_MODE_ATTITUDE 2 -//#define MAVLINK_OFFBOARD_CONTROL_MODE_VELOCITY 3 -//#define MAVLINK_OFFBOARD_CONTROL_MODE_POSITION 4 -//#define MAVLINK_OFFBOARD_CONTROL_FLAG_ARMED 0x10 - -//#warning THIS IS A HUGE HACK AND SHOULD NEVER SHOW UP IN ANY GIT REPOSITORY -// mavlink_message_t message; - -// mavlink_set_quad_swarm_roll_pitch_yaw_thrust_t sp; - -// sp.group = 0; - -// /* set rate mode, set zero rates and 20% throttle */ -// sp.mode = MAVLINK_OFFBOARD_CONTROL_MODE_RATES | MAVLINK_OFFBOARD_CONTROL_FLAG_ARMED; - -// sp.roll[0] = INT16_MAX * 0.0f; -// sp.pitch[0] = INT16_MAX * 0.0f; -// sp.yaw[0] = INT16_MAX * 0.0f; -// sp.thrust[0] = UINT16_MAX * 0.3f; - - -// /* send from system 200 and component 0 */ -// mavlink_msg_set_quad_swarm_roll_pitch_yaw_thrust_encode(200, 0, &message, &sp); - -// sendMessage(message); -} - -/** -* If the acitve UAS (the UAS that was selected) is not the one that is currently -* active, then change the active UAS to the one that was selected. -*/ -void UAS::setSelected() -{ - if (UASManager::instance()->getActiveUAS() != this) - { - UASManager::instance()->setActiveUAS(this); - emit systemSelected(true); - } -} - -/** -* @return if the active UAS is the current UAS -**/ -bool UAS::getSelected() const -{ - return (UASManager::instance()->getActiveUAS() == this); -} - -void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) -{ - if (!link) return; - if (!links->contains(link)) - { - addLink(link); - // qDebug() << __FILE__ << __LINE__ << "ADDED LINK!" << link->getName(); - } - - if (!components.contains(message.compid)) - { - QString componentName; - - switch (message.compid) - { - case MAV_COMP_ID_ALL: - { - componentName = "ANONYMOUS"; - break; - } - case MAV_COMP_ID_IMU: - { - componentName = "IMU #1"; - break; - } - case MAV_COMP_ID_CAMERA: - { - componentName = "CAMERA"; - break; - } - case MAV_COMP_ID_MISSIONPLANNER: - { - componentName = "MISSIONPLANNER"; - break; - } - } - - components.insert(message.compid, componentName); - emit componentCreated(uasId, message.compid, componentName); - } - - // qDebug() << "UAS RECEIVED from" << message.sysid << "component" << message.compid << "msg id" << message.msgid << "seq no" << message.seq; - - // Only accept messages from this system (condition 1) - // and only then if a) attitudeStamped is disabled OR b) attitudeStamped is enabled - // and we already got one attitude packet - if (message.sysid == uasId && (!attitudeStamped || (attitudeStamped && (lastAttitude != 0)) || message.msgid == MAVLINK_MSG_ID_ATTITUDE)) - { - QString uasState; - QString stateDescription; - - bool multiComponentSourceDetected = false; - bool wrongComponent = false; - - switch (message.compid) - { - case MAV_COMP_ID_IMU_2: - // Prefer IMU 2 over IMU 1 (FIXME) - componentID[message.msgid] = MAV_COMP_ID_IMU_2; - break; - default: - // Do nothing - break; - } - - // Store component ID - if (componentID[message.msgid] == -1) - { - // Prefer the first component - componentID[message.msgid] = message.compid; - } - else - { - // Got this message already - if (componentID[message.msgid] != message.compid) - { - componentMulti[message.msgid] = true; - wrongComponent = true; - } - } - - if (componentMulti[message.msgid] == true) multiComponentSourceDetected = true; - - - switch (message.msgid) - { - case MAVLINK_MSG_ID_HEARTBEAT: - { - if (multiComponentSourceDetected && wrongComponent) - { - break; - } - lastHeartbeat = QGC::groundTimeUsecs(); - emit heartbeat(this); - mavlink_heartbeat_t state; - mavlink_msg_heartbeat_decode(&message, &state); - - // Send the base_mode and system_status values to the plotter. This uses the ground time - // so the Ground Time checkbox must be ticked for these values to display - quint64 time = getUnixTime(); - QString name = QString("M%1:HEARTBEAT.%2").arg(message.sysid); - emit valueChanged(uasId, name.arg("base_mode"), "bits", state.base_mode, time); - emit valueChanged(uasId, name.arg("custom_mode"), "bits", state.custom_mode, time); - emit valueChanged(uasId, name.arg("system_status"), "-", state.system_status, time); - - // Set new type if it has changed - if (this->type != state.type) - { - this->type = state.type; - if (airframe == 0) - { - switch (type) - { - case MAV_TYPE_FIXED_WING: - setAirframe(UASInterface::QGC_AIRFRAME_EASYSTAR); - break; - case MAV_TYPE_QUADROTOR: - setAirframe(UASInterface::QGC_AIRFRAME_CHEETAH); - break; - case MAV_TYPE_HEXAROTOR: - setAirframe(UASInterface::QGC_AIRFRAME_HEXCOPTER); - break; - default: - // Do nothing - break; - } - } - this->autopilot = state.autopilot; - emit systemTypeSet(this, type); - } - - bool currentlyArmed = state.base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY; - - if (systemIsArmed != currentlyArmed) - { - systemIsArmed = currentlyArmed; - emit armingChanged(systemIsArmed); - if (systemIsArmed) - { - emit armed(); - } - else - { - emit disarmed(); - } - } - - QString audiostring = QString("System %1").arg(uasId); - QString stateAudio = ""; - QString modeAudio = ""; - QString navModeAudio = ""; - bool statechanged = false; - bool modechanged = false; - - QString audiomodeText = getAudioModeTextFor(static_cast(state.base_mode)); - - - if ((state.system_status != this->status) && state.system_status != MAV_STATE_UNINIT) - { - statechanged = true; - this->status = state.system_status; - getStatusForCode((int)state.system_status, uasState, stateDescription); - emit statusChanged(this, uasState, stateDescription); - emit statusChanged(this->status); - - shortStateText = uasState; - - // Adjust for better audio - if (uasState == QString("STANDBY")) uasState = QString("standing by"); - if (uasState == QString("EMERGENCY")) uasState = QString("emergency condition"); - if (uasState == QString("CRITICAL")) uasState = QString("critical condition"); - if (uasState == QString("SHUTDOWN")) uasState = QString("shutting down"); - - stateAudio = uasState; - } - - if (this->mode != static_cast(state.base_mode)) - { - modechanged = true; - this->mode = static_cast(state.base_mode); - shortModeText = getShortModeTextFor(this->mode); - - emit modeChanged(this->getUASID(), shortModeText, ""); - - modeAudio = " is now in " + audiomodeText; - } - - if (navMode != state.custom_mode) - { - emit navModeChanged(uasId, state.custom_mode, getNavModeText(state.custom_mode)); - navMode = state.custom_mode; - //navModeAudio = tr(" changed nav mode to ") + tr("FIXME"); - } - - // AUDIO - if (modechanged && statechanged) - { - // Output both messages - audiostring += modeAudio + " and " + stateAudio; - } - else if (modechanged || statechanged) - { - // Output the one message - audiostring += modeAudio + stateAudio + navModeAudio; - } - - if (statechanged && ((int)state.system_status == (int)MAV_STATE_CRITICAL || state.system_status == (int)MAV_STATE_EMERGENCY)) - { - GAudioOutput::instance()->say(QString("emergency for system %1").arg(this->getUASID())); - QTimer::singleShot(3000, GAudioOutput::instance(), SLOT(startEmergency())); - } - else if (modechanged || statechanged) - { - GAudioOutput::instance()->stopEmergency(); - GAudioOutput::instance()->say(audiostring.toLower()); - } - } - - break; - case MAVLINK_MSG_ID_SYS_STATUS: - { - if (multiComponentSourceDetected && wrongComponent) - { - break; - } - mavlink_sys_status_t state; - mavlink_msg_sys_status_decode(&message, &state); - - // Prepare for sending data to the realtime plotter, which is every field excluding onboard_control_sensors_present. - quint64 time = getUnixTime(); - QString name = QString("M%1:SYS_STATUS.%2").arg(message.sysid); - emit valueChanged(uasId, name.arg("sensors_enabled"), "bits", state.onboard_control_sensors_enabled, time); - emit valueChanged(uasId, name.arg("sensors_health"), "bits", state.onboard_control_sensors_health, time); - emit valueChanged(uasId, name.arg("errors_comm"), "-", state.errors_comm, time); - emit valueChanged(uasId, name.arg("errors_count1"), "-", state.errors_count1, time); - emit valueChanged(uasId, name.arg("errors_count2"), "-", state.errors_count2, time); - emit valueChanged(uasId, name.arg("errors_count3"), "-", state.errors_count3, time); - emit valueChanged(uasId, name.arg("errors_count4"), "-", state.errors_count4, time); - - // Process CPU load. - emit loadChanged(this,state.load/10.0f); - emit valueChanged(uasId, name.arg("load"), "%", state.load/10.0f, time); - - // Battery charge/time remaining/voltage calculations - currentVoltage = state.voltage_battery/1000.0f; - lpVoltage = filterVoltage(currentVoltage); - tickLowpassVoltage = tickLowpassVoltage*0.8f + 0.2f*currentVoltage; - - - // We don't want to tick above the threshold - if (tickLowpassVoltage > tickVoltage) - { - lastTickVoltageValue = tickLowpassVoltage; - } - - if ((startVoltage > 0.0f) && (tickLowpassVoltage < tickVoltage) && (fabs(lastTickVoltageValue - tickLowpassVoltage) > 0.1f) - /* warn if lower than treshold */ - && (lpVoltage < tickVoltage) - /* warn only if we have at least the voltage of an empty LiPo cell, else we're sampling something wrong */ - && (currentVoltage > 3.3f) - /* warn only if current voltage is really still lower by a reasonable amount */ - && ((currentVoltage - 0.2f) < tickVoltage) - /* warn only every 12 seconds */ - && (QGC::groundTimeUsecs() - lastVoltageWarning) > 12000000) - { - GAudioOutput::instance()->say(QString("voltage warning: %1 volts").arg(lpVoltage, 0, 'f', 1, QChar(' '))); - lastVoltageWarning = QGC::groundTimeUsecs(); - lastTickVoltageValue = tickLowpassVoltage; - } - - if (startVoltage == -1.0f && currentVoltage > 0.1f) startVoltage = currentVoltage; - timeRemaining = calculateTimeRemaining(); - if (!batteryRemainingEstimateEnabled && chargeLevel != -1) - { - chargeLevel = state.battery_remaining; - } - emit batteryChanged(this, lpVoltage, getChargeLevel(), timeRemaining); - emit valueChanged(uasId, name.arg("battery_remaining"), "%", getChargeLevel(), time); - emit voltageChanged(message.sysid, currentVoltage); - emit valueChanged(uasId, name.arg("battery_voltage"), "V", currentVoltage, time); - - // And if the battery current draw is measured, log that also. - if (state.current_battery != -1) - { - emit valueChanged(uasId, name.arg("battery_current"), "A", ((double)state.current_battery) / 100.0f, time); - } - - // LOW BATTERY ALARM - if (lpVoltage < warnVoltage && (currentVoltage - 0.2f) < warnVoltage && (currentVoltage > 3.3)) - { - startLowBattAlarm(); - } - else - { - stopLowBattAlarm(); - } - - // control_sensors_enabled: - // relevant bits: 11: attitude stabilization, 12: yaw position, 13: z/altitude control, 14: x/y position control - emit attitudeControlEnabled(state.onboard_control_sensors_enabled & (1 << 11)); - emit positionYawControlEnabled(state.onboard_control_sensors_enabled & (1 << 12)); - emit positionZControlEnabled(state.onboard_control_sensors_enabled & (1 << 13)); - emit positionXYControlEnabled(state.onboard_control_sensors_enabled & (1 << 14)); - - // Trigger drop rate updates as needed. Here we convert the incoming - // drop_rate_comm value from 1/100 of a percent in a uint16 to a true - // percentage as a float. We also cap the incoming value at 100% as defined - // by the MAVLink specifications. - if (state.drop_rate_comm > 10000) - { - state.drop_rate_comm = 10000; - } - emit dropRateChanged(this->getUASID(), state.drop_rate_comm/100.0f); - emit valueChanged(uasId, name.arg("drop_rate_comm"), "%", state.drop_rate_comm/100.0f, time); - } - break; - case MAVLINK_MSG_ID_ATTITUDE: - { - mavlink_attitude_t attitude; - mavlink_msg_attitude_decode(&message, &attitude); - quint64 time = getUnixReferenceTime(attitude.time_boot_ms); - - emit attitudeChanged(this, message.compid, QGC::limitAngleToPMPIf(attitude.roll), QGC::limitAngleToPMPIf(attitude.pitch), QGC::limitAngleToPMPIf(attitude.yaw), time); - - if (!wrongComponent) - { - lastAttitude = time; - roll = QGC::limitAngleToPMPIf(attitude.roll); - pitch = QGC::limitAngleToPMPIf(attitude.pitch); - yaw = QGC::limitAngleToPMPIf(attitude.yaw); - - // // Emit in angles - - // // Convert yaw angle to compass value - // // in 0 - 360 deg range - // float compass = (yaw/M_PI)*180.0+360.0f; - // if (compass > -10000 && compass < 10000) - // { - // while (compass > 360.0f) { - // compass -= 360.0f; - // } - // } - // else - // { - // // Set to 0, since it is an invalid value - // compass = 0.0f; - // } - - attitudeKnown = true; - emit attitudeChanged(this, roll, pitch, yaw, time); - emit attitudeSpeedChanged(uasId, attitude.rollspeed, attitude.pitchspeed, attitude.yawspeed, time); - } - } - break; - case MAVLINK_MSG_ID_LOCAL_POSITION_NED_SYSTEM_GLOBAL_OFFSET: - { - mavlink_local_position_ned_system_global_offset_t offset; - mavlink_msg_local_position_ned_system_global_offset_decode(&message, &offset); - nedPosGlobalOffset.setX(offset.x); - nedPosGlobalOffset.setY(offset.y); - nedPosGlobalOffset.setZ(offset.z); - nedAttGlobalOffset.setX(offset.roll); - nedAttGlobalOffset.setY(offset.pitch); - nedAttGlobalOffset.setZ(offset.yaw); - } - break; - case MAVLINK_MSG_ID_HIL_CONTROLS: - { - mavlink_hil_controls_t hil; - mavlink_msg_hil_controls_decode(&message, &hil); - emit hilControlsChanged(hil.time_usec, hil.roll_ailerons, hil.pitch_elevator, hil.yaw_rudder, hil.throttle, hil.mode, hil.nav_mode); - } - break; - case MAVLINK_MSG_ID_VFR_HUD: - { - mavlink_vfr_hud_t hud; - mavlink_msg_vfr_hud_decode(&message, &hud); - quint64 time = getUnixTime(); - // Display updated values - emit thrustChanged(this, hud.throttle/100.0); - - if (!attitudeKnown) - { - yaw = QGC::limitAngleToPMPId((((double)hud.heading-180.0)/360.0)*M_PI); - emit attitudeChanged(this, roll, pitch, yaw, time); - } - - emit altitudeChanged(uasId, hud.alt); - emit speedChanged(this, hud.airspeed, 0.0f, hud.climb, time); - } - break; - case MAVLINK_MSG_ID_LOCAL_POSITION_NED: - //std::cerr << std::endl; - //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; - { - mavlink_local_position_ned_t pos; - mavlink_msg_local_position_ned_decode(&message, &pos); - quint64 time = getUnixTime(pos.time_boot_ms); - - // Emit position always with component ID - emit localPositionChanged(this, message.compid, pos.x, pos.y, pos.z, time); - - - if (!wrongComponent) - { - localX = pos.x; - localY = pos.y; - localZ = pos.z; - - // Emit - - emit localPositionChanged(this, pos.x, pos.y, pos.z, time); - emit speedChanged(this, pos.vx, pos.vy, pos.vz, time); - - // Set internal state - if (!positionLock) { - // If position was not locked before, notify positive - GAudioOutput::instance()->notifyPositive(); - } - positionLock = true; - isLocalPositionKnown = true; - } - } - break; - case MAVLINK_MSG_ID_GLOBAL_VISION_POSITION_ESTIMATE: - { - mavlink_global_vision_position_estimate_t pos; - mavlink_msg_global_vision_position_estimate_decode(&message, &pos); - quint64 time = getUnixTime(pos.usec); - emit localPositionChanged(this, message.compid, pos.x, pos.y, pos.z, time); - emit attitudeChanged(this, message.compid, pos.roll, pos.pitch, pos.yaw, time); - } - break; - case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: - //std::cerr << std::endl; - //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; - { - mavlink_global_position_int_t pos; - mavlink_msg_global_position_int_decode(&message, &pos); - quint64 time = getUnixTime(); - latitude = pos.lat/(double)1E7; - longitude = pos.lon/(double)1E7; - altitude = pos.alt/1000.0; - speedX = pos.vx/100.0; - speedY = pos.vy/100.0; - speedZ = pos.vz/100.0; - emit globalPositionChanged(this, latitude, longitude, altitude, time); - emit speedChanged(this, speedX, speedY, speedZ, time); - - // Set internal state - if (!positionLock) - { - // If position was not locked before, notify positive - GAudioOutput::instance()->notifyPositive(); - } - positionLock = true; - isGlobalPositionKnown = true; - //TODO fix this hack for forwarding of global position for patch antenna tracking - forwardMessage(message); - } - break; - case MAVLINK_MSG_ID_GPS_RAW_INT: - { - mavlink_gps_raw_int_t pos; - mavlink_msg_gps_raw_int_decode(&message, &pos); - - // SANITY CHECK - // only accept values in a realistic range - // quint64 time = getUnixTime(pos.time_usec); - quint64 time = getUnixTime(pos.time_usec); - - emit gpsLocalizationChanged(this, pos.fix_type); - // TODO: track localization state not only for gps but also for other loc. sources - int loc_type = pos.fix_type; - if (loc_type == 1) - { - loc_type = 0; - } - emit localizationChanged(this, loc_type); - - if (pos.fix_type > 2) - { - emit globalPositionChanged(this, pos.lat/(double)1E7, pos.lon/(double)1E7, pos.alt/1000.0, time); - latitude = pos.lat/(double)1E7; - longitude = pos.lon/(double)1E7; - altitude = pos.alt/1000.0; - positionLock = true; - isGlobalPositionKnown = true; - - // Check for NaN - int alt = pos.alt; - if (!isnan(alt) && !isinf(alt)) - { - alt = 0; - //emit textMessageReceived(uasId, message.compid, 255, "GCS ERROR: RECEIVED NaN or Inf FOR ALTITUDE"); - } - // FIXME REMOVE LATER emit valueChanged(uasId, "altitude", "m", pos.alt/(double)1E3, time); - // Smaller than threshold and not NaN - - float vel = pos.vel/100.0f; - - if (vel < 1000000 && !isnan(vel) && !isinf(vel)) - { - // FIXME REMOVE LATER emit valueChanged(uasId, "speed", "m/s", vel, time); - //qDebug() << "GOT GPS RAW"; - // emit speedChanged(this, (double)pos.v, 0.0, 0.0, time); - } - else - { - emit textMessageReceived(uasId, message.compid, 255, QString("GCS ERROR: RECEIVED INVALID SPEED OF %1 m/s").arg(vel)); - } - } - } - break; - case MAVLINK_MSG_ID_GPS_STATUS: - { - mavlink_gps_status_t pos; - mavlink_msg_gps_status_decode(&message, &pos); - for(int i = 0; i < (int)pos.satellites_visible; i++) - { - emit gpsSatelliteStatusChanged(uasId, (unsigned char)pos.satellite_prn[i], (unsigned char)pos.satellite_elevation[i], (unsigned char)pos.satellite_azimuth[i], (unsigned char)pos.satellite_snr[i], static_cast(pos.satellite_used[i])); - } - } - break; - case MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN: - { - mavlink_gps_global_origin_t pos; - mavlink_msg_gps_global_origin_decode(&message, &pos); - emit homePositionChanged(uasId, pos.latitude / 10000000.0, pos.longitude / 10000000.0, pos.altitude / 1000.0); - } - break; - case MAVLINK_MSG_ID_RC_CHANNELS_RAW: - { - mavlink_rc_channels_raw_t channels; - mavlink_msg_rc_channels_raw_decode(&message, &channels); - emit remoteControlRSSIChanged(channels.rssi/255.0f); - emit remoteControlChannelRawChanged(0, channels.chan1_raw); - emit remoteControlChannelRawChanged(1, channels.chan2_raw); - emit remoteControlChannelRawChanged(2, channels.chan3_raw); - emit remoteControlChannelRawChanged(3, channels.chan4_raw); - emit remoteControlChannelRawChanged(4, channels.chan5_raw); - emit remoteControlChannelRawChanged(5, channels.chan6_raw); - emit remoteControlChannelRawChanged(6, channels.chan7_raw); - emit remoteControlChannelRawChanged(7, channels.chan8_raw); - } - break; - case MAVLINK_MSG_ID_RC_CHANNELS_SCALED: - { - mavlink_rc_channels_scaled_t channels; - mavlink_msg_rc_channels_scaled_decode(&message, &channels); - emit remoteControlRSSIChanged(channels.rssi/255.0f); - emit remoteControlChannelScaledChanged(0, channels.chan1_scaled/10000.0f); - emit remoteControlChannelScaledChanged(1, channels.chan2_scaled/10000.0f); - emit remoteControlChannelScaledChanged(2, channels.chan3_scaled/10000.0f); - emit remoteControlChannelScaledChanged(3, channels.chan4_scaled/10000.0f); - emit remoteControlChannelScaledChanged(4, channels.chan5_scaled/10000.0f); - emit remoteControlChannelScaledChanged(5, channels.chan6_scaled/10000.0f); - emit remoteControlChannelScaledChanged(6, channels.chan7_scaled/10000.0f); - emit remoteControlChannelScaledChanged(7, channels.chan8_scaled/10000.0f); - } - break; - case MAVLINK_MSG_ID_PARAM_VALUE: - { - mavlink_param_value_t value; - mavlink_msg_param_value_decode(&message, &value); - QByteArray bytes(value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN); - // Construct a string stopping at the first NUL (0) character, else copy the whole - // byte array (max MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN, so safe) - QString parameterName(bytes); - int component = message.compid; - mavlink_param_union_t val; - val.param_float = value.param_value; - val.type = value.param_type; - - // Insert component if necessary - if (!parameters.contains(component)) - { - parameters.insert(component, new QMap()); - } - - // Insert parameter into registry - if (parameters.value(component)->contains(parameterName)) parameters.value(component)->remove(parameterName); - - // Insert with correct type - switch (value.param_type) - { - case MAV_PARAM_TYPE_REAL32: - { - // Variant - QVariant param(val.param_float); - parameters.value(component)->insert(parameterName, param); - // Emit change - emit parameterChanged(uasId, message.compid, parameterName, param); - emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); -// qDebug() << "RECEIVED PARAM:" << param; - } - break; - case MAV_PARAM_TYPE_UINT32: - { - // Variant - QVariant param(val.param_uint32); - parameters.value(component)->insert(parameterName, param); - // Emit change - emit parameterChanged(uasId, message.compid, parameterName, param); - emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); -// qDebug() << "RECEIVED PARAM:" << param; - } - break; - case MAV_PARAM_TYPE_INT32: - { - // Variant - QVariant param(val.param_int32); - parameters.value(component)->insert(parameterName, param); - // Emit change - emit parameterChanged(uasId, message.compid, parameterName, param); - emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); -// qDebug() << "RECEIVED PARAM:" << param; - } - break; - default: - qCritical() << "INVALID DATA TYPE USED AS PARAMETER VALUE: " << value.param_type; - } - } - break; - case MAVLINK_MSG_ID_COMMAND_ACK: - { - mavlink_command_ack_t ack; - mavlink_msg_command_ack_decode(&message, &ack); - switch (ack.result) - { - case MAV_RESULT_ACCEPTED: - { - emit textMessageReceived(uasId, message.compid, 0, tr("SUCCESS: Executed CMD: %1").arg(ack.command)); - } - break; - case MAV_RESULT_TEMPORARILY_REJECTED: - { - emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Temporarily rejected CMD: %1").arg(ack.command)); - } - break; - case MAV_RESULT_DENIED: - { - emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Denied CMD: %1").arg(ack.command)); - } - break; - case MAV_RESULT_UNSUPPORTED: - { - emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Unsupported CMD: %1").arg(ack.command)); - } - break; - case MAV_RESULT_FAILED: - { - emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Failed CMD: %1").arg(ack.command)); - } - break; - } - } - case MAVLINK_MSG_ID_ROLL_PITCH_YAW_THRUST_SETPOINT: - { - mavlink_roll_pitch_yaw_thrust_setpoint_t out; - mavlink_msg_roll_pitch_yaw_thrust_setpoint_decode(&message, &out); - quint64 time = getUnixTimeFromMs(out.time_boot_ms); - emit attitudeThrustSetPointChanged(this, out.roll, out.pitch, out.yaw, out.thrust, time); - } - break; - case MAVLINK_MSG_ID_MISSION_COUNT: - { - mavlink_mission_count_t wpc; - mavlink_msg_mission_count_decode(&message, &wpc); - if(wpc.target_system == mavlink->getSystemId() || wpc.target_system == 0) - { - waypointManager.handleWaypointCount(message.sysid, message.compid, wpc.count); - } - else - { - qDebug() << "Got waypoint message, but was wrong system id" << wpc.target_system; - } - } - break; - - case MAVLINK_MSG_ID_MISSION_ITEM: - { - mavlink_mission_item_t wp; - mavlink_msg_mission_item_decode(&message, &wp); - //qDebug() << "got waypoint (" << wp.seq << ") from ID " << message.sysid << " x=" << wp.x << " y=" << wp.y << " z=" << wp.z; - if(wp.target_system == mavlink->getSystemId() || wp.target_system == 0) - { - waypointManager.handleWaypoint(message.sysid, message.compid, &wp); - } - else - { - qDebug() << "Got waypoint message, but was wrong system id" << wp.target_system; - } - } - break; - - case MAVLINK_MSG_ID_MISSION_ACK: - { - mavlink_mission_ack_t wpa; - mavlink_msg_mission_ack_decode(&message, &wpa); - if((wpa.target_system == mavlink->getSystemId() || wpa.target_system == 0) && - (wpa.target_component == mavlink->getComponentId() || wpa.target_component == 0)) - { - waypointManager.handleWaypointAck(message.sysid, message.compid, &wpa); - } - } - break; - - case MAVLINK_MSG_ID_MISSION_REQUEST: - { - mavlink_mission_request_t wpr; - mavlink_msg_mission_request_decode(&message, &wpr); - if(wpr.target_system == mavlink->getSystemId() || wpr.target_system == 0) - { - waypointManager.handleWaypointRequest(message.sysid, message.compid, &wpr); - } - else - { - qDebug() << "Got waypoint message, but was wrong system id" << wpr.target_system; - } - } - break; - - case MAVLINK_MSG_ID_MISSION_ITEM_REACHED: - { - mavlink_mission_item_reached_t wpr; - mavlink_msg_mission_item_reached_decode(&message, &wpr); - waypointManager.handleWaypointReached(message.sysid, message.compid, &wpr); - QString text = QString("System %1 reached waypoint %2").arg(getUASName()).arg(wpr.seq); - GAudioOutput::instance()->say(text); - emit textMessageReceived(message.sysid, message.compid, 0, text); - } - break; - - case MAVLINK_MSG_ID_MISSION_CURRENT: - { - mavlink_mission_current_t wpc; - mavlink_msg_mission_current_decode(&message, &wpc); - waypointManager.handleWaypointCurrent(message.sysid, message.compid, &wpc); - } - break; - - case MAVLINK_MSG_ID_LOCAL_POSITION_SETPOINT: - { - if (multiComponentSourceDetected && wrongComponent) - { - break; - } - mavlink_local_position_setpoint_t p; - mavlink_msg_local_position_setpoint_decode(&message, &p); - emit positionSetPointsChanged(uasId, p.x, p.y, p.z, p.yaw, QGC::groundTimeUsecs()); - } - break; - case MAVLINK_MSG_ID_SET_LOCAL_POSITION_SETPOINT: - { - mavlink_set_local_position_setpoint_t p; - mavlink_msg_set_local_position_setpoint_decode(&message, &p); - emit userPositionSetPointsChanged(uasId, p.x, p.y, p.z, p.yaw); - } - break; - case MAVLINK_MSG_ID_STATUSTEXT: - { - QByteArray b; - b.resize(MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN); - mavlink_msg_statustext_get_text(&message, b.data()); - //b.append('\0'); - QString text = QString(b); - int severity = mavlink_msg_statustext_get_severity(&message); - //qDebug() << "RECEIVED STATUS:" << text;false - //emit statusTextReceived(severity, text); - - if (text.startsWith("#audio:")) - { - text.remove("#audio:"); - emit textMessageReceived(uasId, message.compid, severity, QString("Audio message: ") + text); - GAudioOutput::instance()->say(text, severity); - } - else - { - emit textMessageReceived(uasId, message.compid, severity, text); - } - } - break; - case MAVLINK_MSG_ID_SERVO_OUTPUT_RAW: - { - mavlink_servo_output_raw_t raw; - mavlink_msg_servo_output_raw_decode(&message, &raw); - - if (hilEnabled) - { - emit hilActuatorsChanged(static_cast(getUnixTimeFromMs(raw.time_boot_ms)), static_cast(raw.servo1_raw), - static_cast(raw.servo2_raw), static_cast(raw.servo3_raw), - static_cast(raw.servo4_raw), static_cast(raw.servo5_raw), static_cast(raw.servo6_raw), - static_cast(raw.servo7_raw), static_cast(raw.servo8_raw)); - } - } - break; -#ifdef MAVLINK_ENABLED_PIXHAWK - case MAVLINK_MSG_ID_DATA_TRANSMISSION_HANDSHAKE: - { - mavlink_data_transmission_handshake_t p; - mavlink_msg_data_transmission_handshake_decode(&message, &p); - imageSize = p.size; - imagePackets = p.packets; - imagePayload = p.payload; - imageQuality = p.jpg_quality; - imageType = p.type; - imageWidth = p.width; - imageHeight = p.height; - imageStart = QGC::groundTimeMilliseconds(); - } - break; - - case MAVLINK_MSG_ID_ENCAPSULATED_DATA: - { - mavlink_encapsulated_data_t img; - mavlink_msg_encapsulated_data_decode(&message, &img); - int seq = img.seqnr; - int pos = seq * imagePayload; - - // Check if we have a valid transaction - if (imagePackets == 0) - { - // NO VALID TRANSACTION - ABORT - // Restart statemachine - imagePacketsArrived = 0; - } - - for (int i = 0; i < imagePayload; ++i) - { - if (pos <= imageSize) { - imageRecBuffer[pos] = img.data[i]; - } - ++pos; - } - - ++imagePacketsArrived; - - // emit signal if all packets arrived - if ((imagePacketsArrived >= imagePackets)) - { - // Restart statemachine - imagePacketsArrived = 0; - emit imageReady(this); - //qDebug() << "imageReady emitted. all packets arrived"; - } - } - break; - - - -#endif - // case MAVLINK_MSG_ID_OBJECT_DETECTION_EVENT: - // { - // mavlink_object_detection_event_t event; - // mavlink_msg_object_detection_event_decode(&message, &event); - // QString str(event.name); - // emit objectDetected(event.time, event.object_id, event.type, str, event.quality, event.bearing, event.distance); - // } - // break; - // WILL BE ENABLED ONCE MESSAGE IS IN COMMON MESSAGE SET - // case MAVLINK_MSG_ID_MEMORY_VECT: - // { - // mavlink_memory_vect_t vect; - // mavlink_msg_memory_vect_decode(&message, &vect); - // QString str("mem_%1"); - // quint64 time = getUnixTime(0); - // int16_t *mem0 = (int16_t *)&vect.value[0]; - // uint16_t *mem1 = (uint16_t *)&vect.value[0]; - // int32_t *mem2 = (int32_t *)&vect.value[0]; - // // uint32_t *mem3 = (uint32_t *)&vect.value[0]; causes overload problem - // float *mem4 = (float *)&vect.value[0]; - // if ( vect.ver == 0) vect.type = 0, vect.ver = 1; else ; - // if ( vect.ver == 1) - // { - // switch (vect.type) { - // default: - // case 0: - // for (int i = 0; i < 16; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "i16", mem0[i], time); - // break; - // case 1: - // for (int i = 0; i < 16; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "ui16", mem1[i], time); - // break; - // case 2: - // for (int i = 0; i < 16; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "Q15", (float)mem0[i]/32767.0, time); - // break; - // case 3: - // for (int i = 0; i < 16; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "1Q14", (float)mem0[i]/16383.0, time); - // break; - // case 4: - // for (int i = 0; i < 8; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "i32", mem2[i], time); - // break; - // case 5: - // for (int i = 0; i < 8; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "i32", mem2[i], time); - // break; - // case 6: - // for (int i = 0; i < 8; i++) - // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "float", mem4[i], time); - // break; - // } - // } - // } - // break; -#ifdef MAVLINK_ENABLED_UALBERTA - case MAVLINK_MSG_ID_NAV_FILTER_BIAS: - { - mavlink_nav_filter_bias_t bias; - mavlink_msg_nav_filter_bias_decode(&message, &bias); - quint64 time = getUnixTime(); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[0]", "raw", bias.accel_0, time); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[1]", "raw", bias.accel_1, time); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[2]", "raw", bias.accel_2, time); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[0]", "raw", bias.gyro_0, time); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[1]", "raw", bias.gyro_1, time); - // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[2]", "raw", bias.gyro_2, time); - } - break; - case MAVLINK_MSG_ID_RADIO_CALIBRATION: - { - mavlink_radio_calibration_t radioMsg; - mavlink_msg_radio_calibration_decode(&message, &radioMsg); - QVector aileron; - QVector elevator; - QVector rudder; - QVector gyro; - QVector pitch; - QVector throttle; - - for (int i=0; i radioData = new RadioCalibrationData(aileron, elevator, rudder, gyro, pitch, throttle); - emit radioCalibrationReceived(radioData); - delete radioData; - } - break; - -#endif - // Messages to ignore - case MAVLINK_MSG_ID_RAW_IMU: - case MAVLINK_MSG_ID_SCALED_IMU: - case MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT: - case MAVLINK_MSG_ID_RAW_PRESSURE: - case MAVLINK_MSG_ID_SCALED_PRESSURE: - case MAVLINK_MSG_ID_OPTICAL_FLOW: - case MAVLINK_MSG_ID_DEBUG_VECT: - case MAVLINK_MSG_ID_DEBUG: - case MAVLINK_MSG_ID_NAMED_VALUE_FLOAT: - case MAVLINK_MSG_ID_NAMED_VALUE_INT: - case MAVLINK_MSG_ID_MANUAL_CONTROL: - case MAVLINK_MSG_ID_HIGHRES_IMU: - break; - default: - { - if (!unknownPackets.contains(message.msgid)) - { - unknownPackets.append(message.msgid); - QString errString = tr("UNABLE TO DECODE MESSAGE NUMBER %1").arg(message.msgid); - //GAudioOutput::instance()->say(errString+tr(", please check console for details.")); - emit textMessageReceived(uasId, message.compid, 255, errString); - std::cout << "Unable to decode message from system " << std::dec << static_cast(message.sysid) << " with message id:" << static_cast(message.msgid) << std::endl; - //qDebug() << std::cerr << "Unable to decode message from system " << std::dec << static_cast(message.acid) << " with message id:" << static_cast(message.msgid) << std::endl; - } - } - break; - } - } -} - - -#if defined(QGC_PROTOBUF_ENABLED) -/** -* Receive an extended message. -* @param link -* @param message -*/ -void UAS::receiveExtendedMessage(LinkInterface* link, std::tr1::shared_ptr message) -{ - if (!link) - { - return; - } - if (!links->contains(link)) - { - addLink(link); - } - - const google::protobuf::Descriptor* descriptor = message->GetDescriptor(); - if (!descriptor) - { - return; - } - - const google::protobuf::FieldDescriptor* headerField = descriptor->FindFieldByName("header"); - if (!headerField) - { - return; - } - - const google::protobuf::Descriptor* headerDescriptor = headerField->message_type(); - if (!headerDescriptor) - { - return; - } - - const google::protobuf::FieldDescriptor* sourceSysIdField = headerDescriptor->FindFieldByName("source_sysid"); - if (!sourceSysIdField) - { - return; - } - - const google::protobuf::Reflection* reflection = message->GetReflection(); - const google::protobuf::Message& headerMsg = reflection->GetMessage(*message, headerField); - const google::protobuf::Reflection* headerReflection = headerMsg.GetReflection(); - - int source_sysid = headerReflection->GetInt32(headerMsg, sourceSysIdField); - - if (source_sysid != uasId) - { - return; - } - -#ifdef QGC_USE_PIXHAWK_MESSAGES - if (message->GetTypeName() == overlay.GetTypeName()) - { - receivedOverlayTimestamp = QGC::groundTimeSeconds(); - overlayMutex.lock(); - overlay.CopyFrom(*message); - overlayMutex.unlock(); - emit overlayChanged(this); - } - else if (message->GetTypeName() == obstacleList.GetTypeName()) - { - receivedObstacleListTimestamp = QGC::groundTimeSeconds(); - obstacleListMutex.lock(); - obstacleList.CopyFrom(*message); - obstacleListMutex.unlock(); - emit obstacleListChanged(this); - } - else if (message->GetTypeName() == path.GetTypeName()) - { - receivedPathTimestamp = QGC::groundTimeSeconds(); - pathMutex.lock(); - path.CopyFrom(*message); - pathMutex.unlock(); - emit pathChanged(this); - } - else if (message->GetTypeName() == pointCloud.GetTypeName()) - { - receivedPointCloudTimestamp = QGC::groundTimeSeconds(); - pointCloudMutex.lock(); - pointCloud.CopyFrom(*message); - pointCloudMutex.unlock(); - emit pointCloudChanged(this); - } - else if (message->GetTypeName() == rgbdImage.GetTypeName()) - { - receivedRGBDImageTimestamp = QGC::groundTimeSeconds(); - rgbdImageMutex.lock(); - rgbdImage.CopyFrom(*message); - rgbdImageMutex.unlock(); - emit rgbdImageChanged(this); - } -#endif -} - -#endif - -/** -* Set the home position of the UAS. -* @param lat The latitude fo the home position -* @param lon The longitute of the home position -* @param alt The altitude of the home position -*/ -void UAS::setHomePosition(double lat, double lon, double alt) -{ - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Warning); - msgBox.setText("Setting new World Coordinate Frame Origin"); - msgBox.setInformativeText("Do you want to set a new origin? Waypoints defined in the local frame will be shifted in their physical location"); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Cancel); - int ret = msgBox.exec(); - - // Close the message box shortly after the click to prevent accidental clicks - QTimer::singleShot(5000, &msgBox, SLOT(reject())); - - - if (ret == QMessageBox::Yes) - { - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_DO_SET_HOME, 1, 0, 0, 0, 0, lat, lon, alt); - // Send message twice to increase chance that it reaches its goal - sendMessage(msg); - - // Send new home position to UAS - mavlink_set_gps_global_origin_t home; - home.target_system = uasId; - home.latitude = lat*1E7; - home.longitude = lon*1E7; - home.altitude = alt*1000; - qDebug() << "lat:" << home.latitude << " lon:" << home.longitude; - mavlink_msg_set_gps_global_origin_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &home); - sendMessage(msg); - } -} - -/** -* Set the origin to the current GPS location. -**/ -void UAS::setLocalOriginAtCurrentGPSPosition() -{ - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Warning); - msgBox.setText("Setting new World Coordinate Frame Origin"); - msgBox.setInformativeText("Do you want to set a new origin? Waypoints defined in the local frame will be shifted in their physical location"); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Cancel); - int ret = msgBox.exec(); - - // Close the message box shortly after the click to prevent accidental clicks - QTimer::singleShot(5000, &msgBox, SLOT(reject())); - - - if (ret == QMessageBox::Yes) - { - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_DO_SET_HOME, 1, 1, 0, 0, 0, 0, 0, 0); - // Send message twice to increase chance that it reaches its goal - sendMessage(msg); - } -} - -/** -* Set a local position setpoint. -* @param x postion -* @param y position -* @param z position -*/ -void UAS::setLocalPositionSetpoint(float x, float y, float z, float yaw) -{ -#ifdef MAVLINK_ENABLED_PIXHAWK - mavlink_message_t msg; - mavlink_msg_set_local_position_setpoint_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_FRAME_LOCAL_NED, x, y, z, yaw/M_PI*180.0); - sendMessage(msg); -#else - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(z); - Q_UNUSED(yaw); -#endif -} - -/** -* Set a offset of the local position. -* @param x position -* @param y position -* @param z position -* @param yaw -*/ -void UAS::setLocalPositionOffset(float x, float y, float z, float yaw) -{ -#ifdef MAVLINK_ENABLED_PIXHAWK - mavlink_message_t msg; - mavlink_msg_set_position_control_offset_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, x, y, z, yaw); - sendMessage(msg); -#else - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(z); - Q_UNUSED(yaw); -#endif -} - -void UAS::startRadioControlCalibration() -{ - mavlink_message_t msg; - // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 0, 0, 1, 0, 0, 0); - sendMessage(msg); -} - -void UAS::startDataRecording() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 2, 0, 0, 0); - sendMessage(msg); -} - -void UAS::stopDataRecording() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 0, 0, 0, 0); - sendMessage(msg); -} - -void UAS::startMagnetometerCalibration() -{ - mavlink_message_t msg; - // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 1, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -void UAS::startGyroscopeCalibration() -{ - mavlink_message_t msg; - // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 1, 0, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -void UAS::startPressureCalibration() -{ - mavlink_message_t msg; - // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 0, 1, 0, 0, 0, 0); - sendMessage(msg); -} - -/** -* Check if time is smaller than 40 years, assuming no system without Unix -* timestamp runs longer than 40 years continuously without reboot. In worst case -* this will add/subtract the communication delay between GCS and MAV, it will -* never alter the timestamp in a safety critical way. -*/ -quint64 UAS::getUnixReferenceTime(quint64 time) -{ - // Same as getUnixTime, but does not react to attitudeStamped mode - if (time == 0) - { - // qDebug() << "XNEW time:" < has to be - // a Unix epoch timestamp. Do nothing. - return time/1000; - } -} - -/** -* @warning If attitudeStamped is enabled, this function will not actually return -* the precise time stamp of this measurement augmented to UNIX time, but will -* MOVE the timestamp IN TIME to match the last measured attitude. There is no -* reason why one would want this, except for system setups where the onboard -* clock is not present or broken and datasets should be collected that are still -* roughly synchronized. PLEASE NOTE THAT ENABLING ATTITUDE STAMPED RUINS THE -* SCIENTIFIC NATURE OF THE CORRECT LOGGING FUNCTIONS OF QGROUNDCONTROL! -*/ -quint64 UAS::getUnixTimeFromMs(quint64 time) -{ - return getUnixTime(time*1000); -} - -/** -* @warning If attitudeStamped is enabled, this function will not actually return -* the precise time stam of this measurement augmented to UNIX time, but will -* MOVE the timestamp IN TIME to match the last measured attitude. There is no -* reason why one would want this, except for system setups where the onboard -* clock is not present or broken and datasets should be collected that are -* still roughly synchronized. PLEASE NOTE THAT ENABLING ATTITUDE STAMPED -* RUINS THE SCIENTIFIC NATURE OF THE CORRECT LOGGING FUNCTIONS OF QGROUNDCONTROL! -*/ -quint64 UAS::getUnixTime(quint64 time) -{ - quint64 ret = 0; - if (attitudeStamped) - { - ret = lastAttitude; - } - - if (time == 0) - { - ret = QGC::groundTimeMilliseconds(); - } - // Check if time is smaller than 40 years, - // assuming no system without Unix timestamp - // runs longer than 40 years continuously without - // reboot. In worst case this will add/subtract the - // communication delay between GCS and MAV, - // it will never alter the timestamp in a safety - // critical way. - // - // Calculation: - // 40 years - // 365 days - // 24 hours - // 60 minutes - // 60 seconds - // 1000 milliseconds - // 1000 microseconds -#ifndef _MSC_VER - else if (time < 1261440000000000LLU) -#else - else if (time < 1261440000000000) -#endif - { - // qDebug() << "GEN time:" << time/1000 + onboardTimeOffset; - if (onboardTimeOffset == 0 || time < (lastNonNullTime - 100)) - { - lastNonNullTime = time; - onboardTimeOffset = QGC::groundTimeMilliseconds() - time/1000; - } - if (time > lastNonNullTime) lastNonNullTime = time; - - ret = time/1000 + onboardTimeOffset; - } - else - { - // Time is not zero and larger than 40 years -> has to be - // a Unix epoch timestamp. Do nothing. - ret = time/1000; - } - - return ret; -} - -/** -* @param component that will be searched for in the map of parameters. -*/ -QList UAS::getParameterNames(int component) -{ - if (parameters.contains(component)) - { - return parameters.value(component)->keys(); - } - else - { - return QList(); - } -} - -QList UAS::getComponentIds() -{ - return parameters.keys(); -} - -/** -* @param mode that UAS is to be set to. -*/ -void UAS::setMode(int mode) -{ - //this->mode = mode; //no call assignament, update receive message from UAS - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, (uint8_t)uasId, (uint8_t)mode, (uint16_t)navMode); - sendMessage(msg); - qDebug() << "SENDING REQUEST TO SET MODE TO SYSTEM" << uasId << ", REQUEST TO SET MODE " << (uint8_t)mode; -} - -/** -* Send a message to every link that is connected. -* @param message that is to be sent -*/ -void UAS::sendMessage(mavlink_message_t message) -{ - if (!LinkManager::instance()) return; - // Emit message on all links that are currently connected - foreach (LinkInterface* link, *links) - { - if (LinkManager::instance()->getLinks().contains(link)) - { - sendMessage(link, message); - } - else - { - // Remove from list - links->removeAt(links->indexOf(link)); - } - } -} - -/** -* Forward a message to all links that are currently connected. -* @param message that is to be forwarded -*/ -void UAS::forwardMessage(mavlink_message_t message) -{ - // Emit message on all links that are currently connected - QListlink_list = LinkManager::instance()->getLinksForProtocol(mavlink); - - foreach(LinkInterface* link, link_list) - { - if (link) - { - SerialLink* serial = dynamic_cast(link); - if(serial != 0) - { - for(int i=0; isize(); i++) - { - if(serial != links->at(i)) - { - qDebug()<<"Antenna tracking: Forwarding Over link: "<getName()<<" "<getSystemId(), mavlink->getComponentId(), link->getId(), message.len, messageKeys[message.msgid]); - // If link is connected - if (link->isConnected()) - { - // Send the portion of the buffer now occupied by the message - link->writeBytes((const char*)buffer, len); - } -} - -/** - * @param value battery voltage - */ -float UAS::filterVoltage(float value) const -{ - return lpVoltage * 0.7f + value * 0.3f; -} - -/** -* The mode can be preflight or unknown. -* @Return the mode of the autopilot -*/ -QString UAS::getNavModeText(int mode) -{ - if (autopilot == MAV_AUTOPILOT_PIXHAWK) - { - switch (mode) - { - case 0: - return QString("PREFLIGHT"); - break; - default: - return QString("UNKNOWN"); - } - } - else if (autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA) - { - return QString("UNKNOWN"); - } - else if (autopilot == MAV_AUTOPILOT_OPENPILOT) - { - return QString("UNKNOWN"); - } - // If nothing matches, return unknown - return QString("UNKNOWN"); -} - -/** -* Get the status of the code and a description of the status. -* Status can be unitialized, booting up, calibrating sensors, active -* standby, cirtical, emergency, shutdown or unknown. -*/ -void UAS::getStatusForCode(int statusCode, QString& uasState, QString& stateDescription) -{ - switch (statusCode) - { - case MAV_STATE_UNINIT: - uasState = tr("UNINIT"); - stateDescription = tr("Unitialized, booting up."); - break; - case MAV_STATE_BOOT: - uasState = tr("BOOT"); - stateDescription = tr("Booting system, please wait."); - break; - case MAV_STATE_CALIBRATING: - uasState = tr("CALIBRATING"); - stateDescription = tr("Calibrating sensors, please wait."); - break; - case MAV_STATE_ACTIVE: - uasState = tr("ACTIVE"); - stateDescription = tr("Active, normal operation."); - break; - case MAV_STATE_STANDBY: - uasState = tr("STANDBY"); - stateDescription = tr("Standby mode, ready for launch."); - break; - case MAV_STATE_CRITICAL: - uasState = tr("CRITICAL"); - stateDescription = tr("FAILURE: Continuing operation."); - break; - case MAV_STATE_EMERGENCY: - uasState = tr("EMERGENCY"); - stateDescription = tr("EMERGENCY: Land Immediately!"); - break; - //case MAV_STATE_HILSIM: - //uasState = tr("HIL SIM"); - //stateDescription = tr("HIL Simulation, Sensors read from SIM"); - //break; - - case MAV_STATE_POWEROFF: - uasState = tr("SHUTDOWN"); - stateDescription = tr("Powering off system."); - break; - - default: - uasState = tr("UNKNOWN"); - stateDescription = tr("Unknown system state"); - break; - } -} - -QImage UAS::getImage() -{ -#ifdef MAVLINK_ENABLED_PIXHAWK - -// qDebug() << "IMAGE TYPE:" << imageType; - - // RAW greyscale - if (imageType == MAVLINK_DATA_STREAM_IMG_RAW8U) - { - // TODO FIXME - int imgColors = 255;//imageSize/(imageWidth*imageHeight); - //const int headerSize = 15; - - // Construct PGM header - QString header("P5\n%1 %2\n%3\n"); - header = header.arg(imageWidth).arg(imageHeight).arg(imgColors); - - QByteArray tmpImage(header.toStdString().c_str(), header.toStdString().size()); - tmpImage.append(imageRecBuffer); - - //qDebug() << "IMAGE SIZE:" << tmpImage.size() << "HEADER SIZE: (15):" << header.size() << "HEADER: " << header; - - if (imageRecBuffer.isNull()) - { - qDebug()<< "could not convertToPGM()"; - return QImage(); - } - - if (!image.loadFromData(tmpImage, "PGM")) - { - qDebug()<< "could not create extracted image"; - return QImage(); - } - - } - // BMP with header - else if (imageType == MAVLINK_DATA_STREAM_IMG_BMP || - imageType == MAVLINK_DATA_STREAM_IMG_JPEG || - imageType == MAVLINK_DATA_STREAM_IMG_PGM || - imageType == MAVLINK_DATA_STREAM_IMG_PNG) - { - if (!image.loadFromData(imageRecBuffer)) - { - qDebug() << "Loading data from image buffer failed!"; - } - } - // Restart statemachine - imagePacketsArrived = 0; - //imageRecBuffer.clear(); - return image; -#else - return QImage(); -#endif - -} - -void UAS::requestImage() -{ -#ifdef MAVLINK_ENABLED_PIXHAWK - qDebug() << "trying to get an image from the uas..."; - - // check if there is already an image transmission going on - if (imagePacketsArrived == 0) - { - mavlink_message_t msg; - mavlink_msg_data_transmission_handshake_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, DATA_TYPE_JPEG_IMAGE, 0, 0, 0, 0, 0, 50); - sendMessage(msg); - } -#endif -} - - -/* MANAGEMENT */ - -/** - * - * @return The uptime in milliseconds - * - */ -quint64 UAS::getUptime() const -{ - if(startTime == 0) - { - return 0; - } - else - { - return QGC::groundTimeMilliseconds() - startTime; - } -} - -int UAS::getCommunicationStatus() const -{ - return commStatus; -} - -void UAS::requestParameters() -{ - mavlink_message_t msg; - mavlink_msg_param_request_list_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), MAV_COMP_ID_ALL); - sendMessage(msg); - qDebug() << __FILE__ << __LINE__ << "LOADING PARAM LIST"; -} - -void UAS::writeParametersToStorage() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 1, -1, -1, -1, 0, 0, 0); - qDebug() << "SENT COMMAND" << MAV_CMD_PREFLIGHT_STORAGE; - sendMessage(msg); -} - -void UAS::readParametersFromStorage() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 0, -1, -1, -1, 0, 0, 0); - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableAllDataTransmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - // 0 is a magic ID and will enable/disable the standard message set except for heartbeat - stream.req_stream_id = MAV_DATA_STREAM_ALL; - // Select the update rate in Hz the message should be send - // All messages will be send with their default rate - // TODO: use 0 to turn off and get rid of enable/disable? will require - // a different magic flag for turning on defaults, possibly something really high like 1111 ? - stream.req_message_rate = 0; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableRawSensorDataTransmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_RAW_SENSORS; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableExtendedSystemStatusTransmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_EXTENDED_STATUS; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableRCChannelDataTransmission(int rate) -{ -#if defined(MAVLINK_ENABLED_UALBERTA_MESSAGES) - mavlink_message_t msg; - mavlink_msg_request_rc_channels_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, enabled); - sendMessage(msg); -#else - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_RC_CHANNELS; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -#endif -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableRawControllerDataTransmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_RAW_CONTROLLER; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -} - -//void UAS::enableRawSensorFusionTransmission(int rate) -//{ -// // Buffers to write data to -// mavlink_message_t msg; -// mavlink_request_data_stream_t stream; -// // Select the message to request from now on -// stream.req_stream_id = MAV_DATA_STREAM_RAW_SENSOR_FUSION; -// // Select the update rate in Hz the message should be send -// stream.req_message_rate = rate; -// // Start / stop the message -// stream.start_stop = (rate) ? 1 : 0; -// // The system which should take this command -// stream.target_system = uasId; -// // The component / subsystem which should take this command -// stream.target_component = 0; -// // Encode and send the message -// mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); -// // Send message twice to increase chance of reception -// sendMessage(msg); -// sendMessage(msg); -//} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enablePositionTransmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_POSITION; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableExtra1Transmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_EXTRA1; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableExtra2Transmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_EXTRA2; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); - sendMessage(msg); -} - -/** -* @param rate The update rate in Hz the message should be sent -*/ -void UAS::enableExtra3Transmission(int rate) -{ - // Buffers to write data to - mavlink_message_t msg; - mavlink_request_data_stream_t stream; - // Select the message to request from now on - stream.req_stream_id = MAV_DATA_STREAM_EXTRA3; - // Select the update rate in Hz the message should be send - stream.req_message_rate = rate; - // Start / stop the message - stream.start_stop = (rate) ? 1 : 0; - // The system which should take this command - stream.target_system = uasId; - // The component / subsystem which should take this command - stream.target_component = 0; - // Encode and send the message - mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); - // Send message twice to increase chance of reception - sendMessage(msg); - sendMessage(msg); -} - -/** - * Set a parameter value onboard - * - * @param component The component to set the parameter - * @param id Name of the parameter - */ -void UAS::setParameter(const int component, const QString& id, const QVariant& value) -{ - if (!id.isNull()) - { - mavlink_message_t msg; - mavlink_param_set_t p; - mavlink_param_union_t union_value; - - // Assign correct value based on QVariant - switch (value.type()) - { - case QVariant::Int: - union_value.param_int32 = value.toInt(); - p.param_type = MAV_PARAM_TYPE_INT32; - break; - case QVariant::UInt: - union_value.param_uint32 = value.toUInt(); - p.param_type = MAV_PARAM_TYPE_UINT32; - break; - case QMetaType::Float: - union_value.param_float = value.toFloat(); - p.param_type = MAV_PARAM_TYPE_REAL32; - break; - default: - qCritical() << "ABORTED PARAM SEND, NO VALID QVARIANT TYPE"; - return; - } - - p.param_value = union_value.param_float; - p.target_system = (uint8_t)uasId; - p.target_component = (uint8_t)component; - - //qDebug() << "SENT PARAM:" << value; - - // Copy string into buffer, ensuring not to exceed the buffer size - for (unsigned int i = 0; i < sizeof(p.param_id); i++) - { - // String characters - if ((int)i < id.length()) - { - p.param_id[i] = id.toAscii()[i]; - } - else - { - // Fill rest with zeros - p.param_id[i] = 0; - } - } - mavlink_msg_param_set_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &p); - sendMessage(msg); - } -} - -/** -* Request parameter, use parameter name to request it. -*/ -void UAS::requestParameter(int component, int id) -{ - // Request parameter, use parameter name to request it - mavlink_message_t msg; - mavlink_param_request_read_t read; - read.param_index = id; - read.param_id[0] = '\0'; // Enforce null termination - read.target_system = uasId; - read.target_component = component; - mavlink_msg_param_request_read_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &read); - sendMessage(msg); - //qDebug() << __FILE__ << __LINE__ << "REQUESTING PARAM RETRANSMISSION FROM COMPONENT" << component << "FOR PARAM ID" << id; -} - -/** -* Request a parameter, use parameter name to request it. -*/ -void UAS::requestParameter(int component, const QString& parameter) -{ - // Request parameter, use parameter name to request it - mavlink_message_t msg; - mavlink_param_request_read_t read; - read.param_index = -1; - // Copy full param name or maximum max field size - if (parameter.length() > MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN) - { - emit textMessageReceived(uasId, 0, 255, QString("QGC WARNING: Parameter name %1 is more than %2 bytes long. This might lead to errors and mishaps!").arg(parameter).arg(MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN-1)); - } - memcpy(read.param_id, parameter.toStdString().c_str(), qMax(parameter.length(), MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN)); - read.param_id[15] = '\0'; // Enforce null termination - read.target_system = uasId; - read.target_component = component; - mavlink_msg_param_request_read_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &read); - sendMessage(msg); - qDebug() << __FILE__ << __LINE__ << "REQUESTING PARAM RETRANSMISSION FROM COMPONENT" << component << "FOR PARAM NAME" << parameter; -} - -/** -* @param systemType Type of MAV. -*/ -void UAS::setSystemType(int systemType) -{ - if((systemType >= MAV_TYPE_GENERIC) && (systemType < MAV_TYPE_ENUM_END)) - { - type = systemType; - - // If the airframe is still generic, change it to a close default type - if (airframe == 0) - { - switch (systemType) - { - case MAV_TYPE_FIXED_WING: - airframe = QGC_AIRFRAME_EASYSTAR; - break; - case MAV_TYPE_QUADROTOR: - airframe = QGC_AIRFRAME_MIKROKOPTER; - break; - } - } - emit systemSpecsChanged(uasId); - } -} - -void UAS::setUASName(const QString& name) -{ - if (name != "") - { - this->name = name; - writeSettings(); - emit nameChanged(name); - emit systemSpecsChanged(uasId); - } -} - -void UAS::executeCommand(MAV_CMD command) -{ - mavlink_message_t msg; - mavlink_command_long_t cmd; - cmd.command = (uint16_t)command; - cmd.confirmation = 0; - cmd.param1 = 0.0f; - cmd.param2 = 0.0f; - cmd.param3 = 0.0f; - cmd.param4 = 0.0f; - cmd.param5 = 0.0f; - cmd.param6 = 0.0f; - cmd.param7 = 0.0f; - cmd.target_system = uasId; - cmd.target_component = 0; - mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); - sendMessage(msg); -} - -void UAS::executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component) -{ - mavlink_message_t msg; - mavlink_command_long_t cmd; - cmd.command = (uint16_t)command; - cmd.confirmation = confirmation; - cmd.param1 = param1; - cmd.param2 = param2; - cmd.param3 = param3; - cmd.param4 = param4; - cmd.param5 = param5; - cmd.param6 = param6; - cmd.param7 = param7; - cmd.target_system = uasId; - cmd.target_component = component; - mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); - sendMessage(msg); -} - -/** - * Launches the system - * - */ -void UAS::launch() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_NAV_TAKEOFF, 1, 0, 0, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -/** - * @warning Depending on the UAS, this might make the rotors of a helicopter spinning - * - */ -void UAS::armSystem() -{ - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_SAFETY_ARMED, navMode); - sendMessage(msg); -} - -/** - * @warning Depending on the UAS, this might completely stop all motors. - * - */ -void UAS::disarmSystem() -{ - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode & ~MAV_MODE_FLAG_SAFETY_ARMED, navMode); - sendMessage(msg); -} - -/** -* Set the manual control commands. -* This can only be done if the system has manual inputs enabled and is armed. -*/ -void UAS::setManualControlCommands(double roll, double pitch, double yaw, double thrust, int xHat, int yHat, int buttons) -{ - // Scale values - double rollPitchScaling = 1.0f * 1000.0f; - double yawScaling = 1.0f * 1000.0f; - double thrustScaling = 1.0f * 1000.0f; - - manualRollAngle = roll * rollPitchScaling; - manualPitchAngle = pitch * rollPitchScaling; - manualYawAngle = yaw * yawScaling; - manualThrust = thrust * thrustScaling; - - // If system has manual inputs enabled and is armed - if(((mode & MAV_MODE_FLAG_DECODE_POSITION_MANUAL) && (mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY)) || (mode & MAV_MODE_FLAG_HIL_ENABLED)) - { - mavlink_message_t message; - mavlink_msg_manual_control_pack(mavlink->getSystemId(), mavlink->getComponentId(), &message, this->uasId, (float)manualPitchAngle, (float)manualRollAngle, (float)manualThrust, (float)manualYawAngle, buttons); - sendMessage(message); - //qDebug() << __FILE__ << __LINE__ << ": SENT MANUAL CONTROL MESSAGE: roll" << manualRollAngle << " pitch: " << manualPitchAngle << " yaw: " << manualYawAngle << " thrust: " << manualThrust; - - emit attitudeThrustSetPointChanged(this, roll, pitch, yaw, thrust, QGC::groundTimeMilliseconds()); - } - else - { - qDebug() << "JOYSTICK/MANUAL CONTROL: IGNORING COMMANDS: Set mode to MANUAL to send joystick commands first"; - } -} - -/** -* @return the type of the system -*/ -int UAS::getSystemType() -{ - return this->type; -} - -/** -* @param buttonIndex -*/ -void UAS::receiveButton(int buttonIndex) -{ - switch (buttonIndex) - { - case 0: - - break; - case 1: - - break; - default: - - break; - } - // qDebug() << __FILE__ << __LINE__ << ": Received button clicked signal (button # is: " << buttonIndex << "), UNIMPLEMENTED IN MAVLINK!"; - -} - -/** -* Halt the uas. -*/ -void UAS::halt() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_HOLD, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -/** -* Make the UAS move. -*/ -void UAS::go() -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_CONTINUE, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -/** -* Order the robot to return home -*/ -void UAS::home() -{ - mavlink_message_t msg; - - double latitude = UASManager::instance()->getHomeLatitude(); - double longitude = UASManager::instance()->getHomeLongitude(); - double altitude = UASManager::instance()->getHomeAltitude(); - int frame = UASManager::instance()->getHomeFrame(); - - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_CONTINUE, MAV_GOTO_HOLD_AT_CURRENT_POSITION, frame, 0, latitude, longitude, altitude); - sendMessage(msg); -} - -/** -* Order the robot to land on the runway -*/ -void UAS::land() -{ - mavlink_message_t msg; - - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_LAND, 1, 0, 0, 0, 0, 0, 0, 0); - sendMessage(msg); -} - -/** - * The MAV starts the emergency landing procedure. The behaviour depends on the onboard implementation - * and might differ between systems. - */ -void UAS::emergencySTOP() -{ - // FIXME MAVLINKV10PORTINGNEEDED - halt(); -} - -/** - * Shut down this mav - All onboard systems are immediately shut down (e.g. the - * main power line is cut). - * @warning This might lead to a crash. - * - * The command will not be executed until emergencyKILLConfirm is issues immediately afterwards - */ -bool UAS::emergencyKILL() -{ - halt(); - // FIXME MAVLINKV10PORTINGNEEDED - // bool result = false; - // QMessageBox msgBox; - // msgBox.setIcon(QMessageBox::Critical); - // msgBox.setText("EMERGENCY: KILL ALL MOTORS ON UAS"); - // msgBox.setInformativeText("Do you want to cut power on all systems?"); - // msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - // msgBox.setDefaultButton(QMessageBox::Cancel); - // int ret = msgBox.exec(); - - // // Close the message box shortly after the click to prevent accidental clicks - // QTimer::singleShot(5000, &msgBox, SLOT(reject())); - - - // if (ret == QMessageBox::Yes) - // { - // mavlink_message_t msg; - // // TODO Replace MG System ID with static function call and allow to change ID in GUI - // mavlink_msg_action_pack(MG::SYSTEM::ID, MG::SYSTEM::COMPID, &msg, this->getUASID(), MAV_COMP_ID_IMU, (int)MAV_ACTION_EMCY_KILL); - // // Send message twice to increase chance of reception - // sendMessage(msg); - // sendMessage(msg); - // result = true; - // } - // return result; - return false; -} - -/** -* If enabled, connect the flight gear link. -*/ -void UAS::enableHilFlightGear(bool enable, QString options) -{ - QGCFlightGearLink* link = dynamic_cast(simulation); - if (!link || !simulation) { - // Delete wrong sim - if (simulation) { - stopHil(); - delete simulation; - } - simulation = new QGCFlightGearLink(this, options); - } - // Connect Flight Gear Link - link = dynamic_cast(simulation); - link->setStartupArguments(options); - if (enable) - { - startHil(); - } - else - { - stopHil(); - } -} - -/** -* If enabled, connect the X-plane gear link. -*/ -void UAS::enableHilXPlane(bool enable) -{ - QGCXPlaneLink* link = dynamic_cast(simulation); - if (!link || !simulation) { - if (simulation) { - stopHil(); - delete simulation; - } - qDebug() << "CREATED NEW XPLANE LINK"; - simulation = new QGCXPlaneLink(this); - } - // Connect X-Plane Link - if (enable) - { - startHil(); - } - else - { - stopHil(); - } -} - -/** -* @param time_us Timestamp (microseconds since UNIX epoch or microseconds since system boot) -* @param roll Roll angle (rad) -* @param pitch Pitch angle (rad) -* @param yaw Yaw angle (rad) -* @param rollspeed Roll angular speed (rad/s) -* @param pitchspeed Pitch angular speed (rad/s) -* @param yawspeed Yaw angular speed (rad/s) -* @param lat Latitude, expressed as * 1E7 -* @param lon Longitude, expressed as * 1E7 -* @param alt Altitude in meters, expressed as * 1000 (millimeters) -* @param vx Ground X Speed (Latitude), expressed as m/s * 100 -* @param vy Ground Y Speed (Longitude), expressed as m/s * 100 -* @param vz Ground Z Speed (Altitude), expressed as m/s * 100 -* @param xacc X acceleration (mg) -* @param yacc Y acceleration (mg) -* @param zacc Z acceleration (mg) -*/ -void UAS::sendHilState(uint64_t time_us, float roll, float pitch, float yaw, float rollspeed, - float pitchspeed, float yawspeed, int32_t lat, int32_t lon, int32_t alt, - int16_t vx, int16_t vy, int16_t vz, int16_t xacc, int16_t yacc, int16_t zacc) -{ - if (this->mode & MAV_MODE_FLAG_HIL_ENABLED) - { - mavlink_message_t msg; - mavlink_msg_hil_state_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, - time_us, roll, pitch, yaw, rollspeed, pitchspeed, yawspeed, - lat, lon, alt, vx, vy, vz, xacc, yacc, zacc); - sendMessage(msg); - } - else - { - // Attempt to set HIL mode - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_HIL_ENABLED, navMode); - sendMessage(msg); - qDebug() << __FILE__ << __LINE__ << "HIL is onboard not enabled, trying to enable."; - } -} - -/** -* Connect flight gear link. -**/ -void UAS::startHil() -{ - if (hilEnabled) return; - hilEnabled = true; - // Connect HIL simulation link - simulation->connectSimulation(); - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_HIL_ENABLED, navMode); - sendMessage(msg); -} - -/** -* disable flight gear link. -*/ -void UAS::stopHil() -{ - if (simulation) simulation->disconnectSimulation(); - mavlink_message_t msg; - mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode & !MAV_MODE_FLAG_HIL_ENABLED, navMode); - sendMessage(msg); - hilEnabled = false; -} - -void UAS::shutdown() -{ - bool result = false; - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Critical); - msgBox.setText("Shutting down the UAS"); - msgBox.setInformativeText("Do you want to shut down the onboard computer?"); - - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Cancel); - int ret = msgBox.exec(); - - // Close the message box shortly after the click to prevent accidental clicks - QTimer::singleShot(5000, &msgBox, SLOT(reject())); - - if (ret == QMessageBox::Yes) - { - // If the active UAS is set, execute command - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, 1, 0, 2, 0, 0, 0, 0, 0); - sendMessage(msg); - result = true; - } -} - -/** -* @param x position -* @param y position -* @param z position -* @param yaw -*/ -void UAS::setTargetPosition(float x, float y, float z, float yaw) -{ - mavlink_message_t msg; - mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_PATHPLANNING, 1, 1, 1, 0, yaw, x, y, z); - sendMessage(msg); -} - -/** - * @return The name of this system as string in human-readable form - */ -QString UAS::getUASName(void) const -{ - QString result; - if (name == "") - { - result = tr("MAV ") + result.sprintf("%03d", getUASID()); - } - else - { - result = name; - } - return result; -} - -/** -* @return the state of the uas as a short text. -*/ -const QString& UAS::getShortState() const -{ - return shortStateText; -} - -/** -* The mode can be autonomous, guided, manual or armed. It will also return if -* hardware in the loop is being used. -* @return the audio mode text for the id given. -*/ -QString UAS::getAudioModeTextFor(int id) -{ - QString mode; - uint8_t modeid = id; - - // BASE MODE DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_AUTO) - { - mode += "autonomous"; - } - else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_GUIDED) - { - mode += "guided"; - } - else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_MANUAL) - { - mode += "manual"; - } - else - { - // Nothing else applies, we're in preflight - mode += "preflight"; - } - - if (modeid != 0) - { - mode += " mode"; - } - - // ARMED STATE DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_SAFETY) - { - mode.append(" and armed"); - } - - // HARDWARE IN THE LOOP DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_HIL) - { - mode.append(" using hardware in the loop simulation"); - } - - return mode; -} - -/** -* The mode returned can be auto, stabilized, test, manual, preflight or unknown. -* @return the short text of the mode for the id given. -*/ -QString UAS::getShortModeTextFor(int id) -{ - QString mode; - uint8_t modeid = id; - - qDebug() << "MODE:" << modeid; - - // BASE MODE DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_AUTO) - { - mode += "AUTO"; - } - else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_GUIDED) - { - mode += "|STABILIZED"; - } -// if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_STABILIZE) -// { -// mode += "|STAB"; -// } - else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_TEST) - { - mode += "|TEST"; - } - else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_MANUAL) - { - mode += "|MANUAL"; - } - else if (modeid == 0) - { - mode = "|PREFLIGHT"; - } - else - { - mode = "|UNKNOWN"; - } - - // ARMED STATE DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_SAFETY) - { - mode.prepend("A"); - } - else - { - mode.prepend("D"); - } - - // HARDWARE IN THE LOOP DECODING - if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_HIL) - { - mode.prepend("HIL:"); - } - - return mode; -} - -const QString& UAS::getShortMode() const -{ - return shortModeText; -} - -/** -* Add the link and connect a signal to it which will be set off when it is destroyed. -*/ -void UAS::addLink(LinkInterface* link) -{ - if (!links->contains(link)) - { - links->append(link); - connect(link, SIGNAL(destroyed(QObject*)), this, SLOT(removeLink(QObject*))); - } -} - -void UAS::removeLink(QObject* object) -{ - LinkInterface* link = dynamic_cast(object); - if (link) - { - links->removeAt(links->indexOf(link)); - } -} - -/** -* @return the list of links -*/ -QList* UAS::getLinks() -{ - return links; -} - -/** -* @rerturn the map of the components -*/ -QMap UAS::getComponents() -{ - return components; -} - -/** -* Set the battery type and the number of cells. -* @param type of the battery -* @param cells Number of cells. -*/ -void UAS::setBattery(BatteryType type, int cells) -{ - this->batteryType = type; - this->cells = cells; - switch (batteryType) - { - case NICD: - break; - case NIMH: - break; - case LIION: - break; - case LIPOLY: - fullVoltage = this->cells * UAS::lipoFull; - emptyVoltage = this->cells * UAS::lipoEmpty; - break; - case LIFE: - break; - case AGZN: - break; - } -} - -/** -* Set the battery specificaitons: empty voltage, warning voltage, and full voltage. -* @param specifications of the battery -*/ -void UAS::setBatterySpecs(const QString& specs) -{ - if (specs.length() == 0 || specs.contains("%")) - { - batteryRemainingEstimateEnabled = false; - bool ok; - QString percent = specs; - percent = percent.remove("%"); - float temp = percent.toFloat(&ok); - if (ok) - { - warnLevelPercent = temp; - } - else - { - emit textMessageReceived(0, 0, 0, "Could not set battery options, format is wrong"); - } - } - else - { - batteryRemainingEstimateEnabled = true; - QString stringList = specs; - stringList = stringList.remove("V"); - stringList = stringList.remove("v"); - QStringList parts = stringList.split(","); - if (parts.length() == 3) - { - float temp; - bool ok; - // Get the empty voltage - temp = parts.at(0).toFloat(&ok); - if (ok) emptyVoltage = temp; - // Get the warning voltage - temp = parts.at(1).toFloat(&ok); - if (ok) warnVoltage = temp; - // Get the full voltage - temp = parts.at(2).toFloat(&ok); - if (ok) fullVoltage = temp; - } - else - { - emit textMessageReceived(0, 0, 0, "Could not set battery options, format is wrong"); - } - } -} - -/** -* @return the battery specifications(empty voltage, warning voltage, full voltage) -*/ -QString UAS::getBatterySpecs() -{ - if (batteryRemainingEstimateEnabled) - { - return QString("%1V,%2V,%3V").arg(emptyVoltage).arg(warnVoltage).arg(fullVoltage); - } - else - { - return QString("%1%").arg(warnLevelPercent); - } -} - -/** -* @return the time remaining. -*/ -int UAS::calculateTimeRemaining() -{ - quint64 dt = QGC::groundTimeMilliseconds() - startTime; - double seconds = dt / 1000.0f; - double voltDifference = startVoltage - currentVoltage; - if (voltDifference <= 0) voltDifference = 0.00000000001f; - double dischargePerSecond = voltDifference / seconds; - int remaining = static_cast((currentVoltage - emptyVoltage) / dischargePerSecond); - // Can never be below 0 - if (remaining < 0) remaining = 0; - return remaining; -} - -/** - * @return charge level in percent - 0 - 100 - */ -float UAS::getChargeLevel() -{ - if (batteryRemainingEstimateEnabled) - { - if (lpVoltage < emptyVoltage) - { - chargeLevel = 0.0f; - } - else if (lpVoltage > fullVoltage) - { - chargeLevel = 100.0f; - } - else - { - chargeLevel = 100.0f * ((lpVoltage - emptyVoltage)/(fullVoltage - emptyVoltage)); - } - } - return chargeLevel; -} - -void UAS::startLowBattAlarm() -{ - if (!lowBattAlarm) - { - GAudioOutput::instance()->alert(tr("system %1 has low battery").arg(getUASName())); - QTimer::singleShot(3000, GAudioOutput::instance(), SLOT(startEmergency())); - lowBattAlarm = true; - } -} - -void UAS::stopLowBattAlarm() -{ - if (lowBattAlarm) - { - GAudioOutput::instance()->stopEmergency(); - lowBattAlarm = false; - } -} +/*=================================================================== +======================================================================*/ + +/** + * @file + * @brief Represents one unmanned aerial vehicle + * + * @author Lorenz Meier + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "UAS.h" +#include "LinkInterface.h" +#include "UASManager.h" +#include "QGC.h" +#include "GAudioOutput.h" +#include "MAVLinkProtocol.h" +#include "QGCMAVLink.h" +#include "LinkManager.h" +#include "SerialLink.h" + +#ifdef QGC_PROTOBUF_ENABLED +#include +#endif + +/** +* Gets the settings from the previous UAS (name, airframe, autopilot, battery specs) +* by calling readSettings. This means the new UAS will have the same settings +* as the previous one created unless one calls deleteSettings in the code after +* creating the UAS. +*/ +UAS::UAS(MAVLinkProtocol* protocol, int id) : UASInterface(), + uasId(id), + startTime(QGC::groundTimeMilliseconds()), + commStatus(COMM_DISCONNECTED), + name(""), + autopilot(-1), + links(new QList()), + unknownPackets(), + mavlink(protocol), + waypointManager(this), + thrustSum(0), + thrustMax(10), + startVoltage(-1.0f), + tickVoltage(10.5f), + lastTickVoltageValue(13.0f), + tickLowpassVoltage(12.0f), + warnVoltage(9.5f), + warnLevelPercent(20.0f), + currentVoltage(12.6f), + lpVoltage(12.0f), + batteryRemainingEstimateEnabled(true), + mode(-1), + status(-1), + navMode(-1), + onboardTimeOffset(0), + controlRollManual(true), + controlPitchManual(true), + controlYawManual(true), + controlThrustManual(true), + manualRollAngle(0), + manualPitchAngle(0), + manualYawAngle(0), + manualThrust(0), + receiveDropRate(0), + sendDropRate(0), + lowBattAlarm(false), + positionLock(false), + localX(0.0), + localY(0.0), + localZ(0.0), + latitude(0.0), + longitude(0.0), + altitude(0.0), + roll(0.0), + pitch(0.0), + yaw(0.0), + statusTimeout(new QTimer(this)), + #if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) + receivedOverlayTimestamp(0.0), + receivedObstacleListTimestamp(0.0), + receivedPathTimestamp(0.0), + receivedPointCloudTimestamp(0.0), + receivedRGBDImageTimestamp(0.0), + #endif + paramsOnceRequested(false), + airframe(QGC_AIRFRAME_GENERIC), + attitudeKnown(false), + paramManager(NULL), + attitudeStamped(false), + lastAttitude(0), + simulation(0), + isLocalPositionKnown(false), + isGlobalPositionKnown(false), + systemIsArmed(false), + nedPosGlobalOffset(0,0,0), + nedAttGlobalOffset(0,0,0), + connectionLost(false), + lastVoltageWarning(0), + lastNonNullTime(0), + onboardTimeOffsetInvalidCount(0), + hilEnabled(false) + +{ + for (unsigned int i = 0; i<255;++i) + { + componentID[i] = -1; + componentMulti[i] = false; + } + + color = UASInterface::getNextColor(); + setBatterySpecs(QString("9V,9.5V,12.6V")); + connect(statusTimeout, SIGNAL(timeout()), this, SLOT(updateState())); + connect(this, SIGNAL(systemSpecsChanged(int)), this, SLOT(writeSettings())); + statusTimeout->start(500); + readSettings(); + type = MAV_TYPE_GENERIC; + // Initial signals + emit disarmed(); + emit armingChanged(false); +} + +/** +* Saves the settings of name, airframe, autopilot type and battery specifications +* by calling writeSettings. +*/ +UAS::~UAS() +{ + writeSettings(); + delete links; + delete statusTimeout; + delete simulation; +} + +/** +* Saves the settings of name, airframe, autopilot type and battery specifications +* for the next instantiation of UAS. +*/ +void UAS::writeSettings() +{ + QSettings settings; + settings.beginGroup(QString("MAV%1").arg(uasId)); + settings.setValue("NAME", this->name); + settings.setValue("AIRFRAME", this->airframe); + settings.setValue("AP_TYPE", this->autopilot); + settings.setValue("BATTERY_SPECS", getBatterySpecs()); + settings.endGroup(); + settings.sync(); +} + +/** +* Reads in the settings: name, airframe, autopilot type, and battery specifications +* for the new UAS. +*/ +void UAS::readSettings() +{ + QSettings settings; + settings.beginGroup(QString("MAV%1").arg(uasId)); + this->name = settings.value("NAME", this->name).toString(); + this->airframe = settings.value("AIRFRAME", this->airframe).toInt(); + this->autopilot = settings.value("AP_TYPE", this->autopilot).toInt(); + if (settings.contains("BATTERY_SPECS")) + { + setBatterySpecs(settings.value("BATTERY_SPECS").toString()); + } + settings.endGroup(); +} + +/** +* Deletes the settings origianally read into the UAS by readSettings. +* This is in case one does not want the old values but would rather +* start with the values assigned by the constructor. +*/ +void UAS::deleteSettings() +{ + this->name = ""; + this->airframe = QGC_AIRFRAME_GENERIC; + this->autopilot = -1; + setBatterySpecs(QString("9V,9.5V,12.6V")); +} + +/** +* @ return the id of the uas +*/ +int UAS::getUASID() const +{ + return uasId; +} + +/** +* Update the heartbeat. +*/ +void UAS::updateState() +{ + // Check if heartbeat timed out + quint64 heartbeatInterval = QGC::groundTimeUsecs() - lastHeartbeat; + if (!connectionLost && (heartbeatInterval > timeoutIntervalHeartbeat)) + { + connectionLost = true; + QString audiostring = QString("Link lost to system %1").arg(this->getUASID()); + GAudioOutput::instance()->say(audiostring.toLower()); + } + + // Update connection loss time on each iteration + if (connectionLost && (heartbeatInterval > timeoutIntervalHeartbeat)) + { + connectionLossTime = heartbeatInterval; + emit heartbeatTimeout(true, heartbeatInterval/1000); + } + + // Connection gained + if (connectionLost && (heartbeatInterval < timeoutIntervalHeartbeat)) + { + QString audiostring = QString("Link regained to system %1 after %2 seconds").arg(this->getUASID()).arg((int)(connectionLossTime/1000000)); + GAudioOutput::instance()->say(audiostring.toLower()); + connectionLost = false; + connectionLossTime = 0; + emit heartbeatTimeout(false, 0); + } + + // Position lock is set by the MAVLink message handler + // if no position lock is available, indicate an error + if (positionLock) + { + positionLock = false; + } + else + { + if (((mode&MAV_MODE_FLAG_DECODE_POSITION_AUTO) || (mode&MAV_MODE_FLAG_DECODE_POSITION_GUIDED)) && positionLock) + { + GAudioOutput::instance()->notifyNegative(); + } + } + +//#define MAVLINK_OFFBOARD_CONTROL_MODE_NONE 0 +//#define MAVLINK_OFFBOARD_CONTROL_MODE_RATES 1 +//#define MAVLINK_OFFBOARD_CONTROL_MODE_ATTITUDE 2 +//#define MAVLINK_OFFBOARD_CONTROL_MODE_VELOCITY 3 +//#define MAVLINK_OFFBOARD_CONTROL_MODE_POSITION 4 +//#define MAVLINK_OFFBOARD_CONTROL_FLAG_ARMED 0x10 + +//#warning THIS IS A HUGE HACK AND SHOULD NEVER SHOW UP IN ANY GIT REPOSITORY +// mavlink_message_t message; + +// mavlink_set_quad_swarm_roll_pitch_yaw_thrust_t sp; + +// sp.group = 0; + +// /* set rate mode, set zero rates and 20% throttle */ +// sp.mode = MAVLINK_OFFBOARD_CONTROL_MODE_RATES | MAVLINK_OFFBOARD_CONTROL_FLAG_ARMED; + +// sp.roll[0] = INT16_MAX * 0.0f; +// sp.pitch[0] = INT16_MAX * 0.0f; +// sp.yaw[0] = INT16_MAX * 0.0f; +// sp.thrust[0] = UINT16_MAX * 0.3f; + + +// /* send from system 200 and component 0 */ +// mavlink_msg_set_quad_swarm_roll_pitch_yaw_thrust_encode(200, 0, &message, &sp); + +// sendMessage(message); +} + +/** +* If the acitve UAS (the UAS that was selected) is not the one that is currently +* active, then change the active UAS to the one that was selected. +*/ +void UAS::setSelected() +{ + if (UASManager::instance()->getActiveUAS() != this) + { + UASManager::instance()->setActiveUAS(this); + emit systemSelected(true); + } +} + +/** +* @return if the active UAS is the current UAS +**/ +bool UAS::getSelected() const +{ + return (UASManager::instance()->getActiveUAS() == this); +} + +void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) +{ + if (!link) return; + if (!links->contains(link)) + { + addLink(link); + // qDebug() << __FILE__ << __LINE__ << "ADDED LINK!" << link->getName(); + } + + if (!components.contains(message.compid)) + { + QString componentName; + + switch (message.compid) + { + case MAV_COMP_ID_ALL: + { + componentName = "ANONYMOUS"; + break; + } + case MAV_COMP_ID_IMU: + { + componentName = "IMU #1"; + break; + } + case MAV_COMP_ID_CAMERA: + { + componentName = "CAMERA"; + break; + } + case MAV_COMP_ID_MISSIONPLANNER: + { + componentName = "MISSIONPLANNER"; + break; + } + } + + components.insert(message.compid, componentName); + emit componentCreated(uasId, message.compid, componentName); + } + + // qDebug() << "UAS RECEIVED from" << message.sysid << "component" << message.compid << "msg id" << message.msgid << "seq no" << message.seq; + + // Only accept messages from this system (condition 1) + // and only then if a) attitudeStamped is disabled OR b) attitudeStamped is enabled + // and we already got one attitude packet + if (message.sysid == uasId && (!attitudeStamped || (attitudeStamped && (lastAttitude != 0)) || message.msgid == MAVLINK_MSG_ID_ATTITUDE)) + { + QString uasState; + QString stateDescription; + + bool multiComponentSourceDetected = false; + bool wrongComponent = false; + + switch (message.compid) + { + case MAV_COMP_ID_IMU_2: + // Prefer IMU 2 over IMU 1 (FIXME) + componentID[message.msgid] = MAV_COMP_ID_IMU_2; + break; + default: + // Do nothing + break; + } + + // Store component ID + if (componentID[message.msgid] == -1) + { + // Prefer the first component + componentID[message.msgid] = message.compid; + } + else + { + // Got this message already + if (componentID[message.msgid] != message.compid) + { + componentMulti[message.msgid] = true; + wrongComponent = true; + } + } + + if (componentMulti[message.msgid] == true) multiComponentSourceDetected = true; + + + switch (message.msgid) + { + case MAVLINK_MSG_ID_HEARTBEAT: + { + if (multiComponentSourceDetected && wrongComponent) + { + break; + } + lastHeartbeat = QGC::groundTimeUsecs(); + emit heartbeat(this); + mavlink_heartbeat_t state; + mavlink_msg_heartbeat_decode(&message, &state); + + // Send the base_mode and system_status values to the plotter. This uses the ground time + // so the Ground Time checkbox must be ticked for these values to display + quint64 time = getUnixTime(); + QString name = QString("M%1:HEARTBEAT.%2").arg(message.sysid); + emit valueChanged(uasId, name.arg("base_mode"), "bits", state.base_mode, time); + emit valueChanged(uasId, name.arg("custom_mode"), "bits", state.custom_mode, time); + emit valueChanged(uasId, name.arg("system_status"), "-", state.system_status, time); + + // Set new type if it has changed + if (this->type != state.type) + { + this->type = state.type; + if (airframe == 0) + { + switch (type) + { + case MAV_TYPE_FIXED_WING: + setAirframe(UASInterface::QGC_AIRFRAME_EASYSTAR); + break; + case MAV_TYPE_QUADROTOR: + setAirframe(UASInterface::QGC_AIRFRAME_CHEETAH); + break; + case MAV_TYPE_HEXAROTOR: + setAirframe(UASInterface::QGC_AIRFRAME_HEXCOPTER); + break; + default: + // Do nothing + break; + } + } + this->autopilot = state.autopilot; + emit systemTypeSet(this, type); + } + + bool currentlyArmed = state.base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY; + + if (systemIsArmed != currentlyArmed) + { + systemIsArmed = currentlyArmed; + emit armingChanged(systemIsArmed); + if (systemIsArmed) + { + emit armed(); + } + else + { + emit disarmed(); + } + } + + QString audiostring = QString("System %1").arg(uasId); + QString stateAudio = ""; + QString modeAudio = ""; + QString navModeAudio = ""; + bool statechanged = false; + bool modechanged = false; + + QString audiomodeText = getAudioModeTextFor(static_cast(state.base_mode)); + + + if ((state.system_status != this->status) && state.system_status != MAV_STATE_UNINIT) + { + statechanged = true; + this->status = state.system_status; + getStatusForCode((int)state.system_status, uasState, stateDescription); + emit statusChanged(this, uasState, stateDescription); + emit statusChanged(this->status); + + shortStateText = uasState; + + // Adjust for better audio + if (uasState == QString("STANDBY")) uasState = QString("standing by"); + if (uasState == QString("EMERGENCY")) uasState = QString("emergency condition"); + if (uasState == QString("CRITICAL")) uasState = QString("critical condition"); + if (uasState == QString("SHUTDOWN")) uasState = QString("shutting down"); + + stateAudio = uasState; + } + + if (this->mode != static_cast(state.base_mode)) + { + modechanged = true; + this->mode = static_cast(state.base_mode); + shortModeText = getShortModeTextFor(this->mode); + + emit modeChanged(this->getUASID(), shortModeText, ""); + + modeAudio = " is now in " + audiomodeText; + } + + if (navMode != state.custom_mode) + { + emit navModeChanged(uasId, state.custom_mode, getNavModeText(state.custom_mode)); + navMode = state.custom_mode; + //navModeAudio = tr(" changed nav mode to ") + tr("FIXME"); + } + + // AUDIO + if (modechanged && statechanged) + { + // Output both messages + audiostring += modeAudio + " and " + stateAudio; + } + else if (modechanged || statechanged) + { + // Output the one message + audiostring += modeAudio + stateAudio + navModeAudio; + } + + if (statechanged && ((int)state.system_status == (int)MAV_STATE_CRITICAL || state.system_status == (int)MAV_STATE_EMERGENCY)) + { + GAudioOutput::instance()->say(QString("emergency for system %1").arg(this->getUASID())); + QTimer::singleShot(3000, GAudioOutput::instance(), SLOT(startEmergency())); + } + else if (modechanged || statechanged) + { + GAudioOutput::instance()->stopEmergency(); + GAudioOutput::instance()->say(audiostring.toLower()); + } + } + + break; + case MAVLINK_MSG_ID_SYS_STATUS: + { + if (multiComponentSourceDetected && wrongComponent) + { + break; + } + mavlink_sys_status_t state; + mavlink_msg_sys_status_decode(&message, &state); + + // Prepare for sending data to the realtime plotter, which is every field excluding onboard_control_sensors_present. + quint64 time = getUnixTime(); + QString name = QString("M%1:SYS_STATUS.%2").arg(message.sysid); + emit valueChanged(uasId, name.arg("sensors_enabled"), "bits", state.onboard_control_sensors_enabled, time); + emit valueChanged(uasId, name.arg("sensors_health"), "bits", state.onboard_control_sensors_health, time); + emit valueChanged(uasId, name.arg("errors_comm"), "-", state.errors_comm, time); + emit valueChanged(uasId, name.arg("errors_count1"), "-", state.errors_count1, time); + emit valueChanged(uasId, name.arg("errors_count2"), "-", state.errors_count2, time); + emit valueChanged(uasId, name.arg("errors_count3"), "-", state.errors_count3, time); + emit valueChanged(uasId, name.arg("errors_count4"), "-", state.errors_count4, time); + + // Process CPU load. + emit loadChanged(this,state.load/10.0f); + emit valueChanged(uasId, name.arg("load"), "%", state.load/10.0f, time); + + // Battery charge/time remaining/voltage calculations + currentVoltage = state.voltage_battery/1000.0f; + lpVoltage = filterVoltage(currentVoltage); + tickLowpassVoltage = tickLowpassVoltage*0.8f + 0.2f*currentVoltage; + + + // We don't want to tick above the threshold + if (tickLowpassVoltage > tickVoltage) + { + lastTickVoltageValue = tickLowpassVoltage; + } + + if ((startVoltage > 0.0f) && (tickLowpassVoltage < tickVoltage) && (fabs(lastTickVoltageValue - tickLowpassVoltage) > 0.1f) + /* warn if lower than treshold */ + && (lpVoltage < tickVoltage) + /* warn only if we have at least the voltage of an empty LiPo cell, else we're sampling something wrong */ + && (currentVoltage > 3.3f) + /* warn only if current voltage is really still lower by a reasonable amount */ + && ((currentVoltage - 0.2f) < tickVoltage) + /* warn only every 12 seconds */ + && (QGC::groundTimeUsecs() - lastVoltageWarning) > 12000000) + { + GAudioOutput::instance()->say(QString("voltage warning: %1 volts").arg(lpVoltage, 0, 'f', 1, QChar(' '))); + lastVoltageWarning = QGC::groundTimeUsecs(); + lastTickVoltageValue = tickLowpassVoltage; + } + + if (startVoltage == -1.0f && currentVoltage > 0.1f) startVoltage = currentVoltage; + timeRemaining = calculateTimeRemaining(); + if (!batteryRemainingEstimateEnabled && chargeLevel != -1) + { + chargeLevel = state.battery_remaining; + } + emit batteryChanged(this, lpVoltage, getChargeLevel(), timeRemaining); + emit valueChanged(uasId, name.arg("battery_remaining"), "%", getChargeLevel(), time); + emit voltageChanged(message.sysid, currentVoltage); + emit valueChanged(uasId, name.arg("battery_voltage"), "V", currentVoltage, time); + + // And if the battery current draw is measured, log that also. + if (state.current_battery != -1) + { + emit valueChanged(uasId, name.arg("battery_current"), "A", ((double)state.current_battery) / 100.0f, time); + } + + // LOW BATTERY ALARM + if (lpVoltage < warnVoltage && (currentVoltage - 0.2f) < warnVoltage && (currentVoltage > 3.3)) + { + startLowBattAlarm(); + } + else + { + stopLowBattAlarm(); + } + + // control_sensors_enabled: + // relevant bits: 11: attitude stabilization, 12: yaw position, 13: z/altitude control, 14: x/y position control + emit attitudeControlEnabled(state.onboard_control_sensors_enabled & (1 << 11)); + emit positionYawControlEnabled(state.onboard_control_sensors_enabled & (1 << 12)); + emit positionZControlEnabled(state.onboard_control_sensors_enabled & (1 << 13)); + emit positionXYControlEnabled(state.onboard_control_sensors_enabled & (1 << 14)); + + // Trigger drop rate updates as needed. Here we convert the incoming + // drop_rate_comm value from 1/100 of a percent in a uint16 to a true + // percentage as a float. We also cap the incoming value at 100% as defined + // by the MAVLink specifications. + if (state.drop_rate_comm > 10000) + { + state.drop_rate_comm = 10000; + } + emit dropRateChanged(this->getUASID(), state.drop_rate_comm/100.0f); + emit valueChanged(uasId, name.arg("drop_rate_comm"), "%", state.drop_rate_comm/100.0f, time); + } + break; + case MAVLINK_MSG_ID_ATTITUDE: + { + mavlink_attitude_t attitude; + mavlink_msg_attitude_decode(&message, &attitude); + quint64 time = getUnixReferenceTime(attitude.time_boot_ms); + + emit attitudeChanged(this, message.compid, QGC::limitAngleToPMPIf(attitude.roll), QGC::limitAngleToPMPIf(attitude.pitch), QGC::limitAngleToPMPIf(attitude.yaw), time); + + if (!wrongComponent) + { + lastAttitude = time; + roll = QGC::limitAngleToPMPIf(attitude.roll); + pitch = QGC::limitAngleToPMPIf(attitude.pitch); + yaw = QGC::limitAngleToPMPIf(attitude.yaw); + + // // Emit in angles + + // // Convert yaw angle to compass value + // // in 0 - 360 deg range + // float compass = (yaw/M_PI)*180.0+360.0f; + // if (compass > -10000 && compass < 10000) + // { + // while (compass > 360.0f) { + // compass -= 360.0f; + // } + // } + // else + // { + // // Set to 0, since it is an invalid value + // compass = 0.0f; + // } + + attitudeKnown = true; + emit attitudeChanged(this, roll, pitch, yaw, time); + emit attitudeSpeedChanged(uasId, attitude.rollspeed, attitude.pitchspeed, attitude.yawspeed, time); + } + } + break; + case MAVLINK_MSG_ID_LOCAL_POSITION_NED_SYSTEM_GLOBAL_OFFSET: + { + mavlink_local_position_ned_system_global_offset_t offset; + mavlink_msg_local_position_ned_system_global_offset_decode(&message, &offset); + nedPosGlobalOffset.setX(offset.x); + nedPosGlobalOffset.setY(offset.y); + nedPosGlobalOffset.setZ(offset.z); + nedAttGlobalOffset.setX(offset.roll); + nedAttGlobalOffset.setY(offset.pitch); + nedAttGlobalOffset.setZ(offset.yaw); + } + break; + case MAVLINK_MSG_ID_HIL_CONTROLS: + { + mavlink_hil_controls_t hil; + mavlink_msg_hil_controls_decode(&message, &hil); + emit hilControlsChanged(hil.time_usec, hil.roll_ailerons, hil.pitch_elevator, hil.yaw_rudder, hil.throttle, hil.mode, hil.nav_mode); + } + break; + case MAVLINK_MSG_ID_VFR_HUD: + { + mavlink_vfr_hud_t hud; + mavlink_msg_vfr_hud_decode(&message, &hud); + quint64 time = getUnixTime(); + // Display updated values + emit thrustChanged(this, hud.throttle/100.0); + + if (!attitudeKnown) + { + yaw = QGC::limitAngleToPMPId((((double)hud.heading-180.0)/360.0)*M_PI); + emit attitudeChanged(this, roll, pitch, yaw, time); + } + + emit altitudeChanged(uasId, hud.alt); + emit speedChanged(this, hud.airspeed, 0.0f, hud.climb, time); + } + break; + case MAVLINK_MSG_ID_LOCAL_POSITION_NED: + //std::cerr << std::endl; + //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; + { + mavlink_local_position_ned_t pos; + mavlink_msg_local_position_ned_decode(&message, &pos); + quint64 time = getUnixTime(pos.time_boot_ms); + + // Emit position always with component ID + emit localPositionChanged(this, message.compid, pos.x, pos.y, pos.z, time); + + + if (!wrongComponent) + { + localX = pos.x; + localY = pos.y; + localZ = pos.z; + + // Emit + + emit localPositionChanged(this, pos.x, pos.y, pos.z, time); + emit speedChanged(this, pos.vx, pos.vy, pos.vz, time); + + // Set internal state + if (!positionLock) { + // If position was not locked before, notify positive + GAudioOutput::instance()->notifyPositive(); + } + positionLock = true; + isLocalPositionKnown = true; + } + } + break; + case MAVLINK_MSG_ID_GLOBAL_VISION_POSITION_ESTIMATE: + { + mavlink_global_vision_position_estimate_t pos; + mavlink_msg_global_vision_position_estimate_decode(&message, &pos); + quint64 time = getUnixTime(pos.usec); + emit localPositionChanged(this, message.compid, pos.x, pos.y, pos.z, time); + emit attitudeChanged(this, message.compid, pos.roll, pos.pitch, pos.yaw, time); + } + break; + case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: + //std::cerr << std::endl; + //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; + { + mavlink_global_position_int_t pos; + mavlink_msg_global_position_int_decode(&message, &pos); + quint64 time = getUnixTime(); + latitude = pos.lat/(double)1E7; + longitude = pos.lon/(double)1E7; + altitude = pos.alt/1000.0; + speedX = pos.vx/100.0; + speedY = pos.vy/100.0; + speedZ = pos.vz/100.0; + emit globalPositionChanged(this, latitude, longitude, altitude, time); + emit speedChanged(this, speedX, speedY, speedZ, time); + + // Set internal state + if (!positionLock) + { + // If position was not locked before, notify positive + GAudioOutput::instance()->notifyPositive(); + } + positionLock = true; + isGlobalPositionKnown = true; + //TODO fix this hack for forwarding of global position for patch antenna tracking + forwardMessage(message); + } + break; + case MAVLINK_MSG_ID_GPS_RAW_INT: + { + mavlink_gps_raw_int_t pos; + mavlink_msg_gps_raw_int_decode(&message, &pos); + + // SANITY CHECK + // only accept values in a realistic range + // quint64 time = getUnixTime(pos.time_usec); + quint64 time = getUnixTime(pos.time_usec); + + emit gpsLocalizationChanged(this, pos.fix_type); + // TODO: track localization state not only for gps but also for other loc. sources + int loc_type = pos.fix_type; + if (loc_type == 1) + { + loc_type = 0; + } + emit localizationChanged(this, loc_type); + + if (pos.fix_type > 2) + { + emit globalPositionChanged(this, pos.lat/(double)1E7, pos.lon/(double)1E7, pos.alt/1000.0, time); + latitude = pos.lat/(double)1E7; + longitude = pos.lon/(double)1E7; + altitude = pos.alt/1000.0; + positionLock = true; + isGlobalPositionKnown = true; + + // Check for NaN + int alt = pos.alt; + if (!isnan(alt) && !isinf(alt)) + { + alt = 0; + //emit textMessageReceived(uasId, message.compid, 255, "GCS ERROR: RECEIVED NaN or Inf FOR ALTITUDE"); + } + // FIXME REMOVE LATER emit valueChanged(uasId, "altitude", "m", pos.alt/(double)1E3, time); + // Smaller than threshold and not NaN + + float vel = pos.vel/100.0f; + + if (vel < 1000000 && !isnan(vel) && !isinf(vel)) + { + // FIXME REMOVE LATER emit valueChanged(uasId, "speed", "m/s", vel, time); + //qDebug() << "GOT GPS RAW"; + // emit speedChanged(this, (double)pos.v, 0.0, 0.0, time); + } + else + { + emit textMessageReceived(uasId, message.compid, 255, QString("GCS ERROR: RECEIVED INVALID SPEED OF %1 m/s").arg(vel)); + } + } + } + break; + case MAVLINK_MSG_ID_GPS_STATUS: + { + mavlink_gps_status_t pos; + mavlink_msg_gps_status_decode(&message, &pos); + for(int i = 0; i < (int)pos.satellites_visible; i++) + { + emit gpsSatelliteStatusChanged(uasId, (unsigned char)pos.satellite_prn[i], (unsigned char)pos.satellite_elevation[i], (unsigned char)pos.satellite_azimuth[i], (unsigned char)pos.satellite_snr[i], static_cast(pos.satellite_used[i])); + } + } + break; + case MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN: + { + mavlink_gps_global_origin_t pos; + mavlink_msg_gps_global_origin_decode(&message, &pos); + emit homePositionChanged(uasId, pos.latitude / 10000000.0, pos.longitude / 10000000.0, pos.altitude / 1000.0); + } + break; + case MAVLINK_MSG_ID_RC_CHANNELS_RAW: + { + mavlink_rc_channels_raw_t channels; + mavlink_msg_rc_channels_raw_decode(&message, &channels); + emit remoteControlRSSIChanged(channels.rssi/255.0f); + emit remoteControlChannelRawChanged(0, channels.chan1_raw); + emit remoteControlChannelRawChanged(1, channels.chan2_raw); + emit remoteControlChannelRawChanged(2, channels.chan3_raw); + emit remoteControlChannelRawChanged(3, channels.chan4_raw); + emit remoteControlChannelRawChanged(4, channels.chan5_raw); + emit remoteControlChannelRawChanged(5, channels.chan6_raw); + emit remoteControlChannelRawChanged(6, channels.chan7_raw); + emit remoteControlChannelRawChanged(7, channels.chan8_raw); + } + break; + case MAVLINK_MSG_ID_RC_CHANNELS_SCALED: + { + mavlink_rc_channels_scaled_t channels; + mavlink_msg_rc_channels_scaled_decode(&message, &channels); + emit remoteControlRSSIChanged(channels.rssi/255.0f); + emit remoteControlChannelScaledChanged(0, channels.chan1_scaled/10000.0f); + emit remoteControlChannelScaledChanged(1, channels.chan2_scaled/10000.0f); + emit remoteControlChannelScaledChanged(2, channels.chan3_scaled/10000.0f); + emit remoteControlChannelScaledChanged(3, channels.chan4_scaled/10000.0f); + emit remoteControlChannelScaledChanged(4, channels.chan5_scaled/10000.0f); + emit remoteControlChannelScaledChanged(5, channels.chan6_scaled/10000.0f); + emit remoteControlChannelScaledChanged(6, channels.chan7_scaled/10000.0f); + emit remoteControlChannelScaledChanged(7, channels.chan8_scaled/10000.0f); + } + break; + case MAVLINK_MSG_ID_PARAM_VALUE: + { + mavlink_param_value_t value; + mavlink_msg_param_value_decode(&message, &value); + QByteArray bytes(value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN); + // Construct a string stopping at the first NUL (0) character, else copy the whole + // byte array (max MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN, so safe) + QString parameterName(bytes); + int component = message.compid; + mavlink_param_union_t val; + val.param_float = value.param_value; + val.type = value.param_type; + + // Insert component if necessary + if (!parameters.contains(component)) + { + parameters.insert(component, new QMap()); + } + + // Insert parameter into registry + if (parameters.value(component)->contains(parameterName)) parameters.value(component)->remove(parameterName); + + // Insert with correct type + switch (value.param_type) + { + case MAV_PARAM_TYPE_REAL32: + { + // Variant + QVariant param(val.param_float); + parameters.value(component)->insert(parameterName, param); + // Emit change + emit parameterChanged(uasId, message.compid, parameterName, param); + emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); +// qDebug() << "RECEIVED PARAM:" << param; + } + break; + case MAV_PARAM_TYPE_UINT32: + { + // Variant + QVariant param(val.param_uint32); + parameters.value(component)->insert(parameterName, param); + // Emit change + emit parameterChanged(uasId, message.compid, parameterName, param); + emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); +// qDebug() << "RECEIVED PARAM:" << param; + } + break; + case MAV_PARAM_TYPE_INT32: + { + // Variant + QVariant param(val.param_int32); + parameters.value(component)->insert(parameterName, param); + // Emit change + emit parameterChanged(uasId, message.compid, parameterName, param); + emit parameterChanged(uasId, message.compid, value.param_count, value.param_index, parameterName, param); +// qDebug() << "RECEIVED PARAM:" << param; + } + break; + default: + qCritical() << "INVALID DATA TYPE USED AS PARAMETER VALUE: " << value.param_type; + } + } + break; + case MAVLINK_MSG_ID_COMMAND_ACK: + { + mavlink_command_ack_t ack; + mavlink_msg_command_ack_decode(&message, &ack); + switch (ack.result) + { + case MAV_RESULT_ACCEPTED: + { + emit textMessageReceived(uasId, message.compid, 0, tr("SUCCESS: Executed CMD: %1").arg(ack.command)); + } + break; + case MAV_RESULT_TEMPORARILY_REJECTED: + { + emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Temporarily rejected CMD: %1").arg(ack.command)); + } + break; + case MAV_RESULT_DENIED: + { + emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Denied CMD: %1").arg(ack.command)); + } + break; + case MAV_RESULT_UNSUPPORTED: + { + emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Unsupported CMD: %1").arg(ack.command)); + } + break; + case MAV_RESULT_FAILED: + { + emit textMessageReceived(uasId, message.compid, 0, tr("FAILURE: Failed CMD: %1").arg(ack.command)); + } + break; + } + } + case MAVLINK_MSG_ID_ROLL_PITCH_YAW_THRUST_SETPOINT: + { + mavlink_roll_pitch_yaw_thrust_setpoint_t out; + mavlink_msg_roll_pitch_yaw_thrust_setpoint_decode(&message, &out); + quint64 time = getUnixTimeFromMs(out.time_boot_ms); + emit attitudeThrustSetPointChanged(this, out.roll, out.pitch, out.yaw, out.thrust, time); + } + break; + case MAVLINK_MSG_ID_MISSION_COUNT: + { + mavlink_mission_count_t wpc; + mavlink_msg_mission_count_decode(&message, &wpc); + if(wpc.target_system == mavlink->getSystemId() || wpc.target_system == 0) + { + waypointManager.handleWaypointCount(message.sysid, message.compid, wpc.count); + } + else + { + qDebug() << "Got waypoint message, but was wrong system id" << wpc.target_system; + } + } + break; + + case MAVLINK_MSG_ID_MISSION_ITEM: + { + mavlink_mission_item_t wp; + mavlink_msg_mission_item_decode(&message, &wp); + //qDebug() << "got waypoint (" << wp.seq << ") from ID " << message.sysid << " x=" << wp.x << " y=" << wp.y << " z=" << wp.z; + if(wp.target_system == mavlink->getSystemId() || wp.target_system == 0) + { + waypointManager.handleWaypoint(message.sysid, message.compid, &wp); + } + else + { + qDebug() << "Got waypoint message, but was wrong system id" << wp.target_system; + } + } + break; + + case MAVLINK_MSG_ID_MISSION_ACK: + { + mavlink_mission_ack_t wpa; + mavlink_msg_mission_ack_decode(&message, &wpa); + if((wpa.target_system == mavlink->getSystemId() || wpa.target_system == 0) && + (wpa.target_component == mavlink->getComponentId() || wpa.target_component == 0)) + { + waypointManager.handleWaypointAck(message.sysid, message.compid, &wpa); + } + } + break; + + case MAVLINK_MSG_ID_MISSION_REQUEST: + { + mavlink_mission_request_t wpr; + mavlink_msg_mission_request_decode(&message, &wpr); + if(wpr.target_system == mavlink->getSystemId() || wpr.target_system == 0) + { + waypointManager.handleWaypointRequest(message.sysid, message.compid, &wpr); + } + else + { + qDebug() << "Got waypoint message, but was wrong system id" << wpr.target_system; + } + } + break; + + case MAVLINK_MSG_ID_MISSION_ITEM_REACHED: + { + mavlink_mission_item_reached_t wpr; + mavlink_msg_mission_item_reached_decode(&message, &wpr); + waypointManager.handleWaypointReached(message.sysid, message.compid, &wpr); + QString text = QString("System %1 reached waypoint %2").arg(getUASName()).arg(wpr.seq); + GAudioOutput::instance()->say(text); + emit textMessageReceived(message.sysid, message.compid, 0, text); + } + break; + + case MAVLINK_MSG_ID_MISSION_CURRENT: + { + mavlink_mission_current_t wpc; + mavlink_msg_mission_current_decode(&message, &wpc); + waypointManager.handleWaypointCurrent(message.sysid, message.compid, &wpc); + } + break; + + case MAVLINK_MSG_ID_LOCAL_POSITION_SETPOINT: + { + if (multiComponentSourceDetected && wrongComponent) + { + break; + } + mavlink_local_position_setpoint_t p; + mavlink_msg_local_position_setpoint_decode(&message, &p); + emit positionSetPointsChanged(uasId, p.x, p.y, p.z, p.yaw, QGC::groundTimeUsecs()); + } + break; + case MAVLINK_MSG_ID_SET_LOCAL_POSITION_SETPOINT: + { + mavlink_set_local_position_setpoint_t p; + mavlink_msg_set_local_position_setpoint_decode(&message, &p); + emit userPositionSetPointsChanged(uasId, p.x, p.y, p.z, p.yaw); + } + break; + case MAVLINK_MSG_ID_STATUSTEXT: + { + QByteArray b; + b.resize(MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN); + mavlink_msg_statustext_get_text(&message, b.data()); + //b.append('\0'); + QString text = QString(b); + int severity = mavlink_msg_statustext_get_severity(&message); + //qDebug() << "RECEIVED STATUS:" << text;false + //emit statusTextReceived(severity, text); + + if (text.startsWith("#audio:")) + { + text.remove("#audio:"); + emit textMessageReceived(uasId, message.compid, severity, QString("Audio message: ") + text); + GAudioOutput::instance()->say(text, severity); + } + else + { + emit textMessageReceived(uasId, message.compid, severity, text); + } + } + break; + case MAVLINK_MSG_ID_SERVO_OUTPUT_RAW: + { + mavlink_servo_output_raw_t raw; + mavlink_msg_servo_output_raw_decode(&message, &raw); + + if (hilEnabled) + { + emit hilActuatorsChanged(static_cast(getUnixTimeFromMs(raw.time_boot_ms)), static_cast(raw.servo1_raw), + static_cast(raw.servo2_raw), static_cast(raw.servo3_raw), + static_cast(raw.servo4_raw), static_cast(raw.servo5_raw), static_cast(raw.servo6_raw), + static_cast(raw.servo7_raw), static_cast(raw.servo8_raw)); + } + } + break; +#ifdef MAVLINK_ENABLED_PIXHAWK + case MAVLINK_MSG_ID_DATA_TRANSMISSION_HANDSHAKE: + { + mavlink_data_transmission_handshake_t p; + mavlink_msg_data_transmission_handshake_decode(&message, &p); + imageSize = p.size; + imagePackets = p.packets; + imagePayload = p.payload; + imageQuality = p.jpg_quality; + imageType = p.type; + imageWidth = p.width; + imageHeight = p.height; + imageStart = QGC::groundTimeMilliseconds(); + } + break; + + case MAVLINK_MSG_ID_ENCAPSULATED_DATA: + { + mavlink_encapsulated_data_t img; + mavlink_msg_encapsulated_data_decode(&message, &img); + int seq = img.seqnr; + int pos = seq * imagePayload; + + // Check if we have a valid transaction + if (imagePackets == 0) + { + // NO VALID TRANSACTION - ABORT + // Restart statemachine + imagePacketsArrived = 0; + } + + for (int i = 0; i < imagePayload; ++i) + { + if (pos <= imageSize) { + imageRecBuffer[pos] = img.data[i]; + } + ++pos; + } + + ++imagePacketsArrived; + + // emit signal if all packets arrived + if ((imagePacketsArrived >= imagePackets)) + { + // Restart statemachine + imagePacketsArrived = 0; + emit imageReady(this); + //qDebug() << "imageReady emitted. all packets arrived"; + } + } + break; + + + +#endif + // case MAVLINK_MSG_ID_OBJECT_DETECTION_EVENT: + // { + // mavlink_object_detection_event_t event; + // mavlink_msg_object_detection_event_decode(&message, &event); + // QString str(event.name); + // emit objectDetected(event.time, event.object_id, event.type, str, event.quality, event.bearing, event.distance); + // } + // break; + // WILL BE ENABLED ONCE MESSAGE IS IN COMMON MESSAGE SET + // case MAVLINK_MSG_ID_MEMORY_VECT: + // { + // mavlink_memory_vect_t vect; + // mavlink_msg_memory_vect_decode(&message, &vect); + // QString str("mem_%1"); + // quint64 time = getUnixTime(0); + // int16_t *mem0 = (int16_t *)&vect.value[0]; + // uint16_t *mem1 = (uint16_t *)&vect.value[0]; + // int32_t *mem2 = (int32_t *)&vect.value[0]; + // // uint32_t *mem3 = (uint32_t *)&vect.value[0]; causes overload problem + // float *mem4 = (float *)&vect.value[0]; + // if ( vect.ver == 0) vect.type = 0, vect.ver = 1; else ; + // if ( vect.ver == 1) + // { + // switch (vect.type) { + // default: + // case 0: + // for (int i = 0; i < 16; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "i16", mem0[i], time); + // break; + // case 1: + // for (int i = 0; i < 16; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "ui16", mem1[i], time); + // break; + // case 2: + // for (int i = 0; i < 16; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "Q15", (float)mem0[i]/32767.0, time); + // break; + // case 3: + // for (int i = 0; i < 16; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*2)), "1Q14", (float)mem0[i]/16383.0, time); + // break; + // case 4: + // for (int i = 0; i < 8; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "i32", mem2[i], time); + // break; + // case 5: + // for (int i = 0; i < 8; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "i32", mem2[i], time); + // break; + // case 6: + // for (int i = 0; i < 8; i++) + // // FIXME REMOVE LATER emit valueChanged(uasId, str.arg(vect.address+(i*4)), "float", mem4[i], time); + // break; + // } + // } + // } + // break; +#ifdef MAVLINK_ENABLED_UALBERTA + case MAVLINK_MSG_ID_NAV_FILTER_BIAS: + { + mavlink_nav_filter_bias_t bias; + mavlink_msg_nav_filter_bias_decode(&message, &bias); + quint64 time = getUnixTime(); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[0]", "raw", bias.accel_0, time); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[1]", "raw", bias.accel_1, time); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_f[2]", "raw", bias.accel_2, time); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[0]", "raw", bias.gyro_0, time); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[1]", "raw", bias.gyro_1, time); + // FIXME REMOVE LATER emit valueChanged(uasId, "b_w[2]", "raw", bias.gyro_2, time); + } + break; + case MAVLINK_MSG_ID_RADIO_CALIBRATION: + { + mavlink_radio_calibration_t radioMsg; + mavlink_msg_radio_calibration_decode(&message, &radioMsg); + QVector aileron; + QVector elevator; + QVector rudder; + QVector gyro; + QVector pitch; + QVector throttle; + + for (int i=0; i radioData = new RadioCalibrationData(aileron, elevator, rudder, gyro, pitch, throttle); + emit radioCalibrationReceived(radioData); + delete radioData; + } + break; + +#endif + // Messages to ignore + case MAVLINK_MSG_ID_RAW_IMU: + case MAVLINK_MSG_ID_SCALED_IMU: + case MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT: + case MAVLINK_MSG_ID_RAW_PRESSURE: + case MAVLINK_MSG_ID_SCALED_PRESSURE: + case MAVLINK_MSG_ID_OPTICAL_FLOW: + case MAVLINK_MSG_ID_DEBUG_VECT: + case MAVLINK_MSG_ID_DEBUG: + case MAVLINK_MSG_ID_NAMED_VALUE_FLOAT: + case MAVLINK_MSG_ID_NAMED_VALUE_INT: + case MAVLINK_MSG_ID_MANUAL_CONTROL: + case MAVLINK_MSG_ID_HIGHRES_IMU: + break; + default: + { + if (!unknownPackets.contains(message.msgid)) + { + unknownPackets.append(message.msgid); + QString errString = tr("UNABLE TO DECODE MESSAGE NUMBER %1").arg(message.msgid); + //GAudioOutput::instance()->say(errString+tr(", please check console for details.")); + emit textMessageReceived(uasId, message.compid, 255, errString); + std::cout << "Unable to decode message from system " << std::dec << static_cast(message.sysid) << " with message id:" << static_cast(message.msgid) << std::endl; + //qDebug() << std::cerr << "Unable to decode message from system " << std::dec << static_cast(message.acid) << " with message id:" << static_cast(message.msgid) << std::endl; + } + } + break; + } + } +} + + +#if defined(QGC_PROTOBUF_ENABLED) +/** +* Receive an extended message. +* @param link +* @param message +*/ +void UAS::receiveExtendedMessage(LinkInterface* link, std::tr1::shared_ptr message) +{ + if (!link) + { + return; + } + if (!links->contains(link)) + { + addLink(link); + } + + const google::protobuf::Descriptor* descriptor = message->GetDescriptor(); + if (!descriptor) + { + return; + } + + const google::protobuf::FieldDescriptor* headerField = descriptor->FindFieldByName("header"); + if (!headerField) + { + return; + } + + const google::protobuf::Descriptor* headerDescriptor = headerField->message_type(); + if (!headerDescriptor) + { + return; + } + + const google::protobuf::FieldDescriptor* sourceSysIdField = headerDescriptor->FindFieldByName("source_sysid"); + if (!sourceSysIdField) + { + return; + } + + const google::protobuf::Reflection* reflection = message->GetReflection(); + const google::protobuf::Message& headerMsg = reflection->GetMessage(*message, headerField); + const google::protobuf::Reflection* headerReflection = headerMsg.GetReflection(); + + int source_sysid = headerReflection->GetInt32(headerMsg, sourceSysIdField); + + if (source_sysid != uasId) + { + return; + } + +#ifdef QGC_USE_PIXHAWK_MESSAGES + if (message->GetTypeName() == overlay.GetTypeName()) + { + receivedOverlayTimestamp = QGC::groundTimeSeconds(); + overlayMutex.lock(); + overlay.CopyFrom(*message); + overlayMutex.unlock(); + emit overlayChanged(this); + } + else if (message->GetTypeName() == obstacleList.GetTypeName()) + { + receivedObstacleListTimestamp = QGC::groundTimeSeconds(); + obstacleListMutex.lock(); + obstacleList.CopyFrom(*message); + obstacleListMutex.unlock(); + emit obstacleListChanged(this); + } + else if (message->GetTypeName() == path.GetTypeName()) + { + receivedPathTimestamp = QGC::groundTimeSeconds(); + pathMutex.lock(); + path.CopyFrom(*message); + pathMutex.unlock(); + emit pathChanged(this); + } + else if (message->GetTypeName() == pointCloud.GetTypeName()) + { + receivedPointCloudTimestamp = QGC::groundTimeSeconds(); + pointCloudMutex.lock(); + pointCloud.CopyFrom(*message); + pointCloudMutex.unlock(); + emit pointCloudChanged(this); + } + else if (message->GetTypeName() == rgbdImage.GetTypeName()) + { + receivedRGBDImageTimestamp = QGC::groundTimeSeconds(); + rgbdImageMutex.lock(); + rgbdImage.CopyFrom(*message); + rgbdImageMutex.unlock(); + emit rgbdImageChanged(this); + } +#endif +} + +#endif + +/** +* Set the home position of the UAS. +* @param lat The latitude fo the home position +* @param lon The longitute of the home position +* @param alt The altitude of the home position +*/ +void UAS::setHomePosition(double lat, double lon, double alt) +{ + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText("Setting new World Coordinate Frame Origin"); + msgBox.setInformativeText("Do you want to set a new origin? Waypoints defined in the local frame will be shifted in their physical location"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + + // Close the message box shortly after the click to prevent accidental clicks + QTimer::singleShot(5000, &msgBox, SLOT(reject())); + + + if (ret == QMessageBox::Yes) + { + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_DO_SET_HOME, 1, 0, 0, 0, 0, lat, lon, alt); + // Send message twice to increase chance that it reaches its goal + sendMessage(msg); + + // Send new home position to UAS + mavlink_set_gps_global_origin_t home; + home.target_system = uasId; + home.latitude = lat*1E7; + home.longitude = lon*1E7; + home.altitude = alt*1000; + qDebug() << "lat:" << home.latitude << " lon:" << home.longitude; + mavlink_msg_set_gps_global_origin_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &home); + sendMessage(msg); + } +} + +/** +* Set the origin to the current GPS location. +**/ +void UAS::setLocalOriginAtCurrentGPSPosition() +{ + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText("Setting new World Coordinate Frame Origin"); + msgBox.setInformativeText("Do you want to set a new origin? Waypoints defined in the local frame will be shifted in their physical location"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + + // Close the message box shortly after the click to prevent accidental clicks + QTimer::singleShot(5000, &msgBox, SLOT(reject())); + + + if (ret == QMessageBox::Yes) + { + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_DO_SET_HOME, 1, 1, 0, 0, 0, 0, 0, 0); + // Send message twice to increase chance that it reaches its goal + sendMessage(msg); + } +} + +/** +* Set a local position setpoint. +* @param x postion +* @param y position +* @param z position +*/ +void UAS::setLocalPositionSetpoint(float x, float y, float z, float yaw) +{ +#ifdef MAVLINK_ENABLED_PIXHAWK + mavlink_message_t msg; + mavlink_msg_set_local_position_setpoint_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_FRAME_LOCAL_NED, x, y, z, yaw/M_PI*180.0); + sendMessage(msg); +#else + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); + Q_UNUSED(yaw); +#endif +} + +/** +* Set a offset of the local position. +* @param x position +* @param y position +* @param z position +* @param yaw +*/ +void UAS::setLocalPositionOffset(float x, float y, float z, float yaw) +{ +#ifdef MAVLINK_ENABLED_PIXHAWK + mavlink_message_t msg; + mavlink_msg_set_position_control_offset_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, x, y, z, yaw); + sendMessage(msg); +#else + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(z); + Q_UNUSED(yaw); +#endif +} + +void UAS::startRadioControlCalibration() +{ + mavlink_message_t msg; + // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 0, 0, 1, 0, 0, 0); + sendMessage(msg); +} + +void UAS::startDataRecording() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 2, 0, 0, 0); + sendMessage(msg); +} + +void UAS::stopDataRecording() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_DO_CONTROL_VIDEO, 1, -1, -1, -1, 0, 0, 0, 0); + sendMessage(msg); +} + +void UAS::startMagnetometerCalibration() +{ + mavlink_message_t msg; + // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 1, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +void UAS::startGyroscopeCalibration() +{ + mavlink_message_t msg; + // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 1, 0, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +void UAS::startPressureCalibration() +{ + mavlink_message_t msg; + // Param 1: gyro cal, param 2: mag cal, param 3: pressure cal, Param 4: radio + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_IMU, MAV_CMD_PREFLIGHT_CALIBRATION, 1, 0, 0, 1, 0, 0, 0, 0); + sendMessage(msg); +} + +/** +* Check if time is smaller than 40 years, assuming no system without Unix +* timestamp runs longer than 40 years continuously without reboot. In worst case +* this will add/subtract the communication delay between GCS and MAV, it will +* never alter the timestamp in a safety critical way. +*/ +quint64 UAS::getUnixReferenceTime(quint64 time) +{ + // Same as getUnixTime, but does not react to attitudeStamped mode + if (time == 0) + { + // qDebug() << "XNEW time:" < has to be + // a Unix epoch timestamp. Do nothing. + return time/1000; + } +} + +/** +* @warning If attitudeStamped is enabled, this function will not actually return +* the precise time stamp of this measurement augmented to UNIX time, but will +* MOVE the timestamp IN TIME to match the last measured attitude. There is no +* reason why one would want this, except for system setups where the onboard +* clock is not present or broken and datasets should be collected that are still +* roughly synchronized. PLEASE NOTE THAT ENABLING ATTITUDE STAMPED RUINS THE +* SCIENTIFIC NATURE OF THE CORRECT LOGGING FUNCTIONS OF QGROUNDCONTROL! +*/ +quint64 UAS::getUnixTimeFromMs(quint64 time) +{ + return getUnixTime(time*1000); +} + +/** +* @warning If attitudeStamped is enabled, this function will not actually return +* the precise time stam of this measurement augmented to UNIX time, but will +* MOVE the timestamp IN TIME to match the last measured attitude. There is no +* reason why one would want this, except for system setups where the onboard +* clock is not present or broken and datasets should be collected that are +* still roughly synchronized. PLEASE NOTE THAT ENABLING ATTITUDE STAMPED +* RUINS THE SCIENTIFIC NATURE OF THE CORRECT LOGGING FUNCTIONS OF QGROUNDCONTROL! +*/ +quint64 UAS::getUnixTime(quint64 time) +{ + quint64 ret = 0; + if (attitudeStamped) + { + ret = lastAttitude; + } + + if (time == 0) + { + ret = QGC::groundTimeMilliseconds(); + } + // Check if time is smaller than 40 years, + // assuming no system without Unix timestamp + // runs longer than 40 years continuously without + // reboot. In worst case this will add/subtract the + // communication delay between GCS and MAV, + // it will never alter the timestamp in a safety + // critical way. + // + // Calculation: + // 40 years + // 365 days + // 24 hours + // 60 minutes + // 60 seconds + // 1000 milliseconds + // 1000 microseconds +#ifndef _MSC_VER + else if (time < 1261440000000000LLU) +#else + else if (time < 1261440000000000) +#endif + { + // qDebug() << "GEN time:" << time/1000 + onboardTimeOffset; + if (onboardTimeOffset == 0 || time < (lastNonNullTime - 100)) + { + lastNonNullTime = time; + onboardTimeOffset = QGC::groundTimeMilliseconds() - time/1000; + } + if (time > lastNonNullTime) lastNonNullTime = time; + + ret = time/1000 + onboardTimeOffset; + } + else + { + // Time is not zero and larger than 40 years -> has to be + // a Unix epoch timestamp. Do nothing. + ret = time/1000; + } + + return ret; +} + +/** +* @param component that will be searched for in the map of parameters. +*/ +QList UAS::getParameterNames(int component) +{ + if (parameters.contains(component)) + { + return parameters.value(component)->keys(); + } + else + { + return QList(); + } +} + +QList UAS::getComponentIds() +{ + return parameters.keys(); +} + +/** +* @param mode that UAS is to be set to. +*/ +void UAS::setMode(int mode) +{ + //this->mode = mode; //no call assignament, update receive message from UAS + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, (uint8_t)uasId, (uint8_t)mode, (uint16_t)navMode); + sendMessage(msg); + qDebug() << "SENDING REQUEST TO SET MODE TO SYSTEM" << uasId << ", REQUEST TO SET MODE " << (uint8_t)mode; +} + +/** +* Send a message to every link that is connected. +* @param message that is to be sent +*/ +void UAS::sendMessage(mavlink_message_t message) +{ + if (!LinkManager::instance()) return; + // Emit message on all links that are currently connected + foreach (LinkInterface* link, *links) + { + if (LinkManager::instance()->getLinks().contains(link)) + { + sendMessage(link, message); + } + else + { + // Remove from list + links->removeAt(links->indexOf(link)); + } + } +} + +/** +* Forward a message to all links that are currently connected. +* @param message that is to be forwarded +*/ +void UAS::forwardMessage(mavlink_message_t message) +{ + // Emit message on all links that are currently connected + QListlink_list = LinkManager::instance()->getLinksForProtocol(mavlink); + + foreach(LinkInterface* link, link_list) + { + if (link) + { + SerialLink* serial = dynamic_cast(link); + if(serial != 0) + { + for(int i=0; isize(); i++) + { + if(serial != links->at(i)) + { + qDebug()<<"Antenna tracking: Forwarding Over link: "<getName()<<" "<getSystemId(), mavlink->getComponentId(), link->getId(), message.len, messageKeys[message.msgid]); + // If link is connected + if (link->isConnected()) + { + // Send the portion of the buffer now occupied by the message + link->writeBytes((const char*)buffer, len); + } +} + +/** + * @param value battery voltage + */ +float UAS::filterVoltage(float value) const +{ + return lpVoltage * 0.7f + value * 0.3f; +} + +/** +* The mode can be preflight or unknown. +* @Return the mode of the autopilot +*/ +QString UAS::getNavModeText(int mode) +{ + if (autopilot == MAV_AUTOPILOT_PIXHAWK) + { + switch (mode) + { + case 0: + return QString("PREFLIGHT"); + break; + default: + return QString("UNKNOWN"); + } + } + else if (autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA) + { + return QString("UNKNOWN"); + } + else if (autopilot == MAV_AUTOPILOT_OPENPILOT) + { + return QString("UNKNOWN"); + } + // If nothing matches, return unknown + return QString("UNKNOWN"); +} + +/** +* Get the status of the code and a description of the status. +* Status can be unitialized, booting up, calibrating sensors, active +* standby, cirtical, emergency, shutdown or unknown. +*/ +void UAS::getStatusForCode(int statusCode, QString& uasState, QString& stateDescription) +{ + switch (statusCode) + { + case MAV_STATE_UNINIT: + uasState = tr("UNINIT"); + stateDescription = tr("Unitialized, booting up."); + break; + case MAV_STATE_BOOT: + uasState = tr("BOOT"); + stateDescription = tr("Booting system, please wait."); + break; + case MAV_STATE_CALIBRATING: + uasState = tr("CALIBRATING"); + stateDescription = tr("Calibrating sensors, please wait."); + break; + case MAV_STATE_ACTIVE: + uasState = tr("ACTIVE"); + stateDescription = tr("Active, normal operation."); + break; + case MAV_STATE_STANDBY: + uasState = tr("STANDBY"); + stateDescription = tr("Standby mode, ready for launch."); + break; + case MAV_STATE_CRITICAL: + uasState = tr("CRITICAL"); + stateDescription = tr("FAILURE: Continuing operation."); + break; + case MAV_STATE_EMERGENCY: + uasState = tr("EMERGENCY"); + stateDescription = tr("EMERGENCY: Land Immediately!"); + break; + //case MAV_STATE_HILSIM: + //uasState = tr("HIL SIM"); + //stateDescription = tr("HIL Simulation, Sensors read from SIM"); + //break; + + case MAV_STATE_POWEROFF: + uasState = tr("SHUTDOWN"); + stateDescription = tr("Powering off system."); + break; + + default: + uasState = tr("UNKNOWN"); + stateDescription = tr("Unknown system state"); + break; + } +} + +QImage UAS::getImage() +{ +#ifdef MAVLINK_ENABLED_PIXHAWK + +// qDebug() << "IMAGE TYPE:" << imageType; + + // RAW greyscale + if (imageType == MAVLINK_DATA_STREAM_IMG_RAW8U) + { + // TODO FIXME + int imgColors = 255;//imageSize/(imageWidth*imageHeight); + //const int headerSize = 15; + + // Construct PGM header + QString header("P5\n%1 %2\n%3\n"); + header = header.arg(imageWidth).arg(imageHeight).arg(imgColors); + + QByteArray tmpImage(header.toStdString().c_str(), header.toStdString().size()); + tmpImage.append(imageRecBuffer); + + //qDebug() << "IMAGE SIZE:" << tmpImage.size() << "HEADER SIZE: (15):" << header.size() << "HEADER: " << header; + + if (imageRecBuffer.isNull()) + { + qDebug()<< "could not convertToPGM()"; + return QImage(); + } + + if (!image.loadFromData(tmpImage, "PGM")) + { + qDebug()<< "could not create extracted image"; + return QImage(); + } + + } + // BMP with header + else if (imageType == MAVLINK_DATA_STREAM_IMG_BMP || + imageType == MAVLINK_DATA_STREAM_IMG_JPEG || + imageType == MAVLINK_DATA_STREAM_IMG_PGM || + imageType == MAVLINK_DATA_STREAM_IMG_PNG) + { + if (!image.loadFromData(imageRecBuffer)) + { + qDebug() << "Loading data from image buffer failed!"; + } + } + // Restart statemachine + imagePacketsArrived = 0; + //imageRecBuffer.clear(); + return image; +#else + return QImage(); +#endif + +} + +void UAS::requestImage() +{ +#ifdef MAVLINK_ENABLED_PIXHAWK + qDebug() << "trying to get an image from the uas..."; + + // check if there is already an image transmission going on + if (imagePacketsArrived == 0) + { + mavlink_message_t msg; + mavlink_msg_data_transmission_handshake_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, DATA_TYPE_JPEG_IMAGE, 0, 0, 0, 0, 0, 50); + sendMessage(msg); + } +#endif +} + + +/* MANAGEMENT */ + +/** + * + * @return The uptime in milliseconds + * + */ +quint64 UAS::getUptime() const +{ + if(startTime == 0) + { + return 0; + } + else + { + return QGC::groundTimeMilliseconds() - startTime; + } +} + +int UAS::getCommunicationStatus() const +{ + return commStatus; +} + +void UAS::requestParameters() +{ + mavlink_message_t msg; + mavlink_msg_param_request_list_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), MAV_COMP_ID_ALL); + sendMessage(msg); + qDebug() << __FILE__ << __LINE__ << "LOADING PARAM LIST"; +} + +void UAS::writeParametersToStorage() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 1, -1, -1, -1, 0, 0, 0); + qDebug() << "SENT COMMAND" << MAV_CMD_PREFLIGHT_STORAGE; + sendMessage(msg); +} + +void UAS::readParametersFromStorage() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 0, -1, -1, -1, 0, 0, 0); + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableAllDataTransmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + // 0 is a magic ID and will enable/disable the standard message set except for heartbeat + stream.req_stream_id = MAV_DATA_STREAM_ALL; + // Select the update rate in Hz the message should be send + // All messages will be send with their default rate + // TODO: use 0 to turn off and get rid of enable/disable? will require + // a different magic flag for turning on defaults, possibly something really high like 1111 ? + stream.req_message_rate = 0; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableRawSensorDataTransmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_RAW_SENSORS; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableExtendedSystemStatusTransmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_EXTENDED_STATUS; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableRCChannelDataTransmission(int rate) +{ +#if defined(MAVLINK_ENABLED_UALBERTA_MESSAGES) + mavlink_message_t msg; + mavlink_msg_request_rc_channels_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, enabled); + sendMessage(msg); +#else + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_RC_CHANNELS; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +#endif +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableRawControllerDataTransmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_RAW_CONTROLLER; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +} + +//void UAS::enableRawSensorFusionTransmission(int rate) +//{ +// // Buffers to write data to +// mavlink_message_t msg; +// mavlink_request_data_stream_t stream; +// // Select the message to request from now on +// stream.req_stream_id = MAV_DATA_STREAM_RAW_SENSOR_FUSION; +// // Select the update rate in Hz the message should be send +// stream.req_message_rate = rate; +// // Start / stop the message +// stream.start_stop = (rate) ? 1 : 0; +// // The system which should take this command +// stream.target_system = uasId; +// // The component / subsystem which should take this command +// stream.target_component = 0; +// // Encode and send the message +// mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); +// // Send message twice to increase chance of reception +// sendMessage(msg); +// sendMessage(msg); +//} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enablePositionTransmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_POSITION; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableExtra1Transmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_EXTRA1; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableExtra2Transmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_EXTRA2; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); + sendMessage(msg); +} + +/** +* @param rate The update rate in Hz the message should be sent +*/ +void UAS::enableExtra3Transmission(int rate) +{ + // Buffers to write data to + mavlink_message_t msg; + mavlink_request_data_stream_t stream; + // Select the message to request from now on + stream.req_stream_id = MAV_DATA_STREAM_EXTRA3; + // Select the update rate in Hz the message should be send + stream.req_message_rate = rate; + // Start / stop the message + stream.start_stop = (rate) ? 1 : 0; + // The system which should take this command + stream.target_system = uasId; + // The component / subsystem which should take this command + stream.target_component = 0; + // Encode and send the message + mavlink_msg_request_data_stream_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &stream); + // Send message twice to increase chance of reception + sendMessage(msg); + sendMessage(msg); +} + +/** + * Set a parameter value onboard + * + * @param component The component to set the parameter + * @param id Name of the parameter + */ +void UAS::setParameter(const int component, const QString& id, const QVariant& value) +{ + if (!id.isNull()) + { + mavlink_message_t msg; + mavlink_param_set_t p; + mavlink_param_union_t union_value; + + // Assign correct value based on QVariant + switch (value.type()) + { + case QVariant::Int: + union_value.param_int32 = value.toInt(); + p.param_type = MAV_PARAM_TYPE_INT32; + break; + case QVariant::UInt: + union_value.param_uint32 = value.toUInt(); + p.param_type = MAV_PARAM_TYPE_UINT32; + break; + case QMetaType::Float: + union_value.param_float = value.toFloat(); + p.param_type = MAV_PARAM_TYPE_REAL32; + break; + default: + qCritical() << "ABORTED PARAM SEND, NO VALID QVARIANT TYPE"; + return; + } + + p.param_value = union_value.param_float; + p.target_system = (uint8_t)uasId; + p.target_component = (uint8_t)component; + + //qDebug() << "SENT PARAM:" << value; + + // Copy string into buffer, ensuring not to exceed the buffer size + for (unsigned int i = 0; i < sizeof(p.param_id); i++) + { + // String characters + if ((int)i < id.length()) + { + p.param_id[i] = id.toAscii()[i]; + } + else + { + // Fill rest with zeros + p.param_id[i] = 0; + } + } + mavlink_msg_param_set_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &p); + sendMessage(msg); + } +} + +/** +* Request parameter, use parameter name to request it. +*/ +void UAS::requestParameter(int component, int id) +{ + // Request parameter, use parameter name to request it + mavlink_message_t msg; + mavlink_param_request_read_t read; + read.param_index = id; + read.param_id[0] = '\0'; // Enforce null termination + read.target_system = uasId; + read.target_component = component; + mavlink_msg_param_request_read_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &read); + sendMessage(msg); + //qDebug() << __FILE__ << __LINE__ << "REQUESTING PARAM RETRANSMISSION FROM COMPONENT" << component << "FOR PARAM ID" << id; +} + +/** +* Request a parameter, use parameter name to request it. +*/ +void UAS::requestParameter(int component, const QString& parameter) +{ + // Request parameter, use parameter name to request it + mavlink_message_t msg; + mavlink_param_request_read_t read; + read.param_index = -1; + // Copy full param name or maximum max field size + if (parameter.length() > MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN) + { + emit textMessageReceived(uasId, 0, 255, QString("QGC WARNING: Parameter name %1 is more than %2 bytes long. This might lead to errors and mishaps!").arg(parameter).arg(MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN-1)); + } + memcpy(read.param_id, parameter.toStdString().c_str(), qMax(parameter.length(), MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN)); + read.param_id[15] = '\0'; // Enforce null termination + read.target_system = uasId; + read.target_component = component; + mavlink_msg_param_request_read_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &read); + sendMessage(msg); + qDebug() << __FILE__ << __LINE__ << "REQUESTING PARAM RETRANSMISSION FROM COMPONENT" << component << "FOR PARAM NAME" << parameter; +} + +/** +* @param systemType Type of MAV. +*/ +void UAS::setSystemType(int systemType) +{ + if((systemType >= MAV_TYPE_GENERIC) && (systemType < MAV_TYPE_ENUM_END)) + { + type = systemType; + + // If the airframe is still generic, change it to a close default type + if (airframe == 0) + { + switch (systemType) + { + case MAV_TYPE_FIXED_WING: + airframe = QGC_AIRFRAME_EASYSTAR; + break; + case MAV_TYPE_QUADROTOR: + airframe = QGC_AIRFRAME_MIKROKOPTER; + break; + } + } + emit systemSpecsChanged(uasId); + } +} + +void UAS::setUASName(const QString& name) +{ + if (name != "") + { + this->name = name; + writeSettings(); + emit nameChanged(name); + emit systemSpecsChanged(uasId); + } +} + +void UAS::executeCommand(MAV_CMD command) +{ + mavlink_message_t msg; + mavlink_command_long_t cmd; + cmd.command = (uint16_t)command; + cmd.confirmation = 0; + cmd.param1 = 0.0f; + cmd.param2 = 0.0f; + cmd.param3 = 0.0f; + cmd.param4 = 0.0f; + cmd.param5 = 0.0f; + cmd.param6 = 0.0f; + cmd.param7 = 0.0f; + cmd.target_system = uasId; + cmd.target_component = 0; + mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); + sendMessage(msg); +} + +void UAS::executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component) +{ + mavlink_message_t msg; + mavlink_command_long_t cmd; + cmd.command = (uint16_t)command; + cmd.confirmation = confirmation; + cmd.param1 = param1; + cmd.param2 = param2; + cmd.param3 = param3; + cmd.param4 = param4; + cmd.param5 = param5; + cmd.param6 = param6; + cmd.param7 = param7; + cmd.target_system = uasId; + cmd.target_component = component; + mavlink_msg_command_long_encode(mavlink->getSystemId(), mavlink->getComponentId(), &msg, &cmd); + sendMessage(msg); +} + +/** + * Launches the system + * + */ +void UAS::launch() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), 0, MAV_CMD_NAV_TAKEOFF, 1, 0, 0, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +/** + * @warning Depending on the UAS, this might make the rotors of a helicopter spinning + * + */ +void UAS::armSystem() +{ + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_SAFETY_ARMED, navMode); + sendMessage(msg); +} + +/** + * @warning Depending on the UAS, this might completely stop all motors. + * + */ +void UAS::disarmSystem() +{ + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode & ~MAV_MODE_FLAG_SAFETY_ARMED, navMode); + sendMessage(msg); +} + +/** +* Set the manual control commands. +* This can only be done if the system has manual inputs enabled and is armed. +*/ +void UAS::setManualControlCommands(double roll, double pitch, double yaw, double thrust, int xHat, int yHat, int buttons) +{ + // Scale values + double rollPitchScaling = 1.0f * 1000.0f; + double yawScaling = 1.0f * 1000.0f; + double thrustScaling = 1.0f * 1000.0f; + + manualRollAngle = roll * rollPitchScaling; + manualPitchAngle = pitch * rollPitchScaling; + manualYawAngle = yaw * yawScaling; + manualThrust = thrust * thrustScaling; + + // If system has manual inputs enabled and is armed + if(((mode & MAV_MODE_FLAG_DECODE_POSITION_MANUAL) && (mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY)) || (mode & MAV_MODE_FLAG_HIL_ENABLED)) + { + mavlink_message_t message; + mavlink_msg_manual_control_pack(mavlink->getSystemId(), mavlink->getComponentId(), &message, this->uasId, (float)manualPitchAngle, (float)manualRollAngle, (float)manualThrust, (float)manualYawAngle, buttons); + sendMessage(message); + //qDebug() << __FILE__ << __LINE__ << ": SENT MANUAL CONTROL MESSAGE: roll" << manualRollAngle << " pitch: " << manualPitchAngle << " yaw: " << manualYawAngle << " thrust: " << manualThrust; + + emit attitudeThrustSetPointChanged(this, roll, pitch, yaw, thrust, QGC::groundTimeMilliseconds()); + } + else + { + qDebug() << "JOYSTICK/MANUAL CONTROL: IGNORING COMMANDS: Set mode to MANUAL to send joystick commands first"; + } +} + +void UAS::setManual6DOFControlCommands(double x, double y, double z, double roll, double pitch, double yaw) +{ +// TODO: send mavlink 6DOF manual control message +} + +/** +* @return the type of the system +*/ +int UAS::getSystemType() +{ + return this->type; +} + +/** +* @param buttonIndex +*/ +void UAS::receiveButton(int buttonIndex) +{ + switch (buttonIndex) + { + case 0: + + break; + case 1: + + break; + default: + + break; + } + // qDebug() << __FILE__ << __LINE__ << ": Received button clicked signal (button # is: " << buttonIndex << "), UNIMPLEMENTED IN MAVLINK!"; + +} + +/** +* Halt the uas. +*/ +void UAS::halt() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_HOLD, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +/** +* Make the UAS move. +*/ +void UAS::go() +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_CONTINUE, MAV_GOTO_HOLD_AT_CURRENT_POSITION, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +/** +* Order the robot to return home +*/ +void UAS::home() +{ + mavlink_message_t msg; + + double latitude = UASManager::instance()->getHomeLatitude(); + double longitude = UASManager::instance()->getHomeLongitude(); + double altitude = UASManager::instance()->getHomeAltitude(); + int frame = UASManager::instance()->getHomeFrame(); + + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_OVERRIDE_GOTO, 1, MAV_GOTO_DO_CONTINUE, MAV_GOTO_HOLD_AT_CURRENT_POSITION, frame, 0, latitude, longitude, altitude); + sendMessage(msg); +} + +/** +* Order the robot to land on the runway +*/ +void UAS::land() +{ + mavlink_message_t msg; + + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_LAND, 1, 0, 0, 0, 0, 0, 0, 0); + sendMessage(msg); +} + +/** + * The MAV starts the emergency landing procedure. The behaviour depends on the onboard implementation + * and might differ between systems. + */ +void UAS::emergencySTOP() +{ + // FIXME MAVLINKV10PORTINGNEEDED + halt(); +} + +/** + * Shut down this mav - All onboard systems are immediately shut down (e.g. the + * main power line is cut). + * @warning This might lead to a crash. + * + * The command will not be executed until emergencyKILLConfirm is issues immediately afterwards + */ +bool UAS::emergencyKILL() +{ + halt(); + // FIXME MAVLINKV10PORTINGNEEDED + // bool result = false; + // QMessageBox msgBox; + // msgBox.setIcon(QMessageBox::Critical); + // msgBox.setText("EMERGENCY: KILL ALL MOTORS ON UAS"); + // msgBox.setInformativeText("Do you want to cut power on all systems?"); + // msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + // msgBox.setDefaultButton(QMessageBox::Cancel); + // int ret = msgBox.exec(); + + // // Close the message box shortly after the click to prevent accidental clicks + // QTimer::singleShot(5000, &msgBox, SLOT(reject())); + + + // if (ret == QMessageBox::Yes) + // { + // mavlink_message_t msg; + // // TODO Replace MG System ID with static function call and allow to change ID in GUI + // mavlink_msg_action_pack(MG::SYSTEM::ID, MG::SYSTEM::COMPID, &msg, this->getUASID(), MAV_COMP_ID_IMU, (int)MAV_ACTION_EMCY_KILL); + // // Send message twice to increase chance of reception + // sendMessage(msg); + // sendMessage(msg); + // result = true; + // } + // return result; + return false; +} + +/** +* If enabled, connect the flight gear link. +*/ +void UAS::enableHilFlightGear(bool enable, QString options) +{ + QGCFlightGearLink* link = dynamic_cast(simulation); + if (!link || !simulation) { + // Delete wrong sim + if (simulation) { + stopHil(); + delete simulation; + } + simulation = new QGCFlightGearLink(this, options); + } + // Connect Flight Gear Link + link = dynamic_cast(simulation); + link->setStartupArguments(options); + if (enable) + { + startHil(); + } + else + { + stopHil(); + } +} + +/** +* If enabled, connect the X-plane gear link. +*/ +void UAS::enableHilXPlane(bool enable) +{ + QGCXPlaneLink* link = dynamic_cast(simulation); + if (!link || !simulation) { + if (simulation) { + stopHil(); + delete simulation; + } + qDebug() << "CREATED NEW XPLANE LINK"; + simulation = new QGCXPlaneLink(this); + } + // Connect X-Plane Link + if (enable) + { + startHil(); + } + else + { + stopHil(); + } +} + +/** +* @param time_us Timestamp (microseconds since UNIX epoch or microseconds since system boot) +* @param roll Roll angle (rad) +* @param pitch Pitch angle (rad) +* @param yaw Yaw angle (rad) +* @param rollspeed Roll angular speed (rad/s) +* @param pitchspeed Pitch angular speed (rad/s) +* @param yawspeed Yaw angular speed (rad/s) +* @param lat Latitude, expressed as * 1E7 +* @param lon Longitude, expressed as * 1E7 +* @param alt Altitude in meters, expressed as * 1000 (millimeters) +* @param vx Ground X Speed (Latitude), expressed as m/s * 100 +* @param vy Ground Y Speed (Longitude), expressed as m/s * 100 +* @param vz Ground Z Speed (Altitude), expressed as m/s * 100 +* @param xacc X acceleration (mg) +* @param yacc Y acceleration (mg) +* @param zacc Z acceleration (mg) +*/ +void UAS::sendHilState(uint64_t time_us, float roll, float pitch, float yaw, float rollspeed, + float pitchspeed, float yawspeed, int32_t lat, int32_t lon, int32_t alt, + int16_t vx, int16_t vy, int16_t vz, int16_t xacc, int16_t yacc, int16_t zacc) +{ + if (this->mode & MAV_MODE_FLAG_HIL_ENABLED) + { + mavlink_message_t msg; + mavlink_msg_hil_state_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, + time_us, roll, pitch, yaw, rollspeed, pitchspeed, yawspeed, + lat, lon, alt, vx, vy, vz, xacc, yacc, zacc); + sendMessage(msg); + } + else + { + // Attempt to set HIL mode + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_HIL_ENABLED, navMode); + sendMessage(msg); + qDebug() << __FILE__ << __LINE__ << "HIL is onboard not enabled, trying to enable."; + } +} + +/** +* Connect flight gear link. +**/ +void UAS::startHil() +{ + if (hilEnabled) return; + hilEnabled = true; + // Connect HIL simulation link + simulation->connectSimulation(); + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode | MAV_MODE_FLAG_HIL_ENABLED, navMode); + sendMessage(msg); +} + +/** +* disable flight gear link. +*/ +void UAS::stopHil() +{ + if (simulation) simulation->disconnectSimulation(); + mavlink_message_t msg; + mavlink_msg_set_mode_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, this->getUASID(), mode & !MAV_MODE_FLAG_HIL_ENABLED, navMode); + sendMessage(msg); + hilEnabled = false; +} + +void UAS::shutdown() +{ + bool result = false; + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText("Shutting down the UAS"); + msgBox.setInformativeText("Do you want to shut down the onboard computer?"); + + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + + // Close the message box shortly after the click to prevent accidental clicks + QTimer::singleShot(5000, &msgBox, SLOT(reject())); + + if (ret == QMessageBox::Yes) + { + // If the active UAS is set, execute command + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, 1, 0, 2, 0, 0, 0, 0, 0); + sendMessage(msg); + result = true; + } +} + +/** +* @param x position +* @param y position +* @param z position +* @param yaw +*/ +void UAS::setTargetPosition(float x, float y, float z, float yaw) +{ + mavlink_message_t msg; + mavlink_msg_command_long_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, uasId, MAV_COMP_ID_ALL, MAV_CMD_NAV_PATHPLANNING, 1, 1, 1, 0, yaw, x, y, z); + sendMessage(msg); +} + +/** + * @return The name of this system as string in human-readable form + */ +QString UAS::getUASName(void) const +{ + QString result; + if (name == "") + { + result = tr("MAV ") + result.sprintf("%03d", getUASID()); + } + else + { + result = name; + } + return result; +} + +/** +* @return the state of the uas as a short text. +*/ +const QString& UAS::getShortState() const +{ + return shortStateText; +} + +/** +* The mode can be autonomous, guided, manual or armed. It will also return if +* hardware in the loop is being used. +* @return the audio mode text for the id given. +*/ +QString UAS::getAudioModeTextFor(int id) +{ + QString mode; + uint8_t modeid = id; + + // BASE MODE DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_AUTO) + { + mode += "autonomous"; + } + else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_GUIDED) + { + mode += "guided"; + } + else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_MANUAL) + { + mode += "manual"; + } + else + { + // Nothing else applies, we're in preflight + mode += "preflight"; + } + + if (modeid != 0) + { + mode += " mode"; + } + + // ARMED STATE DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_SAFETY) + { + mode.append(" and armed"); + } + + // HARDWARE IN THE LOOP DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_HIL) + { + mode.append(" using hardware in the loop simulation"); + } + + return mode; +} + +/** +* The mode returned can be auto, stabilized, test, manual, preflight or unknown. +* @return the short text of the mode for the id given. +*/ +QString UAS::getShortModeTextFor(int id) +{ + QString mode; + uint8_t modeid = id; + + qDebug() << "MODE:" << modeid; + + // BASE MODE DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_AUTO) + { + mode += "AUTO"; + } + else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_GUIDED) + { + mode += "|STABILIZED"; + } +// if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_STABILIZE) +// { +// mode += "|STAB"; +// } + else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_TEST) + { + mode += "|TEST"; + } + else if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_MANUAL) + { + mode += "|MANUAL"; + } + else if (modeid == 0) + { + mode = "|PREFLIGHT"; + } + else + { + mode = "|UNKNOWN"; + } + + // ARMED STATE DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_SAFETY) + { + mode.prepend("A"); + } + else + { + mode.prepend("D"); + } + + // HARDWARE IN THE LOOP DECODING + if (modeid & (uint8_t)MAV_MODE_FLAG_DECODE_POSITION_HIL) + { + mode.prepend("HIL:"); + } + + return mode; +} + +const QString& UAS::getShortMode() const +{ + return shortModeText; +} + +/** +* Add the link and connect a signal to it which will be set off when it is destroyed. +*/ +void UAS::addLink(LinkInterface* link) +{ + if (!links->contains(link)) + { + links->append(link); + connect(link, SIGNAL(destroyed(QObject*)), this, SLOT(removeLink(QObject*))); + } +} + +void UAS::removeLink(QObject* object) +{ + LinkInterface* link = dynamic_cast(object); + if (link) + { + links->removeAt(links->indexOf(link)); + } +} + +/** +* @return the list of links +*/ +QList* UAS::getLinks() +{ + return links; +} + +/** +* @rerturn the map of the components +*/ +QMap UAS::getComponents() +{ + return components; +} + +/** +* Set the battery type and the number of cells. +* @param type of the battery +* @param cells Number of cells. +*/ +void UAS::setBattery(BatteryType type, int cells) +{ + this->batteryType = type; + this->cells = cells; + switch (batteryType) + { + case NICD: + break; + case NIMH: + break; + case LIION: + break; + case LIPOLY: + fullVoltage = this->cells * UAS::lipoFull; + emptyVoltage = this->cells * UAS::lipoEmpty; + break; + case LIFE: + break; + case AGZN: + break; + } +} + +/** +* Set the battery specificaitons: empty voltage, warning voltage, and full voltage. +* @param specifications of the battery +*/ +void UAS::setBatterySpecs(const QString& specs) +{ + if (specs.length() == 0 || specs.contains("%")) + { + batteryRemainingEstimateEnabled = false; + bool ok; + QString percent = specs; + percent = percent.remove("%"); + float temp = percent.toFloat(&ok); + if (ok) + { + warnLevelPercent = temp; + } + else + { + emit textMessageReceived(0, 0, 0, "Could not set battery options, format is wrong"); + } + } + else + { + batteryRemainingEstimateEnabled = true; + QString stringList = specs; + stringList = stringList.remove("V"); + stringList = stringList.remove("v"); + QStringList parts = stringList.split(","); + if (parts.length() == 3) + { + float temp; + bool ok; + // Get the empty voltage + temp = parts.at(0).toFloat(&ok); + if (ok) emptyVoltage = temp; + // Get the warning voltage + temp = parts.at(1).toFloat(&ok); + if (ok) warnVoltage = temp; + // Get the full voltage + temp = parts.at(2).toFloat(&ok); + if (ok) fullVoltage = temp; + } + else + { + emit textMessageReceived(0, 0, 0, "Could not set battery options, format is wrong"); + } + } +} + +/** +* @return the battery specifications(empty voltage, warning voltage, full voltage) +*/ +QString UAS::getBatterySpecs() +{ + if (batteryRemainingEstimateEnabled) + { + return QString("%1V,%2V,%3V").arg(emptyVoltage).arg(warnVoltage).arg(fullVoltage); + } + else + { + return QString("%1%").arg(warnLevelPercent); + } +} + +/** +* @return the time remaining. +*/ +int UAS::calculateTimeRemaining() +{ + quint64 dt = QGC::groundTimeMilliseconds() - startTime; + double seconds = dt / 1000.0f; + double voltDifference = startVoltage - currentVoltage; + if (voltDifference <= 0) voltDifference = 0.00000000001f; + double dischargePerSecond = voltDifference / seconds; + int remaining = static_cast((currentVoltage - emptyVoltage) / dischargePerSecond); + // Can never be below 0 + if (remaining < 0) remaining = 0; + return remaining; +} + +/** + * @return charge level in percent - 0 - 100 + */ +float UAS::getChargeLevel() +{ + if (batteryRemainingEstimateEnabled) + { + if (lpVoltage < emptyVoltage) + { + chargeLevel = 0.0f; + } + else if (lpVoltage > fullVoltage) + { + chargeLevel = 100.0f; + } + else + { + chargeLevel = 100.0f * ((lpVoltage - emptyVoltage)/(fullVoltage - emptyVoltage)); + } + } + return chargeLevel; +} + +void UAS::startLowBattAlarm() +{ + if (!lowBattAlarm) + { + GAudioOutput::instance()->alert(tr("system %1 has low battery").arg(getUASName())); + QTimer::singleShot(3000, GAudioOutput::instance(), SLOT(startEmergency())); + lowBattAlarm = true; + } +} + +void UAS::stopLowBattAlarm() +{ + if (lowBattAlarm) + { + GAudioOutput::instance()->stopEmergency(); + lowBattAlarm = false; + } +} diff --git a/src/uas/UAS.h b/src/uas/UAS.h index 478edf60b7a5fa4645c67b01d1f01bda26d62b08..e6d6c1b2e8dd242e8a65d2f6e69a3ea051cd6d37 100644 --- a/src/uas/UAS.h +++ b/src/uas/UAS.h @@ -1,707 +1,710 @@ -/*===================================================================== - -QGroundControl Open Source Ground Control Station - -(c) 2009, 2010 QGROUNDCONTROL PROJECT - -This file is part of the QGROUNDCONTROL project - - QGROUNDCONTROL is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - QGROUNDCONTROL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with QGROUNDCONTROL. If not, see . - -======================================================================*/ - -/** - * @file - * @brief Definition of Unmanned Aerial Vehicle object - * - * @author Lorenz Meier - * - */ - -#ifndef _UAS_H_ -#define _UAS_H_ - -#include "UASInterface.h" -#include -#include -#include "QGCMAVLink.h" -#include "QGCHilLink.h" -#include "QGCFlightGearLink.h" -#include "QGCXPlaneLink.h" - -/** - * @brief A generic MAVLINK-connected MAV/UAV - * - * This class represents one vehicle. It can be used like the real vehicle, e.g. a call to halt() - * will automatically send the appropriate messages to the vehicle. The vehicle state will also be - * automatically updated by the comm architecture, so when writing code to e.g. control the vehicle - * no knowledge of the communication infrastructure is needed. - */ -class UAS : public UASInterface -{ - Q_OBJECT -public: - UAS(MAVLinkProtocol* protocol, int id = 0); - ~UAS(); - - enum BatteryType - { - NICD = 0, - NIMH = 1, - LIION = 2, - LIPOLY = 3, - LIFE = 4, - AGZN = 5 - }; ///< The type of battery used - - static const int lipoFull = 4.2f; ///< 100% charged voltage - static const int lipoEmpty = 3.5f; ///< Discharged voltage - - /* MANAGEMENT */ - - /** @brief The name of the robot */ - QString getUASName(void) const; - /** @brief Get short state */ - const QString& getShortState() const; - /** @brief Get short mode */ - const QString& getShortMode() const; - /** @brief Translate from mode id to text */ - static QString getShortModeTextFor(int id); - /** @brief Translate from mode id to audio text */ - static QString getAudioModeTextFor(int id); - /** @brief Get the unique system id */ - int getUASID() const; - /** @brief Get the airframe */ - int getAirframe() const - { - return airframe; - } - /** @brief Get the components */ - QMap getComponents(); - - /** @brief The time interval the robot is switched on */ - quint64 getUptime() const; - /** @brief Get the status flag for the communication */ - int getCommunicationStatus() const; - /** @brief Add one measurement and get low-passed voltage */ - float filterVoltage(float value) const; - /** @brief Get the links associated with this robot */ - QList* getLinks(); - - double getLocalX() const - { - return localX; - } - double getLocalY() const - { - return localY; - } - double getLocalZ() const - { - return localZ; - } - double getLatitude() const - { - return latitude; - } - double getLongitude() const - { - return longitude; - } - double getAltitude() const - { - return altitude; - } - virtual bool localPositionKnown() const - { - return isLocalPositionKnown; - } - virtual bool globalPositionKnown() const - { - return isGlobalPositionKnown; - } - - double getRoll() const - { - return roll; - } - double getPitch() const - { - return pitch; - } - double getYaw() const - { - return yaw; - } - bool getSelected() const; - QVector3D getNedPosGlobalOffset() const - { - return nedPosGlobalOffset; - } - - QVector3D getNedAttGlobalOffset() const - { - return nedAttGlobalOffset; - } - -#if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) - px::GLOverlay getOverlay() - { - QMutexLocker locker(&overlayMutex); - return overlay; - } - - px::GLOverlay getOverlay(qreal& receivedTimestamp) - { - receivedTimestamp = receivedOverlayTimestamp; - QMutexLocker locker(&overlayMutex); - return overlay; - } - - px::ObstacleList getObstacleList() { - QMutexLocker locker(&obstacleListMutex); - return obstacleList; - } - - px::ObstacleList getObstacleList(qreal& receivedTimestamp) { - receivedTimestamp = receivedObstacleListTimestamp; - QMutexLocker locker(&obstacleListMutex); - return obstacleList; - } - - px::Path getPath() { - QMutexLocker locker(&pathMutex); - return path; - } - - px::Path getPath(qreal& receivedTimestamp) { - receivedTimestamp = receivedPathTimestamp; - QMutexLocker locker(&pathMutex); - return path; - } - - px::PointCloudXYZRGB getPointCloud() { - QMutexLocker locker(&pointCloudMutex); - return pointCloud; - } - - px::PointCloudXYZRGB getPointCloud(qreal& receivedTimestamp) { - receivedTimestamp = receivedPointCloudTimestamp; - QMutexLocker locker(&pointCloudMutex); - return pointCloud; - } - - px::RGBDImage getRGBDImage() { - QMutexLocker locker(&rgbdImageMutex); - return rgbdImage; - } - - px::RGBDImage getRGBDImage(qreal& receivedTimestamp) { - receivedTimestamp = receivedRGBDImageTimestamp; - QMutexLocker locker(&rgbdImageMutex); - return rgbdImage; - } -#endif - - friend class UASWaypointManager; - -protected: //COMMENTS FOR TEST UNIT - int uasId; ///< Unique system ID - unsigned char type; ///< UAS type (from type enum) - quint64 startTime; ///< The time the UAS was switched on - CommStatus commStatus; ///< Communication status - QString name; ///< Human-friendly name of the vehicle, e.g. bravo - int autopilot; ///< Type of the Autopilot: -1: None, 0: Generic, 1: PIXHAWK, 2: SLUGS, 3: Ardupilot (up to 15 types), defined in MAV_AUTOPILOT_TYPE ENUM - QList* links; ///< List of links this UAS can be reached by - QList unknownPackets; ///< Packet IDs which are unknown and have been received - MAVLinkProtocol* mavlink; ///< Reference to the MAVLink instance - BatteryType batteryType; ///< The battery type - int cells; ///< Number of cells - - UASWaypointManager waypointManager; - - QList actuatorValues; - QList actuatorNames; - - QList motorValues; - QList motorNames; - QMap components; ///< IDs and names of all detected onboard components - - double thrustSum; ///< Sum of forward/up thrust of all thrust actuators, in Newtons - double thrustMax; ///< Maximum forward/up thrust of this vehicle, in Newtons - - // Battery stats - float fullVoltage; ///< Voltage of the fully charged battery (100%) - float emptyVoltage; ///< Voltage of the empty battery (0%) - float startVoltage; ///< Voltage at system start - float tickVoltage; ///< Voltage where 0.1 V ticks are told - float lastTickVoltageValue; ///< The last voltage where a tick was announced - float tickLowpassVoltage; ///< Lowpass-filtered voltage for the tick announcement - float warnVoltage; ///< Voltage where QGC will start to warn about low battery - float warnLevelPercent; ///< Warning level, in percent - double currentVoltage; ///< Voltage currently measured - float lpVoltage; ///< Low-pass filtered voltage - bool batteryRemainingEstimateEnabled; ///< If the estimate is enabled, QGC will try to estimate the remaining battery life - float chargeLevel; ///< Charge level of battery, in percent - int timeRemaining; ///< Remaining time calculated based on previous and current - uint8_t mode; ///< The current mode of the MAV - uint32_t custom_mode; ///< The current mode of the MAV - int status; ///< The current status of the MAV - uint32_t navMode; ///< The current navigation mode of the MAV - quint64 onboardTimeOffset; - - bool controlRollManual; ///< status flag, true if roll is controlled manually - bool controlPitchManual; ///< status flag, true if pitch is controlled manually - bool controlYawManual; ///< status flag, true if yaw is controlled manually - bool controlThrustManual; ///< status flag, true if thrust is controlled manually - - double manualRollAngle; ///< Roll angle set by human pilot (radians) - double manualPitchAngle; ///< Pitch angle set by human pilot (radians) - double manualYawAngle; ///< Yaw angle set by human pilot (radians) - double manualThrust; ///< Thrust set by human pilot (radians) - float receiveDropRate; ///< Percentage of packets that were dropped on the MAV's receiving link (from GCS and other MAVs) - float sendDropRate; ///< Percentage of packets that were not received from the MAV by the GCS - bool lowBattAlarm; ///< Switch if battery is low - bool positionLock; ///< Status if position information is available or not - double localX; - double localY; - double localZ; - double latitude; - double longitude; - double altitude; - double speedX; ///< True speed in X axis - double speedY; ///< True speed in Y axis - double speedZ; ///< True speed in Z axis - double roll; - double pitch; - double yaw; - quint64 lastHeartbeat; ///< Time of the last heartbeat message - QTimer* statusTimeout; ///< Timer for various status timeouts - - int imageSize; ///< Image size being transmitted (bytes) - int imagePackets; ///< Number of data packets being sent for this image - int imagePacketsArrived; ///< Number of data packets recieved - int imagePayload; ///< Payload size per transmitted packet (bytes). Standard is 254, and decreases when image resolution increases. - int imageQuality; ///< Quality of the transmitted image (percentage) - int imageType; ///< Type of the transmitted image (BMP, PNG, JPEG, RAW 8 bit, RAW 32 bit) - int imageWidth; ///< Width of the image stream - int imageHeight; ///< Width of the image stream - QByteArray imageRecBuffer; ///< Buffer for the incoming bytestream - QImage image; ///< Image data of last completely transmitted image - quint64 imageStart; - -#if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) - px::GLOverlay overlay; - QMutex overlayMutex; - qreal receivedOverlayTimestamp; - - px::ObstacleList obstacleList; - QMutex obstacleListMutex; - qreal receivedObstacleListTimestamp; - - px::Path path; - QMutex pathMutex; - qreal receivedPathTimestamp; - - px::PointCloudXYZRGB pointCloud; - QMutex pointCloudMutex; - qreal receivedPointCloudTimestamp; - - px::RGBDImage rgbdImage; - QMutex rgbdImageMutex; - qreal receivedRGBDImageTimestamp; -#endif - - QMap* > parameters; ///< All parameters - bool paramsOnceRequested; ///< If the parameter list has been read at least once - int airframe; ///< The airframe type - bool attitudeKnown; ///< True if attitude was received, false else - QGCUASParamManager* paramManager; ///< Parameter manager class - QString shortStateText; ///< Short textual state description - QString shortModeText; ///< Short textual mode description - bool attitudeStamped; ///< Should arriving data be timestamped with the last attitude? This helps with broken system time clocks on the MAV - quint64 lastAttitude; ///< Timestamp of last attitude measurement - QGCHilLink* simulation; ///< Hardware in the loop simulation link - bool isLocalPositionKnown; ///< If the local position has been received for this MAV - bool isGlobalPositionKnown; ///< If the global position has been received for this MAV - bool systemIsArmed; ///< If the system is armed - QVector3D nedPosGlobalOffset; ///< Offset between the system's NED position measurements and the swarm / global 0/0/0 origin - QVector3D nedAttGlobalOffset; ///< Offset between the system's NED position measurements and the swarm / global 0/0/0 origin - -public: - /** @brief Set the current battery type */ - void setBattery(BatteryType type, int cells); - /** @brief Estimate how much flight time is remaining */ - int calculateTimeRemaining(); - /** @brief Get the current charge level */ - float getChargeLevel(); - /** @brief Get the human-readable status message for this code */ - void getStatusForCode(int statusCode, QString& uasState, QString& stateDescription); - /** @brief Get the human-readable navigation mode translation for this mode */ - QString getNavModeText(int mode); - /** @brief Check if vehicle is in autonomous mode */ - bool isAuto(); - /** @brief Check if vehicle is armed */ - bool isArmed() const { return systemIsArmed; } - - UASWaypointManager* getWaypointManager() { - return &waypointManager; - } - /** @brief Get reference to the param manager **/ - QGCUASParamManager* getParamManager() const { - return paramManager; - } - /** @brief Get the HIL simulation */ - QGCHilLink* getHILSimulation() const { - return simulation; - } - // TODO Will be removed - /** @brief Set reference to the param manager **/ - void setParamManager(QGCUASParamManager* manager) { - paramManager = manager; - } - int getSystemType(); - QString getSystemTypeName() - { - switch(type) - { - case MAV_TYPE_GENERIC: - return "GENERIC"; - break; - case MAV_TYPE_FIXED_WING: - return "FIXED_WING"; - break; - case MAV_TYPE_QUADROTOR: - return "QUADROTOR"; - break; - case MAV_TYPE_COAXIAL: - return "COAXIAL"; - break; - case MAV_TYPE_HELICOPTER: - return "HELICOPTER"; - break; - case MAV_TYPE_ANTENNA_TRACKER: - return "ANTENNA_TRACKER"; - break; - case MAV_TYPE_GCS: - return "GCS"; - break; - case MAV_TYPE_AIRSHIP: - return "AIRSHIP"; - break; - case MAV_TYPE_FREE_BALLOON: - return "FREE_BALLOON"; - break; - case MAV_TYPE_ROCKET: - return "ROCKET"; - break; - case MAV_TYPE_GROUND_ROVER: - return "GROUND_ROVER"; - break; - case MAV_TYPE_SURFACE_BOAT: - return "BOAT"; - break; - case MAV_TYPE_SUBMARINE: - return "SUBMARINE"; - break; - case MAV_TYPE_HEXAROTOR: - return "HEXAROTOR"; - break; - case MAV_TYPE_OCTOROTOR: - return "OCTOROTOR"; - break; - case MAV_TYPE_TRICOPTER: - return "TRICOPTER"; - break; - case MAV_TYPE_FLAPPING_WING: - return "FLAPPING_WING"; - break; - default: - return ""; - break; - } - } - - QImage getImage(); - void requestImage(); - int getAutopilotType(){ - return autopilot; - } - QString getAutopilotTypeName() - { - switch (autopilot) - { - case MAV_AUTOPILOT_GENERIC: - return "GENERIC"; - break; - case MAV_AUTOPILOT_PIXHAWK: - return "PIXHAWK"; - break; - case MAV_AUTOPILOT_SLUGS: - return "SLUGS"; - break; - case MAV_AUTOPILOT_ARDUPILOTMEGA: - return "ARDUPILOTMEGA"; - break; - case MAV_AUTOPILOT_OPENPILOT: - return "OPENPILOT"; - break; - case MAV_AUTOPILOT_GENERIC_WAYPOINTS_ONLY: - return "GENERIC_WAYPOINTS_ONLY"; - break; - case MAV_AUTOPILOT_GENERIC_WAYPOINTS_AND_SIMPLE_NAVIGATION_ONLY: - return "GENERIC_MISSION_NAVIGATION_ONLY"; - break; - case MAV_AUTOPILOT_GENERIC_MISSION_FULL: - return "GENERIC_MISSION_FULL"; - break; - case MAV_AUTOPILOT_INVALID: - return "NO AP"; - break; - case MAV_AUTOPILOT_PPZ: - return "PPZ"; - break; - case MAV_AUTOPILOT_UDB: - return "UDB"; - break; - case MAV_AUTOPILOT_FP: - return "FP"; - break; - case MAV_AUTOPILOT_PX4: - return "PX4"; - break; - default: - return ""; - break; - } - } - -public slots: - /** @brief Set the autopilot type */ - void setAutopilotType(int apType) - { - autopilot = apType; - emit systemSpecsChanged(uasId); - } - /** @brief Set the type of airframe */ - void setSystemType(int systemType); - /** @brief Set the specific airframe type */ - void setAirframe(int airframe) - { - if((airframe >= QGC_AIRFRAME_GENERIC) && (airframe < QGC_AIRFRAME_END_OF_ENUM)) - { - this->airframe = airframe; - emit systemSpecsChanged(uasId); - } - - } - /** @brief Set a new name **/ - void setUASName(const QString& name); - /** @brief Executes a command **/ - void executeCommand(MAV_CMD command); - /** @brief Executes a command with 7 params */ - void executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component); - /** @brief Set the current battery type and voltages */ - void setBatterySpecs(const QString& specs); - /** @brief Get the current battery type and specs */ - QString getBatterySpecs(); - - /** @brief Launches the system **/ - void launch(); - /** @brief Write this waypoint to the list of waypoints */ - //void setWaypoint(Waypoint* wp); FIXME tbd - /** @brief Set currently active waypoint */ - //void setWaypointActive(int id); FIXME tbd - /** @brief Order the robot to return home **/ - void home(); - /** @brief Order the robot to land **/ - void land(); - void halt(); - void go(); - - /** @brief Enable / disable HIL */ - void enableHilFlightGear(bool enable, QString options); - void enableHilXPlane(bool enable); - - - /** @brief Send the full HIL state to the MAV */ - - void sendHilState( uint64_t time_us, float roll, float pitch, float yaw, float rollspeed, - float pitchspeed, float yawspeed, int32_t lat, int32_t lon, int32_t alt, - int16_t vx, int16_t vy, int16_t vz, int16_t xacc, int16_t yacc, int16_t zacc); - - /** @brief Places the UAV in Hardware-in-the-Loop simulation status **/ - void startHil(); - - /** @brief Stops the UAV's Hardware-in-the-Loop simulation status **/ - void stopHil(); - - - /** @brief Stops the robot system. If it is an MAV, the robot starts the emergency landing procedure **/ - void emergencySTOP(); - - /** @brief Kills the robot. All systems are immediately shut down (e.g. the main power line is cut). This might lead to a crash **/ - bool emergencyKILL(); - - /** @brief Shut the system cleanly down. Will shut down any onboard computers **/ - void shutdown(); - - /** @brief Set the target position for the robot to navigate to. */ - void setTargetPosition(float x, float y, float z, float yaw); - - void startLowBattAlarm(); - void stopLowBattAlarm(); - - /** @brief Arm system */ - void armSystem(); - /** @brief Disable the motors */ - void disarmSystem(); - - /** @brief Set the values for the manual control of the vehicle */ - void setManualControlCommands(double roll, double pitch, double yaw, double thrust, int xHat, int yHat, int buttons); - /** @brief Receive a button pressed event from an input device, e.g. joystick */ - void receiveButton(int buttonIndex); - - /** @brief Add a link associated with this robot */ - void addLink(LinkInterface* link); - /** @brief Remove a link associated with this robot */ - void removeLink(QObject* object); - - /** @brief Receive a message from one of the communication links. */ - virtual void receiveMessage(LinkInterface* link, mavlink_message_t message); - -#ifdef QGC_PROTOBUF_ENABLED - /** @brief Receive a message from one of the communication links. */ - virtual void receiveExtendedMessage(LinkInterface* link, std::tr1::shared_ptr message); -#endif - - /** @brief Send a message over this link (to this or to all UAS on this link) */ - void sendMessage(LinkInterface* link, mavlink_message_t message); - /** @brief Send a message over all links this UAS can be reached with (!= all links) */ - void sendMessage(mavlink_message_t message); - - /** @brief Temporary Hack for sending packets to patch Antenna. Send a message over all serial links except for this UAS's */ - void forwardMessage(mavlink_message_t message); - - /** @brief Set this UAS as the system currently in focus, e.g. in the main display widgets */ - void setSelected(); - - /** @brief Set current mode of operation, e.g. auto or manual */ - void setMode(int mode); - - /** @brief Request all parameters */ - void requestParameters(); - - /** @brief Request a single parameter by name */ - void requestParameter(int component, const QString& parameter); - /** @brief Request a single parameter by index */ - void requestParameter(int component, int id); - - /** @brief Set a system parameter */ - void setParameter(const int component, const QString& id, const QVariant& value); - - /** @brief Write parameters to permanent storage */ - void writeParametersToStorage(); - /** @brief Read parameters from permanent storage */ - void readParametersFromStorage(); - - /** @brief Get the names of all parameters */ - QList getParameterNames(int component); - - /** @brief Get the ids of all components */ - QList getComponentIds(); - - void enableAllDataTransmission(int rate); - void enableRawSensorDataTransmission(int rate); - void enableExtendedSystemStatusTransmission(int rate); - void enableRCChannelDataTransmission(int rate); - void enableRawControllerDataTransmission(int rate); - //void enableRawSensorFusionTransmission(int rate); - void enablePositionTransmission(int rate); - void enableExtra1Transmission(int rate); - void enableExtra2Transmission(int rate); - void enableExtra3Transmission(int rate); - - /** @brief Update the system state */ - void updateState(); - - /** @brief Set world frame origin at current GPS position */ - void setLocalOriginAtCurrentGPSPosition(); - /** @brief Set world frame origin / home position at this GPS position */ - void setHomePosition(double lat, double lon, double alt); - /** @brief Set local position setpoint */ - void setLocalPositionSetpoint(float x, float y, float z, float yaw); - /** @brief Add an offset in body frame to the setpoint */ - void setLocalPositionOffset(float x, float y, float z, float yaw); - - void startRadioControlCalibration(); - void startMagnetometerCalibration(); - void startGyroscopeCalibration(); - void startPressureCalibration(); - - void startDataRecording(); - void stopDataRecording(); - void deleteSettings(); -signals: - /** @brief The main/battery voltage has changed/was updated */ - //void voltageChanged(int uasId, double voltage); // Defined in UASInterface already - /** @brief An actuator value has changed */ - //void actuatorChanged(UASInterface*, int actId, double value); // Defined in UASInterface already - /** @brief An actuator value has changed */ - void actuatorChanged(UASInterface* uas, QString actuatorName, double min, double max, double value); - void motorChanged(UASInterface* uas, QString motorName, double min, double max, double value); - /** @brief The system load (MCU/CPU usage) changed */ - void loadChanged(UASInterface* uas, double load); - /** @brief Propagate a heartbeat received from the system */ - //void heartbeat(UASInterface* uas); // Defined in UASInterface already - void imageStarted(quint64 timestamp); - /** @brief A new camera image has arrived */ - void imageReady(UASInterface* uas); - /** @brief HIL controls have changed */ - void hilControlsChanged(uint64_t time, float rollAilerons, float pitchElevator, float yawRudder, float throttle, uint8_t systemMode, uint8_t navMode); - /** @brief HIL actuator outputs have changed */ - void hilActuatorsChanged(uint64_t time, float act1, float act2, float act3, float act4, float act5, float act6, float act7, float act8); - -protected: - /** @brief Get the UNIX timestamp in milliseconds, enter microseconds */ - quint64 getUnixTime(quint64 time=0); - /** @brief Get the UNIX timestamp in milliseconds, enter milliseconds */ - quint64 getUnixTimeFromMs(quint64 time); - /** @brief Get the UNIX timestamp in milliseconds, ignore attitudeStamped mode */ - quint64 getUnixReferenceTime(quint64 time); - int componentID[256]; - bool componentMulti[256]; - bool connectionLost; ///< Flag indicates a timed out connection - quint64 connectionLossTime; ///< Time the connection was interrupted - quint64 lastVoltageWarning; ///< Time at which the last voltage warning occured - quint64 lastNonNullTime; ///< The last timestamp from the MAV that was not null - unsigned int onboardTimeOffsetInvalidCount; ///< Count when the offboard time offset estimation seemed wrong - bool hilEnabled; ///< Set to true if HIL mode is enabled from GCS (UAS might be in HIL even if this flag is not set, this defines the GCS HIL setting) - -protected slots: - /** @brief Write settings to disk */ - void writeSettings(); - /** @brief Read settings from disk */ - void readSettings(); - -// // MESSAGE RECEPTION -// /** @brief Receive a named value message */ -// void receiveMessageNamedValue(const mavlink_message_t& message); - -private: -// unsigned int mode; ///< The current mode of the MAV -}; - - -#endif // _UAS_H_ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2010 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Definition of Unmanned Aerial Vehicle object + * + * @author Lorenz Meier + * + */ + +#ifndef _UAS_H_ +#define _UAS_H_ + +#include "UASInterface.h" +#include +#include +#include "QGCMAVLink.h" +#include "QGCHilLink.h" +#include "QGCFlightGearLink.h" +#include "QGCXPlaneLink.h" + +/** + * @brief A generic MAVLINK-connected MAV/UAV + * + * This class represents one vehicle. It can be used like the real vehicle, e.g. a call to halt() + * will automatically send the appropriate messages to the vehicle. The vehicle state will also be + * automatically updated by the comm architecture, so when writing code to e.g. control the vehicle + * no knowledge of the communication infrastructure is needed. + */ +class UAS : public UASInterface +{ + Q_OBJECT +public: + UAS(MAVLinkProtocol* protocol, int id = 0); + ~UAS(); + + enum BatteryType + { + NICD = 0, + NIMH = 1, + LIION = 2, + LIPOLY = 3, + LIFE = 4, + AGZN = 5 + }; ///< The type of battery used + + static const int lipoFull = 4.2f; ///< 100% charged voltage + static const int lipoEmpty = 3.5f; ///< Discharged voltage + + /* MANAGEMENT */ + + /** @brief The name of the robot */ + QString getUASName(void) const; + /** @brief Get short state */ + const QString& getShortState() const; + /** @brief Get short mode */ + const QString& getShortMode() const; + /** @brief Translate from mode id to text */ + static QString getShortModeTextFor(int id); + /** @brief Translate from mode id to audio text */ + static QString getAudioModeTextFor(int id); + /** @brief Get the unique system id */ + int getUASID() const; + /** @brief Get the airframe */ + int getAirframe() const + { + return airframe; + } + /** @brief Get the components */ + QMap getComponents(); + + /** @brief The time interval the robot is switched on */ + quint64 getUptime() const; + /** @brief Get the status flag for the communication */ + int getCommunicationStatus() const; + /** @brief Add one measurement and get low-passed voltage */ + float filterVoltage(float value) const; + /** @brief Get the links associated with this robot */ + QList* getLinks(); + + double getLocalX() const + { + return localX; + } + double getLocalY() const + { + return localY; + } + double getLocalZ() const + { + return localZ; + } + double getLatitude() const + { + return latitude; + } + double getLongitude() const + { + return longitude; + } + double getAltitude() const + { + return altitude; + } + virtual bool localPositionKnown() const + { + return isLocalPositionKnown; + } + virtual bool globalPositionKnown() const + { + return isGlobalPositionKnown; + } + + double getRoll() const + { + return roll; + } + double getPitch() const + { + return pitch; + } + double getYaw() const + { + return yaw; + } + bool getSelected() const; + QVector3D getNedPosGlobalOffset() const + { + return nedPosGlobalOffset; + } + + QVector3D getNedAttGlobalOffset() const + { + return nedAttGlobalOffset; + } + +#if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) + px::GLOverlay getOverlay() + { + QMutexLocker locker(&overlayMutex); + return overlay; + } + + px::GLOverlay getOverlay(qreal& receivedTimestamp) + { + receivedTimestamp = receivedOverlayTimestamp; + QMutexLocker locker(&overlayMutex); + return overlay; + } + + px::ObstacleList getObstacleList() { + QMutexLocker locker(&obstacleListMutex); + return obstacleList; + } + + px::ObstacleList getObstacleList(qreal& receivedTimestamp) { + receivedTimestamp = receivedObstacleListTimestamp; + QMutexLocker locker(&obstacleListMutex); + return obstacleList; + } + + px::Path getPath() { + QMutexLocker locker(&pathMutex); + return path; + } + + px::Path getPath(qreal& receivedTimestamp) { + receivedTimestamp = receivedPathTimestamp; + QMutexLocker locker(&pathMutex); + return path; + } + + px::PointCloudXYZRGB getPointCloud() { + QMutexLocker locker(&pointCloudMutex); + return pointCloud; + } + + px::PointCloudXYZRGB getPointCloud(qreal& receivedTimestamp) { + receivedTimestamp = receivedPointCloudTimestamp; + QMutexLocker locker(&pointCloudMutex); + return pointCloud; + } + + px::RGBDImage getRGBDImage() { + QMutexLocker locker(&rgbdImageMutex); + return rgbdImage; + } + + px::RGBDImage getRGBDImage(qreal& receivedTimestamp) { + receivedTimestamp = receivedRGBDImageTimestamp; + QMutexLocker locker(&rgbdImageMutex); + return rgbdImage; + } +#endif + + friend class UASWaypointManager; + +protected: //COMMENTS FOR TEST UNIT + int uasId; ///< Unique system ID + unsigned char type; ///< UAS type (from type enum) + quint64 startTime; ///< The time the UAS was switched on + CommStatus commStatus; ///< Communication status + QString name; ///< Human-friendly name of the vehicle, e.g. bravo + int autopilot; ///< Type of the Autopilot: -1: None, 0: Generic, 1: PIXHAWK, 2: SLUGS, 3: Ardupilot (up to 15 types), defined in MAV_AUTOPILOT_TYPE ENUM + QList* links; ///< List of links this UAS can be reached by + QList unknownPackets; ///< Packet IDs which are unknown and have been received + MAVLinkProtocol* mavlink; ///< Reference to the MAVLink instance + BatteryType batteryType; ///< The battery type + int cells; ///< Number of cells + + UASWaypointManager waypointManager; + + QList actuatorValues; + QList actuatorNames; + + QList motorValues; + QList motorNames; + QMap components; ///< IDs and names of all detected onboard components + + double thrustSum; ///< Sum of forward/up thrust of all thrust actuators, in Newtons + double thrustMax; ///< Maximum forward/up thrust of this vehicle, in Newtons + + // Battery stats + float fullVoltage; ///< Voltage of the fully charged battery (100%) + float emptyVoltage; ///< Voltage of the empty battery (0%) + float startVoltage; ///< Voltage at system start + float tickVoltage; ///< Voltage where 0.1 V ticks are told + float lastTickVoltageValue; ///< The last voltage where a tick was announced + float tickLowpassVoltage; ///< Lowpass-filtered voltage for the tick announcement + float warnVoltage; ///< Voltage where QGC will start to warn about low battery + float warnLevelPercent; ///< Warning level, in percent + double currentVoltage; ///< Voltage currently measured + float lpVoltage; ///< Low-pass filtered voltage + bool batteryRemainingEstimateEnabled; ///< If the estimate is enabled, QGC will try to estimate the remaining battery life + float chargeLevel; ///< Charge level of battery, in percent + int timeRemaining; ///< Remaining time calculated based on previous and current + uint8_t mode; ///< The current mode of the MAV + uint32_t custom_mode; ///< The current mode of the MAV + int status; ///< The current status of the MAV + uint32_t navMode; ///< The current navigation mode of the MAV + quint64 onboardTimeOffset; + + bool controlRollManual; ///< status flag, true if roll is controlled manually + bool controlPitchManual; ///< status flag, true if pitch is controlled manually + bool controlYawManual; ///< status flag, true if yaw is controlled manually + bool controlThrustManual; ///< status flag, true if thrust is controlled manually + + double manualRollAngle; ///< Roll angle set by human pilot (radians) + double manualPitchAngle; ///< Pitch angle set by human pilot (radians) + double manualYawAngle; ///< Yaw angle set by human pilot (radians) + double manualThrust; ///< Thrust set by human pilot (radians) + float receiveDropRate; ///< Percentage of packets that were dropped on the MAV's receiving link (from GCS and other MAVs) + float sendDropRate; ///< Percentage of packets that were not received from the MAV by the GCS + bool lowBattAlarm; ///< Switch if battery is low + bool positionLock; ///< Status if position information is available or not + double localX; + double localY; + double localZ; + double latitude; + double longitude; + double altitude; + double speedX; ///< True speed in X axis + double speedY; ///< True speed in Y axis + double speedZ; ///< True speed in Z axis + double roll; + double pitch; + double yaw; + quint64 lastHeartbeat; ///< Time of the last heartbeat message + QTimer* statusTimeout; ///< Timer for various status timeouts + + int imageSize; ///< Image size being transmitted (bytes) + int imagePackets; ///< Number of data packets being sent for this image + int imagePacketsArrived; ///< Number of data packets recieved + int imagePayload; ///< Payload size per transmitted packet (bytes). Standard is 254, and decreases when image resolution increases. + int imageQuality; ///< Quality of the transmitted image (percentage) + int imageType; ///< Type of the transmitted image (BMP, PNG, JPEG, RAW 8 bit, RAW 32 bit) + int imageWidth; ///< Width of the image stream + int imageHeight; ///< Width of the image stream + QByteArray imageRecBuffer; ///< Buffer for the incoming bytestream + QImage image; ///< Image data of last completely transmitted image + quint64 imageStart; + +#if defined(QGC_PROTOBUF_ENABLED) && defined(QGC_USE_PIXHAWK_MESSAGES) + px::GLOverlay overlay; + QMutex overlayMutex; + qreal receivedOverlayTimestamp; + + px::ObstacleList obstacleList; + QMutex obstacleListMutex; + qreal receivedObstacleListTimestamp; + + px::Path path; + QMutex pathMutex; + qreal receivedPathTimestamp; + + px::PointCloudXYZRGB pointCloud; + QMutex pointCloudMutex; + qreal receivedPointCloudTimestamp; + + px::RGBDImage rgbdImage; + QMutex rgbdImageMutex; + qreal receivedRGBDImageTimestamp; +#endif + + QMap* > parameters; ///< All parameters + bool paramsOnceRequested; ///< If the parameter list has been read at least once + int airframe; ///< The airframe type + bool attitudeKnown; ///< True if attitude was received, false else + QGCUASParamManager* paramManager; ///< Parameter manager class + QString shortStateText; ///< Short textual state description + QString shortModeText; ///< Short textual mode description + bool attitudeStamped; ///< Should arriving data be timestamped with the last attitude? This helps with broken system time clocks on the MAV + quint64 lastAttitude; ///< Timestamp of last attitude measurement + QGCHilLink* simulation; ///< Hardware in the loop simulation link + bool isLocalPositionKnown; ///< If the local position has been received for this MAV + bool isGlobalPositionKnown; ///< If the global position has been received for this MAV + bool systemIsArmed; ///< If the system is armed + QVector3D nedPosGlobalOffset; ///< Offset between the system's NED position measurements and the swarm / global 0/0/0 origin + QVector3D nedAttGlobalOffset; ///< Offset between the system's NED position measurements and the swarm / global 0/0/0 origin + +public: + /** @brief Set the current battery type */ + void setBattery(BatteryType type, int cells); + /** @brief Estimate how much flight time is remaining */ + int calculateTimeRemaining(); + /** @brief Get the current charge level */ + float getChargeLevel(); + /** @brief Get the human-readable status message for this code */ + void getStatusForCode(int statusCode, QString& uasState, QString& stateDescription); + /** @brief Get the human-readable navigation mode translation for this mode */ + QString getNavModeText(int mode); + /** @brief Check if vehicle is in autonomous mode */ + bool isAuto(); + /** @brief Check if vehicle is armed */ + bool isArmed() const { return systemIsArmed; } + + UASWaypointManager* getWaypointManager() { + return &waypointManager; + } + /** @brief Get reference to the param manager **/ + QGCUASParamManager* getParamManager() const { + return paramManager; + } + /** @brief Get the HIL simulation */ + QGCHilLink* getHILSimulation() const { + return simulation; + } + // TODO Will be removed + /** @brief Set reference to the param manager **/ + void setParamManager(QGCUASParamManager* manager) { + paramManager = manager; + } + int getSystemType(); + QString getSystemTypeName() + { + switch(type) + { + case MAV_TYPE_GENERIC: + return "GENERIC"; + break; + case MAV_TYPE_FIXED_WING: + return "FIXED_WING"; + break; + case MAV_TYPE_QUADROTOR: + return "QUADROTOR"; + break; + case MAV_TYPE_COAXIAL: + return "COAXIAL"; + break; + case MAV_TYPE_HELICOPTER: + return "HELICOPTER"; + break; + case MAV_TYPE_ANTENNA_TRACKER: + return "ANTENNA_TRACKER"; + break; + case MAV_TYPE_GCS: + return "GCS"; + break; + case MAV_TYPE_AIRSHIP: + return "AIRSHIP"; + break; + case MAV_TYPE_FREE_BALLOON: + return "FREE_BALLOON"; + break; + case MAV_TYPE_ROCKET: + return "ROCKET"; + break; + case MAV_TYPE_GROUND_ROVER: + return "GROUND_ROVER"; + break; + case MAV_TYPE_SURFACE_BOAT: + return "BOAT"; + break; + case MAV_TYPE_SUBMARINE: + return "SUBMARINE"; + break; + case MAV_TYPE_HEXAROTOR: + return "HEXAROTOR"; + break; + case MAV_TYPE_OCTOROTOR: + return "OCTOROTOR"; + break; + case MAV_TYPE_TRICOPTER: + return "TRICOPTER"; + break; + case MAV_TYPE_FLAPPING_WING: + return "FLAPPING_WING"; + break; + default: + return ""; + break; + } + } + + QImage getImage(); + void requestImage(); + int getAutopilotType(){ + return autopilot; + } + QString getAutopilotTypeName() + { + switch (autopilot) + { + case MAV_AUTOPILOT_GENERIC: + return "GENERIC"; + break; + case MAV_AUTOPILOT_PIXHAWK: + return "PIXHAWK"; + break; + case MAV_AUTOPILOT_SLUGS: + return "SLUGS"; + break; + case MAV_AUTOPILOT_ARDUPILOTMEGA: + return "ARDUPILOTMEGA"; + break; + case MAV_AUTOPILOT_OPENPILOT: + return "OPENPILOT"; + break; + case MAV_AUTOPILOT_GENERIC_WAYPOINTS_ONLY: + return "GENERIC_WAYPOINTS_ONLY"; + break; + case MAV_AUTOPILOT_GENERIC_WAYPOINTS_AND_SIMPLE_NAVIGATION_ONLY: + return "GENERIC_MISSION_NAVIGATION_ONLY"; + break; + case MAV_AUTOPILOT_GENERIC_MISSION_FULL: + return "GENERIC_MISSION_FULL"; + break; + case MAV_AUTOPILOT_INVALID: + return "NO AP"; + break; + case MAV_AUTOPILOT_PPZ: + return "PPZ"; + break; + case MAV_AUTOPILOT_UDB: + return "UDB"; + break; + case MAV_AUTOPILOT_FP: + return "FP"; + break; + case MAV_AUTOPILOT_PX4: + return "PX4"; + break; + default: + return ""; + break; + } + } + +public slots: + /** @brief Set the autopilot type */ + void setAutopilotType(int apType) + { + autopilot = apType; + emit systemSpecsChanged(uasId); + } + /** @brief Set the type of airframe */ + void setSystemType(int systemType); + /** @brief Set the specific airframe type */ + void setAirframe(int airframe) + { + if((airframe >= QGC_AIRFRAME_GENERIC) && (airframe < QGC_AIRFRAME_END_OF_ENUM)) + { + this->airframe = airframe; + emit systemSpecsChanged(uasId); + } + + } + /** @brief Set a new name **/ + void setUASName(const QString& name); + /** @brief Executes a command **/ + void executeCommand(MAV_CMD command); + /** @brief Executes a command with 7 params */ + void executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component); + /** @brief Set the current battery type and voltages */ + void setBatterySpecs(const QString& specs); + /** @brief Get the current battery type and specs */ + QString getBatterySpecs(); + + /** @brief Launches the system **/ + void launch(); + /** @brief Write this waypoint to the list of waypoints */ + //void setWaypoint(Waypoint* wp); FIXME tbd + /** @brief Set currently active waypoint */ + //void setWaypointActive(int id); FIXME tbd + /** @brief Order the robot to return home **/ + void home(); + /** @brief Order the robot to land **/ + void land(); + void halt(); + void go(); + + /** @brief Enable / disable HIL */ + void enableHilFlightGear(bool enable, QString options); + void enableHilXPlane(bool enable); + + + /** @brief Send the full HIL state to the MAV */ + + void sendHilState( uint64_t time_us, float roll, float pitch, float yaw, float rollspeed, + float pitchspeed, float yawspeed, int32_t lat, int32_t lon, int32_t alt, + int16_t vx, int16_t vy, int16_t vz, int16_t xacc, int16_t yacc, int16_t zacc); + + /** @brief Places the UAV in Hardware-in-the-Loop simulation status **/ + void startHil(); + + /** @brief Stops the UAV's Hardware-in-the-Loop simulation status **/ + void stopHil(); + + + /** @brief Stops the robot system. If it is an MAV, the robot starts the emergency landing procedure **/ + void emergencySTOP(); + + /** @brief Kills the robot. All systems are immediately shut down (e.g. the main power line is cut). This might lead to a crash **/ + bool emergencyKILL(); + + /** @brief Shut the system cleanly down. Will shut down any onboard computers **/ + void shutdown(); + + /** @brief Set the target position for the robot to navigate to. */ + void setTargetPosition(float x, float y, float z, float yaw); + + void startLowBattAlarm(); + void stopLowBattAlarm(); + + /** @brief Arm system */ + void armSystem(); + /** @brief Disable the motors */ + void disarmSystem(); + + /** @brief Set the values for the manual control of the vehicle */ + void setManualControlCommands(double roll, double pitch, double yaw, double thrust, int xHat, int yHat, int buttons); + /** @brief Receive a button pressed event from an input device, e.g. joystick */ + void receiveButton(int buttonIndex); + + /** @brief Set the values for the 6dof manual control of the vehicle */ + void setManual6DOFControlCommands(double x, double y, double z, double roll, double pitch, double yaw); + + /** @brief Add a link associated with this robot */ + void addLink(LinkInterface* link); + /** @brief Remove a link associated with this robot */ + void removeLink(QObject* object); + + /** @brief Receive a message from one of the communication links. */ + virtual void receiveMessage(LinkInterface* link, mavlink_message_t message); + +#ifdef QGC_PROTOBUF_ENABLED + /** @brief Receive a message from one of the communication links. */ + virtual void receiveExtendedMessage(LinkInterface* link, std::tr1::shared_ptr message); +#endif + + /** @brief Send a message over this link (to this or to all UAS on this link) */ + void sendMessage(LinkInterface* link, mavlink_message_t message); + /** @brief Send a message over all links this UAS can be reached with (!= all links) */ + void sendMessage(mavlink_message_t message); + + /** @brief Temporary Hack for sending packets to patch Antenna. Send a message over all serial links except for this UAS's */ + void forwardMessage(mavlink_message_t message); + + /** @brief Set this UAS as the system currently in focus, e.g. in the main display widgets */ + void setSelected(); + + /** @brief Set current mode of operation, e.g. auto or manual */ + void setMode(int mode); + + /** @brief Request all parameters */ + void requestParameters(); + + /** @brief Request a single parameter by name */ + void requestParameter(int component, const QString& parameter); + /** @brief Request a single parameter by index */ + void requestParameter(int component, int id); + + /** @brief Set a system parameter */ + void setParameter(const int component, const QString& id, const QVariant& value); + + /** @brief Write parameters to permanent storage */ + void writeParametersToStorage(); + /** @brief Read parameters from permanent storage */ + void readParametersFromStorage(); + + /** @brief Get the names of all parameters */ + QList getParameterNames(int component); + + /** @brief Get the ids of all components */ + QList getComponentIds(); + + void enableAllDataTransmission(int rate); + void enableRawSensorDataTransmission(int rate); + void enableExtendedSystemStatusTransmission(int rate); + void enableRCChannelDataTransmission(int rate); + void enableRawControllerDataTransmission(int rate); + //void enableRawSensorFusionTransmission(int rate); + void enablePositionTransmission(int rate); + void enableExtra1Transmission(int rate); + void enableExtra2Transmission(int rate); + void enableExtra3Transmission(int rate); + + /** @brief Update the system state */ + void updateState(); + + /** @brief Set world frame origin at current GPS position */ + void setLocalOriginAtCurrentGPSPosition(); + /** @brief Set world frame origin / home position at this GPS position */ + void setHomePosition(double lat, double lon, double alt); + /** @brief Set local position setpoint */ + void setLocalPositionSetpoint(float x, float y, float z, float yaw); + /** @brief Add an offset in body frame to the setpoint */ + void setLocalPositionOffset(float x, float y, float z, float yaw); + + void startRadioControlCalibration(); + void startMagnetometerCalibration(); + void startGyroscopeCalibration(); + void startPressureCalibration(); + + void startDataRecording(); + void stopDataRecording(); + void deleteSettings(); +signals: + /** @brief The main/battery voltage has changed/was updated */ + //void voltageChanged(int uasId, double voltage); // Defined in UASInterface already + /** @brief An actuator value has changed */ + //void actuatorChanged(UASInterface*, int actId, double value); // Defined in UASInterface already + /** @brief An actuator value has changed */ + void actuatorChanged(UASInterface* uas, QString actuatorName, double min, double max, double value); + void motorChanged(UASInterface* uas, QString motorName, double min, double max, double value); + /** @brief The system load (MCU/CPU usage) changed */ + void loadChanged(UASInterface* uas, double load); + /** @brief Propagate a heartbeat received from the system */ + //void heartbeat(UASInterface* uas); // Defined in UASInterface already + void imageStarted(quint64 timestamp); + /** @brief A new camera image has arrived */ + void imageReady(UASInterface* uas); + /** @brief HIL controls have changed */ + void hilControlsChanged(uint64_t time, float rollAilerons, float pitchElevator, float yawRudder, float throttle, uint8_t systemMode, uint8_t navMode); + /** @brief HIL actuator outputs have changed */ + void hilActuatorsChanged(uint64_t time, float act1, float act2, float act3, float act4, float act5, float act6, float act7, float act8); + +protected: + /** @brief Get the UNIX timestamp in milliseconds, enter microseconds */ + quint64 getUnixTime(quint64 time=0); + /** @brief Get the UNIX timestamp in milliseconds, enter milliseconds */ + quint64 getUnixTimeFromMs(quint64 time); + /** @brief Get the UNIX timestamp in milliseconds, ignore attitudeStamped mode */ + quint64 getUnixReferenceTime(quint64 time); + int componentID[256]; + bool componentMulti[256]; + bool connectionLost; ///< Flag indicates a timed out connection + quint64 connectionLossTime; ///< Time the connection was interrupted + quint64 lastVoltageWarning; ///< Time at which the last voltage warning occured + quint64 lastNonNullTime; ///< The last timestamp from the MAV that was not null + unsigned int onboardTimeOffsetInvalidCount; ///< Count when the offboard time offset estimation seemed wrong + bool hilEnabled; ///< Set to true if HIL mode is enabled from GCS (UAS might be in HIL even if this flag is not set, this defines the GCS HIL setting) + +protected slots: + /** @brief Write settings to disk */ + void writeSettings(); + /** @brief Read settings from disk */ + void readSettings(); + +// // MESSAGE RECEPTION +// /** @brief Receive a named value message */ +// void receiveMessageNamedValue(const mavlink_message_t& message); + +private: +// unsigned int mode; ///< The current mode of the MAV +}; + + +#endif // _UAS_H_ diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index da698e4b6de3c808fabfb068d2f686cd1d505ebe..f026a66efa1eb0a7297a84193908459b83446c8a 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -1,1721 +1,1728 @@ -/*===================================================================== - -QGroundControl Open Source Ground Control Station - -(c) 2009 - 2011 QGROUNDCONTROL PROJECT - -This file is part of the QGROUNDCONTROL project - - QGROUNDCONTROL is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - QGROUNDCONTROL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with QGROUNDCONTROL. If not, see . - -======================================================================*/ - -/** - * @file - * @brief Implementation of class MainWindow - * @author Lorenz Meier - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "QGC.h" -#include "MAVLinkSimulationLink.h" -#include "SerialLink.h" -#include "UDPLink.h" -#include "MAVLinkProtocol.h" -#include "CommConfigurationWindow.h" -#include "QGCWaypointListMulti.h" -#include "MainWindow.h" -#include "JoystickWidget.h" -#include "GAudioOutput.h" -#include "QGCToolWidget.h" -#include "QGCMAVLinkLogPlayer.h" -#include "QGCSettingsWidget.h" -#include "QGCMapTool.h" -#include "MAVLinkDecoder.h" -#include "QGCMAVLinkMessageSender.h" -#include "QGCRGBDView.h" -#include "QGCFirmwareUpdate.h" - -#ifdef QGC_OSG_ENABLED -#include "Q3DWidgetFactory.h" -#endif - -// FIXME Move -#include "PxQuadMAV.h" -#include "SlugsMAV.h" - - -#include "LogCompressor.h" - -MainWindow* MainWindow::instance(QSplashScreen* screen) -{ - static MainWindow* _instance = 0; - if(_instance == 0) - { - _instance = new MainWindow(); - if (screen) connect(_instance, SIGNAL(initStatusChanged(QString)), screen, SLOT(showMessage(QString))); - - /* Set the application as parent to ensure that this object - * will be destroyed when the main application exits */ - //_instance->setParent(qApp); - } - return _instance; -} - -/** -* Create new mainwindow. The constructor instantiates all parts of the user -* interface. It does NOT show the mainwindow. To display it, call the show() -* method. -* -* @see QMainWindow::show() -**/ -MainWindow::MainWindow(QWidget *parent): - QMainWindow(parent), - currentView(VIEW_UNCONNECTED), - currentStyle(QGC_MAINWINDOW_STYLE_INDOOR), - aboutToCloseFlag(false), - changingViewsFlag(false), - centerStackActionGroup(new QActionGroup(this)), - styleFileName(QCoreApplication::applicationDirPath() + "/style-indoor.css"), - autoReconnect(false), - lowPowerMode(false) -{ - hide(); - emit initStatusChanged("Loading UI Settings.."); - loadSettings(); - if (!settings.contains("CURRENT_VIEW")) - { - // Set this view as default view - settings.setValue("CURRENT_VIEW", currentView); - } - else - { - // LOAD THE LAST VIEW - VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt(); - if (currentViewCandidate != VIEW_ENGINEER && - currentViewCandidate != VIEW_OPERATOR && - currentViewCandidate != VIEW_PILOT && - currentViewCandidate != VIEW_FULL) - { - currentView = currentViewCandidate; - } - } - - settings.sync(); - - emit initStatusChanged("Loading Style."); - loadStyle(currentStyle); - - emit initStatusChanged("Setting up user interface."); - - // Setup user interface - ui.setupUi(this); - hide(); - - // Set dock options - setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks); - statusBar()->setSizeGripEnabled(true); - - configureWindowName(); - - // Setup corners - setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); - - // Setup UI state machines - centerStackActionGroup->setExclusive(true); - - centerStack = new QStackedWidget(this); - setCentralWidget(centerStack); - - // Load Toolbar - toolBar = new QGCToolBar(this); - this->addToolBar(toolBar); - // Add actions - toolBar->addPerspectiveChangeAction(ui.actionOperatorsView); - toolBar->addPerspectiveChangeAction(ui.actionEngineersView); - toolBar->addPerspectiveChangeAction(ui.actionPilotsView); - - emit initStatusChanged("Building common widgets."); - - buildCommonWidgets(); - connectCommonWidgets(); - - emit initStatusChanged("Building common actions."); - - // Create actions - connectCommonActions(); - - // Populate link menu - emit initStatusChanged("Populating link menu"); - QList links = LinkManager::instance()->getLinks(); - foreach(LinkInterface* link, links) - { - this->addLink(link); - } - - connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*))); - - // Connect user interface devices - emit initStatusChanged("Initializing joystick interface."); - joystickWidget = 0; - joystick = new JoystickInput(); - - // Connect link - if (autoReconnect) - { - SerialLink* link = new SerialLink(); - // Add to registry - LinkManager::instance()->add(link); - LinkManager::instance()->addProtocol(link, mavlink); - link->connect(); - } - - // Set low power mode - enableLowPowerMode(lowPowerMode); - - // Initialize window state - windowStateVal = windowState(); - - emit initStatusChanged("Restoring last view state."); - - // Restore the window setup - loadViewState(); - - emit initStatusChanged("Restoring last window size."); - // Restore the window position and size - if (settings.contains(getWindowGeometryKey())) - { - // Restore the window geometry - restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray()); - show(); - } - else - { - // Adjust the size - const int screenWidth = QApplication::desktop()->width(); - const int screenHeight = QApplication::desktop()->height(); - - if (screenWidth < 1200) - { - showFullScreen(); - } - else - { - resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f))); - show(); - } - - } - - connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName())); - windowNameUpdateTimer.start(15000); - emit initStatusChanged("Done."); - show(); -} - -MainWindow::~MainWindow() -{ - if (mavlink) - { - delete mavlink; - mavlink = NULL; - } -// if (simulationLink) -// { -// simulationLink->deleteLater(); -// simulationLink = NULL; -// } - if (joystick) - { - delete joystick; - joystick = NULL; - } - - // Get and delete all dockwidgets and contained - // widgets - QObjectList childList(this->children()); - - QObjectList::iterator i; - QDockWidget* dockWidget; - for (i = childList.begin(); i != childList.end(); ++i) - { - dockWidget = dynamic_cast(*i); - if (dockWidget) - { - // Remove dock widget from main window - // removeDockWidget(dockWidget); - // delete dockWidget->widget(); - delete dockWidget; - dockWidget = NULL; - } - else if (dynamic_cast(*i)) - { - delete dynamic_cast(*i); - *i = NULL; - } - } - // Delete all UAS objects -} - -void MainWindow::resizeEvent(QResizeEvent * event) -{ - if (height() < 800) - { - ui.statusBar->setVisible(false); - } - else - { - ui.statusBar->setVisible(true); - ui.statusBar->setSizeGripEnabled(true); - } - - if (width() > 1200) - { - toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - } - else - { - toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); - } - - QMainWindow::resizeEvent(event); -} - -QString MainWindow::getWindowStateKey() -{ - return QString::number(currentView)+"_windowstate"; -} - -QString MainWindow::getWindowGeometryKey() -{ - //return QString::number(currentView)+"_geometry"; - return "_geometry"; -} - -void MainWindow::buildCustomWidget() -{ - // Create custom widgets - QList widgets = QGCToolWidget::createWidgetsFromSettings(this); - - if (widgets.size() > 0) - { - ui.menuTools->addSeparator(); - } - - for(int i = 0; i < widgets.size(); ++i) - { - // Check if this widget already has a parent, do not create it in this case - QGCToolWidget* tool = widgets.at(i); - QDockWidget* dock = dynamic_cast(tool->parentWidget()); - if (!dock) - { - QDockWidget* dock = new QDockWidget(tool->windowTitle(), this); - dock->setObjectName(tool->objectName()+"_DOCK"); - dock->setWidget(tool); - connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); - QAction* showAction = new QAction(widgets.at(i)->windowTitle(), this); - showAction->setCheckable(true); - connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); - connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); - widgets.at(i)->setMainMenuAction(showAction); - ui.menuTools->addAction(showAction); - - // Load dock widget location (default is bottom) - Qt::DockWidgetArea location = static_cast (tool->getDockWidgetArea(currentView)); - - addDockWidget(location, dock); - } - } -} - -void MainWindow::buildCommonWidgets() -{ - //TODO: move protocol outside UI - mavlink = new MAVLinkProtocol(); - connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection); - // Add generic MAVLink decoder - mavlinkDecoder = new MAVLinkDecoder(mavlink, this); - - // Dock widgets - if (!controlDockWidget) - { - controlDockWidget = new QDockWidget(tr("Control"), this); - controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET"); - controlDockWidget->setWidget( new UASControlWidget(this) ); - addTool(controlDockWidget, tr("Control"), Qt::LeftDockWidgetArea); - } - - if (!listDockWidget) - { - listDockWidget = new QDockWidget(tr("Unmanned Systems"), this); - listDockWidget->setWidget( new UASListWidget(this) ); - listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET"); - addTool(listDockWidget, tr("Unmanned Systems"), Qt::RightDockWidgetArea); - } - - if (!waypointsDockWidget) - { - waypointsDockWidget = new QDockWidget(tr("Mission Plan"), this); - waypointsDockWidget->setWidget( new QGCWaypointListMulti(this) ); - waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET"); - addTool(waypointsDockWidget, tr("Mission Plan"), Qt::BottomDockWidgetArea); - } - - if (!infoDockWidget) - { - infoDockWidget = new QDockWidget(tr("Status Details"), this); - infoDockWidget->setWidget( new UASInfoWidget(this) ); - infoDockWidget->setObjectName("UAS_STATUS_DETAILS_DOCKWIDGET"); - addTool(infoDockWidget, tr("Status Details"), Qt::RightDockWidgetArea); - } - - if (!debugConsoleDockWidget) - { - debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this); - debugConsoleDockWidget->setWidget( new DebugConsole(this) ); - debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET"); - - DebugConsole *debugConsole = dynamic_cast(debugConsoleDockWidget->widget()); - connect(mavlinkDecoder, SIGNAL(textMessageReceived(int, int, int, const QString)), debugConsole, SLOT(receiveTextMessage(int, int, int, const QString))); - - addTool(debugConsoleDockWidget, tr("Communication Console"), Qt::BottomDockWidgetArea); - } - - if (!logPlayerDockWidget) - { - logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this); - logPlayer = new QGCMAVLinkLogPlayer(mavlink, this); - toolBar->setLogPlayer(logPlayer); - logPlayerDockWidget->setWidget(logPlayer); - logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET"); - addTool(logPlayerDockWidget, tr("MAVLink Log Replay"), Qt::RightDockWidgetArea); - } - - if (!mavlinkInspectorWidget) - { - mavlinkInspectorWidget = new QDockWidget(tr("MAVLink Message Inspector"), this); - mavlinkInspectorWidget->setWidget( new QGCMAVLinkInspector(mavlink, this) ); - mavlinkInspectorWidget->setObjectName("MAVLINK_INSPECTOR_DOCKWIDGET"); - addTool(mavlinkInspectorWidget, tr("MAVLink Inspector"), Qt::RightDockWidgetArea); - } - - if (!mavlinkSenderWidget) - { -// mavlinkSenderWidget = new QDockWidget(tr("MAVLink Message Sender"), this); -// mavlinkSenderWidget->setWidget( new QGCMAVLinkMessageSender(mavlink, this) ); -// mavlinkSenderWidget->setObjectName("MAVLINK_SENDER_DOCKWIDGET"); -// addTool(mavlinkSenderWidget, tr("MAVLink Sender"), Qt::RightDockWidgetArea); - } - - //FIXME: memory of acceptList will never be freed again - QStringList* acceptList = new QStringList(); - acceptList->append("-3.3,ATTITUDE.roll,rad,+3.3,s"); - acceptList->append("-3.3,ATTITUDE.pitch,deg,+3.3,s"); - acceptList->append("-3.3,ATTITUDE.yaw,deg,+3.3,s"); - - //FIXME: memory of acceptList2 will never be freed again - QStringList* acceptList2 = new QStringList(); - acceptList2->append("0,RAW_PRESSURE.pres_abs,hPa,65500"); - - if (!parametersDockWidget) - { - parametersDockWidget = new QDockWidget(tr("Onboard Parameters"), this); - parametersDockWidget->setWidget( new ParameterInterface(this) ); - parametersDockWidget->setObjectName("PARAMETER_INTERFACE_DOCKWIDGET"); - addTool(parametersDockWidget, tr("Onboard Parameters"), Qt::RightDockWidgetArea); - } - - if (!hsiDockWidget) - { - hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this); - hsiDockWidget->setWidget( new HSIDisplay(this) ); - hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET"); - addTool(hsiDockWidget, tr("Horizontal Situation"), Qt::BottomDockWidgetArea); - } - - if (!headDown1DockWidget) - { - headDown1DockWidget = new QDockWidget(tr("Flight Display"), this); - HDDisplay* hdDisplay = new HDDisplay(acceptList, "Flight Display", this); - hdDisplay->addSource(mavlinkDecoder); - headDown1DockWidget->setWidget(hdDisplay); - headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET"); - addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea); - } - - if (!headDown2DockWidget) - { - headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this); - HDDisplay* hdDisplay = new HDDisplay(acceptList2, "Actuator Status", this); - hdDisplay->addSource(mavlinkDecoder); - headDown2DockWidget->setWidget(hdDisplay); - headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET"); - addTool(headDown2DockWidget, tr("Actuator Status"), Qt::RightDockWidgetArea); - } - - if (!rcViewDockWidget) - { - rcViewDockWidget = new QDockWidget(tr("Radio Control"), this); - rcViewDockWidget->setWidget( new QGCRemoteControlView(this) ); - rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET"); - addTool(rcViewDockWidget, tr("Radio Control"), Qt::BottomDockWidgetArea); - } - - if (!headUpDockWidget) - { - headUpDockWidget = new QDockWidget(tr("HUD"), this); - headUpDockWidget->setWidget( new HUD(320, 240, this)); - headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET"); - addTool(headUpDockWidget, tr("Head Up Display"), Qt::RightDockWidgetArea); - } - - if (!video1DockWidget) - { - video1DockWidget = new QDockWidget(tr("Video Stream 1"), this); - QGCRGBDView* video1 = new QGCRGBDView(160, 120, this); - video1->enableHUDInstruments(false); - video1->enableVideo(false); - // FIXME select video stream as well - video1DockWidget->setWidget(video1); - video1DockWidget->setObjectName("VIDEO_STREAM_1_DOCK_WIDGET"); - addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea); - } - - if (!video2DockWidget) - { - video2DockWidget = new QDockWidget(tr("Video Stream 2"), this); - QGCRGBDView* video2 = new QGCRGBDView(160, 120, this); - video2->enableHUDInstruments(false); - video2->enableVideo(false); - // FIXME select video stream as well - video2DockWidget->setWidget(video2); - video2DockWidget->setObjectName("VIDEO_STREAM_2_DOCK_WIDGET"); - addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea); - } - -// if (!rgbd1DockWidget) { -// rgbd1DockWidget = new QDockWidget(tr("Video Stream 1"), this); -// HUD* video1 = new HUD(160, 120, this); -// video1->enableHUDInstruments(false); -// video1->enableVideo(true); -// // FIXME select video stream as well -// video1DockWidget->setWidget(video1); -// video1DockWidget->setObjectName("VIDEO_STREAM_1_DOCK_WIDGET"); -// addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea); -// } - -// if (!rgbd2DockWidget) { -// video2DockWidget = new QDockWidget(tr("Video Stream 2"), this); -// HUD* video2 = new HUD(160, 120, this); -// video2->enableHUDInstruments(false); -// video2->enableVideo(true); -// // FIXME select video stream as well -// video2DockWidget->setWidget(video2); -// video2DockWidget->setObjectName("VIDEO_STREAM_2_DOCK_WIDGET"); -// addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea); -// } - - // Custom widgets, added last to all menus and layouts - buildCustomWidget(); - - // Center widgets - if (!mapWidget) - { - mapWidget = new QGCMapTool(this); - addCentralWidget(mapWidget, "Maps"); - } - - if (!protocolWidget) - { - protocolWidget = new XMLCommProtocolWidget(this); - addCentralWidget(protocolWidget, "Mavlink Generator"); - } - -// if (!firmwareUpdateWidget) -// { -// firmwareUpdateWidget = new QGCFirmwareUpdate(this); -// addCentralWidget(firmwareUpdateWidget, "Firmware Update"); -// } - - if (!hudWidget) - { - hudWidget = new HUD(320, 240, this); - addCentralWidget(hudWidget, tr("Head Up Display")); - } - - if (!configWidget) - { - configWidget = new QGCVehicleConfig(this); - addCentralWidget(configWidget, tr("Vehicle Configuration")); - } - - if (!dataplotWidget) - { - dataplotWidget = new QGCDataPlot2D(this); - addCentralWidget(dataplotWidget, tr("Logfile Plot")); - } - -#ifdef QGC_OSG_ENABLED - if (!_3DWidget) - { - _3DWidget = Q3DWidgetFactory::get("PIXHAWK", this); - addCentralWidget(_3DWidget, tr("Local 3D")); - } -#endif - -#if (defined _MSC_VER) | (defined Q_OS_MAC) - if (!gEarthWidget) - { - gEarthWidget = new QGCGoogleEarthView(this); - addCentralWidget(gEarthWidget, tr("Google Earth")); - } -#endif -} - -void MainWindow::addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea area) -{ - QAction* tempAction = ui.menuTools->addAction(title); - - tempAction->setCheckable(true); - QVariant var; - var.setValue((QWidget*)widget); - tempAction->setData(var); - connect(tempAction,SIGNAL(triggered(bool)),this, SLOT(showTool(bool))); - connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool))); - tempAction->setChecked(widget->isVisible()); - addDockWidget(area, widget); -} - - -void MainWindow::showTool(bool show) -{ - QAction* act = qobject_cast(sender()); - QWidget* widget = qVariantValue(act->data()); - widget->setVisible(show); -} - -void MainWindow::addCentralWidget(QWidget* widget, const QString& title) -{ - // Check if this widget already has been added - if (centerStack->indexOf(widget) == -1) - { - centerStack->addWidget(widget); - - QAction* tempAction = ui.menuMain->addAction(title); - - tempAction->setCheckable(true); - QVariant var; - var.setValue((QWidget*)widget); - tempAction->setData(var); - centerStackActionGroup->addAction(tempAction); - connect(tempAction,SIGNAL(triggered()),this, SLOT(showCentralWidget())); - connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool))); - tempAction->setChecked(widget->isVisible()); - } -} - - -void MainWindow::showCentralWidget() -{ - QAction* act = qobject_cast(sender()); - QWidget* widget = qVariantValue(act->data()); - centerStack->setCurrentWidget(widget); -} - -void MainWindow::showHILConfigurationWidget(UASInterface* uas) -{ - // Add simulation configuration widget - UAS* mav = dynamic_cast(uas); - - if (mav) - { - QGCHilConfiguration* hconf = new QGCHilConfiguration(mav, this); - QString hilDockName = tr("HIL Config (%1)").arg(uas->getUASName()); - QDockWidget* hilDock = new QDockWidget(hilDockName, this); - hilDock->setWidget(hconf); - hilDock->setObjectName(QString("HIL_CONFIG_%1").arg(uas->getUASID())); - addTool(hilDock, hilDockName, Qt::RightDockWidgetArea); - - } - - // Reload view state in case new widgets were added - loadViewState(); -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - if (isVisible()) storeViewState(); - storeSettings(); - aboutToCloseFlag = true; - mavlink->storeSettings(); - UASManager::instance()->storeSettings(); - QMainWindow::closeEvent(event); -} - -/** - * Connect the signals and slots of the common window widgets - */ -void MainWindow::connectCommonWidgets() -{ - if (infoDockWidget && infoDockWidget->widget()) - { - connect(mavlink, SIGNAL(receiveLossChanged(int, float)), - infoDockWidget->widget(), SLOT(updateSendLoss(int, float))); - } -} - -void MainWindow::createCustomWidget() -{ - QDockWidget* dock = new QDockWidget("Unnamed Tool", this); - QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock); - - if (QGCToolWidget::instances()->size() < 2) - { - // This is the first widget - ui.menuTools->addSeparator(); - } - - connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); - dock->setWidget(tool); - - QAction* showAction = new QAction(tool->getTitle(), this); - showAction->setCheckable(true); - connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); - connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); - tool->setMainMenuAction(showAction); - ui.menuTools->addAction(showAction); - this->addDockWidget(Qt::BottomDockWidgetArea, dock); - dock->setVisible(true); -} - -void MainWindow::loadCustomWidget() -{ - QString widgetFileExtension(".qgw"); - QString fileName = QFileDialog::getOpenFileName(this, tr("Specify Widget File Name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("QGroundControl Widget (*%1);;").arg(widgetFileExtension)); - if (fileName != "") loadCustomWidget(fileName); -} - -void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance) -{ - QGCToolWidget* tool = new QGCToolWidget("", this); - if (tool->loadSettings(fileName, true) || !singleinstance) - { - // Add widget to UI - QDockWidget* dock = new QDockWidget(tool->getTitle(), this); - connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); - dock->setWidget(tool); - tool->setParent(dock); - - QAction* showAction = new QAction(tool->getTitle(), this); - showAction->setCheckable(true); - connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); - connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); - tool->setMainMenuAction(showAction); - ui.menuTools->addAction(showAction); - this->addDockWidget(Qt::BottomDockWidgetArea, dock); - dock->setVisible(true); - } - else - { - return; - } -} - -void MainWindow::loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType) -{ - QString defaultsDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/widgets/"; - QString platformDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/" + systemType.toLower() + "/widgets/"; - - QDir widgets(defaultsDir); - QStringList files = widgets.entryList(); - QDir platformWidgets(platformDir); - files.append(platformWidgets.entryList()); - - if (files.count() == 0) - { - qDebug() << "No default custom widgets for system " << systemType << "autopilot" << autopilotType << " found"; - qDebug() << "Tried with path: " << defaultsDir; - showStatusMessage(tr("Did not find any custom widgets in %1").arg(defaultsDir)); - } - - // Load all custom widgets found in the AP folder - for(int i = 0; i < files.count(); ++i) - { - QString file = files[i]; - if (file.endsWith(".qgw")) - { - // Will only be loaded if not already a custom widget with - // the same name is present - loadCustomWidget(defaultsDir+"/"+file, true); - showStatusMessage(tr("Loaded custom widget %1").arg(defaultsDir+"/"+file)); - } - } -} - -void MainWindow::loadSettings() -{ - QSettings settings; - settings.beginGroup("QGC_MAINWINDOW"); - autoReconnect = settings.value("AUTO_RECONNECT", autoReconnect).toBool(); - currentStyle = (QGC_MAINWINDOW_STYLE)settings.value("CURRENT_STYLE", currentStyle).toInt(); - lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool(); - settings.endGroup(); -} - -void MainWindow::storeSettings() -{ - QSettings settings; - settings.beginGroup("QGC_MAINWINDOW"); - settings.setValue("AUTO_RECONNECT", autoReconnect); - settings.setValue("CURRENT_STYLE", currentStyle); - settings.endGroup(); - if (!aboutToCloseFlag && isVisible()) - { - settings.setValue(getWindowGeometryKey(), saveGeometry()); - // Save the last current view in any case - settings.setValue("CURRENT_VIEW", currentView); - // Save the current window state, but only if a system is connected (else no real number of widgets would be present) - if (UASManager::instance()->getUASList().length() > 0) settings.setValue(getWindowStateKey(), saveState(QGC::applicationVersion())); - // Save the current view only if a UAS is connected - if (UASManager::instance()->getUASList().length() > 0) settings.setValue("CURRENT_VIEW_WITH_UAS_CONNECTED", currentView); - // Save the current power mode - } - settings.setValue("LOW_POWER_MODE", lowPowerMode); - settings.sync(); -} - -void MainWindow::configureWindowName() -{ - QList hostAddresses = QNetworkInterface::allAddresses(); - QString windowname = qApp->applicationName() + " " + qApp->applicationVersion(); - bool prevAddr = false; - - windowname.append(" (" + QHostInfo::localHostName() + ": "); - - for (int i = 0; i < hostAddresses.size(); i++) - { - // Exclude loopback IPv4 and all IPv6 addresses - if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":")) - { - if(prevAddr) windowname.append("/"); - windowname.append(hostAddresses.at(i).toString()); - prevAddr = true; - } - } - - windowname.append(")"); - - setWindowTitle(windowname); - -#ifndef Q_WS_MAC - //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png")); -#endif -} - -void MainWindow::startVideoCapture() -{ - QString format = "bmp"; - QString initialPath = QDir::currentPath() + tr("/untitled.") + format; - - QString screenFileName = QFileDialog::getSaveFileName(this, tr("Save As"), - initialPath, - tr("%1 Files (*.%2);;All Files (*)") - .arg(format.toUpper()) - .arg(format)); - delete videoTimer; - videoTimer = new QTimer(this); - //videoTimer->setInterval(40); - //connect(videoTimer, SIGNAL(timeout()), this, SLOT(saveScreen())); - //videoTimer->stop(); -} - -void MainWindow::stopVideoCapture() -{ - videoTimer->stop(); - - // TODO Convert raw images to PNG -} - -void MainWindow::saveScreen() -{ - QPixmap window = QPixmap::grabWindow(this->winId()); - QString format = "bmp"; - - if (!screenFileName.isEmpty()) - { - window.save(screenFileName, format.toAscii()); - } -} - -void MainWindow::enableAutoReconnect(bool enabled) -{ - autoReconnect = enabled; -} - -void MainWindow::loadNativeStyle() -{ - loadStyle(QGC_MAINWINDOW_STYLE_NATIVE); -} - -void MainWindow::loadIndoorStyle() -{ - loadStyle(QGC_MAINWINDOW_STYLE_INDOOR); -} - -void MainWindow::loadOutdoorStyle() -{ - loadStyle(QGC_MAINWINDOW_STYLE_OUTDOOR); -} - -void MainWindow::loadStyle(QGC_MAINWINDOW_STYLE style) -{ - switch (style) { - case QGC_MAINWINDOW_STYLE_NATIVE: { - // Native mode means setting no style - // so if we were already in native mode - // take no action - // Only if a style was set, remove it. - if (style != currentStyle) { - qApp->setStyleSheet(""); - showInfoMessage(tr("Please restart QGroundControl"), tr("Please restart QGroundControl to switch to fully native look and feel. Currently you have loaded Qt's plastique style.")); - } - } - break; - case QGC_MAINWINDOW_STYLE_INDOOR: - qApp->setStyle("plastique"); - styleFileName = ":files/styles/style-indoor.css"; - reloadStylesheet(); - break; - case QGC_MAINWINDOW_STYLE_OUTDOOR: - qApp->setStyle("plastique"); - styleFileName = ":files/styles/style-outdoor.css"; - reloadStylesheet(); - break; - } - currentStyle = style; -} - -void MainWindow::selectStylesheet() -{ - // Let user select style sheet - styleFileName = QFileDialog::getOpenFileName(this, tr("Specify stylesheet"), styleFileName, tr("CSS Stylesheet (*.css);;")); - - if (!styleFileName.endsWith(".css")) - { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Information); - msgBox.setText(tr("QGroundControl did lot load a new style")); - msgBox.setInformativeText(tr("No suitable .css file selected. Please select a valid .css file.")); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - return; - } - - // Load style sheet - reloadStylesheet(); -} - -void MainWindow::reloadStylesheet() -{ - // Load style sheet - QFile* styleSheet = new QFile(styleFileName); - if (!styleSheet->exists()) - { - styleSheet = new QFile(":files/styles/style-indoor.css"); - } - if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) - { - QString style = QString(styleSheet->readAll()); - style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "files/styles/"); - qApp->setStyleSheet(style); - } - else - { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Information); - msgBox.setText(tr("QGroundControl did lot load a new style")); - msgBox.setInformativeText(tr("Stylesheet file %1 was not readable").arg(styleFileName)); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - } - delete styleSheet; -} - -/** - * The status message will be overwritten if a new message is posted to this function - * - * @param status message text - * @param timeout how long the status should be displayed - */ -void MainWindow::showStatusMessage(const QString& status, int timeout) -{ - statusBar()->showMessage(status, timeout); -} - -/** - * The status message will be overwritten if a new message is posted to this function. - * it will be automatically hidden after 5 seconds. - * - * @param status message text - */ -void MainWindow::showStatusMessage(const QString& status) -{ - statusBar()->showMessage(status, 20000); -} - -void MainWindow::showCriticalMessage(const QString& title, const QString& message) -{ - QMessageBox msgBox(this); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setText(title); - msgBox.setInformativeText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); -} - -void MainWindow::showInfoMessage(const QString& title, const QString& message) -{ - QMessageBox msgBox(this); - msgBox.setIcon(QMessageBox::Information); - msgBox.setText(title); - msgBox.setInformativeText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); -} - -/** -* @brief Create all actions associated to the main window -* -**/ -void MainWindow::connectCommonActions() -{ - // Bind together the perspective actions - QActionGroup* perspectives = new QActionGroup(ui.menuPerspectives); - perspectives->addAction(ui.actionEngineersView); - perspectives->addAction(ui.actionMavlinkView); - perspectives->addAction(ui.actionPilotsView); - perspectives->addAction(ui.actionOperatorsView); - perspectives->addAction(ui.actionFirmwareUpdateView); - perspectives->addAction(ui.actionUnconnectedView); - perspectives->setExclusive(true); - - // Mark the right one as selected - if (currentView == VIEW_ENGINEER) ui.actionEngineersView->setChecked(true); - if (currentView == VIEW_MAVLINK) ui.actionMavlinkView->setChecked(true); - if (currentView == VIEW_PILOT) ui.actionPilotsView->setChecked(true); - if (currentView == VIEW_OPERATOR) ui.actionOperatorsView->setChecked(true); - if (currentView == VIEW_FIRMWAREUPDATE) ui.actionFirmwareUpdateView->setChecked(true); - if (currentView == VIEW_UNCONNECTED) ui.actionUnconnectedView->setChecked(true); - - // The UAS actions are not enabled without connection to system - ui.actionLiftoff->setEnabled(false); - ui.actionLand->setEnabled(false); - ui.actionEmergency_Kill->setEnabled(false); - ui.actionEmergency_Land->setEnabled(false); - ui.actionShutdownMAV->setEnabled(false); - - // Connect actions from ui - connect(ui.actionAdd_Link, SIGNAL(triggered()), this, SLOT(addLink())); - - // Connect internal actions - connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(UASCreated(UASInterface*))); - connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*))); - - // Unmanned System controls - connect(ui.actionLiftoff, SIGNAL(triggered()), UASManager::instance(), SLOT(launchActiveUAS())); - connect(ui.actionLand, SIGNAL(triggered()), UASManager::instance(), SLOT(returnActiveUAS())); - connect(ui.actionEmergency_Land, SIGNAL(triggered()), UASManager::instance(), SLOT(stopActiveUAS())); - connect(ui.actionEmergency_Kill, SIGNAL(triggered()), UASManager::instance(), SLOT(killActiveUAS())); - connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS())); - connect(ui.actionConfiguration, SIGNAL(triggered()), UASManager::instance(), SLOT(configureActiveUAS())); - - // Views actions - connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView())); - connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView())); - connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView())); - connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView())); - - connect(ui.actionFirmwareUpdateView, SIGNAL(triggered()), this, SLOT(loadFirmwareUpdateView())); - connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView())); - - connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet())); - connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet())); - - // Help Actions - connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp())); - connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits())); - connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap())); - - // Custom widget actions - connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget())); - connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget())); - - // Audio output - ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted()); - connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool))); - connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool))); - - // User interaction - // NOTE: Joystick thread is not started and - // configuration widget is not instantiated - // unless it is actually used - // so no ressources spend on this. - ui.actionJoystickSettings->setVisible(true); - - // Configuration - // Joystick - connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure())); - // Application Settings - connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings())); -} - -void MainWindow::showHelp() -{ - if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) - { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Critical); - msgBox.setText("Could not open help in browser"); - msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/user_guide in a browser."); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - } -} - -void MainWindow::showCredits() -{ - if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) - { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Critical); - msgBox.setText("Could not open credits in browser"); - msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/credits in a browser."); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - } -} - -void MainWindow::showRoadMap() -{ - if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) - { - QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Critical); - msgBox.setText("Could not open roadmap in browser"); - msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/roadmap in a browser."); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - } -} - -void MainWindow::configure() -{ - if (!joystickWidget) - { - if (!joystick->isRunning()) - { - joystick->start(); - } - joystickWidget = new JoystickWidget(joystick); - } - joystickWidget->show(); -} - -void MainWindow::showSettings() -{ - QGCSettingsWidget* settings = new QGCSettingsWidget(this); - settings->show(); -} - -void MainWindow::addLink() -{ - SerialLink* link = new SerialLink(); - // TODO This should be only done in the dialog itself - - LinkManager::instance()->add(link); - LinkManager::instance()->addProtocol(link, mavlink); - - // Go fishing for this link's configuration window - QList actions = ui.menuNetwork->actions(); - - const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link)); - const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId()); - - foreach (QAction* act, actions) - { - if (act->data().toInt() == linkID) - { // LinkManager::instance()->getLinks().indexOf(link) - act->trigger(); - break; - } - } -} - -void MainWindow::addLink(LinkInterface *link) -{ - // IMPORTANT! KEEP THESE TWO LINES - // THEY MAKE SURE THE LINK IS PROPERLY REGISTERED - // BEFORE LINKING THE UI AGAINST IT - // Register (does nothing if already registered) - LinkManager::instance()->add(link); - LinkManager::instance()->addProtocol(link, mavlink); - - // Go fishing for this link's configuration window - QList actions = ui.menuNetwork->actions(); - - bool found(false); - - const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link)); - const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId()); - - foreach (QAction* act, actions) - { - if (act->data().toInt() == linkID) - { // LinkManager::instance()->getLinks().indexOf(link) - found = true; - } - } - - //UDPLink* udp = dynamic_cast(link); - - if (!found) - { // || udp - CommConfigurationWindow* commWidget = new CommConfigurationWindow(link, mavlink, this); - QAction* action = commWidget->getAction(); - ui.menuNetwork->addAction(action); - - // Error handling - connect(link, SIGNAL(communicationError(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection); - // Special case for simulationlink - MAVLinkSimulationLink* sim = dynamic_cast(link); - if (sim) - { - connect(ui.actionSimulate, SIGNAL(triggered(bool)), sim, SLOT(connectLink(bool))); - } - } -} - -void MainWindow::setActiveUAS(UASInterface* uas) -{ - // Enable and rename menu - ui.menuUnmanned_System->setTitle(uas->getUASName()); - if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true); -} - -void MainWindow::UASSpecsChanged(int uas) -{ - UASInterface* activeUAS = UASManager::instance()->getActiveUAS(); - if (activeUAS) - { - if (activeUAS->getUASID() == uas) - { - ui.menuUnmanned_System->setTitle(activeUAS->getUASName()); - } - } - else - { - // Last system deleted - ui.menuUnmanned_System->setTitle(tr("No System")); - ui.menuUnmanned_System->setEnabled(false); - } -} - -void MainWindow::UASCreated(UASInterface* uas) -{ - - // Connect the UAS to the full user interface - - //if (uas != NULL) - //{ - // The pilot, operator and engineer views were not available on startup, enable them now - ui.actionPilotsView->setEnabled(true); - ui.actionOperatorsView->setEnabled(true); - ui.actionEngineersView->setEnabled(true); - // The UAS actions are not enabled without connection to system - ui.actionLiftoff->setEnabled(true); - ui.actionLand->setEnabled(true); - ui.actionEmergency_Kill->setEnabled(true); - ui.actionEmergency_Land->setEnabled(true); - ui.actionShutdownMAV->setEnabled(true); - - QIcon icon; - // Set matching icon - switch (uas->getSystemType()) - { - case MAV_TYPE_GENERIC: - icon = QIcon(":files/images/mavs/generic.svg"); - break; - case MAV_TYPE_FIXED_WING: - icon = QIcon(":files/images/mavs/fixed-wing.svg"); - break; - case MAV_TYPE_QUADROTOR: - icon = QIcon(":files/images/mavs/quadrotor.svg"); - break; - case MAV_TYPE_COAXIAL: - icon = QIcon(":files/images/mavs/coaxial.svg"); - break; - case MAV_TYPE_HELICOPTER: - icon = QIcon(":files/images/mavs/helicopter.svg"); - break; - case MAV_TYPE_ANTENNA_TRACKER: - icon = QIcon(":files/images/mavs/antenna-tracker.svg"); - break; - case MAV_TYPE_GCS: - icon = QIcon(":files/images/mavs/groundstation.svg"); - break; - case MAV_TYPE_AIRSHIP: - icon = QIcon(":files/images/mavs/airship.svg"); - break; - case MAV_TYPE_FREE_BALLOON: - icon = QIcon(":files/images/mavs/free-balloon.svg"); - break; - case MAV_TYPE_ROCKET: - icon = QIcon(":files/images/mavs/rocket.svg"); - break; - case MAV_TYPE_GROUND_ROVER: - icon = QIcon(":files/images/mavs/ground-rover.svg"); - break; - case MAV_TYPE_SURFACE_BOAT: - icon = QIcon(":files/images/mavs/surface-boat.svg"); - break; - case MAV_TYPE_SUBMARINE: - icon = QIcon(":files/images/mavs/submarine.svg"); - break; - case MAV_TYPE_HEXAROTOR: - icon = QIcon(":files/images/mavs/hexarotor.svg"); - break; - case MAV_TYPE_OCTOROTOR: - icon = QIcon(":files/images/mavs/octorotor.svg"); - break; - case MAV_TYPE_TRICOPTER: - icon = QIcon(":files/images/mavs/tricopter.svg"); - break; - case MAV_TYPE_FLAPPING_WING: - icon = QIcon(":files/images/mavs/flapping-wing.svg"); - break; - case MAV_TYPE_KITE: - icon = QIcon(":files/images/mavs/kite.svg"); - break; - default: - icon = QIcon(":files/images/mavs/unknown.svg"); - break; - } - - QAction* uasAction = new QAction(icon, tr("Select %1 for control").arg(uas->getUASName()), ui.menuConnected_Systems); - connect(uas, SIGNAL(systemRemoved()), uasAction, SLOT(deleteLater())); - connect(uasAction, SIGNAL(triggered()), uas, SLOT(setSelected())); - connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(UASSpecsChanged(int))); - - ui.menuConnected_Systems->addAction(uasAction); - - // FIXME Should be not inside the mainwindow - if (debugConsoleDockWidget) - { - DebugConsole *debugConsole = dynamic_cast(debugConsoleDockWidget->widget()); - if (debugConsole) - { - connect(uas, SIGNAL(textMessageReceived(int,int,int,QString)), - debugConsole, SLOT(receiveTextMessage(int,int,int,QString))); - } - } - - // Health / System status indicator - if (infoDockWidget) - { - UASInfoWidget *infoWidget = dynamic_cast(infoDockWidget->widget()); - if (infoWidget) - { - infoWidget->addUAS(uas); - } - } - - // UAS List - if (listDockWidget) - { - UASListWidget *listWidget = dynamic_cast(listDockWidget->widget()); - if (listWidget) - { - listWidget->addUAS(uas); - } - } - - // Line chart - if (!linechartWidget) - { - // Center widgets - linechartWidget = new Linecharts(this); - linechartWidget->addSource(mavlinkDecoder); - addCentralWidget(linechartWidget, tr("Realtime Plot")); - } - - // Load default custom widgets for this autopilot type - loadCustomWidgetsFromDefaults(uas->getSystemTypeName(), uas->getAutopilotTypeName()); - - - if (uas->getAutopilotType() == MAV_AUTOPILOT_PIXHAWK) - { - // Dock widgets - if (!detectionDockWidget) - { - detectionDockWidget = new QDockWidget(tr("Object Recognition"), this); - detectionDockWidget->setWidget( new ObjectDetectionView("files/images/patterns", this) ); - detectionDockWidget->setObjectName("OBJECT_DETECTION_DOCK_WIDGET"); - addTool(detectionDockWidget, tr("Object Recognition"), Qt::RightDockWidgetArea); - } - - if (!watchdogControlDockWidget) - { - watchdogControlDockWidget = new QDockWidget(tr("Process Control"), this); - watchdogControlDockWidget->setWidget( new WatchdogControl(this) ); - watchdogControlDockWidget->setObjectName("WATCHDOG_CONTROL_DOCKWIDGET"); - addTool(watchdogControlDockWidget, tr("Process Control"), Qt::BottomDockWidgetArea); - } - } - - // Change the view only if this is the first UAS - - // If this is the first connected UAS, it is both created as well as - // the currently active UAS - if (UASManager::instance()->getUASList().size() == 1) - { - // Load last view if setting is present - if (settings.contains("CURRENT_VIEW_WITH_UAS_CONNECTED")) - { - int view = settings.value("CURRENT_VIEW_WITH_UAS_CONNECTED").toInt(); - switch (view) - { - case VIEW_ENGINEER: - loadEngineerView(); - break; - case VIEW_MAVLINK: - loadMAVLinkView(); - break; - case VIEW_FIRMWAREUPDATE: - loadFirmwareUpdateView(); - break; - case VIEW_PILOT: - loadPilotView(); - break; - case VIEW_UNCONNECTED: - loadUnconnectedView(); - break; - case VIEW_OPERATOR: - default: - loadOperatorView(); - break; - } - } - else - { - loadOperatorView(); - } - } - - //} - - if (!ui.menuConnected_Systems->isEnabled()) ui.menuConnected_Systems->setEnabled(true); - if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true); - - // Reload view state in case new widgets were added - loadViewState(); -} - -void MainWindow::UASDeleted(UASInterface* uas) -{ - if (UASManager::instance()->getUASList().count() == 0) - { - // Last system deleted - ui.menuUnmanned_System->setTitle(tr("No System")); - ui.menuUnmanned_System->setEnabled(false); - } - - QAction* act; - QList actions = ui.menuConnected_Systems->actions(); - - foreach (act, actions) - { - if (act->text().contains(uas->getUASName())) - ui.menuConnected_Systems->removeAction(act); - } -} - -/** - * Stores the current view state - */ -void MainWindow::storeViewState() -{ - if (!aboutToCloseFlag) - { - // Save current state - settings.setValue(getWindowStateKey(), saveState(QGC::applicationVersion())); - settings.setValue(getWindowStateKey()+"CENTER_WIDGET", centerStack->currentIndex()); - // Although we want save the state of the window, we do not want to change the top-leve state (minimized, maximized, etc) - // therefore this state is stored here and restored after applying the rest of the settings in the new - // perspective. - windowStateVal = this->windowState(); - settings.setValue(getWindowGeometryKey(), saveGeometry()); - } -} - -void MainWindow::loadViewState() -{ - // Restore center stack state - int index = settings.value(getWindowStateKey()+"CENTER_WIDGET", -1).toInt(); - // The offline plot view is usually the consequence of a logging run, always show the realtime view first - if (centerStack->indexOf(dataplotWidget) == index) - { - // Rewrite to realtime plot - index = centerStack->indexOf(linechartWidget); - } - - if (index != -1) - { - centerStack->setCurrentIndex(index); - } - else - { - // Hide custom widgets - if (detectionDockWidget) detectionDockWidget->hide(); - if (watchdogControlDockWidget) watchdogControlDockWidget->hide(); - - // Load defaults - switch (currentView) - { - case VIEW_ENGINEER: - centerStack->setCurrentWidget(linechartWidget); - controlDockWidget->hide(); - listDockWidget->hide(); - waypointsDockWidget->hide(); - infoDockWidget->hide(); - debugConsoleDockWidget->show(); - logPlayerDockWidget->show(); - mavlinkInspectorWidget->show(); - //mavlinkSenderWidget->show(); - parametersDockWidget->show(); - hsiDockWidget->hide(); - headDown1DockWidget->hide(); - headDown2DockWidget->hide(); - rcViewDockWidget->hide(); - headUpDockWidget->hide(); - video1DockWidget->hide(); - video2DockWidget->hide(); - break; - case VIEW_PILOT: - centerStack->setCurrentWidget(hudWidget); - controlDockWidget->hide(); - listDockWidget->hide(); - waypointsDockWidget->hide(); - infoDockWidget->hide(); - debugConsoleDockWidget->hide(); - logPlayerDockWidget->hide(); - mavlinkInspectorWidget->hide(); - parametersDockWidget->hide(); - hsiDockWidget->show(); - headDown1DockWidget->show(); - headDown2DockWidget->show(); - rcViewDockWidget->hide(); - headUpDockWidget->hide(); - video1DockWidget->hide(); - video2DockWidget->hide(); - break; - case VIEW_MAVLINK: - centerStack->setCurrentWidget(protocolWidget); - controlDockWidget->hide(); - listDockWidget->hide(); - waypointsDockWidget->hide(); - infoDockWidget->hide(); - debugConsoleDockWidget->hide(); - logPlayerDockWidget->hide(); - mavlinkInspectorWidget->show(); - //mavlinkSenderWidget->show(); - parametersDockWidget->hide(); - hsiDockWidget->hide(); - headDown1DockWidget->hide(); - headDown2DockWidget->hide(); - rcViewDockWidget->hide(); - headUpDockWidget->hide(); - video1DockWidget->hide(); - video2DockWidget->hide(); - break; - case VIEW_FIRMWAREUPDATE: - centerStack->setCurrentWidget(firmwareUpdateWidget); - controlDockWidget->hide(); - listDockWidget->hide(); - waypointsDockWidget->hide(); - infoDockWidget->hide(); - debugConsoleDockWidget->hide(); - logPlayerDockWidget->hide(); - mavlinkInspectorWidget->hide(); - //mavlinkSenderWidget->hide(); - parametersDockWidget->hide(); - hsiDockWidget->hide(); - headDown1DockWidget->hide(); - headDown2DockWidget->hide(); - rcViewDockWidget->hide(); - headUpDockWidget->hide(); - video1DockWidget->hide(); - video2DockWidget->hide(); - break; - case VIEW_OPERATOR: - centerStack->setCurrentWidget(mapWidget); - controlDockWidget->hide(); - listDockWidget->show(); - waypointsDockWidget->show(); - infoDockWidget->hide(); - debugConsoleDockWidget->show(); - logPlayerDockWidget->show(); - parametersDockWidget->hide(); - hsiDockWidget->show(); - headDown1DockWidget->hide(); - headDown2DockWidget->hide(); - rcViewDockWidget->hide(); - headUpDockWidget->show(); - video1DockWidget->hide(); - video2DockWidget->hide(); - mavlinkInspectorWidget->hide(); - break; - case VIEW_UNCONNECTED: - case VIEW_FULL: - default: - centerStack->setCurrentWidget(mapWidget); - controlDockWidget->hide(); - listDockWidget->show(); - waypointsDockWidget->hide(); - infoDockWidget->hide(); - debugConsoleDockWidget->show(); - logPlayerDockWidget->show(); - parametersDockWidget->hide(); - hsiDockWidget->hide(); - headDown1DockWidget->hide(); - headDown2DockWidget->hide(); - rcViewDockWidget->hide(); - headUpDockWidget->show(); - video1DockWidget->hide(); - video2DockWidget->hide(); - mavlinkInspectorWidget->show(); - break; - } - } - - // Restore the widget positions and size - if (settings.contains(getWindowStateKey())) - { - restoreState(settings.value(getWindowStateKey()).toByteArray(), QGC::applicationVersion()); - } -} - -void MainWindow::loadEngineerView() -{ - if (currentView != VIEW_ENGINEER) - { - storeViewState(); - currentView = VIEW_ENGINEER; - ui.actionEngineersView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadOperatorView() -{ - if (currentView != VIEW_OPERATOR) - { - storeViewState(); - currentView = VIEW_OPERATOR; - ui.actionOperatorsView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadUnconnectedView() -{ - if (currentView != VIEW_UNCONNECTED) - { - storeViewState(); - currentView = VIEW_UNCONNECTED; - ui.actionUnconnectedView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadPilotView() -{ - if (currentView != VIEW_PILOT) - { - storeViewState(); - currentView = VIEW_PILOT; - ui.actionPilotsView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadMAVLinkView() -{ - if (currentView != VIEW_MAVLINK) - { - storeViewState(); - currentView = VIEW_MAVLINK; - ui.actionMavlinkView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadFirmwareUpdateView() -{ - if (currentView != VIEW_FIRMWAREUPDATE) - { - storeViewState(); - currentView = VIEW_FIRMWAREUPDATE; - ui.actionFirmwareUpdateView->setChecked(true); - loadViewState(); - } -} - -void MainWindow::loadDataView(QString fileName) -{ - // Plot is now selected, now load data from file - if (dataplotWidget) - { - dataplotWidget->loadFile(fileName); - } - QStackedWidget *centerStack = dynamic_cast(centralWidget()); - if (centerStack) - { - centerStack->setCurrentWidget(dataplotWidget); - dataplotWidget->loadFile(fileName); - } -} - - -QList MainWindow::listLinkMenuActions(void) -{ - return ui.menuNetwork->actions(); -} +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009 - 2011 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Implementation of class MainWindow + * @author Lorenz Meier + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QGC.h" +#include "MAVLinkSimulationLink.h" +#include "SerialLink.h" +#include "UDPLink.h" +#include "MAVLinkProtocol.h" +#include "CommConfigurationWindow.h" +#include "QGCWaypointListMulti.h" +#include "MainWindow.h" +#include "JoystickWidget.h" +#include "GAudioOutput.h" +#include "QGCToolWidget.h" +#include "QGCMAVLinkLogPlayer.h" +#include "QGCSettingsWidget.h" +#include "QGCMapTool.h" +#include "MAVLinkDecoder.h" +#include "QGCMAVLinkMessageSender.h" +#include "QGCRGBDView.h" +#include "QGCFirmwareUpdate.h" + +#ifdef QGC_OSG_ENABLED +#include "Q3DWidgetFactory.h" +#endif + +// FIXME Move +#include "PxQuadMAV.h" +#include "SlugsMAV.h" + + +#include "LogCompressor.h" + +MainWindow* MainWindow::instance(QSplashScreen* screen) +{ + static MainWindow* _instance = 0; + if(_instance == 0) + { + _instance = new MainWindow(); + if (screen) connect(_instance, SIGNAL(initStatusChanged(QString)), screen, SLOT(showMessage(QString))); + + /* Set the application as parent to ensure that this object + * will be destroyed when the main application exits */ + //_instance->setParent(qApp); + } + return _instance; +} + +/** +* Create new mainwindow. The constructor instantiates all parts of the user +* interface. It does NOT show the mainwindow. To display it, call the show() +* method. +* +* @see QMainWindow::show() +**/ +MainWindow::MainWindow(QWidget *parent): + QMainWindow(parent), + currentView(VIEW_UNCONNECTED), + currentStyle(QGC_MAINWINDOW_STYLE_INDOOR), + aboutToCloseFlag(false), + changingViewsFlag(false), + centerStackActionGroup(new QActionGroup(this)), + styleFileName(QCoreApplication::applicationDirPath() + "/style-indoor.css"), + autoReconnect(false), + lowPowerMode(false) +{ + hide(); + emit initStatusChanged("Loading UI Settings.."); + loadSettings(); + if (!settings.contains("CURRENT_VIEW")) + { + // Set this view as default view + settings.setValue("CURRENT_VIEW", currentView); + } + else + { + // LOAD THE LAST VIEW + VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt(); + if (currentViewCandidate != VIEW_ENGINEER && + currentViewCandidate != VIEW_OPERATOR && + currentViewCandidate != VIEW_PILOT && + currentViewCandidate != VIEW_FULL) + { + currentView = currentViewCandidate; + } + } + + settings.sync(); + + emit initStatusChanged("Loading Style."); + loadStyle(currentStyle); + + emit initStatusChanged("Setting up user interface."); + + // Setup user interface + ui.setupUi(this); + hide(); + + // Set dock options + setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks); + statusBar()->setSizeGripEnabled(true); + + configureWindowName(); + + // Setup corners + setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); + + // Setup UI state machines + centerStackActionGroup->setExclusive(true); + + centerStack = new QStackedWidget(this); + setCentralWidget(centerStack); + + // Load Toolbar + toolBar = new QGCToolBar(this); + this->addToolBar(toolBar); + // Add actions + toolBar->addPerspectiveChangeAction(ui.actionOperatorsView); + toolBar->addPerspectiveChangeAction(ui.actionEngineersView); + toolBar->addPerspectiveChangeAction(ui.actionPilotsView); + + emit initStatusChanged("Building common widgets."); + + buildCommonWidgets(); + connectCommonWidgets(); + + emit initStatusChanged("Building common actions."); + + // Create actions + connectCommonActions(); + + // Populate link menu + emit initStatusChanged("Populating link menu"); + QList links = LinkManager::instance()->getLinks(); + foreach(LinkInterface* link, links) + { + this->addLink(link); + } + + connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*))); + + // Connect user interface devices + emit initStatusChanged("Initializing joystick interface."); + joystickWidget = 0; + joystick = new JoystickInput(); + +#ifdef MOUSE_ENABLED_WIN + emit initStatusChanged("Initializing 3D mouse interface."); + + mouseInput = new Mouse3DInput(this); + mouse = new Mouse6dofInput(mouseInput); +#endif //MOUSE_ENABLED_WIN + + // Connect link + if (autoReconnect) + { + SerialLink* link = new SerialLink(); + // Add to registry + LinkManager::instance()->add(link); + LinkManager::instance()->addProtocol(link, mavlink); + link->connect(); + } + + // Set low power mode + enableLowPowerMode(lowPowerMode); + + // Initialize window state + windowStateVal = windowState(); + + emit initStatusChanged("Restoring last view state."); + + // Restore the window setup + loadViewState(); + + emit initStatusChanged("Restoring last window size."); + // Restore the window position and size + if (settings.contains(getWindowGeometryKey())) + { + // Restore the window geometry + restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray()); + show(); + } + else + { + // Adjust the size + const int screenWidth = QApplication::desktop()->width(); + const int screenHeight = QApplication::desktop()->height(); + + if (screenWidth < 1200) + { + showFullScreen(); + } + else + { + resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f))); + show(); + } + + } + + connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName())); + windowNameUpdateTimer.start(15000); + emit initStatusChanged("Done."); + show(); +} + +MainWindow::~MainWindow() +{ + if (mavlink) + { + delete mavlink; + mavlink = NULL; + } +// if (simulationLink) +// { +// simulationLink->deleteLater(); +// simulationLink = NULL; +// } + if (joystick) + { + delete joystick; + joystick = NULL; + } + + // Get and delete all dockwidgets and contained + // widgets + QObjectList childList(this->children()); + + QObjectList::iterator i; + QDockWidget* dockWidget; + for (i = childList.begin(); i != childList.end(); ++i) + { + dockWidget = dynamic_cast(*i); + if (dockWidget) + { + // Remove dock widget from main window + // removeDockWidget(dockWidget); + // delete dockWidget->widget(); + delete dockWidget; + dockWidget = NULL; + } + else if (dynamic_cast(*i)) + { + delete dynamic_cast(*i); + *i = NULL; + } + } + // Delete all UAS objects +} + +void MainWindow::resizeEvent(QResizeEvent * event) +{ + if (height() < 800) + { + ui.statusBar->setVisible(false); + } + else + { + ui.statusBar->setVisible(true); + ui.statusBar->setSizeGripEnabled(true); + } + + if (width() > 1200) + { + toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + } + else + { + toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); + } + + QMainWindow::resizeEvent(event); +} + +QString MainWindow::getWindowStateKey() +{ + return QString::number(currentView)+"_windowstate"; +} + +QString MainWindow::getWindowGeometryKey() +{ + //return QString::number(currentView)+"_geometry"; + return "_geometry"; +} + +void MainWindow::buildCustomWidget() +{ + // Create custom widgets + QList widgets = QGCToolWidget::createWidgetsFromSettings(this); + + if (widgets.size() > 0) + { + ui.menuTools->addSeparator(); + } + + for(int i = 0; i < widgets.size(); ++i) + { + // Check if this widget already has a parent, do not create it in this case + QGCToolWidget* tool = widgets.at(i); + QDockWidget* dock = dynamic_cast(tool->parentWidget()); + if (!dock) + { + QDockWidget* dock = new QDockWidget(tool->windowTitle(), this); + dock->setObjectName(tool->objectName()+"_DOCK"); + dock->setWidget(tool); + connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); + QAction* showAction = new QAction(widgets.at(i)->windowTitle(), this); + showAction->setCheckable(true); + connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); + connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); + widgets.at(i)->setMainMenuAction(showAction); + ui.menuTools->addAction(showAction); + + // Load dock widget location (default is bottom) + Qt::DockWidgetArea location = static_cast (tool->getDockWidgetArea(currentView)); + + addDockWidget(location, dock); + } + } +} + +void MainWindow::buildCommonWidgets() +{ + //TODO: move protocol outside UI + mavlink = new MAVLinkProtocol(); + connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection); + // Add generic MAVLink decoder + mavlinkDecoder = new MAVLinkDecoder(mavlink, this); + + // Dock widgets + if (!controlDockWidget) + { + controlDockWidget = new QDockWidget(tr("Control"), this); + controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET"); + controlDockWidget->setWidget( new UASControlWidget(this) ); + addTool(controlDockWidget, tr("Control"), Qt::LeftDockWidgetArea); + } + + if (!listDockWidget) + { + listDockWidget = new QDockWidget(tr("Unmanned Systems"), this); + listDockWidget->setWidget( new UASListWidget(this) ); + listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET"); + addTool(listDockWidget, tr("Unmanned Systems"), Qt::RightDockWidgetArea); + } + + if (!waypointsDockWidget) + { + waypointsDockWidget = new QDockWidget(tr("Mission Plan"), this); + waypointsDockWidget->setWidget( new QGCWaypointListMulti(this) ); + waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET"); + addTool(waypointsDockWidget, tr("Mission Plan"), Qt::BottomDockWidgetArea); + } + + if (!infoDockWidget) + { + infoDockWidget = new QDockWidget(tr("Status Details"), this); + infoDockWidget->setWidget( new UASInfoWidget(this) ); + infoDockWidget->setObjectName("UAS_STATUS_DETAILS_DOCKWIDGET"); + addTool(infoDockWidget, tr("Status Details"), Qt::RightDockWidgetArea); + } + + if (!debugConsoleDockWidget) + { + debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this); + debugConsoleDockWidget->setWidget( new DebugConsole(this) ); + debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET"); + + DebugConsole *debugConsole = dynamic_cast(debugConsoleDockWidget->widget()); + connect(mavlinkDecoder, SIGNAL(textMessageReceived(int, int, int, const QString)), debugConsole, SLOT(receiveTextMessage(int, int, int, const QString))); + + addTool(debugConsoleDockWidget, tr("Communication Console"), Qt::BottomDockWidgetArea); + } + + if (!logPlayerDockWidget) + { + logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this); + logPlayer = new QGCMAVLinkLogPlayer(mavlink, this); + toolBar->setLogPlayer(logPlayer); + logPlayerDockWidget->setWidget(logPlayer); + logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET"); + addTool(logPlayerDockWidget, tr("MAVLink Log Replay"), Qt::RightDockWidgetArea); + } + + if (!mavlinkInspectorWidget) + { + mavlinkInspectorWidget = new QDockWidget(tr("MAVLink Message Inspector"), this); + mavlinkInspectorWidget->setWidget( new QGCMAVLinkInspector(mavlink, this) ); + mavlinkInspectorWidget->setObjectName("MAVLINK_INSPECTOR_DOCKWIDGET"); + addTool(mavlinkInspectorWidget, tr("MAVLink Inspector"), Qt::RightDockWidgetArea); + } + + if (!mavlinkSenderWidget) + { +// mavlinkSenderWidget = new QDockWidget(tr("MAVLink Message Sender"), this); +// mavlinkSenderWidget->setWidget( new QGCMAVLinkMessageSender(mavlink, this) ); +// mavlinkSenderWidget->setObjectName("MAVLINK_SENDER_DOCKWIDGET"); +// addTool(mavlinkSenderWidget, tr("MAVLink Sender"), Qt::RightDockWidgetArea); + } + + //FIXME: memory of acceptList will never be freed again + QStringList* acceptList = new QStringList(); + acceptList->append("-3.3,ATTITUDE.roll,rad,+3.3,s"); + acceptList->append("-3.3,ATTITUDE.pitch,deg,+3.3,s"); + acceptList->append("-3.3,ATTITUDE.yaw,deg,+3.3,s"); + + //FIXME: memory of acceptList2 will never be freed again + QStringList* acceptList2 = new QStringList(); + acceptList2->append("0,RAW_PRESSURE.pres_abs,hPa,65500"); + + if (!parametersDockWidget) + { + parametersDockWidget = new QDockWidget(tr("Onboard Parameters"), this); + parametersDockWidget->setWidget( new ParameterInterface(this) ); + parametersDockWidget->setObjectName("PARAMETER_INTERFACE_DOCKWIDGET"); + addTool(parametersDockWidget, tr("Onboard Parameters"), Qt::RightDockWidgetArea); + } + + if (!hsiDockWidget) + { + hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this); + hsiDockWidget->setWidget( new HSIDisplay(this) ); + hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET"); + addTool(hsiDockWidget, tr("Horizontal Situation"), Qt::BottomDockWidgetArea); + } + + if (!headDown1DockWidget) + { + headDown1DockWidget = new QDockWidget(tr("Flight Display"), this); + HDDisplay* hdDisplay = new HDDisplay(acceptList, "Flight Display", this); + hdDisplay->addSource(mavlinkDecoder); + headDown1DockWidget->setWidget(hdDisplay); + headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET"); + addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea); + } + + if (!headDown2DockWidget) + { + headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this); + HDDisplay* hdDisplay = new HDDisplay(acceptList2, "Actuator Status", this); + hdDisplay->addSource(mavlinkDecoder); + headDown2DockWidget->setWidget(hdDisplay); + headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET"); + addTool(headDown2DockWidget, tr("Actuator Status"), Qt::RightDockWidgetArea); + } + + if (!rcViewDockWidget) + { + rcViewDockWidget = new QDockWidget(tr("Radio Control"), this); + rcViewDockWidget->setWidget( new QGCRemoteControlView(this) ); + rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET"); + addTool(rcViewDockWidget, tr("Radio Control"), Qt::BottomDockWidgetArea); + } + + if (!headUpDockWidget) + { + headUpDockWidget = new QDockWidget(tr("HUD"), this); + headUpDockWidget->setWidget( new HUD(320, 240, this)); + headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET"); + addTool(headUpDockWidget, tr("Head Up Display"), Qt::RightDockWidgetArea); + } + + if (!video1DockWidget) + { + video1DockWidget = new QDockWidget(tr("Video Stream 1"), this); + QGCRGBDView* video1 = new QGCRGBDView(160, 120, this); + video1->enableHUDInstruments(false); + video1->enableVideo(false); + // FIXME select video stream as well + video1DockWidget->setWidget(video1); + video1DockWidget->setObjectName("VIDEO_STREAM_1_DOCK_WIDGET"); + addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea); + } + + if (!video2DockWidget) + { + video2DockWidget = new QDockWidget(tr("Video Stream 2"), this); + QGCRGBDView* video2 = new QGCRGBDView(160, 120, this); + video2->enableHUDInstruments(false); + video2->enableVideo(false); + // FIXME select video stream as well + video2DockWidget->setWidget(video2); + video2DockWidget->setObjectName("VIDEO_STREAM_2_DOCK_WIDGET"); + addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea); + } + +// if (!rgbd1DockWidget) { +// rgbd1DockWidget = new QDockWidget(tr("Video Stream 1"), this); +// HUD* video1 = new HUD(160, 120, this); +// video1->enableHUDInstruments(false); +// video1->enableVideo(true); +// // FIXME select video stream as well +// video1DockWidget->setWidget(video1); +// video1DockWidget->setObjectName("VIDEO_STREAM_1_DOCK_WIDGET"); +// addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea); +// } + +// if (!rgbd2DockWidget) { +// video2DockWidget = new QDockWidget(tr("Video Stream 2"), this); +// HUD* video2 = new HUD(160, 120, this); +// video2->enableHUDInstruments(false); +// video2->enableVideo(true); +// // FIXME select video stream as well +// video2DockWidget->setWidget(video2); +// video2DockWidget->setObjectName("VIDEO_STREAM_2_DOCK_WIDGET"); +// addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea); +// } + + // Custom widgets, added last to all menus and layouts + buildCustomWidget(); + + // Center widgets + if (!mapWidget) + { + mapWidget = new QGCMapTool(this); + addCentralWidget(mapWidget, "Maps"); + } + + if (!protocolWidget) + { + protocolWidget = new XMLCommProtocolWidget(this); + addCentralWidget(protocolWidget, "Mavlink Generator"); + } + +// if (!firmwareUpdateWidget) +// { +// firmwareUpdateWidget = new QGCFirmwareUpdate(this); +// addCentralWidget(firmwareUpdateWidget, "Firmware Update"); +// } + + if (!hudWidget) + { + hudWidget = new HUD(320, 240, this); + addCentralWidget(hudWidget, tr("Head Up Display")); + } + + if (!configWidget) + { + configWidget = new QGCVehicleConfig(this); + addCentralWidget(configWidget, tr("Vehicle Configuration")); + } + + if (!dataplotWidget) + { + dataplotWidget = new QGCDataPlot2D(this); + addCentralWidget(dataplotWidget, tr("Logfile Plot")); + } + +#ifdef QGC_OSG_ENABLED + if (!_3DWidget) + { + _3DWidget = Q3DWidgetFactory::get("PIXHAWK", this); + addCentralWidget(_3DWidget, tr("Local 3D")); + } +#endif + +#if (defined _MSC_VER) | (defined Q_OS_MAC) + if (!gEarthWidget) + { + gEarthWidget = new QGCGoogleEarthView(this); + addCentralWidget(gEarthWidget, tr("Google Earth")); + } +#endif +} + +void MainWindow::addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea area) +{ + QAction* tempAction = ui.menuTools->addAction(title); + + tempAction->setCheckable(true); + QVariant var; + var.setValue((QWidget*)widget); + tempAction->setData(var); + connect(tempAction,SIGNAL(triggered(bool)),this, SLOT(showTool(bool))); + connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool))); + tempAction->setChecked(widget->isVisible()); + addDockWidget(area, widget); +} + + +void MainWindow::showTool(bool show) +{ + QAction* act = qobject_cast(sender()); + QWidget* widget = qVariantValue(act->data()); + widget->setVisible(show); +} + +void MainWindow::addCentralWidget(QWidget* widget, const QString& title) +{ + // Check if this widget already has been added + if (centerStack->indexOf(widget) == -1) + { + centerStack->addWidget(widget); + + QAction* tempAction = ui.menuMain->addAction(title); + + tempAction->setCheckable(true); + QVariant var; + var.setValue((QWidget*)widget); + tempAction->setData(var); + centerStackActionGroup->addAction(tempAction); + connect(tempAction,SIGNAL(triggered()),this, SLOT(showCentralWidget())); + connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool))); + tempAction->setChecked(widget->isVisible()); + } +} + + +void MainWindow::showCentralWidget() +{ + QAction* act = qobject_cast(sender()); + QWidget* widget = qVariantValue(act->data()); + centerStack->setCurrentWidget(widget); +} + +void MainWindow::showHILConfigurationWidget(UASInterface* uas) +{ + // Add simulation configuration widget + UAS* mav = dynamic_cast(uas); + + if (mav) + { + QGCHilConfiguration* hconf = new QGCHilConfiguration(mav, this); + QString hilDockName = tr("HIL Config (%1)").arg(uas->getUASName()); + QDockWidget* hilDock = new QDockWidget(hilDockName, this); + hilDock->setWidget(hconf); + hilDock->setObjectName(QString("HIL_CONFIG_%1").arg(uas->getUASID())); + addTool(hilDock, hilDockName, Qt::RightDockWidgetArea); + + } + + // Reload view state in case new widgets were added + loadViewState(); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (isVisible()) storeViewState(); + storeSettings(); + aboutToCloseFlag = true; + mavlink->storeSettings(); + UASManager::instance()->storeSettings(); + QMainWindow::closeEvent(event); +} + +/** + * Connect the signals and slots of the common window widgets + */ +void MainWindow::connectCommonWidgets() +{ + if (infoDockWidget && infoDockWidget->widget()) + { + connect(mavlink, SIGNAL(receiveLossChanged(int, float)), + infoDockWidget->widget(), SLOT(updateSendLoss(int, float))); + } +} + +void MainWindow::createCustomWidget() +{ + QDockWidget* dock = new QDockWidget("Unnamed Tool", this); + QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock); + + if (QGCToolWidget::instances()->size() < 2) + { + // This is the first widget + ui.menuTools->addSeparator(); + } + + connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); + dock->setWidget(tool); + + QAction* showAction = new QAction(tool->getTitle(), this); + showAction->setCheckable(true); + connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); + connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); + tool->setMainMenuAction(showAction); + ui.menuTools->addAction(showAction); + this->addDockWidget(Qt::BottomDockWidgetArea, dock); + dock->setVisible(true); +} + +void MainWindow::loadCustomWidget() +{ + QString widgetFileExtension(".qgw"); + QString fileName = QFileDialog::getOpenFileName(this, tr("Specify Widget File Name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("QGroundControl Widget (*%1);;").arg(widgetFileExtension)); + if (fileName != "") loadCustomWidget(fileName); +} + +void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance) +{ + QGCToolWidget* tool = new QGCToolWidget("", this); + if (tool->loadSettings(fileName, true) || !singleinstance) + { + // Add widget to UI + QDockWidget* dock = new QDockWidget(tool->getTitle(), this); + connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater())); + dock->setWidget(tool); + tool->setParent(dock); + + QAction* showAction = new QAction(tool->getTitle(), this); + showAction->setCheckable(true); + connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool))); + connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool))); + tool->setMainMenuAction(showAction); + ui.menuTools->addAction(showAction); + this->addDockWidget(Qt::BottomDockWidgetArea, dock); + dock->setVisible(true); + } + else + { + return; + } +} + +void MainWindow::loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType) +{ + QString defaultsDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/widgets/"; + QString platformDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/" + systemType.toLower() + "/widgets/"; + + QDir widgets(defaultsDir); + QStringList files = widgets.entryList(); + QDir platformWidgets(platformDir); + files.append(platformWidgets.entryList()); + + if (files.count() == 0) + { + qDebug() << "No default custom widgets for system " << systemType << "autopilot" << autopilotType << " found"; + qDebug() << "Tried with path: " << defaultsDir; + showStatusMessage(tr("Did not find any custom widgets in %1").arg(defaultsDir)); + } + + // Load all custom widgets found in the AP folder + for(int i = 0; i < files.count(); ++i) + { + QString file = files[i]; + if (file.endsWith(".qgw")) + { + // Will only be loaded if not already a custom widget with + // the same name is present + loadCustomWidget(defaultsDir+"/"+file, true); + showStatusMessage(tr("Loaded custom widget %1").arg(defaultsDir+"/"+file)); + } + } +} + +void MainWindow::loadSettings() +{ + QSettings settings; + settings.beginGroup("QGC_MAINWINDOW"); + autoReconnect = settings.value("AUTO_RECONNECT", autoReconnect).toBool(); + currentStyle = (QGC_MAINWINDOW_STYLE)settings.value("CURRENT_STYLE", currentStyle).toInt(); + lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool(); + settings.endGroup(); +} + +void MainWindow::storeSettings() +{ + QSettings settings; + settings.beginGroup("QGC_MAINWINDOW"); + settings.setValue("AUTO_RECONNECT", autoReconnect); + settings.setValue("CURRENT_STYLE", currentStyle); + settings.endGroup(); + if (!aboutToCloseFlag && isVisible()) + { + settings.setValue(getWindowGeometryKey(), saveGeometry()); + // Save the last current view in any case + settings.setValue("CURRENT_VIEW", currentView); + // Save the current window state, but only if a system is connected (else no real number of widgets would be present) + if (UASManager::instance()->getUASList().length() > 0) settings.setValue(getWindowStateKey(), saveState(QGC::applicationVersion())); + // Save the current view only if a UAS is connected + if (UASManager::instance()->getUASList().length() > 0) settings.setValue("CURRENT_VIEW_WITH_UAS_CONNECTED", currentView); + // Save the current power mode + } + settings.setValue("LOW_POWER_MODE", lowPowerMode); + settings.sync(); +} + +void MainWindow::configureWindowName() +{ + QList hostAddresses = QNetworkInterface::allAddresses(); + QString windowname = qApp->applicationName() + " " + qApp->applicationVersion(); + bool prevAddr = false; + + windowname.append(" (" + QHostInfo::localHostName() + ": "); + + for (int i = 0; i < hostAddresses.size(); i++) + { + // Exclude loopback IPv4 and all IPv6 addresses + if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":")) + { + if(prevAddr) windowname.append("/"); + windowname.append(hostAddresses.at(i).toString()); + prevAddr = true; + } + } + + windowname.append(")"); + + setWindowTitle(windowname); + +#ifndef Q_WS_MAC + //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png")); +#endif +} + +void MainWindow::startVideoCapture() +{ + QString format = "bmp"; + QString initialPath = QDir::currentPath() + tr("/untitled.") + format; + + QString screenFileName = QFileDialog::getSaveFileName(this, tr("Save As"), + initialPath, + tr("%1 Files (*.%2);;All Files (*)") + .arg(format.toUpper()) + .arg(format)); + delete videoTimer; + videoTimer = new QTimer(this); + //videoTimer->setInterval(40); + //connect(videoTimer, SIGNAL(timeout()), this, SLOT(saveScreen())); + //videoTimer->stop(); +} + +void MainWindow::stopVideoCapture() +{ + videoTimer->stop(); + + // TODO Convert raw images to PNG +} + +void MainWindow::saveScreen() +{ + QPixmap window = QPixmap::grabWindow(this->winId()); + QString format = "bmp"; + + if (!screenFileName.isEmpty()) + { + window.save(screenFileName, format.toAscii()); + } +} + +void MainWindow::enableAutoReconnect(bool enabled) +{ + autoReconnect = enabled; +} + +void MainWindow::loadNativeStyle() +{ + loadStyle(QGC_MAINWINDOW_STYLE_NATIVE); +} + +void MainWindow::loadIndoorStyle() +{ + loadStyle(QGC_MAINWINDOW_STYLE_INDOOR); +} + +void MainWindow::loadOutdoorStyle() +{ + loadStyle(QGC_MAINWINDOW_STYLE_OUTDOOR); +} + +void MainWindow::loadStyle(QGC_MAINWINDOW_STYLE style) +{ + switch (style) { + case QGC_MAINWINDOW_STYLE_NATIVE: { + // Native mode means setting no style + // so if we were already in native mode + // take no action + // Only if a style was set, remove it. + if (style != currentStyle) { + qApp->setStyleSheet(""); + showInfoMessage(tr("Please restart QGroundControl"), tr("Please restart QGroundControl to switch to fully native look and feel. Currently you have loaded Qt's plastique style.")); + } + } + break; + case QGC_MAINWINDOW_STYLE_INDOOR: + qApp->setStyle("plastique"); + styleFileName = ":files/styles/style-indoor.css"; + reloadStylesheet(); + break; + case QGC_MAINWINDOW_STYLE_OUTDOOR: + qApp->setStyle("plastique"); + styleFileName = ":files/styles/style-outdoor.css"; + reloadStylesheet(); + break; + } + currentStyle = style; +} + +void MainWindow::selectStylesheet() +{ + // Let user select style sheet + styleFileName = QFileDialog::getOpenFileName(this, tr("Specify stylesheet"), styleFileName, tr("CSS Stylesheet (*.css);;")); + + if (!styleFileName.endsWith(".css")) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(tr("QGroundControl did lot load a new style")); + msgBox.setInformativeText(tr("No suitable .css file selected. Please select a valid .css file.")); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + return; + } + + // Load style sheet + reloadStylesheet(); +} + +void MainWindow::reloadStylesheet() +{ + // Load style sheet + QFile* styleSheet = new QFile(styleFileName); + if (!styleSheet->exists()) + { + styleSheet = new QFile(":files/styles/style-indoor.css"); + } + if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) + { + QString style = QString(styleSheet->readAll()); + style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "files/styles/"); + qApp->setStyleSheet(style); + } + else + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(tr("QGroundControl did lot load a new style")); + msgBox.setInformativeText(tr("Stylesheet file %1 was not readable").arg(styleFileName)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } + delete styleSheet; +} + +/** + * The status message will be overwritten if a new message is posted to this function + * + * @param status message text + * @param timeout how long the status should be displayed + */ +void MainWindow::showStatusMessage(const QString& status, int timeout) +{ + statusBar()->showMessage(status, timeout); +} + +/** + * The status message will be overwritten if a new message is posted to this function. + * it will be automatically hidden after 5 seconds. + * + * @param status message text + */ +void MainWindow::showStatusMessage(const QString& status) +{ + statusBar()->showMessage(status, 20000); +} + +void MainWindow::showCriticalMessage(const QString& title, const QString& message) +{ + QMessageBox msgBox(this); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText(title); + msgBox.setInformativeText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); +} + +void MainWindow::showInfoMessage(const QString& title, const QString& message) +{ + QMessageBox msgBox(this); + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(title); + msgBox.setInformativeText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); +} + +/** +* @brief Create all actions associated to the main window +* +**/ +void MainWindow::connectCommonActions() +{ + // Bind together the perspective actions + QActionGroup* perspectives = new QActionGroup(ui.menuPerspectives); + perspectives->addAction(ui.actionEngineersView); + perspectives->addAction(ui.actionMavlinkView); + perspectives->addAction(ui.actionPilotsView); + perspectives->addAction(ui.actionOperatorsView); + perspectives->addAction(ui.actionFirmwareUpdateView); + perspectives->addAction(ui.actionUnconnectedView); + perspectives->setExclusive(true); + + // Mark the right one as selected + if (currentView == VIEW_ENGINEER) ui.actionEngineersView->setChecked(true); + if (currentView == VIEW_MAVLINK) ui.actionMavlinkView->setChecked(true); + if (currentView == VIEW_PILOT) ui.actionPilotsView->setChecked(true); + if (currentView == VIEW_OPERATOR) ui.actionOperatorsView->setChecked(true); + if (currentView == VIEW_FIRMWAREUPDATE) ui.actionFirmwareUpdateView->setChecked(true); + if (currentView == VIEW_UNCONNECTED) ui.actionUnconnectedView->setChecked(true); + + // The UAS actions are not enabled without connection to system + ui.actionLiftoff->setEnabled(false); + ui.actionLand->setEnabled(false); + ui.actionEmergency_Kill->setEnabled(false); + ui.actionEmergency_Land->setEnabled(false); + ui.actionShutdownMAV->setEnabled(false); + + // Connect actions from ui + connect(ui.actionAdd_Link, SIGNAL(triggered()), this, SLOT(addLink())); + + // Connect internal actions + connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(UASCreated(UASInterface*))); + connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*))); + + // Unmanned System controls + connect(ui.actionLiftoff, SIGNAL(triggered()), UASManager::instance(), SLOT(launchActiveUAS())); + connect(ui.actionLand, SIGNAL(triggered()), UASManager::instance(), SLOT(returnActiveUAS())); + connect(ui.actionEmergency_Land, SIGNAL(triggered()), UASManager::instance(), SLOT(stopActiveUAS())); + connect(ui.actionEmergency_Kill, SIGNAL(triggered()), UASManager::instance(), SLOT(killActiveUAS())); + connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS())); + connect(ui.actionConfiguration, SIGNAL(triggered()), UASManager::instance(), SLOT(configureActiveUAS())); + + // Views actions + connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView())); + connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView())); + connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView())); + connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView())); + + connect(ui.actionFirmwareUpdateView, SIGNAL(triggered()), this, SLOT(loadFirmwareUpdateView())); + connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView())); + + connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet())); + connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet())); + + // Help Actions + connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp())); + connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits())); + connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap())); + + // Custom widget actions + connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget())); + connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget())); + + // Audio output + ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted()); + connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool))); + connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool))); + + // User interaction + // NOTE: Joystick thread is not started and + // configuration widget is not instantiated + // unless it is actually used + // so no ressources spend on this. + ui.actionJoystickSettings->setVisible(true); + + // Configuration + // Joystick + connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure())); + // Application Settings + connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings())); +} + +void MainWindow::showHelp() +{ + if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText("Could not open help in browser"); + msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/user_guide in a browser."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } +} + +void MainWindow::showCredits() +{ + if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText("Could not open credits in browser"); + msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/credits in a browser."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } +} + +void MainWindow::showRoadMap() +{ + if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Critical); + msgBox.setText("Could not open roadmap in browser"); + msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/roadmap in a browser."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } +} + +void MainWindow::configure() +{ + if (!joystickWidget) + { + if (!joystick->isRunning()) + { + joystick->start(); + } + joystickWidget = new JoystickWidget(joystick); + } + joystickWidget->show(); +} + +void MainWindow::showSettings() +{ + QGCSettingsWidget* settings = new QGCSettingsWidget(this); + settings->show(); +} + +void MainWindow::addLink() +{ + SerialLink* link = new SerialLink(); + // TODO This should be only done in the dialog itself + + LinkManager::instance()->add(link); + LinkManager::instance()->addProtocol(link, mavlink); + + // Go fishing for this link's configuration window + QList actions = ui.menuNetwork->actions(); + + const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link)); + const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId()); + + foreach (QAction* act, actions) + { + if (act->data().toInt() == linkID) + { // LinkManager::instance()->getLinks().indexOf(link) + act->trigger(); + break; + } + } +} + +void MainWindow::addLink(LinkInterface *link) +{ + // IMPORTANT! KEEP THESE TWO LINES + // THEY MAKE SURE THE LINK IS PROPERLY REGISTERED + // BEFORE LINKING THE UI AGAINST IT + // Register (does nothing if already registered) + LinkManager::instance()->add(link); + LinkManager::instance()->addProtocol(link, mavlink); + + // Go fishing for this link's configuration window + QList actions = ui.menuNetwork->actions(); + + bool found(false); + + const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link)); + const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId()); + + foreach (QAction* act, actions) + { + if (act->data().toInt() == linkID) + { // LinkManager::instance()->getLinks().indexOf(link) + found = true; + } + } + + //UDPLink* udp = dynamic_cast(link); + + if (!found) + { // || udp + CommConfigurationWindow* commWidget = new CommConfigurationWindow(link, mavlink, this); + QAction* action = commWidget->getAction(); + ui.menuNetwork->addAction(action); + + // Error handling + connect(link, SIGNAL(communicationError(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection); + // Special case for simulationlink + MAVLinkSimulationLink* sim = dynamic_cast(link); + if (sim) + { + connect(ui.actionSimulate, SIGNAL(triggered(bool)), sim, SLOT(connectLink(bool))); + } + } +} + +void MainWindow::setActiveUAS(UASInterface* uas) +{ + // Enable and rename menu + ui.menuUnmanned_System->setTitle(uas->getUASName()); + if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true); +} + +void MainWindow::UASSpecsChanged(int uas) +{ + UASInterface* activeUAS = UASManager::instance()->getActiveUAS(); + if (activeUAS) + { + if (activeUAS->getUASID() == uas) + { + ui.menuUnmanned_System->setTitle(activeUAS->getUASName()); + } + } + else + { + // Last system deleted + ui.menuUnmanned_System->setTitle(tr("No System")); + ui.menuUnmanned_System->setEnabled(false); + } +} + +void MainWindow::UASCreated(UASInterface* uas) +{ + + // Connect the UAS to the full user interface + + //if (uas != NULL) + //{ + // The pilot, operator and engineer views were not available on startup, enable them now + ui.actionPilotsView->setEnabled(true); + ui.actionOperatorsView->setEnabled(true); + ui.actionEngineersView->setEnabled(true); + // The UAS actions are not enabled without connection to system + ui.actionLiftoff->setEnabled(true); + ui.actionLand->setEnabled(true); + ui.actionEmergency_Kill->setEnabled(true); + ui.actionEmergency_Land->setEnabled(true); + ui.actionShutdownMAV->setEnabled(true); + + QIcon icon; + // Set matching icon + switch (uas->getSystemType()) + { + case MAV_TYPE_GENERIC: + icon = QIcon(":files/images/mavs/generic.svg"); + break; + case MAV_TYPE_FIXED_WING: + icon = QIcon(":files/images/mavs/fixed-wing.svg"); + break; + case MAV_TYPE_QUADROTOR: + icon = QIcon(":files/images/mavs/quadrotor.svg"); + break; + case MAV_TYPE_COAXIAL: + icon = QIcon(":files/images/mavs/coaxial.svg"); + break; + case MAV_TYPE_HELICOPTER: + icon = QIcon(":files/images/mavs/helicopter.svg"); + break; + case MAV_TYPE_ANTENNA_TRACKER: + icon = QIcon(":files/images/mavs/antenna-tracker.svg"); + break; + case MAV_TYPE_GCS: + icon = QIcon(":files/images/mavs/groundstation.svg"); + break; + case MAV_TYPE_AIRSHIP: + icon = QIcon(":files/images/mavs/airship.svg"); + break; + case MAV_TYPE_FREE_BALLOON: + icon = QIcon(":files/images/mavs/free-balloon.svg"); + break; + case MAV_TYPE_ROCKET: + icon = QIcon(":files/images/mavs/rocket.svg"); + break; + case MAV_TYPE_GROUND_ROVER: + icon = QIcon(":files/images/mavs/ground-rover.svg"); + break; + case MAV_TYPE_SURFACE_BOAT: + icon = QIcon(":files/images/mavs/surface-boat.svg"); + break; + case MAV_TYPE_SUBMARINE: + icon = QIcon(":files/images/mavs/submarine.svg"); + break; + case MAV_TYPE_HEXAROTOR: + icon = QIcon(":files/images/mavs/hexarotor.svg"); + break; + case MAV_TYPE_OCTOROTOR: + icon = QIcon(":files/images/mavs/octorotor.svg"); + break; + case MAV_TYPE_TRICOPTER: + icon = QIcon(":files/images/mavs/tricopter.svg"); + break; + case MAV_TYPE_FLAPPING_WING: + icon = QIcon(":files/images/mavs/flapping-wing.svg"); + break; + case MAV_TYPE_KITE: + icon = QIcon(":files/images/mavs/kite.svg"); + break; + default: + icon = QIcon(":files/images/mavs/unknown.svg"); + break; + } + + QAction* uasAction = new QAction(icon, tr("Select %1 for control").arg(uas->getUASName()), ui.menuConnected_Systems); + connect(uas, SIGNAL(systemRemoved()), uasAction, SLOT(deleteLater())); + connect(uasAction, SIGNAL(triggered()), uas, SLOT(setSelected())); + connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(UASSpecsChanged(int))); + + ui.menuConnected_Systems->addAction(uasAction); + + // FIXME Should be not inside the mainwindow + if (debugConsoleDockWidget) + { + DebugConsole *debugConsole = dynamic_cast(debugConsoleDockWidget->widget()); + if (debugConsole) + { + connect(uas, SIGNAL(textMessageReceived(int,int,int,QString)), + debugConsole, SLOT(receiveTextMessage(int,int,int,QString))); + } + } + + // Health / System status indicator + if (infoDockWidget) + { + UASInfoWidget *infoWidget = dynamic_cast(infoDockWidget->widget()); + if (infoWidget) + { + infoWidget->addUAS(uas); + } + } + + // UAS List + if (listDockWidget) + { + UASListWidget *listWidget = dynamic_cast(listDockWidget->widget()); + if (listWidget) + { + listWidget->addUAS(uas); + } + } + + // Line chart + if (!linechartWidget) + { + // Center widgets + linechartWidget = new Linecharts(this); + linechartWidget->addSource(mavlinkDecoder); + addCentralWidget(linechartWidget, tr("Realtime Plot")); + } + + // Load default custom widgets for this autopilot type + loadCustomWidgetsFromDefaults(uas->getSystemTypeName(), uas->getAutopilotTypeName()); + + + if (uas->getAutopilotType() == MAV_AUTOPILOT_PIXHAWK) + { + // Dock widgets + if (!detectionDockWidget) + { + detectionDockWidget = new QDockWidget(tr("Object Recognition"), this); + detectionDockWidget->setWidget( new ObjectDetectionView("files/images/patterns", this) ); + detectionDockWidget->setObjectName("OBJECT_DETECTION_DOCK_WIDGET"); + addTool(detectionDockWidget, tr("Object Recognition"), Qt::RightDockWidgetArea); + } + + if (!watchdogControlDockWidget) + { + watchdogControlDockWidget = new QDockWidget(tr("Process Control"), this); + watchdogControlDockWidget->setWidget( new WatchdogControl(this) ); + watchdogControlDockWidget->setObjectName("WATCHDOG_CONTROL_DOCKWIDGET"); + addTool(watchdogControlDockWidget, tr("Process Control"), Qt::BottomDockWidgetArea); + } + } + + // Change the view only if this is the first UAS + + // If this is the first connected UAS, it is both created as well as + // the currently active UAS + if (UASManager::instance()->getUASList().size() == 1) + { + // Load last view if setting is present + if (settings.contains("CURRENT_VIEW_WITH_UAS_CONNECTED")) + { + int view = settings.value("CURRENT_VIEW_WITH_UAS_CONNECTED").toInt(); + switch (view) + { + case VIEW_ENGINEER: + loadEngineerView(); + break; + case VIEW_MAVLINK: + loadMAVLinkView(); + break; + case VIEW_FIRMWAREUPDATE: + loadFirmwareUpdateView(); + break; + case VIEW_PILOT: + loadPilotView(); + break; + case VIEW_UNCONNECTED: + loadUnconnectedView(); + break; + case VIEW_OPERATOR: + default: + loadOperatorView(); + break; + } + } + else + { + loadOperatorView(); + } + } + + //} + + if (!ui.menuConnected_Systems->isEnabled()) ui.menuConnected_Systems->setEnabled(true); + if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true); + + // Reload view state in case new widgets were added + loadViewState(); +} + +void MainWindow::UASDeleted(UASInterface* uas) +{ + if (UASManager::instance()->getUASList().count() == 0) + { + // Last system deleted + ui.menuUnmanned_System->setTitle(tr("No System")); + ui.menuUnmanned_System->setEnabled(false); + } + + QAction* act; + QList actions = ui.menuConnected_Systems->actions(); + + foreach (act, actions) + { + if (act->text().contains(uas->getUASName())) + ui.menuConnected_Systems->removeAction(act); + } +} + +/** + * Stores the current view state + */ +void MainWindow::storeViewState() +{ + if (!aboutToCloseFlag) + { + // Save current state + settings.setValue(getWindowStateKey(), saveState(QGC::applicationVersion())); + settings.setValue(getWindowStateKey()+"CENTER_WIDGET", centerStack->currentIndex()); + // Although we want save the state of the window, we do not want to change the top-leve state (minimized, maximized, etc) + // therefore this state is stored here and restored after applying the rest of the settings in the new + // perspective. + windowStateVal = this->windowState(); + settings.setValue(getWindowGeometryKey(), saveGeometry()); + } +} + +void MainWindow::loadViewState() +{ + // Restore center stack state + int index = settings.value(getWindowStateKey()+"CENTER_WIDGET", -1).toInt(); + // The offline plot view is usually the consequence of a logging run, always show the realtime view first + if (centerStack->indexOf(dataplotWidget) == index) + { + // Rewrite to realtime plot + index = centerStack->indexOf(linechartWidget); + } + + if (index != -1) + { + centerStack->setCurrentIndex(index); + } + else + { + // Hide custom widgets + if (detectionDockWidget) detectionDockWidget->hide(); + if (watchdogControlDockWidget) watchdogControlDockWidget->hide(); + + // Load defaults + switch (currentView) + { + case VIEW_ENGINEER: + centerStack->setCurrentWidget(linechartWidget); + controlDockWidget->hide(); + listDockWidget->hide(); + waypointsDockWidget->hide(); + infoDockWidget->hide(); + debugConsoleDockWidget->show(); + logPlayerDockWidget->show(); + mavlinkInspectorWidget->show(); + //mavlinkSenderWidget->show(); + parametersDockWidget->show(); + hsiDockWidget->hide(); + headDown1DockWidget->hide(); + headDown2DockWidget->hide(); + rcViewDockWidget->hide(); + headUpDockWidget->hide(); + video1DockWidget->hide(); + video2DockWidget->hide(); + break; + case VIEW_PILOT: + centerStack->setCurrentWidget(hudWidget); + controlDockWidget->hide(); + listDockWidget->hide(); + waypointsDockWidget->hide(); + infoDockWidget->hide(); + debugConsoleDockWidget->hide(); + logPlayerDockWidget->hide(); + mavlinkInspectorWidget->hide(); + parametersDockWidget->hide(); + hsiDockWidget->show(); + headDown1DockWidget->show(); + headDown2DockWidget->show(); + rcViewDockWidget->hide(); + headUpDockWidget->hide(); + video1DockWidget->hide(); + video2DockWidget->hide(); + break; + case VIEW_MAVLINK: + centerStack->setCurrentWidget(protocolWidget); + controlDockWidget->hide(); + listDockWidget->hide(); + waypointsDockWidget->hide(); + infoDockWidget->hide(); + debugConsoleDockWidget->hide(); + logPlayerDockWidget->hide(); + mavlinkInspectorWidget->show(); + //mavlinkSenderWidget->show(); + parametersDockWidget->hide(); + hsiDockWidget->hide(); + headDown1DockWidget->hide(); + headDown2DockWidget->hide(); + rcViewDockWidget->hide(); + headUpDockWidget->hide(); + video1DockWidget->hide(); + video2DockWidget->hide(); + break; + case VIEW_FIRMWAREUPDATE: + centerStack->setCurrentWidget(firmwareUpdateWidget); + controlDockWidget->hide(); + listDockWidget->hide(); + waypointsDockWidget->hide(); + infoDockWidget->hide(); + debugConsoleDockWidget->hide(); + logPlayerDockWidget->hide(); + mavlinkInspectorWidget->hide(); + //mavlinkSenderWidget->hide(); + parametersDockWidget->hide(); + hsiDockWidget->hide(); + headDown1DockWidget->hide(); + headDown2DockWidget->hide(); + rcViewDockWidget->hide(); + headUpDockWidget->hide(); + video1DockWidget->hide(); + video2DockWidget->hide(); + break; + case VIEW_OPERATOR: + centerStack->setCurrentWidget(mapWidget); + controlDockWidget->hide(); + listDockWidget->show(); + waypointsDockWidget->show(); + infoDockWidget->hide(); + debugConsoleDockWidget->show(); + logPlayerDockWidget->show(); + parametersDockWidget->hide(); + hsiDockWidget->show(); + headDown1DockWidget->hide(); + headDown2DockWidget->hide(); + rcViewDockWidget->hide(); + headUpDockWidget->show(); + video1DockWidget->hide(); + video2DockWidget->hide(); + mavlinkInspectorWidget->hide(); + break; + case VIEW_UNCONNECTED: + case VIEW_FULL: + default: + centerStack->setCurrentWidget(mapWidget); + controlDockWidget->hide(); + listDockWidget->show(); + waypointsDockWidget->hide(); + infoDockWidget->hide(); + debugConsoleDockWidget->show(); + logPlayerDockWidget->show(); + parametersDockWidget->hide(); + hsiDockWidget->hide(); + headDown1DockWidget->hide(); + headDown2DockWidget->hide(); + rcViewDockWidget->hide(); + headUpDockWidget->show(); + video1DockWidget->hide(); + video2DockWidget->hide(); + mavlinkInspectorWidget->show(); + break; + } + } + + // Restore the widget positions and size + if (settings.contains(getWindowStateKey())) + { + restoreState(settings.value(getWindowStateKey()).toByteArray(), QGC::applicationVersion()); + } +} + +void MainWindow::loadEngineerView() +{ + if (currentView != VIEW_ENGINEER) + { + storeViewState(); + currentView = VIEW_ENGINEER; + ui.actionEngineersView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadOperatorView() +{ + if (currentView != VIEW_OPERATOR) + { + storeViewState(); + currentView = VIEW_OPERATOR; + ui.actionOperatorsView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadUnconnectedView() +{ + if (currentView != VIEW_UNCONNECTED) + { + storeViewState(); + currentView = VIEW_UNCONNECTED; + ui.actionUnconnectedView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadPilotView() +{ + if (currentView != VIEW_PILOT) + { + storeViewState(); + currentView = VIEW_PILOT; + ui.actionPilotsView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadMAVLinkView() +{ + if (currentView != VIEW_MAVLINK) + { + storeViewState(); + currentView = VIEW_MAVLINK; + ui.actionMavlinkView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadFirmwareUpdateView() +{ + if (currentView != VIEW_FIRMWAREUPDATE) + { + storeViewState(); + currentView = VIEW_FIRMWAREUPDATE; + ui.actionFirmwareUpdateView->setChecked(true); + loadViewState(); + } +} + +void MainWindow::loadDataView(QString fileName) +{ + // Plot is now selected, now load data from file + if (dataplotWidget) + { + dataplotWidget->loadFile(fileName); + } + QStackedWidget *centerStack = dynamic_cast(centralWidget()); + if (centerStack) + { + centerStack->setCurrentWidget(dataplotWidget); + dataplotWidget->loadFile(fileName); + } +} + + +QList MainWindow::listLinkMenuActions(void) +{ + return ui.menuNetwork->actions(); +} diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index b5d1f0a40aed8e02ca88545cf812c422d03c9ec1..002f3202970e8f747a608ad9dbba94ba58431534 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -1,402 +1,409 @@ -/*===================================================================== - -QGroundControl Open Source Ground Control Station - -(c) 2009, 2010 QGROUNDCONTROL PROJECT - -This file is part of the QGROUNDCONTROL project - - QGROUNDCONTROL is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - QGROUNDCONTROL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with QGROUNDCONTROL. If not, see . - -======================================================================*/ - -/** - * @file - * @brief Definition of class MainWindow - * @author Lorenz Meier - * - */ - -#ifndef _MAINWINDOW_H_ -#define _MAINWINDOW_H_ - -#include -#include -#include -#include -#include - -#include "ui_MainWindow.h" -#include "LinkManager.h" -#include "LinkInterface.h" -#include "UASInterface.h" -#include "UASManager.h" -#include "UASControlWidget.h" -#include "Linecharts.h" -#include "UASInfoWidget.h" -#include "WaypointList.h" -#include "CameraView.h" -#include "UASListWidget.h" -#include "MAVLinkProtocol.h" -#include "MAVLinkSimulationLink.h" -#include "ObjectDetectionView.h" -#include "HUD.h" -#include "JoystickWidget.h" -#include "input/JoystickInput.h" -#include "DebugConsole.h" -#include "ParameterInterface.h" -#include "XMLCommProtocolWidget.h" -#include "HDDisplay.h" -#include "WatchdogControl.h" -#include "HSIDisplay.h" -#include "QGCDataPlot2D.h" -#include "QGCRemoteControlView.h" -#include "opmapcontrol.h" -#if (defined Q_OS_MAC) | (defined _MSC_VER) -#include "QGCGoogleEarthView.h" -#endif -#include "QGCToolBar.h" -#include "SlugsDataSensorView.h" -#include "LogCompressor.h" - -#include "SlugsHilSim.h" - -#include "SlugsPadCameraControl.h" -#include "UASControlParameters.h" -#include "QGCMAVLinkInspector.h" -#include "QGCMAVLinkLogPlayer.h" -#include "QGCVehicleConfig.h" -#include "MAVLinkDecoder.h" - -class QGCMapTool; -class QGCMAVLinkMessageSender; -class QGCFirmwareUpdate; -class QSplashScreen; - -/** - * @brief Main Application Window - * - **/ -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - static MainWindow* instance(QSplashScreen* screen = 0); - ~MainWindow(); - - enum QGC_MAINWINDOW_STYLE - { - QGC_MAINWINDOW_STYLE_NATIVE, - QGC_MAINWINDOW_STYLE_INDOOR, - QGC_MAINWINDOW_STYLE_OUTDOOR - }; - - /** @brief Get current visual style */ - int getStyle() - { - return currentStyle; - } - /** @brief Get auto link reconnect setting */ - bool autoReconnectEnabled() - { - return autoReconnect; - } - - /** @brief Get low power mode setting */ - bool lowPowerModeEnabled() - { - return lowPowerMode; - } - - QList listLinkMenuActions(void); - -public slots: - - /** @brief Shows a status message on the bottom status bar */ - void showStatusMessage(const QString& status, int timeout); - /** @brief Shows a status message on the bottom status bar */ - void showStatusMessage(const QString& status); - /** @brief Shows a critical message as popup or as widget */ - void showCriticalMessage(const QString& title, const QString& message); - /** @brief Shows an info message as popup or as widget */ - void showInfoMessage(const QString& title, const QString& message); - - /** @brief Show the application settings */ - void showSettings(); - /** @brief Add a communication link */ - void addLink(); - void addLink(LinkInterface* link); - void configure(); - /** @brief Set the currently controlled UAS */ - void setActiveUAS(UASInterface* uas); - - /** @brief Add a new UAS */ - void UASCreated(UASInterface* uas); - /** Delete an UAS */ - void UASDeleted(UASInterface* uas); - /** @brief Update system specs of a UAS */ - void UASSpecsChanged(int uas); - void startVideoCapture(); - void stopVideoCapture(); - void saveScreen(); - - /** @brief Load default view when no MAV is connected */ - void loadUnconnectedView(); - /** @brief Load view for pilot */ - void loadPilotView(); - /** @brief Load view for engineer */ - void loadEngineerView(); - /** @brief Load view for operator */ - void loadOperatorView(); - /** @brief Load MAVLink XML generator view */ - void loadMAVLinkView(); - /** @brief Load firmware update view */ - void loadFirmwareUpdateView(); - - /** @brief Show the online help for users */ - void showHelp(); - /** @brief Show the authors / credits */ - void showCredits(); - /** @brief Show the project roadmap */ - void showRoadMap(); - - /** @brief Reload the CSS style sheet */ - void reloadStylesheet(); - /** @brief Let the user select the CSS style sheet */ - void selectStylesheet(); - /** @brief Automatically reconnect last link */ - void enableAutoReconnect(bool enabled); - /** @brief Save power by reducing update rates */ - void enableLowPowerMode(bool enabled) { lowPowerMode = enabled; } - /** @brief Switch to native application style */ - void loadNativeStyle(); - /** @brief Switch to indoor mission style */ - void loadIndoorStyle(); - /** @brief Switch to outdoor mission style */ - void loadOutdoorStyle(); - /** @brief Load a specific style */ - void loadStyle(QGC_MAINWINDOW_STYLE style); - - /** @brief Add a custom tool widget */ - void createCustomWidget(); - - /** @brief Load a custom tool widget from a file chosen by user (QFileDialog) */ - void loadCustomWidget(); - - /** @brief Load a custom tool widget from a file */ - void loadCustomWidget(const QString& fileName, bool singleinstance=false); - - /** @brief Load custom widgets from default file */ - void loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType); - - /** @brief Loads and shows the HIL Configuration Widget for the given UAS*/ - void showHILConfigurationWidget(UASInterface *uas); - - void closeEvent(QCloseEvent* event); - - /** @brief Load data view, allowing to plot flight data */ - void loadDataView(QString fileName); - - /** - * @brief Shows a Docked Widget based on the action sender - * - * This slot is written to be used in conjunction with the addTool() function - * It shows the QDockedWidget based on the action sender - * - */ - void showTool(bool visible); - - /** - * @brief Shows a Widget from the center stack based on the action sender - * - * This slot is written to be used in conjunction with the addCentralWidget() function - * It shows the Widget based on the action sender - * - */ - void showCentralWidget(); - - /** @brief Update the window name */ - void configureWindowName(); - -signals: - void initStatusChanged(const QString& message); - -public: - QGCMAVLinkLogPlayer* getLogPlayer() - { - return logPlayer; - } - - MAVLinkProtocol* getMAVLink() - { - return mavlink; - } - -protected: - - MainWindow(QWidget *parent = 0); - - typedef enum _VIEW_SECTIONS - { - VIEW_ENGINEER, - VIEW_OPERATOR, - VIEW_PILOT, - VIEW_MAVLINK, - VIEW_FIRMWAREUPDATE, - VIEW_UNCONNECTED, ///< View in unconnected mode, when no UAS is available - VIEW_FULL ///< All widgets shown at once - } VIEW_SECTIONS; - - /** - * @brief Adds an already instantiated QDockedWidget to the Tools Menu - * - * This function does all the hosekeeping to have a QDockedWidget added to the - * tools menu and connects the QMenuAction to a slot that shows the widget and - * checks/unchecks the tools menu item - * - * @param widget The QDockWidget being added - * @param title The entry that will appear in the Menu and in the QDockedWidget title bar - * @param location The default location for the QDockedWidget in case there is no previous key in the settings - */ - void addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea location=Qt::RightDockWidgetArea); - - /** - * @brief Adds an already instantiated QWidget to the center stack - * - * This function does all the hosekeeping to have a QWidget added to the tools menu - * tools menu and connects the QMenuAction to a slot that shows the widget and - * checks/unchecks the tools menu item. This is used for all the central widgets (those in - * the center stack. - * - * @param widget The QWidget being added - * @param title The entry that will appear in the Menu - */ - void addCentralWidget(QWidget* widget, const QString& title); - - /** @brief Catch window resize events */ - void resizeEvent(QResizeEvent * event); - - /** @brief Keeps track of the current view */ - VIEW_SECTIONS currentView; - QGC_MAINWINDOW_STYLE currentStyle; - bool aboutToCloseFlag; - bool changingViewsFlag; - - void storeViewState(); - void loadViewState(); - - void buildCustomWidget(); - void buildCommonWidgets(); - void connectCommonWidgets(); - void connectCommonActions(); - void connectSenseSoarActions(); - - void loadSettings(); - void storeSettings(); - - // TODO Should be moved elsewhere, as the protocol does not belong to the UI - MAVLinkProtocol* mavlink; - - MAVLinkSimulationLink* simulationLink; - LinkInterface* udpLink; - - QSettings settings; - QStackedWidget *centerStack; - QActionGroup* centerStackActionGroup; - - // Center widgets - QPointer linechartWidget; - QPointer hudWidget; - QPointer configWidget; - QPointer mapWidget; - QPointer protocolWidget; - QPointer dataplotWidget; -#ifdef QGC_OSG_ENABLED - QPointer _3DWidget; -#endif -#if (defined _MSC_VER) || (defined Q_OS_MAC) - QPointer gEarthWidget; -#endif - QPointer firmwareUpdateWidget; - - // Dock widgets - QPointer controlDockWidget; - QPointer controlParameterWidget; - QPointer infoDockWidget; - QPointer cameraDockWidget; - QPointer listDockWidget; - QPointer waypointsDockWidget; - QPointer detectionDockWidget; - QPointer debugConsoleDockWidget; - QPointer parametersDockWidget; - QPointer headDown1DockWidget; - QPointer headDown2DockWidget; - QPointer watchdogControlDockWidget; - - QPointer headUpDockWidget; - QPointer video1DockWidget; - QPointer video2DockWidget; - QPointer rgbd1DockWidget; - QPointer rgbd2DockWidget; - QPointer logPlayerDockWidget; - - QPointer hsiDockWidget; - QPointer rcViewDockWidget; - QPointer hudDockWidget; - QPointer slugsDataWidget; - QPointer slugsHilSimWidget; - QPointer slugsCamControlWidget; - - QPointer toolBar; - - QPointer mavlinkInspectorWidget; - QPointer mavlinkDecoder; - QPointer mavlinkSenderWidget; - QGCMAVLinkLogPlayer* logPlayer; - - // Popup widgets - JoystickWidget* joystickWidget; - - JoystickInput* joystick; - - /** User interface actions **/ - QAction* connectUASAct; - QAction* disconnectUASAct; - QAction* startUASAct; - QAction* returnUASAct; - QAction* stopUASAct; - QAction* killUASAct; - QAction* simulateUASAct; - - - LogCompressor* comp; - QString screenFileName; - QTimer* videoTimer; - QString styleFileName; - bool autoReconnect; - Qt::WindowStates windowStateVal; - bool lowPowerMode; ///< If enabled, QGC reduces the update rates of all widgets - QGCFlightGearLink* fgLink; - QTimer windowNameUpdateTimer; - -private: - Ui::MainWindow ui; - - QString getWindowStateKey(); - QString getWindowGeometryKey(); - -}; - -#endif /* _MAINWINDOW_H_ */ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2010 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Definition of class MainWindow + * @author Lorenz Meier + * + */ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include +#include +#include +#include +#include + +#include "ui_MainWindow.h" +#include "LinkManager.h" +#include "LinkInterface.h" +#include "UASInterface.h" +#include "UASManager.h" +#include "UASControlWidget.h" +#include "Linecharts.h" +#include "UASInfoWidget.h" +#include "WaypointList.h" +#include "CameraView.h" +#include "UASListWidget.h" +#include "MAVLinkProtocol.h" +#include "MAVLinkSimulationLink.h" +#include "ObjectDetectionView.h" +#include "HUD.h" +#include "JoystickWidget.h" +#include "input/JoystickInput.h" +#include "input/Mouse6dofInput.h" +#include "DebugConsole.h" +#include "ParameterInterface.h" +#include "XMLCommProtocolWidget.h" +#include "HDDisplay.h" +#include "WatchdogControl.h" +#include "HSIDisplay.h" +#include "QGCDataPlot2D.h" +#include "QGCRemoteControlView.h" +#include "opmapcontrol.h" +#if (defined Q_OS_MAC) | (defined _MSC_VER) +#include "QGCGoogleEarthView.h" +#endif +#include "QGCToolBar.h" +#include "SlugsDataSensorView.h" +#include "LogCompressor.h" + +#include "SlugsHilSim.h" + +#include "SlugsPadCameraControl.h" +#include "UASControlParameters.h" +#include "QGCMAVLinkInspector.h" +#include "QGCMAVLinkLogPlayer.h" +#include "QGCVehicleConfig.h" +#include "MAVLinkDecoder.h" + +class QGCMapTool; +class QGCMAVLinkMessageSender; +class QGCFirmwareUpdate; +class QSplashScreen; + +/** + * @brief Main Application Window + * + **/ +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + static MainWindow* instance(QSplashScreen* screen = 0); + ~MainWindow(); + + enum QGC_MAINWINDOW_STYLE + { + QGC_MAINWINDOW_STYLE_NATIVE, + QGC_MAINWINDOW_STYLE_INDOOR, + QGC_MAINWINDOW_STYLE_OUTDOOR + }; + + /** @brief Get current visual style */ + int getStyle() + { + return currentStyle; + } + /** @brief Get auto link reconnect setting */ + bool autoReconnectEnabled() + { + return autoReconnect; + } + + /** @brief Get low power mode setting */ + bool lowPowerModeEnabled() + { + return lowPowerMode; + } + + QList listLinkMenuActions(void); + +public slots: + + /** @brief Shows a status message on the bottom status bar */ + void showStatusMessage(const QString& status, int timeout); + /** @brief Shows a status message on the bottom status bar */ + void showStatusMessage(const QString& status); + /** @brief Shows a critical message as popup or as widget */ + void showCriticalMessage(const QString& title, const QString& message); + /** @brief Shows an info message as popup or as widget */ + void showInfoMessage(const QString& title, const QString& message); + + /** @brief Show the application settings */ + void showSettings(); + /** @brief Add a communication link */ + void addLink(); + void addLink(LinkInterface* link); + void configure(); + /** @brief Set the currently controlled UAS */ + void setActiveUAS(UASInterface* uas); + + /** @brief Add a new UAS */ + void UASCreated(UASInterface* uas); + /** Delete an UAS */ + void UASDeleted(UASInterface* uas); + /** @brief Update system specs of a UAS */ + void UASSpecsChanged(int uas); + void startVideoCapture(); + void stopVideoCapture(); + void saveScreen(); + + /** @brief Load default view when no MAV is connected */ + void loadUnconnectedView(); + /** @brief Load view for pilot */ + void loadPilotView(); + /** @brief Load view for engineer */ + void loadEngineerView(); + /** @brief Load view for operator */ + void loadOperatorView(); + /** @brief Load MAVLink XML generator view */ + void loadMAVLinkView(); + /** @brief Load firmware update view */ + void loadFirmwareUpdateView(); + + /** @brief Show the online help for users */ + void showHelp(); + /** @brief Show the authors / credits */ + void showCredits(); + /** @brief Show the project roadmap */ + void showRoadMap(); + + /** @brief Reload the CSS style sheet */ + void reloadStylesheet(); + /** @brief Let the user select the CSS style sheet */ + void selectStylesheet(); + /** @brief Automatically reconnect last link */ + void enableAutoReconnect(bool enabled); + /** @brief Save power by reducing update rates */ + void enableLowPowerMode(bool enabled) { lowPowerMode = enabled; } + /** @brief Switch to native application style */ + void loadNativeStyle(); + /** @brief Switch to indoor mission style */ + void loadIndoorStyle(); + /** @brief Switch to outdoor mission style */ + void loadOutdoorStyle(); + /** @brief Load a specific style */ + void loadStyle(QGC_MAINWINDOW_STYLE style); + + /** @brief Add a custom tool widget */ + void createCustomWidget(); + + /** @brief Load a custom tool widget from a file chosen by user (QFileDialog) */ + void loadCustomWidget(); + + /** @brief Load a custom tool widget from a file */ + void loadCustomWidget(const QString& fileName, bool singleinstance=false); + + /** @brief Load custom widgets from default file */ + void loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType); + + /** @brief Loads and shows the HIL Configuration Widget for the given UAS*/ + void showHILConfigurationWidget(UASInterface *uas); + + void closeEvent(QCloseEvent* event); + + /** @brief Load data view, allowing to plot flight data */ + void loadDataView(QString fileName); + + /** + * @brief Shows a Docked Widget based on the action sender + * + * This slot is written to be used in conjunction with the addTool() function + * It shows the QDockedWidget based on the action sender + * + */ + void showTool(bool visible); + + /** + * @brief Shows a Widget from the center stack based on the action sender + * + * This slot is written to be used in conjunction with the addCentralWidget() function + * It shows the Widget based on the action sender + * + */ + void showCentralWidget(); + + /** @brief Update the window name */ + void configureWindowName(); + +signals: + void initStatusChanged(const QString& message); + +public: + QGCMAVLinkLogPlayer* getLogPlayer() + { + return logPlayer; + } + + MAVLinkProtocol* getMAVLink() + { + return mavlink; + } + +protected: + + MainWindow(QWidget *parent = 0); + + typedef enum _VIEW_SECTIONS + { + VIEW_ENGINEER, + VIEW_OPERATOR, + VIEW_PILOT, + VIEW_MAVLINK, + VIEW_FIRMWAREUPDATE, + VIEW_UNCONNECTED, ///< View in unconnected mode, when no UAS is available + VIEW_FULL ///< All widgets shown at once + } VIEW_SECTIONS; + + /** + * @brief Adds an already instantiated QDockedWidget to the Tools Menu + * + * This function does all the hosekeeping to have a QDockedWidget added to the + * tools menu and connects the QMenuAction to a slot that shows the widget and + * checks/unchecks the tools menu item + * + * @param widget The QDockWidget being added + * @param title The entry that will appear in the Menu and in the QDockedWidget title bar + * @param location The default location for the QDockedWidget in case there is no previous key in the settings + */ + void addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea location=Qt::RightDockWidgetArea); + + /** + * @brief Adds an already instantiated QWidget to the center stack + * + * This function does all the hosekeeping to have a QWidget added to the tools menu + * tools menu and connects the QMenuAction to a slot that shows the widget and + * checks/unchecks the tools menu item. This is used for all the central widgets (those in + * the center stack. + * + * @param widget The QWidget being added + * @param title The entry that will appear in the Menu + */ + void addCentralWidget(QWidget* widget, const QString& title); + + /** @brief Catch window resize events */ + void resizeEvent(QResizeEvent * event); + + /** @brief Keeps track of the current view */ + VIEW_SECTIONS currentView; + QGC_MAINWINDOW_STYLE currentStyle; + bool aboutToCloseFlag; + bool changingViewsFlag; + + void storeViewState(); + void loadViewState(); + + void buildCustomWidget(); + void buildCommonWidgets(); + void connectCommonWidgets(); + void connectCommonActions(); + void connectSenseSoarActions(); + + void loadSettings(); + void storeSettings(); + + // TODO Should be moved elsewhere, as the protocol does not belong to the UI + MAVLinkProtocol* mavlink; + + MAVLinkSimulationLink* simulationLink; + LinkInterface* udpLink; + + QSettings settings; + QStackedWidget *centerStack; + QActionGroup* centerStackActionGroup; + + // Center widgets + QPointer linechartWidget; + QPointer hudWidget; + QPointer configWidget; + QPointer mapWidget; + QPointer protocolWidget; + QPointer dataplotWidget; +#ifdef QGC_OSG_ENABLED + QPointer _3DWidget; +#endif +#if (defined _MSC_VER) || (defined Q_OS_MAC) + QPointer gEarthWidget; +#endif + QPointer firmwareUpdateWidget; + + // Dock widgets + QPointer controlDockWidget; + QPointer controlParameterWidget; + QPointer infoDockWidget; + QPointer cameraDockWidget; + QPointer listDockWidget; + QPointer waypointsDockWidget; + QPointer detectionDockWidget; + QPointer debugConsoleDockWidget; + QPointer parametersDockWidget; + QPointer headDown1DockWidget; + QPointer headDown2DockWidget; + QPointer watchdogControlDockWidget; + + QPointer headUpDockWidget; + QPointer video1DockWidget; + QPointer video2DockWidget; + QPointer rgbd1DockWidget; + QPointer rgbd2DockWidget; + QPointer logPlayerDockWidget; + + QPointer hsiDockWidget; + QPointer rcViewDockWidget; + QPointer hudDockWidget; + QPointer slugsDataWidget; + QPointer slugsHilSimWidget; + QPointer slugsCamControlWidget; + + QPointer toolBar; + + QPointer mavlinkInspectorWidget; + QPointer mavlinkDecoder; + QPointer mavlinkSenderWidget; + QGCMAVLinkLogPlayer* logPlayer; + + // Popup widgets + JoystickWidget* joystickWidget; + + JoystickInput* joystick; + +#ifdef MOUSE_ENABLED_WIN + /** 3d Mouse support (WIN only) **/ + Mouse3DInput* mouseInput; ///< 3dConnexion 3dMouse SDK + Mouse6dofInput* mouse; ///< Implementation for 3dMouse input +#endif // MOUSE_ENABLED_WIN + + /** User interface actions **/ + QAction* connectUASAct; + QAction* disconnectUASAct; + QAction* startUASAct; + QAction* returnUASAct; + QAction* stopUASAct; + QAction* killUASAct; + QAction* simulateUASAct; + + + LogCompressor* comp; + QString screenFileName; + QTimer* videoTimer; + QString styleFileName; + bool autoReconnect; + Qt::WindowStates windowStateVal; + bool lowPowerMode; ///< If enabled, QGC reduces the update rates of all widgets + QGCFlightGearLink* fgLink; + QTimer windowNameUpdateTimer; + +private: + Ui::MainWindow ui; + + QString getWindowStateKey(); + QString getWindowGeometryKey(); + +}; + +#endif /* _MAINWINDOW_H_ */