Skip to content
Snippets Groups Projects
Vehicle.cc 137 KiB
Newer Older
  • Learn to ignore specific revisions
  •     settings.beginGroup(QString(_settingsGroup).arg(_id));
    
        settings.setValue(_joystickModeSettingsKey, _joystickMode);
    
    
        // The joystick enabled setting should only be changed if a joystick is present
        // since the checkbox can only be clicked if one is present
    
        if (_toolbox->joystickManager()->joysticks().count()) {
    
            settings.setValue(_joystickEnabledSettingsKey, _joystickEnabled);
        }
    
    }
    
    int Vehicle::joystickMode(void)
    {
        return _joystickMode;
    }
    
    void Vehicle::setJoystickMode(int mode)
    {
        if (mode < 0 || mode >= JoystickModeMax) {
            qCWarning(VehicleLog) << "Invalid joystick mode" << mode;
            return;
        }
    
        _joystickMode = (JoystickMode_t)mode;
        _saveSettings();
        emit joystickModeChanged(mode);
    }
    
    QStringList Vehicle::joystickModes(void)
    {
        QStringList list;
    
        list << "Normal" << "Attitude" << "Position" << "Force" << "Velocity";
    
    
    bool Vehicle::joystickEnabled(void)
    {
        return _joystickEnabled;
    }
    
    void Vehicle::setJoystickEnabled(bool enabled)
    {
    
        _saveSettings();
    
        emit joystickEnabledChanged(_joystickEnabled);
    
    }
    
    void Vehicle::_startJoystick(bool start)
    {
    
        Joystick* joystick = _joystickManager->activeJoystick();
    
        if (joystick) {
            if (start) {
    
            } else {
                joystick->stopPolling();
            }
        }
    }
    
    bool Vehicle::active(void)
    {
        return _active;
    }
    
    void Vehicle::setActive(bool active)
    {
    
        if (_active != active) {
            _active = active;
    
            emit activeChanged(_active);
        }
    
    QGeoCoordinate Vehicle::homePosition(void)
    {
        return _homePosition;
    }
    
    Don Gagne's avatar
    Don Gagne committed
    
    void Vehicle::setArmed(bool armed)
    {
        // We specifically use COMMAND_LONG:MAV_CMD_COMPONENT_ARM_DISARM since it is supported by more flight stacks.
    
        sendMavCommand(_defaultComponentId,
    
                       MAV_CMD_COMPONENT_ARM_DISARM,
                       true,    // show error if fails
                       armed ? 1.0f : 0.0f);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    bool Vehicle::flightModeSetAvailable(void)
    {
    
        return _firmwarePlugin->isCapable(this, FirmwarePlugin::SetFlightModeCapability);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    QStringList Vehicle::flightModes(void)
    {
    
    Daniel Agar's avatar
    Daniel Agar committed
        return _firmwarePlugin->flightModes(this);
    
    Don Gagne's avatar
    Don Gagne committed
    QString Vehicle::flightMode(void) const
    
    Don Gagne's avatar
    Don Gagne committed
    {
        return _firmwarePlugin->flightMode(_base_mode, _custom_mode);
    }
    
    void Vehicle::setFlightMode(const QString& flightMode)
    {
        uint8_t     base_mode;
        uint32_t    custom_mode;
    
        if (_firmwarePlugin->setFlightMode(flightMode, &base_mode, &custom_mode)) {
            // setFlightMode will only set MAV_MODE_FLAG_CUSTOM_MODE_ENABLED in base_mode, we need to move back in the existing
            // states.
            uint8_t newBaseMode = _base_mode & ~MAV_MODE_FLAG_DECODE_POSITION_CUSTOM_MODE;
            newBaseMode |= base_mode;
    
            mavlink_message_t msg;
    
            mavlink_msg_set_mode_pack_chan(_mavlink->getSystemId(),
                                           _mavlink->getComponentId(),
                                           priorityLink()->mavlinkChannel(),
                                           &msg,
                                           id(),
                                           newBaseMode,
                                           custom_mode);
            sendMessageOnLink(priorityLink(), msg);
    
    Don Gagne's avatar
    Don Gagne committed
        } else {
    
    Don Gagne's avatar
    Don Gagne committed
            qWarning() << "FirmwarePlugin::setFlightMode failed, flightMode:" << flightMode;
    
    QString Vehicle::priorityLinkName(void) const
    {
    
        if (_priorityLink) {
          return _priorityLink->getName();
        }
    
        return "none";
    
    QVariantList Vehicle::links(void) const {
        QVariantList ret;
    
        foreach( const auto &item, _links )
            ret << QVariant::fromValue(item);
    
        return ret;
    }
    
    
    void Vehicle::setPriorityLinkByName(const QString& priorityLinkName)
    {
    
        if (priorityLinkName == _priorityLink->getName()) {
            // The link did not change
            return;
        }
    
        LinkInterface* newPriorityLink = NULL;
    
    
        for (int i=0; i<_links.count(); i++) {
            if (_links[i]->getName() == priorityLinkName) {
                newPriorityLink = _links[i];
            }
        }
    
        if (newPriorityLink) {
    
            _priorityLink = _toolbox->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink);
    
            _updateHighLatencyLink(true);
    
            emit priorityLinkNameChanged(_priorityLink->getName());
    
            _linkActiveChanged(_priorityLink.data(), _priorityLink->link_active(_id), _id);
    
    Don Gagne's avatar
    Don Gagne committed
    bool Vehicle::hilMode(void)
    {
        return _base_mode & MAV_MODE_FLAG_HIL_ENABLED;
    }
    
    void Vehicle::setHilMode(bool hilMode)
    {
        mavlink_message_t msg;
    
        uint8_t newBaseMode = _base_mode & ~MAV_MODE_FLAG_DECODE_POSITION_HIL;
        if (hilMode) {
            newBaseMode |= MAV_MODE_FLAG_HIL_ENABLED;
        }
    
    
        mavlink_msg_set_mode_pack_chan(_mavlink->getSystemId(),
                                       _mavlink->getComponentId(),
                                       priorityLink()->mavlinkChannel(),
                                       &msg,
                                       id(),
                                       newBaseMode,
                                       _custom_mode);
        sendMessageOnLink(priorityLink(), msg);
    
    void Vehicle::requestDataStream(MAV_DATA_STREAM stream, uint16_t rate, bool sendMultiple)
    
    {
        mavlink_message_t               msg;
        mavlink_request_data_stream_t   dataStream;
    
    Don Gagne's avatar
    Don Gagne committed
        memset(&dataStream, 0, sizeof(dataStream));
    
    
        dataStream.req_stream_id = stream;
        dataStream.req_message_rate = rate;
        dataStream.start_stop = 1;  // start
        dataStream.target_system = id();
    
        dataStream.target_component = _defaultComponentId;
    
        mavlink_msg_request_data_stream_encode_chan(_mavlink->getSystemId(),
                                                    _mavlink->getComponentId(),
                                                    priorityLink()->mavlinkChannel(),
                                                    &msg,
                                                    &dataStream);
    
        if (sendMultiple) {
            // We use sendMessageMultiple since we really want these to make it to the vehicle
            sendMessageMultiple(msg);
        } else {
    
            sendMessageOnLink(priorityLink(), msg);
    
    }
    
    void Vehicle::_sendMessageMultipleNext(void)
    {
        if (_nextSendMessageMultipleIndex < _sendMessageMultipleList.count()) {
            qCDebug(VehicleLog) << "_sendMessageMultipleNext:" << _sendMessageMultipleList[_nextSendMessageMultipleIndex].message.msgid;
    
            sendMessageOnLink(priorityLink(), _sendMessageMultipleList[_nextSendMessageMultipleIndex].message);
    
            if (--_sendMessageMultipleList[_nextSendMessageMultipleIndex].retryCount <= 0) {
                _sendMessageMultipleList.removeAt(_nextSendMessageMultipleIndex);
            } else {
                _nextSendMessageMultipleIndex++;
            }
        }
    
        if (_nextSendMessageMultipleIndex >= _sendMessageMultipleList.count()) {
            _nextSendMessageMultipleIndex = 0;
        }
    }
    
    void Vehicle::sendMessageMultiple(mavlink_message_t message)
    {
        SendMessageMultipleInfo_t   info;
    
        info.message =      message;
        info.retryCount =   _sendMessageMultipleRetries;
    
        _sendMessageMultipleList.append(info);
    }
    
    
    void Vehicle::_missionManagerError(int errorCode, const QString& errorMsg)
    {
        Q_UNUSED(errorCode);
    
        qgcApp()->showMessage(tr("Mission transfer failed. Retry transfer. Error: %1").arg(errorMsg));
    
    void Vehicle::_geoFenceManagerError(int errorCode, const QString& errorMsg)
    {
        Q_UNUSED(errorCode);
    
        qgcApp()->showMessage(tr("GeoFence transfer failed. Retry transfer. Error: %1").arg(errorMsg));
    
    }
    
    void Vehicle::_rallyPointManagerError(int errorCode, const QString& errorMsg)
    {
        Q_UNUSED(errorCode);
    
        qgcApp()->showMessage(tr("Rally Point transfer failed. Retry transfer. Error: %1").arg(errorMsg));
    
    void Vehicle::_addNewMapTrajectoryPoint(void)
    {
        if (_mapTrajectoryHaveFirstCoordinate) {
    
            // Keep three minutes of trajectory on mobile due to perf impact of lines
    #ifdef __mobile__
    
            if (_mapTrajectoryList.count() * _mapTrajectoryMsecsBetweenPoints > 3 * 1000 * 60) {
                _mapTrajectoryList.removeAt(0)->deleteLater();
            }
    
    #endif
    
            _mapTrajectoryList.append(new CoordinateVector(_mapTrajectoryLastCoordinate, _coordinate, this));
    
            _flightDistanceFact.setRawValue(_flightDistanceFact.rawValue().toDouble() + _mapTrajectoryLastCoordinate.distanceTo(_coordinate));
    
        }
        _mapTrajectoryHaveFirstCoordinate = true;
    
        _mapTrajectoryLastCoordinate = _coordinate;
    
        _flightTimeFact.setRawValue((double)_flightTimer.elapsed() / 1000.0);
    
    void Vehicle::_clearTrajectoryPoints(void)
    {
        _mapTrajectoryList.clearAndDeleteContents();
    }
    
    void Vehicle::_clearCameraTriggerPoints(void)
    {
        _cameraTriggerPoints.clearAndDeleteContents();
    
    }
    
    void Vehicle::_mapTrajectoryStart(void)
    {
        _mapTrajectoryHaveFirstCoordinate = false;
    
        _clearTrajectoryPoints();
    
        _mapTrajectoryTimer.start();
    
        _flightTimer.start();
    
        _flightDistanceFact.setRawValue(0);
    
        _flightTimeFact.setRawValue(0);
    
    }
    
    void Vehicle::_mapTrajectoryStop()
    {
        _mapTrajectoryTimer.stop();
    }
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    void Vehicle::_startPlanRequest(void)
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        if (_missionManagerInitialRequestSent) {
            return;
        }
    
        if (_parameterManager->parametersReady() && _vehicleCapabilitiesKnown) {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
            qCDebug(VehicleLog) << "_startPlanRequest";
    
            _missionManagerInitialRequestSent = true;
    
            if (_settingsManager->appSettings()->autoLoadMissions()->rawValue().toBool()) {
                QString missionAutoLoadDirPath = _settingsManager->appSettings()->missionSavePath();
                if (!missionAutoLoadDirPath.isEmpty()) {
                    QDir missionAutoLoadDir(missionAutoLoadDirPath);
    
                    QString autoloadFilename = missionAutoLoadDir.absoluteFilePath(tr("AutoLoad%1.%2").arg(_id).arg(AppSettings::planFileExtension));
                    if (QFile(autoloadFilename).exists()) {
    
                        _initialPlanRequestComplete = true; // We aren't going to load from the vehicle, so we are done
    
                        PlanMasterController::sendPlanToVehicle(this, autoloadFilename);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
            _missionManager->loadFromVehicle();
    
        } else {
            if (!_parameterManager->parametersReady()) {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                qCDebug(VehicleLog) << "Delaying _startPlanRequest due to parameters not ready";
    
            } else if (!_vehicleCapabilitiesKnown) {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                qCDebug(VehicleLog) << "Delaying _startPlanRequest due to vehicle capabilities not known";
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    void Vehicle::_missionLoadComplete(void)
    {
        // After the initial mission request completes we ask for the geofence
        if (!_geoFenceManagerInitialRequestSent) {
            _geoFenceManagerInitialRequestSent = true;
    
            if (_geoFenceManager->supported()) {
                qCDebug(VehicleLog) << "_missionLoadComplete requesting GeoFence";
                _geoFenceManager->loadFromVehicle();
            } else {
                qCDebug(VehicleLog) << "_missionLoadComplete GeoFence not supported skipping";
                _geoFenceLoadComplete();
            }
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        }
    }
    
    void Vehicle::_geoFenceLoadComplete(void)
    {
        // After geofence request completes we ask for the rally points
        if (!_rallyPointManagerInitialRequestSent) {
            _rallyPointManagerInitialRequestSent = true;
    
            if (_rallyPointManager->supported()) {
                qCDebug(VehicleLog) << "_missionLoadComplete requesting Rally Points";
                _rallyPointManager->loadFromVehicle();
            } else {
                qCDebug(VehicleLog) << "_missionLoadComplete Rally Points not supported skipping";
                _rallyPointLoadComplete();
            }
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        }
    }
    
    void Vehicle::_rallyPointLoadComplete(void)
    {
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        qCDebug(VehicleLog) << "_missionLoadComplete _initialPlanRequestComplete = true";
    
        if (!_initialPlanRequestComplete) {
            _initialPlanRequestComplete = true;
    
    Don Gagne's avatar
    Don Gagne committed
            emit initialPlanRequestCompleteChanged(true);
    
    void Vehicle::_parametersReady(bool parametersReady)
    {
    
        // Try to set current unix time to the vehicle
        _sendQGCTimeToVehicle();
        // Send time twice, more likely to get to the vehicle on a noisy link
        _sendQGCTimeToVehicle();
    
        if (parametersReady) {
    
            _setupAutoDisarmSignalling();
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
            _startPlanRequest();
    
    void Vehicle::_sendQGCTimeToVehicle(void)
    {
        mavlink_message_t       msg;
        mavlink_system_time_t   cmd;
    
        // Timestamp of the master clock in microseconds since UNIX epoch.
        cmd.time_unix_usec = QDateTime::currentDateTime().currentMSecsSinceEpoch()*1000;
        // Timestamp of the component clock since boot time in milliseconds (Not necessary).
        cmd.time_boot_ms = 0;
        mavlink_msg_system_time_encode_chan(_mavlink->getSystemId(),
                                        _mavlink->getComponentId(),
                                        priorityLink()->mavlinkChannel(),
                                        &msg,
                                        &cmd);
    
        sendMessageOnLink(priorityLink(), msg);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::disconnectInactiveVehicle(void)
    
    Don Gagne's avatar
    Don Gagne committed
        // Vehicle is no longer communicating with us, disconnect all links
    
        LinkManager* linkMgr = _toolbox->linkManager();
    
        for (int i=0; i<_links.count(); i++) {
    
            // FIXME: This linkInUse check is a hack fix for multiple vehicles on the same link.
            // The real fix requires significant restructuring which will come later.
    
            if (!_toolbox->multiVehicleManager()->linkInUse(_links[i], this)) {
    
                linkMgr->disconnectLink(_links[i]);
            }
    
        emit linksChanged();
    
    dogmaphobic's avatar
    dogmaphobic committed
    void Vehicle::_imageReady(UASInterface*)
    {
        if(_uas)
        {
            QImage img = _uas->getImage();
    
            _toolbox->imageProvider()->setImage(&img, _id);
    
    dogmaphobic's avatar
    dogmaphobic committed
            _flowImageIndex++;
            emit flowImageIndexChanged();
        }
    }
    
    Don Gagne's avatar
    Don Gagne committed
    
    void Vehicle::_remoteControlRSSIChanged(uint8_t rssi)
    {
    
        if (_rcRSSIstore < 0 || _rcRSSIstore > 100) {
            _rcRSSIstore = rssi;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        // Low pass to git rid of jitter
        _rcRSSIstore = (_rcRSSIstore * 0.9f) + ((float)rssi * 0.1);
        uint8_t filteredRSSI = (uint8_t)ceil(_rcRSSIstore);
        if(_rcRSSIstore < 0.1) {
            filteredRSSI = 0;
        }
        if(_rcRSSI != filteredRSSI) {
            _rcRSSI = filteredRSSI;
            emit rcRSSIChanged(_rcRSSI);
        }
    }
    
    Don Gagne's avatar
    Don Gagne committed
    
    void Vehicle::virtualTabletJoystickValue(double roll, double pitch, double yaw, double thrust)
    {
    
        // The following if statement prevents the virtualTabletJoystick from sending values if the standard joystick is enabled
        if ( !_joystickEnabled ) {
            _uas->setExternalControlSetpoint(roll, pitch, yaw, thrust, 0, JoystickModeRC);
        }
    
    
    void Vehicle::setConnectionLostEnabled(bool connectionLostEnabled)
    {
        if (_connectionLostEnabled != connectionLostEnabled) {
            _connectionLostEnabled = connectionLostEnabled;
            emit connectionLostEnabledChanged(_connectionLostEnabled);
        }
    }
    
    
    void Vehicle::_linkActiveChanged(LinkInterface *link, bool active, int vehicleID)
    
        // only continue if the vehicle id is correct
        if (vehicleID != _id) {
            return;
        }
    
    
        emit linksPropertiesChanged();
    
    
        if (link == _priorityLink) {
            if (active && _connectionLost) {
                // communication to priority link regained
                _connectionLost = false;
    
                emit connectionLostChanged(false);
    
    acfloria's avatar
    acfloria committed
                qgcApp()->showMessage((tr("%1 communication to %2 link %3 regained")).arg(_vehicleIdSpeech()).arg((_links.count() > 1) ? "priority" : "").arg(link->getName()));
    
                if (_priorityLink->highLatency()) {
                    _setMaxProtoVersion(100);
                } else {
                    // Re-negotiate protocol version for the link
                    sendMavCommand(MAV_COMP_ID_ALL,                         // Don't know default component id yet.
                                   MAV_CMD_REQUEST_PROTOCOL_VERSION,
                                   false,                                   // No error shown if fails
                                   1);                                     // Request protocol version
                }
    
            } else if (!active && !_connectionLost) {
                // communication to priority link lost
    
    acfloria's avatar
    acfloria committed
                qgcApp()->showMessage((tr("%1 communication to %2 link %3 lost")).arg(_vehicleIdSpeech()).arg((_links.count() > 1) ? "priority" : "").arg(link->getName()));
    
    acfloria's avatar
    acfloria committed
                _updatePriorityLink(false /* updateActive */, true /* sendCommand */);
    
                if (link == _priorityLink) {
    
                    _say(QString(tr("%1 communication lost")).arg(_vehicleIdSpeech()));
                    qgcApp()->showMessage((tr("%1 communication lost")).arg(_vehicleIdSpeech()));
    
                    if (_connectionLostEnabled) {
                        _connectionLost = true;
                        _heardFrom = false;
                        _maxProtoVersion = 0;
                        emit connectionLostChanged(true);
    
                        if (_autoDisconnect) {
                            // Reset link state
                            for (int i = 0; i < _links.length(); i++) {
                                _mavlink->resetMetadataForLink(_links.at(i));
                            }
    
                            disconnectInactiveVehicle();
                        }
                    }
                }
            }
        } else {
    
            qgcApp()->showMessage((tr("%1 communication to auxiliary link %2 %3")).arg(_vehicleIdSpeech()).arg(link->getName()).arg(active ? "regained" : "lost"));
    
    acfloria's avatar
    acfloria committed
            _updatePriorityLink(false /* updateActive */, true /* sendCommand */);
    
    void Vehicle::_say(const QString& text)
    
        _toolbox->audioOutput()->say(text.toLower());
    
    
    bool Vehicle::fixedWing(void) const
    {
    
        return QGCMAVLink::isFixedWing(vehicleType());
    
    Don Gagne's avatar
    Don Gagne committed
    bool Vehicle::rover(void) const
    {
    
        return QGCMAVLink::isRover(vehicleType());
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    
    bool Vehicle::sub(void) const
    {
    
        return QGCMAVLink::isSub(vehicleType());
    
    bool Vehicle::multiRotor(void) const
    {
    
        return QGCMAVLink::isMultiRotor(vehicleType());
    
    Don Gagne's avatar
    Don Gagne committed
    bool Vehicle::vtol(void) const
    {
    
        return _firmwarePlugin->isVtol(this);
    
    bool Vehicle::supportsThrottleModeCenterZero(void) const
    {
        return _firmwarePlugin->supportsThrottleModeCenterZero();
    }
    
    
    bool Vehicle::supportsNegativeThrust(void) const
    {
        return _firmwarePlugin->supportsNegativeThrust();
    }
    
    
    bool Vehicle::supportsRadio(void) const
    {
        return _firmwarePlugin->supportsRadio();
    }
    
    
    bool Vehicle::supportsJSButton(void) const
    {
        return _firmwarePlugin->supportsJSButton();
    }
    
    
    bool Vehicle::supportsMotorInterference(void) const
    {
        return _firmwarePlugin->supportsMotorInterference();
    }
    
    
    bool Vehicle::supportsTerrainFrame(void) const
    {
        return _firmwarePlugin->supportsTerrainFrame();
    }
    
    
    QString Vehicle::vehicleTypeName() const {
        static QMap<int, QString> typeNames = {
            { MAV_TYPE_GENERIC,         tr("Generic micro air vehicle" )},
            { MAV_TYPE_FIXED_WING,      tr("Fixed wing aircraft")},
            { MAV_TYPE_QUADROTOR,       tr("Quadrotor")},
            { MAV_TYPE_COAXIAL,         tr("Coaxial helicopter")},
            { MAV_TYPE_HELICOPTER,      tr("Normal helicopter with tail rotor.")},
            { MAV_TYPE_ANTENNA_TRACKER, tr("Ground installation")},
            { MAV_TYPE_GCS,             tr("Operator control unit / ground control station")},
            { MAV_TYPE_AIRSHIP,         tr("Airship, controlled")},
            { MAV_TYPE_FREE_BALLOON,    tr("Free balloon, uncontrolled")},
            { MAV_TYPE_ROCKET,          tr("Rocket")},
            { MAV_TYPE_GROUND_ROVER,    tr("Ground rover")},
            { MAV_TYPE_SURFACE_BOAT,    tr("Surface vessel, boat, ship")},
            { MAV_TYPE_SUBMARINE,       tr("Submarine")},
            { MAV_TYPE_HEXAROTOR,       tr("Hexarotor")},
            { MAV_TYPE_OCTOROTOR,       tr("Octorotor")},
            { MAV_TYPE_TRICOPTER,       tr("Octorotor")},
            { MAV_TYPE_FLAPPING_WING,   tr("Flapping wing")},
            { MAV_TYPE_KITE,            tr("Flapping wing")},
            { MAV_TYPE_ONBOARD_CONTROLLER, tr("Onboard companion controller")},
            { MAV_TYPE_VTOL_DUOROTOR,   tr("Two-rotor VTOL using control surfaces in vertical operation in addition. Tailsitter")},
            { MAV_TYPE_VTOL_QUADROTOR,  tr("Quad-rotor VTOL using a V-shaped quad config in vertical operation. Tailsitter")},
            { MAV_TYPE_VTOL_TILTROTOR,  tr("Tiltrotor VTOL")},
            { MAV_TYPE_VTOL_RESERVED2,  tr("VTOL reserved 2")},
            { MAV_TYPE_VTOL_RESERVED3,  tr("VTOL reserved 3")},
            { MAV_TYPE_VTOL_RESERVED4,  tr("VTOL reserved 4")},
            { MAV_TYPE_VTOL_RESERVED5,  tr("VTOL reserved 5")},
            { MAV_TYPE_GIMBAL,          tr("Onboard gimbal")},
            { MAV_TYPE_ADSB,            tr("Onboard ADSB peripheral")},
        };
        return typeNames[_vehicleType];
    }
    
    
    /// Returns the string to speak to identify the vehicle
    QString Vehicle::_vehicleIdSpeech(void)
    {
    
        if (_toolbox->multiVehicleManager()->vehicles()->count() > 1) {
    
            return QString(tr("vehicle %1")).arg(id());
    
        } else {
    
            return QString();
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::_handleFlightModeChanged(const QString& flightMode)
    
        _say(QString(tr("%1 %2 flight mode")).arg(_vehicleIdSpeech()).arg(flightMode));
    
    Don Gagne's avatar
    Don Gagne committed
        emit guidedModeChanged(_firmwarePlugin->isGuidedMode(this));
    
    }
    
    void Vehicle::_announceArmedChanged(bool armed)
    {
    
        _say(QString("%1 %2").arg(_vehicleIdSpeech()).arg(armed ? QString(tr("armed")) : QString(tr("disarmed"))));
    
    void Vehicle::_setFlying(bool flying)
    
    Don Gagne's avatar
    Don Gagne committed
    {
    
        if (_flying != flying) {
    
    Don Gagne's avatar
    Don Gagne committed
            _flying = flying;
            emit flyingChanged(flying);
        }
    }
    
    
    void Vehicle::_setLanding(bool landing)
    {
        if (armed() && _landing != landing) {
            _landing = landing;
            emit landingChanged(landing);
        }
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    bool Vehicle::guidedModeSupported(void) const
    {
    
        return _firmwarePlugin->isCapable(this, FirmwarePlugin::GuidedModeCapability);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    bool Vehicle::pauseVehicleSupported(void) const
    {
    
        return _firmwarePlugin->isCapable(this, FirmwarePlugin::PauseVehicleCapability);
    }
    
    bool Vehicle::orbitModeSupported() const
    {
        return _firmwarePlugin->isCapable(this, FirmwarePlugin::OrbitModeCapability);
    
    bool Vehicle::takeoffVehicleSupported() const
    {
        return _firmwarePlugin->isCapable(this, FirmwarePlugin::TakeoffVehicleCapability);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::guidedModeRTL(void)
    {
        if (!guidedModeSupported()) {
    
            qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
    
    Don Gagne's avatar
    Don Gagne committed
            return;
        }
        _firmwarePlugin->guidedModeRTL(this);
    }
    
    void Vehicle::guidedModeLand(void)
    {
        if (!guidedModeSupported()) {
    
            qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
    
    Don Gagne's avatar
    Don Gagne committed
            return;
        }
        _firmwarePlugin->guidedModeLand(this);
    }
    
    
    void Vehicle::guidedModeTakeoff(double altitudeRelative)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        if (!guidedModeSupported()) {
    
            qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
    
    Don Gagne's avatar
    Don Gagne committed
            return;
        }
    
        _firmwarePlugin->guidedModeTakeoff(this, altitudeRelative);
    
    double Vehicle::minimumTakeoffAltitude(void)
    {
        return _firmwarePlugin->minimumTakeoffAltitude(this);
    }
    
    
    void Vehicle::startMission(void)
    {
        _firmwarePlugin->startMission(this);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void Vehicle::guidedModeGotoLocation(const QGeoCoordinate& gotoCoord)
    {
        if (!guidedModeSupported()) {
    
            qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
    
    Don Gagne's avatar
    Don Gagne committed
            return;
        }
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
        if (!coordinate().isValid()) {
            return;
        }
        double maxDistance = 1000.0;
        if (coordinate().distanceTo(gotoCoord) > maxDistance) {
            qgcApp()->showMessage(QString("New location is too far. Must be less than %1 %2").arg(qRound(FactMetaData::metersToAppSettingsDistanceUnits(maxDistance).toDouble())).arg(FactMetaData::appSettingsDistanceUnitsString()));
            return;
        }
    
    Don Gagne's avatar
    Don Gagne committed
        _firmwarePlugin->guidedModeGotoLocation(this, gotoCoord);
    }
    
    
    void Vehicle::guidedModeChangeAltitude(double altitudeChange)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        if (!guidedModeSupported()) {
    
            qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
    
    Don Gagne's avatar
    Don Gagne committed
            return;
        }
    
        _firmwarePlugin->guidedModeChangeAltitude(this, altitudeChange);
    
    void Vehicle::guidedModeOrbit(const QGeoCoordinate& centerCoord, double radius, double velocity, double altitude)
    {
    
        if (!orbitModeSupported()) {
            qgcApp()->showMessage(QStringLiteral("Orbit mode not supported by Vehicle."));
    
            return;
        }
        _firmwarePlugin->guidedModeOrbit(this, centerCoord, radius, velocity, altitude);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::pauseVehicle(void)
    {
        if (!pauseVehicleSupported()) {
            qgcApp()->showMessage(QStringLiteral("Pause not supported by vehicle."));
            return;
        }
        _firmwarePlugin->pauseVehicle(this);
    }
    
    
    void Vehicle::abortLanding(double climbOutAltitude)
    
    {
        sendMavCommand(defaultComponentId(),
                       MAV_CMD_DO_GO_AROUND,
                       true,        // show error if fails
    
    Don Gagne's avatar
    Don Gagne committed
    bool Vehicle::guidedMode(void) const
    {
        return _firmwarePlugin->isGuidedMode(this);
    }
    
    void Vehicle::setGuidedMode(bool guidedMode)
    {
        return _firmwarePlugin->setGuidedMode(this, guidedMode);
    }
    
    void Vehicle::emergencyStop(void)
    {
    
        sendMavCommand(_defaultComponentId,
    
                       MAV_CMD_COMPONENT_ARM_DISARM,
                       true,        // show error if fails
                       0.0f,
                       21196.0f);  // Magic number for emergency stop
    
    void Vehicle::setCurrentMissionSequence(int seq)
    {
        if (!_firmwarePlugin->sendHomePositionToVehicle()) {
            seq--;
        }
        mavlink_message_t msg;
    
        mavlink_msg_mission_set_current_pack_chan(_mavlink->getSystemId(),
                                                  _mavlink->getComponentId(),
                                                  priorityLink()->mavlinkChannel(),
                                                  &msg,
                                                  id(),
                                                  _compID,
                                                  seq);
        sendMessageOnLink(priorityLink(), msg);
    
    void Vehicle::sendMavCommand(int component, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
    
        MavCommandQueueEntry_t entry;
    
        entry.component = component;
        entry.command = command;
        entry.showError = showError;
        entry.rgParam[0] = param1;
        entry.rgParam[1] = param2;
        entry.rgParam[2] = param3;
        entry.rgParam[3] = param4;
        entry.rgParam[4] = param5;
        entry.rgParam[5] = param6;
        entry.rgParam[6] = param7;
    
        _mavCommandQueue.append(entry);
    
        if (_mavCommandQueue.count() == 1) {
            _mavCommandRetryCount = 0;
            _sendMavCommandAgain();
        }
    }
    
    void Vehicle::_sendMavCommandAgain(void)
    {
    
        if(!_mavCommandQueue.size()) {
            qWarning() << "Command resend with no commands in queue";
            _mavCommandAckTimer.stop();
            return;
        }
    
    
        MavCommandQueueEntry_t& queuedCommand = _mavCommandQueue[0];
    
        if (_mavCommandRetryCount++ > _mavCommandMaxRetryCount) {
    
            if (queuedCommand.command == MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES) {
                // We aren't going to get a response back for capabilities, so stop waiting for it before we ask for mission items
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                qCDebug(VehicleLog) << "Vehicle failed to responded to MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES. Setting no capabilities. Starting Plan request.";
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                _setCapabilities(0);
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                _startPlanRequest();
    
            if (queuedCommand.command == MAV_CMD_REQUEST_PROTOCOL_VERSION) {
    
                // We aren't going to get a response back for the protocol version, so assume v1 is all we can do.
                // If the max protocol version is uninitialized, fall back to v1.
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                qCDebug(VehicleLog) << "Vehicle failed to responded to MAV_CMD_REQUEST_PROTOCOL_VERSION. Starting Plan request.";
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                    qCDebug(VehicleLog) << "Setting _maxProtoVersion to 100 since not yet set.";
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                } else {
                    qCDebug(VehicleLog) << "Leaving _maxProtoVersion at current value" << _maxProtoVersion;
    
            emit mavCommandResult(_id, queuedCommand.component, queuedCommand.command, MAV_RESULT_FAILED, true /* noResponsefromVehicle */);
            if (queuedCommand.showError) {
    
                qgcApp()->showMessage(tr("Vehicle did not respond to command: %1").arg(_toolbox->missionCommandTree()->friendlyName(queuedCommand.command)));
    
            }
            _mavCommandQueue.removeFirst();
            _sendNextQueuedMavCommand();
            return;
        }
    
    
        if (_mavCommandRetryCount > 1) {
    
            // We always let AUTOPILOT_CAPABILITIES go through multiple times even if we don't get acks. This is because
            // we really need to get capabilities and version info back over a lossy link.
            if (queuedCommand.command != MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES) {
                if (px4Firmware()) {
                    // Older PX4 firmwares are inconsistent with repect to sending back an Ack from a COMMAND_LONG, hence we can't support retry logic for it.
                    if (_firmwareMajorVersion != versionNotSetValue) {
                        // If no version set assume lastest master dev build, so acks are suppored
                        if (_firmwareMajorVersion <= 1 && _firmwareMinorVersion <= 5 && _firmwarePatchVersion <= 3) {
                            // Acks not supported in this version
                            return;
                        }
                    }
                } else {
                    if (queuedCommand.command == MAV_CMD_START_RX_PAIR) {
                        // The implementation of this command comes from the IO layer and is shared across stacks. So for other firmwares
                        // we aren't really sure whether they are correct or not.
                        return;
                    }
                }
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
            qCDebug(VehicleLog) << "Vehicle::_sendMavCommandAgain retrying command:_mavCommandRetryCount" << queuedCommand.command << _mavCommandRetryCount;
    
        _mavCommandAckTimer.start();
    
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_message_t       msg;
        mavlink_command_long_t  cmd;
    
    
    Don Gagne's avatar
    Don Gagne committed
        memset(&cmd, 0, sizeof(cmd));
    
        cmd.command = queuedCommand.command;
    
    Don Gagne's avatar
    Don Gagne committed
        cmd.confirmation = 0;
    
        cmd.param1 = queuedCommand.rgParam[0];
        cmd.param2 = queuedCommand.rgParam[1];
        cmd.param3 = queuedCommand.rgParam[2];
        cmd.param4 = queuedCommand.rgParam[3];
        cmd.param5 = queuedCommand.rgParam[4];
        cmd.param6 = queuedCommand.rgParam[5];
        cmd.param7 = queuedCommand.rgParam[6];
        cmd.target_system = _id;
        cmd.target_component = queuedCommand.component;
    
        mavlink_msg_command_long_encode_chan(_mavlink->getSystemId(),
                                             _mavlink->getComponentId(),
                                             priorityLink()->mavlinkChannel(),
                                             &msg,
                                             &cmd);
    
        sendMessageOnLink(priorityLink(), msg);
    
    void Vehicle::_sendNextQueuedMavCommand(void)
    {
        if (_mavCommandQueue.count()) {
            _mavCommandRetryCount = 0;
            _sendMavCommandAgain();
        }
    }
    
    
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
    void Vehicle::_handleCommandAck(mavlink_message_t& message)
    {
        bool showError = false;
    
        mavlink_command_ack_t ack;
        mavlink_msg_command_ack_decode(&message, &ack);
    
        if (ack.command == MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES && ack.result != MAV_RESULT_ACCEPTED) {
            // We aren't going to get a response back for capabilities, so stop waiting for it before we ask for mission items
            qCDebug(VehicleLog) << QStringLiteral("Vehicle responded to MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES with error(%1). Setting no capabilities. Starting Plan request.").arg(ack.result);
            _setCapabilities(0);
        }
    
        if (ack.command == MAV_CMD_REQUEST_PROTOCOL_VERSION && ack.result != MAV_RESULT_ACCEPTED) {
            // The autopilot does not understand the request and consequently is likely handling only
            // MAVLink 1
            qCDebug(VehicleLog) << QStringLiteral("Vehicle responded to MAV_CMD_REQUEST_PROTOCOL_VERSION with error(%1).").arg(ack.result);
            if (_maxProtoVersion == 0) {
                qCDebug(VehicleLog) << "Setting _maxProtoVersion to 100 since not yet set.";
                _setMaxProtoVersion(100);
            } else {
                qCDebug(VehicleLog) << "Leaving _maxProtoVersion at current value" << _maxProtoVersion;
            }
            // FIXME: Is this missing here. I believe it is a bug. Debug to verify. May need to go into Stable.
            //_startPlanRequest();
        }
    
        if (_mavCommandQueue.count() && ack.command == _mavCommandQueue[0].command) {
            _mavCommandAckTimer.stop();
            showError = _mavCommandQueue[0].showError;
            _mavCommandQueue.removeFirst();
        }
    
        emit mavCommandResult(_id, message.compid, ack.command, ack.result, false /* noResponsefromVehicle */);
    
        if (showError) {
            QString commandName = _toolbox->missionCommandTree()->friendlyName((MAV_CMD)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;
            }
        }
    
        _sendNextQueuedMavCommand();
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void Vehicle::setPrearmError(const QString& prearmError)
    {
        _prearmError = prearmError;
        emit prearmErrorChanged(_prearmError);
        if (!_prearmError.isEmpty()) {
            _prearmErrorTimer.start();
        }
    }