Skip to content
Snippets Groups Projects
Vehicle.cc 65.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /****************************************************************************
     *
     *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
     *
     * QGroundControl is licensed according to the terms in the file
     * COPYING.md in the root of the source code directory.
     *
     ****************************************************************************/
    
    
    #include "Vehicle.h"
    #include "MAVLinkProtocol.h"
    #include "FirmwarePluginManager.h"
    #include "LinkManager.h"
    #include "FirmwarePlugin.h"
    #include "AutoPilotPluginManager.h"
    
    #include "JoystickManager.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "MissionManager.h"
    
    #include "CoordinateVector.h"
    
    #include "ParameterLoader.h"
    
    #include "QGCApplication.h"
    
    dogmaphobic's avatar
    dogmaphobic committed
    #include "QGCImageProvider.h"
    
    #include "GAudioOutput.h"
    
    Jimmy Johnson's avatar
    Jimmy Johnson committed
    #include "FollowMe.h"
    
    
    QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog")
    
    
    #define UPDATE_TIMER 50
    #define DEFAULT_LAT  38.965767f
    #define DEFAULT_LON -120.083923f
    
    
    extern const char* guided_mode_not_supported_by_vehicle;
    
    
    const char* Vehicle::_settingsGroup =               "Vehicle%1";        // %1 replaced with mavlink system id
    const char* Vehicle::_joystickModeSettingsKey =     "JoystickMode";
    const char* Vehicle::_joystickEnabledSettingsKey =  "JoystickEnabled";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* Vehicle::_rollFactName =                "roll";
    const char* Vehicle::_pitchFactName =               "pitch";
    const char* Vehicle::_headingFactName =             "heading";
    const char* Vehicle::_airSpeedFactName =            "airSpeed";
    const char* Vehicle::_groundSpeedFactName =         "groundSpeed";
    const char* Vehicle::_climbRateFactName =           "climbRate";
    const char* Vehicle::_altitudeRelativeFactName =    "altitudeRelative";
    const char* Vehicle::_altitudeAMSLFactName =        "altitudeAMSL";
    
    Don Gagne's avatar
    Don Gagne committed
    
    const char* Vehicle::_gpsFactGroupName =        "gps";
    const char* Vehicle::_batteryFactGroupName =    "battery";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* Vehicle::_windFactGroupName =       "wind";
    
    const char* Vehicle::_vibrationFactGroupName =  "vibration";
    
    const int Vehicle::_lowBatteryAnnounceRepeatMSecs = 30 * 1000;
    
    Vehicle::Vehicle(LinkInterface*             link,
                     int                        vehicleId,
                     MAV_AUTOPILOT              firmwareType,
                     MAV_TYPE                   vehicleType,
                     FirmwarePluginManager*     firmwarePluginManager,
                     AutoPilotPluginManager*    autopilotPluginManager,
                     JoystickManager*           joystickManager)
    
    Don Gagne's avatar
    Don Gagne committed
        : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json")
        , _id(vehicleId)
    
        , _active(false)
    
        , _disconnectedVehicle(false)
    
        , _firmwareType(firmwareType)
    
        , _vehicleType(vehicleType)
    
        , _firmwarePlugin(NULL)
        , _autopilotPlugin(NULL)
    
        , _mavlink(NULL)
        , _soloFirmware(false)
    
        , _joystickMode(JoystickModeRC)
    
        , _joystickEnabled(false)
    
        , _uas(NULL)
    
        , _coordinate(37.803784, -122.462276)
        , _coordinateValid(false)
    
        , _homePositionAvailable(false)
    
        , _mav(NULL)
        , _currentMessageCount(0)
        , _messageCount(0)
        , _currentErrorCount(0)
        , _currentWarningCount(0)
        , _currentNormalCount(0)
        , _currentMessageType(MessageNone)
        , _navigationAltitudeError(0.0f)
        , _navigationSpeedError(0.0f)
        , _navigationCrosstrackError(0.0f)
        , _navigationTargetBearing(0.0f)
        , _refreshTimer(new QTimer(this))
        , _updateCount(0)
    
        , _rcRSSI(255)
        , _rcRSSIstore(255)
    
    Don Gagne's avatar
    Don Gagne committed
        , _autoDisconnect(false)
    
    Don Gagne's avatar
    Don Gagne committed
        , _flying(false)
    
        , _connectionLost(false)
        , _connectionLostEnabled(true)
    
        , _missionManager(NULL)
    
        , _missionManagerInitialRequestComplete(false)
    
    Don Gagne's avatar
    Don Gagne committed
        , _armed(false)
        , _base_mode(0)
        , _custom_mode(0)
    
        , _nextSendMessageMultipleIndex(0)
    
        , _firmwarePluginManager(firmwarePluginManager)
        , _autopilotPluginManager(autopilotPluginManager)
        , _joystickManager(joystickManager)
    
    dogmaphobic's avatar
    dogmaphobic committed
        , _flowImageIndex(0)
    
    Don Gagne's avatar
    Don Gagne committed
        , _allLinksInactiveSent(false)
    
        , _messagesReceived(0)
        , _messagesSent(0)
        , _messagesLost(0)
        , _messageSeq(0)
        , _compID(0)
        , _heardFrom(false)
    
        , _firmwareMajorVersion(versionNotSetValue)
        , _firmwareMinorVersion(versionNotSetValue)
        , _firmwarePatchVersion(versionNotSetValue)
    
        , _firmwareVersionType(FIRMWARE_VERSION_TYPE_OFFICIAL)
    
    Don Gagne's avatar
    Don Gagne committed
        , _rollFact             (0, _rollFactName,              FactMetaData::valueTypeDouble)
        , _pitchFact            (0, _pitchFactName,             FactMetaData::valueTypeDouble)
        , _headingFact          (0, _headingFactName,           FactMetaData::valueTypeDouble)
        , _groundSpeedFact      (0, _groundSpeedFactName,       FactMetaData::valueTypeDouble)
        , _airSpeedFact         (0, _airSpeedFactName,          FactMetaData::valueTypeDouble)
        , _climbRateFact        (0, _climbRateFactName,         FactMetaData::valueTypeDouble)
        , _altitudeRelativeFact (0, _altitudeRelativeFactName,  FactMetaData::valueTypeDouble)
        , _altitudeAMSLFact     (0, _altitudeAMSLFactName,      FactMetaData::valueTypeDouble)
        , _gpsFactGroup(this)
    
    Don Gagne's avatar
    Don Gagne committed
        , _batteryFactGroup(this)
    
    Don Gagne's avatar
    Don Gagne committed
        , _windFactGroup(this)
    
        , _vibrationFactGroup(this)
    
    {
        _addLink(link);
    
        _mavlink = qgcApp()->toolbox()->mavlinkProtocol();
    
    dogmaphobic's avatar
    dogmaphobic committed
        connect(_mavlink, &MAVLinkProtocol::messageReceived,     this, &Vehicle::_mavlinkMessageReceived);
    
        connect(this, &Vehicle::_sendMessageOnLinkOnThread, this, &Vehicle::_sendMessageOnLink, Qt::QueuedConnection);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(this, &Vehicle::flightModeChanged,          this, &Vehicle::_handleFlightModeChanged);
    
        connect(this, &Vehicle::armedChanged,               this, &Vehicle::_announceArmedChanged);
    
        _uas = new UAS(_mavlink, this, _firmwarePluginManager);
    
        setLatitude(_uas->getLatitude());
        setLongitude(_uas->getLongitude());
    
    Don Gagne's avatar
    Don Gagne committed
        connect(_uas, &UAS::latitudeChanged,                this, &Vehicle::setLatitude);
        connect(_uas, &UAS::longitudeChanged,               this, &Vehicle::setLongitude);
        connect(_uas, &UAS::imageReady,                     this, &Vehicle::_imageReady);
        connect(this, &Vehicle::remoteControlRSSIChanged,   this, &Vehicle::_remoteControlRSSIChanged);
    
    dogmaphobic's avatar
    dogmaphobic committed
    
        _firmwarePlugin     = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
        _autopilotPlugin    = _autopilotPluginManager->newAutopilotPluginForVehicle(this);
    
    
        connect(_autopilotPlugin, &AutoPilotPlugin::parametersReadyChanged,     this, &Vehicle::_parametersReady);
        connect(_autopilotPlugin, &AutoPilotPlugin::missingParametersChanged,   this, &Vehicle::missingParametersChanged);
    
    Jimmy Johnson's avatar
    Jimmy Johnson committed
        // connect this vehicle to the follow me handle manager
        connect(this, &Vehicle::flightModeChanged,qgcApp()->toolbox()->followMe(), &FollowMe::followMeHandleManager);
    
    
        // Refresh timer
    
        connect(_refreshTimer, &QTimer::timeout, this, &Vehicle::_checkUpdate);
    
        _refreshTimer->setInterval(UPDATE_TIMER);
        _refreshTimer->start(UPDATE_TIMER);
    
    Don Gagne's avatar
    Don Gagne committed
        // PreArm Error self-destruct timer
        connect(&_prearmErrorTimer, &QTimer::timeout, this, &Vehicle::_prearmErrorTimeout);
        _prearmErrorTimer.setInterval(_prearmErrorTimeoutMSecs);
        _prearmErrorTimer.setSingleShot(true);
    
    
        // Connection Lost time
        _connectionLostTimer.setInterval(Vehicle::_connectionLostTimeoutMSecs);
        _connectionLostTimer.setSingleShot(false);
        _connectionLostTimer.start();
        connect(&_connectionLostTimer, &QTimer::timeout, this, &Vehicle::_connectionLostTimeout);
    
        // Listen for system messages
    
    dogmaphobic's avatar
    dogmaphobic committed
        connect(qgcApp()->toolbox()->uasMessageHandler(), &UASMessageHandler::textMessageCountChanged,  this, &Vehicle::_handleTextMessage);
        connect(qgcApp()->toolbox()->uasMessageHandler(), &UASMessageHandler::textMessageReceived,      this, &Vehicle::_handletextMessageReceived);
    
        // Now connect the new UAS
        connect(_mav, SIGNAL(attitudeChanged                    (UASInterface*,double,double,double,quint64)),              this, SLOT(_updateAttitude(UASInterface*, double, double, double, quint64)));
        connect(_mav, SIGNAL(attitudeChanged                    (UASInterface*,int,double,double,double,quint64)),          this, SLOT(_updateAttitude(UASInterface*,int,double, double, double, quint64)));
        connect(_mav, SIGNAL(statusChanged                      (UASInterface*,QString,QString)),                           this, SLOT(_updateState(UASInterface*, QString,QString)));
    
    
        connect(_mav, &UASInterface::speedChanged, this, &Vehicle::_updateSpeed);
        connect(_mav, &UASInterface::altitudeChanged, this, &Vehicle::_updateAltitude);
        connect(_mav, &UASInterface::navigationControllerErrorsChanged,this, &Vehicle::_updateNavigationControllerErrors);
    
        connect(_mav, &UASInterface::NavigationControllerDataChanged,   this, &Vehicle::_updateNavigationControllerData);
    
        _loadSettings();
    
        _missionManager = new MissionManager(this);
        connect(_missionManager, &MissionManager::error, this, &Vehicle::_missionManagerError);
    
    
        _parameterLoader = new ParameterLoader(this);
    
        connect(_parameterLoader, &ParameterLoader::parametersReady, _autopilotPlugin, &AutoPilotPlugin::_parametersReadyPreChecks);
        connect(_parameterLoader, &ParameterLoader::parameterListProgress, _autopilotPlugin, &AutoPilotPlugin::parameterListProgress);
    
        // Ask the vehicle for firmware version info. This must be MAV_COMP_ID_ALL since we don't know default component id yet.
    
    
        mavlink_message_t       versionMsg;
        mavlink_command_long_t  versionCmd;
    
        versionCmd.command = MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES;
        versionCmd.confirmation = 0;
        versionCmd.param1 = 1; // Request firmware version
        versionCmd.param2 = versionCmd.param3 = versionCmd.param4 = versionCmd.param5 = versionCmd.param6 = versionCmd.param7 = 0;
        versionCmd.target_system = id();
        versionCmd.target_component = MAV_COMP_ID_ALL;
        mavlink_msg_command_long_encode(_mavlink->getSystemId(), _mavlink->getComponentId(), &versionMsg, &versionCmd);
        sendMessageMultiple(versionMsg);
    
        _firmwarePlugin->initializeVehicle(this);
    
        _sendMultipleTimer.start(_sendMessageMultipleIntraMessageDelay);
        connect(&_sendMultipleTimer, &QTimer::timeout, this, &Vehicle::_sendMessageMultipleNext);
    
        _mapTrajectoryTimer.setInterval(_mapTrajectoryMsecsBetweenPoints);
        connect(&_mapTrajectoryTimer, &QTimer::timeout, this, &Vehicle::_addNewMapTrajectoryPoint);
    
        // Invalidate the timer to signal first announce
        _lowBatteryAnnounceTimer.invalidate();
    
    
    Don Gagne's avatar
    Don Gagne committed
        // Build FactGroup object model
    
        _addFact(&_rollFact,                _rollFactName);
        _addFact(&_pitchFact,               _pitchFactName);
        _addFact(&_headingFact,             _headingFactName);
        _addFact(&_groundSpeedFact,         _groundSpeedFactName);
        _addFact(&_airSpeedFact,            _airSpeedFactName);
        _addFact(&_climbRateFact,           _climbRateFactName);
        _addFact(&_altitudeRelativeFact,    _altitudeRelativeFactName);
        _addFact(&_altitudeAMSLFact,        _altitudeAMSLFactName);
    
    
    Don Gagne's avatar
    Don Gagne committed
        _addFactGroup(&_gpsFactGroup,       _gpsFactGroupName);
        _addFactGroup(&_batteryFactGroup,   _batteryFactGroupName);
    
    Don Gagne's avatar
    Don Gagne committed
        _addFactGroup(&_windFactGroup,      _windFactGroupName);
    
        _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName);
    
    Don Gagne's avatar
    Don Gagne committed
        _gpsFactGroup.setVehicle(this);
    
    Don Gagne's avatar
    Don Gagne committed
        _batteryFactGroup.setVehicle(this);
    
    Don Gagne's avatar
    Don Gagne committed
        _windFactGroup.setVehicle(this);
    
        _vibrationFactGroup.setVehicle(this);
    
    // Disconnected Vehicle
    Vehicle::Vehicle(QObject* parent)
        : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json", parent)
        , _id(0)
        , _active(false)
        , _disconnectedVehicle(false)
        , _firmwareType(MAV_AUTOPILOT_PX4)
        , _vehicleType(MAV_TYPE_QUADROTOR)
        , _firmwarePlugin(NULL)
        , _autopilotPlugin(NULL)
        , _joystickMode(JoystickModeRC)
        , _joystickEnabled(false)
        , _uas(NULL)
        , _coordinate(37.803784, -122.462276)
        , _coordinateValid(false)
        , _homePositionAvailable(false)
        , _mav(NULL)
        , _currentMessageCount(0)
        , _messageCount(0)
        , _currentErrorCount(0)
        , _currentWarningCount(0)
        , _currentNormalCount(0)
        , _currentMessageType(MessageNone)
        , _navigationAltitudeError(0.0f)
        , _navigationSpeedError(0.0f)
        , _navigationCrosstrackError(0.0f)
        , _navigationTargetBearing(0.0f)
        , _refreshTimer(new QTimer(this))
        , _updateCount(0)
    
        , _rcRSSI(255)
        , _rcRSSIstore(255)
    
        , _autoDisconnect(false)
        , _connectionLost(false)
        , _connectionLostEnabled(true)
        , _missionManager(NULL)
        , _missionManagerInitialRequestComplete(false)
        , _parameterLoader(NULL)
        , _armed(false)
        , _base_mode(0)
        , _custom_mode(0)
        , _nextSendMessageMultipleIndex(0)
        , _firmwarePluginManager(NULL)
        , _autopilotPluginManager(NULL)
        , _joystickManager(NULL)
        , _flowImageIndex(0)
        , _allLinksInactiveSent(false)
        , _messagesReceived(0)
        , _messagesSent(0)
        , _messagesLost(0)
        , _messageSeq(0)
        , _compID(0)
        , _heardFrom(false)
    
        , _firmwareMajorVersion(versionNotSetValue)
        , _firmwareMinorVersion(versionNotSetValue)
        , _firmwarePatchVersion(versionNotSetValue)
    
        , _rollFact             (0, _rollFactName,              FactMetaData::valueTypeDouble)
        , _pitchFact            (0, _pitchFactName,             FactMetaData::valueTypeDouble)
        , _headingFact          (0, _headingFactName,           FactMetaData::valueTypeDouble)
        , _groundSpeedFact      (0, _groundSpeedFactName,       FactMetaData::valueTypeDouble)
        , _airSpeedFact         (0, _airSpeedFactName,          FactMetaData::valueTypeDouble)
        , _climbRateFact        (0, _climbRateFactName,         FactMetaData::valueTypeDouble)
        , _altitudeRelativeFact (0, _altitudeRelativeFactName,  FactMetaData::valueTypeDouble)
        , _altitudeAMSLFact     (0, _altitudeAMSLFactName,      FactMetaData::valueTypeDouble)
        , _gpsFactGroup(this)
        , _batteryFactGroup(this)
        , _windFactGroup(this)
        , _vibrationFactGroup(this)
    {
        // Build FactGroup object model
    
        _addFact(&_rollFact,                _rollFactName);
        _addFact(&_pitchFact,               _pitchFactName);
        _addFact(&_headingFact,             _headingFactName);
        _addFact(&_groundSpeedFact,         _groundSpeedFactName);
        _addFact(&_airSpeedFact,            _airSpeedFactName);
        _addFact(&_climbRateFact,           _climbRateFactName);
        _addFact(&_altitudeRelativeFact,    _altitudeRelativeFactName);
        _addFact(&_altitudeAMSLFact,        _altitudeAMSLFactName);
    
        _addFactGroup(&_gpsFactGroup,       _gpsFactGroupName);
        _addFactGroup(&_batteryFactGroup,   _batteryFactGroupName);
        _addFactGroup(&_windFactGroup,      _windFactGroupName);
        _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName);
    
        _gpsFactGroup.setVehicle(NULL);
        _batteryFactGroup.setVehicle(NULL);
        _windFactGroup.setVehicle(NULL);
        _vibrationFactGroup.setVehicle(NULL);
    }
    
    
    Vehicle::~Vehicle()
    {
    
    Don Gagne's avatar
    Don Gagne committed
        qCDebug(VehicleLog) << "~Vehicle" << this;
    
    
        delete _missionManager;
        _missionManager = NULL;
    
        delete _autopilotPlugin;
        _autopilotPlugin = NULL;
    
    
        delete _mav;
        _mav = NULL;
    
    void
    Vehicle::resetCounters()
    {
        _messagesReceived   = 0;
        _messagesSent       = 0;
        _messagesLost       = 0;
        _messageSeq         = 0;
        _heardFrom          = false;
    }
    
    
    void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        if (message.sysid != _id && message.sysid != 0) {
    
        if (!_containsLink(link)) {
            _addLink(link);
        }
    
        //-- Check link status
        _messagesReceived++;
        emit messagesReceivedChanged();
        if(!_heardFrom) {
            if(message.msgid == MAVLINK_MSG_ID_HEARTBEAT) {
                _heardFrom = true;
                _compID = message.compid;
                _messageSeq = message.seq + 1;
            }
        } else {
            if(_compID == message.compid) {
                uint16_t seq_received = (uint16_t)message.seq;
                uint16_t packet_lost_count = 0;
                //-- Account for overflow during packet loss
                if(seq_received < _messageSeq) {
                    packet_lost_count = (seq_received + 255) - _messageSeq;
                } else {
                    packet_lost_count = seq_received - _messageSeq;
                }
                _messageSeq = message.seq + 1;
                _messagesLost += packet_lost_count;
                if(packet_lost_count)
                    emit messagesLostChanged();
            }
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        // Give the plugin a change to adjust the message contents
    
        if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) {
            return;
        }
    
    Don Gagne's avatar
    Don Gagne committed
        switch (message.msgid) {
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_HOME_POSITION:
            _handleHomePosition(message);
            break;
        case MAVLINK_MSG_ID_HEARTBEAT:
            _handleHeartbeat(message);
            break;
        case MAVLINK_MSG_ID_RC_CHANNELS:
            _handleRCChannels(message);
            break;
        case MAVLINK_MSG_ID_RC_CHANNELS_RAW:
            _handleRCChannelsRaw(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_BATTERY_STATUS:
            _handleBatteryStatus(message);
            break;
        case MAVLINK_MSG_ID_SYS_STATUS:
            _handleSysStatus(message);
            break;
    
        case MAVLINK_MSG_ID_RAW_IMU:
            emit mavlinkRawImu(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU:
            emit mavlinkScaledImu1(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU2:
            emit mavlinkScaledImu2(message);
            break;
        case MAVLINK_MSG_ID_SCALED_IMU3:
            emit mavlinkScaledImu3(message);
            break;
    
        case MAVLINK_MSG_ID_VIBRATION:
            _handleVibration(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        case MAVLINK_MSG_ID_EXTENDED_SYS_STATE:
            _handleExtendedSysState(message);
            break;
    
        case MAVLINK_MSG_ID_COMMAND_ACK:
            _handleCommandAck(message);
            break;
    
        case MAVLINK_MSG_ID_AUTOPILOT_VERSION:
            _handleAutopilotVersion(message);
            break;
    
        case MAVLINK_MSG_ID_WIND_COV:
            _handleWindCov(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
    
        // Following are ArduPilot dialect messages
    
        case MAVLINK_MSG_ID_WIND:
            _handleWind(message);
            break;
    
    Don Gagne's avatar
    Don Gagne committed
        emit mavlinkMessageReceived(message);
    
        _uas->receiveMessage(message);
    }
    
    
    void Vehicle::_handleAutopilotVersion(mavlink_message_t& message)
    {
        mavlink_autopilot_version_t autopilotVersion;
        mavlink_msg_autopilot_version_decode(&message, &autopilotVersion);
    
        if (autopilotVersion.flight_sw_version != 0) {
            int majorVersion, minorVersion, patchVersion;
            FIRMWARE_VERSION_TYPE versionType;
    
            majorVersion = (autopilotVersion.flight_sw_version >> (8*3)) & 0xFF;
            minorVersion = (autopilotVersion.flight_sw_version >> (8*2)) & 0xFF;
            patchVersion = (autopilotVersion.flight_sw_version >> (8*1)) & 0xFF;
            versionType = (FIRMWARE_VERSION_TYPE)((autopilotVersion.flight_sw_version >> (8*0)) & 0xFF);
    
    Don Gagne's avatar
    Don Gagne committed
            setFirmwareVersion(majorVersion, minorVersion, patchVersion, versionType);
    
    void Vehicle::_handleCommandAck(mavlink_message_t& message)
    {
        mavlink_command_ack_t ack;
        mavlink_msg_command_ack_decode(&message, &ack);
    
        emit commandLongAck(message.compid, ack.command, ack.result);
    
    
        if (ack.command == MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES) {
            // Disregard failures
            return;
        }
    
    
        QString commandName;
        MavCmdInfo* cmdInfo = qgcApp()->toolbox()->missionCommands()->getMavCmdInfo((MAV_CMD)ack.command, this);
        if (cmdInfo) {
            commandName = cmdInfo->friendlyName();
        } else {
            commandName = tr("cmdid %1").arg(ack.command);
        }
    
        switch (ack.result) {
        case MAV_RESULT_TEMPORARILY_REJECTED:
            qgcApp()->showMessage(tr("%1 command temporarily rejected").arg(commandName));
            break;
        case MAV_RESULT_DENIED:
            qgcApp()->showMessage(tr("%1 command denied").arg(commandName));
            break;
        case MAV_RESULT_UNSUPPORTED:
            qgcApp()->showMessage(tr("%1 command not supported").arg(commandName));
            break;
        case MAV_RESULT_FAILED:
            qgcApp()->showMessage(tr("%1 command failed").arg(commandName));
            break;
        default:
            // Do nothing
            break;
        }
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleExtendedSysState(mavlink_message_t& message)
    {
        mavlink_extended_sys_state_t extendedState;
        mavlink_msg_extended_sys_state_decode(&message, &extendedState);
    
        switch (extendedState.landed_state) {
    
    Don Gagne's avatar
    Don Gagne committed
        case MAV_LANDED_STATE_UNDEFINED:
    
    Don Gagne's avatar
    Don Gagne committed
            break;
        case MAV_LANDED_STATE_ON_GROUND:
            setFlying(false);
            break;
        case MAV_LANDED_STATE_IN_AIR:
            setFlying(true);
            return;
        }
    }
    
    
    void Vehicle::_handleVibration(mavlink_message_t& message)
    {
        mavlink_vibration_t vibration;
        mavlink_msg_vibration_decode(&message, &vibration);
    
        _vibrationFactGroup.xAxis()->setRawValue(vibration.vibration_x);
        _vibrationFactGroup.yAxis()->setRawValue(vibration.vibration_y);
        _vibrationFactGroup.zAxis()->setRawValue(vibration.vibration_z);
        _vibrationFactGroup.clipCount1()->setRawValue(vibration.clipping_0);
        _vibrationFactGroup.clipCount2()->setRawValue(vibration.clipping_1);
        _vibrationFactGroup.clipCount3()->setRawValue(vibration.clipping_2);
    }
    
    
    void Vehicle::_handleWindCov(mavlink_message_t& message)
    {
        mavlink_wind_cov_t wind;
        mavlink_msg_wind_cov_decode(&message, &wind);
    
        float direction = qRadiansToDegrees(qAtan2(wind.wind_y, wind.wind_x));
        float speed = qSqrt(qPow(wind.wind_x, 2) + qPow(wind.wind_y, 2));
    
        _windFactGroup.direction()->setRawValue(direction);
        _windFactGroup.speed()->setRawValue(speed);
        _windFactGroup.verticalSpeed()->setRawValue(0);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleWind(mavlink_message_t& message)
    {
        mavlink_wind_t wind;
        mavlink_msg_wind_decode(&message, &wind);
    
        _windFactGroup.direction()->setRawValue(wind.direction);
        _windFactGroup.speed()->setRawValue(wind.speed);
        _windFactGroup.verticalSpeed()->setRawValue(wind.speed_z);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleSysStatus(mavlink_message_t& message)
    {
        mavlink_sys_status_t sysStatus;
        mavlink_msg_sys_status_decode(&message, &sysStatus);
    
        if (sysStatus.current_battery == -1) {
            _batteryFactGroup.current()->setRawValue(VehicleBatteryFactGroup::_currentUnavailable);
        } else {
            _batteryFactGroup.current()->setRawValue((double)sysStatus.current_battery * 10);
        }
        if (sysStatus.voltage_battery == UINT16_MAX) {
            _batteryFactGroup.voltage()->setRawValue(VehicleBatteryFactGroup::_voltageUnavailable);
        } else {
            _batteryFactGroup.voltage()->setRawValue((double)sysStatus.voltage_battery / 1000.0);
        }
        _batteryFactGroup.percentRemaining()->setRawValue(sysStatus.battery_remaining);
    
    
        if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _batteryFactGroup.percentRemainingAnnounce()->rawValue().toInt()) {
            if (!_lowBatteryAnnounceTimer.isValid() || _lowBatteryAnnounceTimer.elapsed() > _lowBatteryAnnounceRepeatMSecs) {
                _lowBatteryAnnounceTimer.restart();
    
                _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining));
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void Vehicle::_handleBatteryStatus(mavlink_message_t& message)
    {
        mavlink_battery_status_t bat_status;
        mavlink_msg_battery_status_decode(&message, &bat_status);
    
        if (bat_status.temperature == INT16_MAX) {
            _batteryFactGroup.temperature()->setRawValue(VehicleBatteryFactGroup::_temperatureUnavailable);
        } else {
            _batteryFactGroup.temperature()->setRawValue((double)bat_status.temperature / 100.0);
        }
        if (bat_status.current_consumed == -1) {
            _batteryFactGroup.mahConsumed()->setRawValue(VehicleBatteryFactGroup::_mahConsumedUnavailable);
        } else {
            _batteryFactGroup.mahConsumed()->setRawValue(bat_status.current_consumed);
        }
    
        int cellCount = 0;
        for (int i=0; i<10; i++) {
            if (bat_status.voltages[i] != UINT16_MAX) {
                cellCount++;
            }
        }
        if (cellCount == 0) {
            cellCount = -1;
        }
    
        _batteryFactGroup.cellCount()->setRawValue(cellCount);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleHomePosition(mavlink_message_t& message)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        bool emitHomePositionChanged =          false;
        bool emitHomePositionAvailableChanged = false;
    
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_home_position_t homePos;
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_msg_home_position_decode(&message, &homePos);
    
    Don Gagne's avatar
    Don Gagne committed
    
        QGeoCoordinate newHomePosition (homePos.latitude / 10000000.0,
                                        homePos.longitude / 10000000.0,
                                        homePos.altitude / 1000.0);
    
    Don Gagne's avatar
    Don Gagne committed
        if (!_homePositionAvailable || newHomePosition != _homePosition) {
    
    Don Gagne's avatar
    Don Gagne committed
            emitHomePositionChanged = true;
            _homePosition = newHomePosition;
        }
    
        if (!_homePositionAvailable) {
            emitHomePositionAvailableChanged = true;
    
    Don Gagne's avatar
    Don Gagne committed
            _homePositionAvailable = true;
    
    Don Gagne's avatar
    Don Gagne committed
        }
    
        if (emitHomePositionChanged) {
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(VehicleLog) << "New home position" << newHomePosition;
    
            qgcApp()->setLastKnownHomePosition(_homePosition);
    
    Don Gagne's avatar
    Don Gagne committed
            emit homePositionChanged(_homePosition);
        }
        if (emitHomePositionAvailableChanged) {
            emit homePositionAvailableChanged(true);
        }
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void Vehicle::_handleHeartbeat(mavlink_message_t& message)
    {
    
        _connectionActive();
    
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_heartbeat_t heartbeat;
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_msg_heartbeat_decode(&message, &heartbeat);
    
    Don Gagne's avatar
    Don Gagne committed
        bool newArmed = heartbeat.base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY;
    
    Don Gagne's avatar
    Don Gagne committed
        if (_armed != newArmed) {
            _armed = newArmed;
            emit armedChanged(_armed);
    
            // We are transitioning to the armed state, begin tracking trajectory points for the map
            if (_armed) {
                _mapTrajectoryStart();
            } else {
                _mapTrajectoryStop();
            }
    
    Don Gagne's avatar
    Don Gagne committed
        }
    
        if (heartbeat.base_mode != _base_mode || heartbeat.custom_mode != _custom_mode) {
            _base_mode = heartbeat.base_mode;
            _custom_mode = heartbeat.custom_mode;
            emit flightModeChanged(flightMode());
        }
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleRCChannels(mavlink_message_t& message)
    {
        mavlink_rc_channels_t channels;
    
        mavlink_msg_rc_channels_decode(&message, &channels);
    
        uint16_t* _rgChannelvalues[cMaxRcChannels] = {
            &channels.chan1_raw,
            &channels.chan2_raw,
            &channels.chan3_raw,
            &channels.chan4_raw,
            &channels.chan5_raw,
            &channels.chan6_raw,
            &channels.chan7_raw,
            &channels.chan8_raw,
            &channels.chan9_raw,
            &channels.chan10_raw,
            &channels.chan11_raw,
            &channels.chan12_raw,
            &channels.chan13_raw,
            &channels.chan14_raw,
            &channels.chan15_raw,
            &channels.chan16_raw,
            &channels.chan17_raw,
            &channels.chan18_raw,
        };
        int pwmValues[cMaxRcChannels];
    
        for (int i=0; i<cMaxRcChannels; i++) {
            uint16_t channelValue = *_rgChannelvalues[i];
    
            if (i < channels.chancount) {
                pwmValues[i] = channelValue == UINT16_MAX ? -1 : channelValue;
            } else {
                pwmValues[i] = -1;
            }
        }
    
        emit remoteControlRSSIChanged(channels.rssi);
        emit rcChannelsChanged(channels.chancount, pwmValues);
    }
    
    void Vehicle::_handleRCChannelsRaw(mavlink_message_t& message)
    {
        // We handle both RC_CHANNLES and RC_CHANNELS_RAW since different firmware will only
        // send one or the other.
    
        mavlink_rc_channels_raw_t channels;
    
        mavlink_msg_rc_channels_raw_decode(&message, &channels);
    
        uint16_t* _rgChannelvalues[cMaxRcChannels] = {
            &channels.chan1_raw,
            &channels.chan2_raw,
            &channels.chan3_raw,
            &channels.chan4_raw,
            &channels.chan5_raw,
            &channels.chan6_raw,
            &channels.chan7_raw,
            &channels.chan8_raw,
        };
    
        int pwmValues[cMaxRcChannels];
        int channelCount = 0;
    
    
    Don Gagne's avatar
    Don Gagne committed
        for (int i=0; i<cMaxRcChannels; i++) {
            pwmValues[i] = -1;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        for (int i=0; i<8; i++) {
            uint16_t channelValue = *_rgChannelvalues[i];
    
            if (channelValue == UINT16_MAX) {
                pwmValues[i] = -1;
            } else {
    
    Don Gagne's avatar
    Don Gagne committed
                channelCount = i + 1;
    
    Don Gagne's avatar
    Don Gagne committed
                pwmValues[i] = channelValue;
            }
        }
        for (int i=9; i<18; i++) {
            pwmValues[i] = -1;
        }
    
        emit remoteControlRSSIChanged(channels.rssi);
        emit rcChannelsChanged(channelCount, pwmValues);
    }
    
    
    bool Vehicle::_containsLink(LinkInterface* link)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        return _links.contains(link);
    
    }
    
    void Vehicle::_addLink(LinkInterface* link)
    {
        if (!_containsLink(link)) {
    
    Don Gagne's avatar
    Don Gagne committed
            _links += link;
    
            qCDebug(VehicleLog) << "_addLink:" << QString("%1").arg((ulong)link, 0, 16);
    
    Don Gagne's avatar
    Don Gagne committed
            connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkInactive, this, &Vehicle::_linkInactiveOrDeleted);
            connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDeleted, this, &Vehicle::_linkInactiveOrDeleted);
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_linkInactiveOrDeleted(LinkInterface* link)
    
    Don Gagne's avatar
    Don Gagne committed
        qCDebug(VehicleLog) << "_linkInactiveOrDeleted linkCount" << _links.count();
    
    Don Gagne's avatar
    Don Gagne committed
        _links.removeOne(link);
    
    Don Gagne's avatar
    Don Gagne committed
        if (_links.count() == 0 && !_allLinksInactiveSent) {
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(VehicleLog) << "All links inactive";
    
    Don Gagne's avatar
    Don Gagne committed
            // Make sure to not send this more than one time
            _allLinksInactiveSent = true;
            emit allLinksInactive(this);
    
    bool Vehicle::sendMessageOnLink(LinkInterface* link, mavlink_message_t message)
    {
        if (!link || !_links.contains(link) || !link->isConnected()) {
            return false;
        }
    
        emit _sendMessageOnLinkOnThread(link, message);
    
        return true;
    }
    
    void Vehicle::_sendMessageOnLink(LinkInterface* link, mavlink_message_t message)
    {
    
        // Make sure this is still a good link
        if (!link || !_links.contains(link) || !link->isConnected()) {
            return;
        }
    
    
        // Give the plugin a chance to adjust
    
    Don Gagne's avatar
    Don Gagne committed
        _firmwarePlugin->adjustOutgoingMavlinkMessage(this, &message);
    
    
        static const uint8_t messageKeys[256] = MAVLINK_MESSAGE_CRCS;
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        mavlink_finalize_message_chan(&message, _mavlink->getSystemId(), _mavlink->getComponentId(), link->getMavlinkChannel(), message.len, message.len, messageKeys[message.msgid]);
    
    
        // Write message into buffer, prepending start sign
        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
        int len = mavlink_msg_to_send_buffer(buffer, &message);
    
    
        link->writeBytesSafe((const char*)buffer, len);
    
        _messagesSent++;
        emit messagesSentChanged();
    
    }
    
    /// @return Direct usb connection link to board if one, NULL if none
    LinkInterface* Vehicle::priorityLink(void)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __ios__
    
        foreach (LinkInterface* link, _links) {
            if (link->isConnected()) {
                SerialLink* pSerialLink = qobject_cast<SerialLink*>(link);
                if (pSerialLink) {
                    LinkConfiguration* pLinkConfig = pSerialLink->getLinkConfiguration();
                    if (pLinkConfig) {
                        SerialConfiguration* pSerialConfig = qobject_cast<SerialConfiguration*>(pLinkConfig);
                        if (pSerialConfig && pSerialConfig->usbDirect()) {
                            return link;
                        }
                    }
                }
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
        return _links.count() ? _links[0] : NULL;
    
    void Vehicle::setLatitude(double latitude)
    {
    
        _coordinate.setLatitude(latitude);
        emit coordinateChanged(_coordinate);
    
    }
    
    void Vehicle::setLongitude(double longitude){
    
        _coordinate.setLongitude(longitude);
        emit coordinateChanged(_coordinate);
    
    void Vehicle::_updateAttitude(UASInterface*, double roll, double pitch, double yaw, quint64)
    {
    
    Don Gagne's avatar
    Don Gagne committed
            _rollFact.setRawValue(0);
    
    Don Gagne's avatar
    Don Gagne committed
            _rollFact.setRawValue(roll * (180.0 / M_PI));
    
    Don Gagne's avatar
    Don Gagne committed
            _pitchFact.setRawValue(0);
    
    Don Gagne's avatar
    Don Gagne committed
            _pitchFact.setRawValue(pitch * (180.0 / M_PI));
    
    Don Gagne's avatar
    Don Gagne committed
            _headingFact.setRawValue(0);
    
    Don Gagne's avatar
    Don Gagne committed
            yaw = yaw * (180.0 / M_PI);
    
            if (yaw < 0) yaw += 360;
    
    Don Gagne's avatar
    Don Gagne committed
            _headingFact.setRawValue(yaw);
    
        }
    }
    
    void Vehicle::_updateAttitude(UASInterface* uas, int, double roll, double pitch, double yaw, quint64 timestamp)
    {
        _updateAttitude(uas, roll, pitch, yaw, timestamp);
    }
    
    void Vehicle::_updateSpeed(UASInterface*, double groundSpeed, double airSpeed, quint64)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        _groundSpeedFact.setRawValue(groundSpeed);
        _airSpeedFact.setRawValue(airSpeed);
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_updateAltitude(UASInterface*, double altitudeAMSL, double altitudeRelative, double climbRate, quint64)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        _altitudeAMSLFact.setRawValue(altitudeAMSL);
        _altitudeRelativeFact.setRawValue(altitudeRelative);
        _climbRateFact.setRawValue(climbRate);
    
    }
    
    void Vehicle::_updateNavigationControllerErrors(UASInterface*, double altitudeError, double speedError, double xtrackError) {
        _navigationAltitudeError   = altitudeError;
        _navigationSpeedError      = speedError;
        _navigationCrosstrackError = xtrackError;
    }
    
    void Vehicle::_updateNavigationControllerData(UASInterface *uas, float, float, float, float targetBearing, float) {
        if (_mav == uas) {
            _navigationTargetBearing = targetBearing;
        }
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    int Vehicle::motorCount(void)
    {
        switch (_vehicleType) {
        case MAV_TYPE_HELICOPTER:
            return 1;
        case MAV_TYPE_VTOL_DUOROTOR:
            return 2;
        case MAV_TYPE_TRICOPTER:
            return 3;
        case MAV_TYPE_QUADROTOR:
        case MAV_TYPE_VTOL_QUADROTOR:
            return 4;
        case MAV_TYPE_HEXAROTOR:
            return 6;
        case MAV_TYPE_OCTOROTOR:
            return 8;
        default:
            return -1;
        }
    }
    
    bool Vehicle::coaxialMotors(void)
    {
        return _firmwarePlugin->multiRotorCoaxialMotors(this);
    }
    
    bool Vehicle::xConfigMotors(void)
    {
        return _firmwarePlugin->multiRotorXConfig(this);
    }
    
    
    /*
     * Internal
     */
    
    void Vehicle::_checkUpdate()
    {
        // Update current location
        if(_mav) {
    
            if(latitude() != _mav->getLatitude()) {
                setLatitude(_mav->getLatitude());
    
            if(longitude() != _mav->getLongitude()) {
                setLongitude(_mav->getLongitude());
    
            }
        }
    }
    
    QString Vehicle::getMavIconColor()
    {
        // TODO: Not using because not only the colors are ghastly, it doesn't respect dark/light palette
        if(_mav)
            return _mav->getColor().name();
        else
            return QString("black");
    }
    
    
    dogmaphobic's avatar
    dogmaphobic committed
    QString Vehicle::formatedMessages()
    {
        QString messages;
        foreach(UASMessage* message, qgcApp()->toolbox()->uasMessageHandler()->messages()) {
            messages += message->getFormatedText();
        }
        return messages;
    }
    
    
    dogmaphobic's avatar
    dogmaphobic committed
    void Vehicle::clearMessages()
    {
        qgcApp()->toolbox()->uasMessageHandler()->clearMessages();
    }
    
    
    dogmaphobic's avatar
    dogmaphobic committed
    void Vehicle::_handletextMessageReceived(UASMessage* message)
    {
        if(message)
        {
            _formatedMessage = message->getFormatedText();
            emit formatedMessageChanged();
        }
    }
    
    
    void Vehicle::_updateState(UASInterface*, QString name, QString)
    {