Skip to content
Snippets Groups Projects
MockLink.cc 53.9 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.
     *
     ****************************************************************************/
    
    Don Gagne's avatar
    Don Gagne committed
    
    #include "MockLink.h"
    
    #include "QGCLoggingCategory.h"
    
    #include "QGCApplication.h"
    
    
    #ifdef UNITTEST_BUILD
    
    #include "UnitTest.h"
    
    Don Gagne's avatar
    Don Gagne committed
    
    #include <QTimer>
    #include <QDebug>
    #include <QFile>
    
    #include <string.h>
    
    
    // FIXME: Hack to work around clean headers
    #include "FirmwarePlugin/PX4/px4_custom_mode.h"
    
    Daniel Agar's avatar
    Daniel Agar committed
    
    
    QGC_LOGGING_CATEGORY(MockLinkLog, "MockLinkLog")
    
    Don Gagne's avatar
    Don Gagne committed
    QGC_LOGGING_CATEGORY(MockLinkVerboseLog, "MockLinkVerboseLog")
    
    Don Gagne's avatar
    Don Gagne committed
    /// @file
    ///     @brief Mock implementation of a Link.
    ///
    ///     @author Don Gagne <don@thegagnes.com>
    
    
    // Vehicle position is set close to default Gazebo vehicle location. This allows for multi-vehicle
    // testing of a gazebo vehicle and a mocklink vehicle
    
    #if 1
    double      MockLink::_defaultVehicleLatitude =     47.397;
    double      MockLink::_defaultVehicleLongitude =    8.5455;
    double      MockLink::_defaultVehicleAltitude =     488.056;
    #else
    double      MockLink::_defaultVehicleLatitude =     47.6333022928789;
    double      MockLink::_defaultVehicleLongitude =    -122.08833157994995;
    double      MockLink::_defaultVehicleAltitude =     19.0;
    #endif
    
    int         MockLink::_nextVehicleSystemId =        128;
    const char* MockLink::_failParam =                  "COM_FLTMODE6";
    
    const char* MockConfiguration::_firmwareTypeKey =   "FirmwareType";
    
    const char* MockConfiguration::_vehicleTypeKey =    "VehicleType";
    
    const char* MockConfiguration::_sendStatusTextKey = "SendStatusText";
    
    const char* MockConfiguration::_highLatencyKey =    "HighLatency";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* MockConfiguration::_failureModeKey =    "FailureMode";
    
    MockLink::MockLink(SharedLinkConfigurationPointer& config)
    
        : LinkInterface                         (config)
        , _missionItemHandler                   (this, qgcApp()->toolbox()->mavlinkProtocol())
        , _name                                 ("MockLink")
        , _connected                            (false)
        , _mavlinkChannel                       (0)
        , _vehicleSystemId                      (_nextVehicleSystemId++)
        , _vehicleComponentId                   (MAV_COMP_ID_AUTOPILOT1)
        , _inNSH                                (false)
        , _mavlinkStarted                       (true)
        , _mavBaseMode                          (MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_CUSTOM_MODE_ENABLED)
        , _mavState                             (MAV_STATE_STANDBY)
        , _firmwareType                         (MAV_AUTOPILOT_PX4)
        , _vehicleType                          (MAV_TYPE_QUADROTOR)
        , _vehicleLatitude                      (_defaultVehicleLatitude + ((_vehicleSystemId - 128) * 0.0001))     // Slight offset for each vehicle
        , _vehicleLongitude                     (_defaultVehicleLongitude + ((_vehicleSystemId - 128) * 0.0001))
        , _vehicleAltitude                      (_defaultVehicleAltitude)
        , _fileServer                           (NULL)
        , _sendStatusText                       (false)
        , _apmSendHomePositionOnEmptyList       (false)
        , _failureMode                          (MockConfiguration::FailNone)
        , _sendHomePositionDelayCount           (10)    // No home position for 4 seconds
        , _sendGPSPositionDelayCount            (100)   // No gps lock for 5 seconds
    
    Don Gagne's avatar
    Don Gagne committed
        , _currentParamRequestListComponentIndex(-1)
    
        , _currentParamRequestListParamIndex    (-1)
        , _logDownloadCurrentOffset             (0)
        , _logDownloadBytesRemaining            (0)
    
        , _adsbAngle                            (0)
    
        MockConfiguration* mockConfig = qobject_cast<MockConfiguration*>(_config.data());
        _firmwareType = mockConfig->firmwareType();
        _vehicleType = mockConfig->vehicleType();
        _sendStatusText = mockConfig->sendStatusText();
    
        _highLatency = mockConfig->highLatency();
    
        _failureMode = mockConfig->failureMode();
    
    Don Gagne's avatar
    Don Gagne committed
        union px4_custom_mode   px4_cm;
    
        px4_cm.data = 0;
        px4_cm.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL;
        _mavCustomMode = px4_cm.data;
    
    
        _fileServer = new MockLinkFileServer(_vehicleSystemId, _vehicleComponentId, this);
        Q_CHECK_PTR(_fileServer);
    
    Don Gagne's avatar
    Don Gagne committed
        moveToThread(this);
    
    Don Gagne's avatar
    Don Gagne committed
        _loadParams();
    
    
        _adsbVehicleCoordinate = QGeoCoordinate(_vehicleLatitude, _vehicleLongitude).atDistanceAndAzimuth(1000, _adsbAngle);
        _adsbVehicleCoordinate.setAltitude(100);
    
        _runningTime.start();
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    MockLink::~MockLink(void)
    {
    
        if (!_logDownloadFilename.isEmpty()) {
            QFile::remove(_logDownloadFilename);
        }
    
    bool MockLink::_connect(void)
    
        if (!_connected) {
            _connected = true;
    
            _mavlinkChannel = qgcApp()->toolbox()->linkManager()->_reserveMavlinkChannel();
            if (_mavlinkChannel == 0) {
                qWarning() << "No mavlink channels available";
                return false;
            }
            // MockLinks use Mavlink 2.0
            mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(_mavlinkChannel);
            mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
    
            start();
            emit connected();
        }
    
    Don Gagne's avatar
    Don Gagne committed
        return true;
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void MockLink::_disconnect(void)
    
        if (_connected) {
    
            if (_mavlinkChannel != 0) {
                qgcApp()->toolbox()->linkManager()->_freeMavlinkChannel(_mavlinkChannel);
            }
    
            _connected = false;
    
    Daniel Agar's avatar
    Daniel Agar committed
            quit();
            wait();
    
            emit disconnected();
        }
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void MockLink::run(void)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        QTimer  timer1HzTasks;
        QTimer  timer10HzTasks;
        QTimer  timer500HzTasks;
    
    Don Gagne's avatar
    Don Gagne committed
        QObject::connect(&timer1HzTasks,  &QTimer::timeout, this, &MockLink::_run1HzTasks);
        QObject::connect(&timer10HzTasks, &QTimer::timeout, this, &MockLink::_run10HzTasks);
        QObject::connect(&timer500HzTasks, &QTimer::timeout, this, &MockLink::_run500HzTasks);
    
    Don Gagne's avatar
    Don Gagne committed
        timer1HzTasks.start(1000);
        timer10HzTasks.start(100);
        timer500HzTasks.start(2);
    
    Don Gagne's avatar
    Don Gagne committed
        exec();
    
    Don Gagne's avatar
    Don Gagne committed
        QObject::disconnect(&timer1HzTasks,  &QTimer::timeout, this, &MockLink::_run1HzTasks);
        QObject::disconnect(&timer10HzTasks, &QTimer::timeout, this, &MockLink::_run10HzTasks);
        QObject::disconnect(&timer500HzTasks, &QTimer::timeout, this, &MockLink::_run500HzTasks);
    
        _missionItemHandler.shutdown();
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void MockLink::_run1HzTasks(void)
    {
    
        if (_mavlinkStarted && _connected) {
    
            if (_highLatency) {
                _sendHighLatency2();
    
                _sendVibration();
    
                _sendSysStatus();
    
                _sendADSBVehicles();
                if (!qgcApp()->runningUnitTests()) {
                    // Sending RC Channels during unit test breaks RC tests which does it's own RC simulation
                    _sendRCChannels();
                }
                if (_sendHomePositionDelayCount > 0) {
                    // We delay home position for better testing
                    _sendHomePositionDelayCount--;
                } else {
                    _sendHomePosition();
                }
                if (_sendStatusText) {
                    _sendStatusText = false;
                    _sendStatusTextMessages();
                }
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    void MockLink::_run10HzTasks(void)
    {
    
        if (_highLatency) {
            return;
        }
    
    
        if (_mavlinkStarted && _connected) {
    
    Don Gagne's avatar
    Don Gagne committed
            _sendHeartBeat();
    
            if (_sendGPSPositionDelayCount > 0) {
                // We delay gps position for better testing
                _sendGPSPositionDelayCount--;
            } else {
                _sendGpsRawInt();
            }
    
    Don Gagne's avatar
    Don Gagne committed
    void MockLink::_run500HzTasks(void)
    
        if (_highLatency) {
            return;
        }
    
    
        if (_mavlinkStarted && _connected) {
    
    Don Gagne's avatar
    Don Gagne committed
            _paramRequestListWorker();
    
            _logDownloadWorker();
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    void MockLink::_loadParams(void)
    {
    
        QFile paramFile;
    
        if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
            if (_vehicleType == MAV_TYPE_FIXED_WING) {
    
    DonLakeFlyer's avatar
     
    DonLakeFlyer committed
                paramFile.setFileName(":/FirmwarePlugin/APM/Plane.OfflineEditing.params");
    
            } else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
    
                paramFile.setFileName(":/MockLink/APMArduSubMockLink.params");
    
    DonLakeFlyer's avatar
     
    DonLakeFlyer committed
            } else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
                paramFile.setFileName(":/FirmwarePlugin/APM/Rover.OfflineEditing.params");
    
    DonLakeFlyer's avatar
     
    DonLakeFlyer committed
                paramFile.setFileName(":/FirmwarePlugin/APM/Copter.OfflineEditing.params");
    
            paramFile.setFileName(":/MockLink/PX4MockLink.params");
    
    Don Gagne's avatar
    Don Gagne committed
        bool success = paramFile.open(QFile::ReadOnly);
        Q_UNUSED(success);
        Q_ASSERT(success);
    
    Don Gagne's avatar
    Don Gagne committed
        QTextStream paramStream(&paramFile);
    
    Don Gagne's avatar
    Don Gagne committed
        while (!paramStream.atEnd()) {
            QString line = paramStream.readLine();
    
    Don Gagne's avatar
    Don Gagne committed
            if (line.startsWith("#")) {
                continue;
            }
    
    Don Gagne's avatar
    Don Gagne committed
            QStringList paramData = line.split("\t");
            Q_ASSERT(paramData.count() == 5);
    
    Don Gagne's avatar
     
    Don Gagne committed
            int compId = paramData.at(1).toInt();
    
    Don Gagne's avatar
    Don Gagne committed
            QString paramName = paramData.at(2);
            QString valStr = paramData.at(3);
            uint paramType = paramData.at(4).toUInt();
    
    Don Gagne's avatar
    Don Gagne committed
            QVariant paramValue;
            switch (paramType) {
    
            case MAV_PARAM_TYPE_REAL32:
                paramValue = QVariant(valStr.toFloat());
                break;
            case MAV_PARAM_TYPE_UINT32:
                paramValue = QVariant(valStr.toUInt());
                break;
            case MAV_PARAM_TYPE_INT32:
                paramValue = QVariant(valStr.toInt());
                break;
            case MAV_PARAM_TYPE_UINT16:
                paramValue = QVariant((quint16)valStr.toUInt());
                break;
            case MAV_PARAM_TYPE_INT16:
                paramValue = QVariant((qint16)valStr.toInt());
                break;
            case MAV_PARAM_TYPE_UINT8:
                paramValue = QVariant((quint8)valStr.toUInt());
                break;
            case MAV_PARAM_TYPE_INT8:
                paramValue = QVariant((qint8)valStr.toUInt());
                break;
            default:
                qCritical() << "Unknown type" << paramType;
                paramValue = QVariant(valStr.toInt());
                break;
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(MockLinkVerboseLog) << "Loading param" << paramName << paramValue;
    
    Don Gagne's avatar
     
    Don Gagne committed
            _mapParamName2Value[compId][paramName] = paramValue;
            _mapParamName2MavParamType[compId][paramName] = static_cast<MAV_PARAM_TYPE>(paramType);
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    void MockLink::_sendHeartBeat(void)
    {
        mavlink_message_t   msg;
    
    
        mavlink_msg_heartbeat_pack_chan(_vehicleSystemId,
                                        _vehicleComponentId,
    
                                        _mavlinkChannel,
    
                                        &msg,
                                        _vehicleType,        // MAV_TYPE
                                        _firmwareType,      // MAV_AUTOPILOT
                                        _mavBaseMode,        // MAV_MODE
                                        _mavCustomMode,      // custom mode
                                        _mavState);          // MAV_STATE
    
        respondWithMavlinkMessage(msg);
    }
    
    void MockLink::_sendHighLatency2(void)
    {
        mavlink_message_t   msg;
    
    
        union px4_custom_mode   px4_cm;
        px4_cm.data = _mavCustomMode;
    
        qDebug() << "Sending" << _mavCustomMode;
    
        mavlink_msg_high_latency2_pack_chan(_vehicleSystemId,
                                            _vehicleComponentId,
                                            _mavlinkChannel,
                                            &msg,
                                            0,                          // timestamp
                                            _vehicleType,               // MAV_TYPE
                                            _firmwareType,              // MAV_AUTOPILOT
    
                                            px4_cm.custom_mode_hl,      // custom_mode
    
                                            (int32_t)(_vehicleLatitude  * 1E7),
                                            (int32_t)(_vehicleLongitude * 1E7),
                                            (int16_t)_vehicleAltitude,
                                            (int16_t)_vehicleAltitude,  // target_altitude,
                                            0,                          // heading
                                            0,                          // target_heading
                                            0,                          // target_distance
                                            0,                          // throttle
                                            0,                          // airspeed
                                            0,                          // airspeed_sp
                                            0,                          // groundspeed
                                            0,                          // windspeed,
                                            0,                          // wind_heading
                                            UINT8_MAX,                  // eph not known
                                            UINT8_MAX,                  // epv not known
                                            0,                          // temperature_air
                                            0,                          // climb_rate
                                            -1,                         // battery, do not use?
                                            0,                          // wp_num
                                            0,                          // failure_flags
                                            0, 0, 0);                   // custom0, custom1, custom2
        respondWithMavlinkMessage(msg);
    }
    
    
    void MockLink::_sendSysStatus(void)
    {
        if(_batteryRemaining > 50) {
            _batteryRemaining = static_cast<int8_t>(100 - (_runningTime.elapsed() / 1000));
        }
        mavlink_message_t   msg;
        mavlink_msg_sys_status_pack_chan(
            _vehicleSystemId,
            _vehicleComponentId,
            static_cast<uint8_t>(_mavlinkChannel),
            &msg,
            0,          // onboard_control_sensors_present
            0,          // onboard_control_sensors_enabled
            0,          // onboard_control_sensors_health
            250,        // load
            4200 * 4,   // voltage_battery
            8000,       // current_battery
            _batteryRemaining, // battery_remaining
            0,0,0,0,0,0);
        respondWithMavlinkMessage(msg);
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void MockLink::_sendVibration(void)
    {
        mavlink_message_t   msg;
    
    
        mavlink_msg_vibration_pack_chan(_vehicleSystemId,
                                        _vehicleComponentId,
    
                                        _mavlinkChannel,
    
                                        &msg,
                                        0,       // time_usec
                                        50.5,    // vibration_x,
                                        10.5,    // vibration_y,
                                        60.0,    // vibration_z,
                                        1,       // clipping_0
                                        2,       // clipping_0
                                        3);      // clipping_0
    
    Don Gagne's avatar
    Don Gagne committed
    
        respondWithMavlinkMessage(msg);
    }
    
    
    void MockLink::respondWithMavlinkMessage(const mavlink_message_t& msg)
    {
        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
    
    Don Gagne's avatar
    Don Gagne committed
        int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
        QByteArray bytes((char *)buffer, cBuffer);
        emit bytesReceived(this, bytes);
    }
    
    /// @brief Called when QGC wants to write bytes to the MAV
    
    void MockLink::_writeBytes(const QByteArray bytes)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        if (_inNSH) {
            _handleIncomingNSHBytes(bytes.constData(), bytes.count());
        } else {
            if (bytes.startsWith(QByteArray("\r\r\r"))) {
                _inNSH  = true;
                _handleIncomingNSHBytes(&bytes.constData()[3], bytes.count() - 3);
            }
    
    Don Gagne's avatar
    Don Gagne committed
            _handleIncomingMavlinkBytes((uint8_t *)bytes.constData(), bytes.count());
        }
    }
    
    /// @brief Handle incoming bytes which are meant to be interpreted by the NuttX shell
    void MockLink::_handleIncomingNSHBytes(const char* bytes, int cBytes)
    {
        Q_UNUSED(cBytes);
    
    Don Gagne's avatar
    Don Gagne committed
        // Drop back out of NSH
        if (cBytes == 4 && bytes[0] == '\r' && bytes[1] == '\r' && bytes[2] == '\r') {
            _inNSH  = false;
            return;
        }
    
        if (cBytes > 0) {
            qDebug() << "NSH:" << (const char*)bytes;
    
    Don Gagne's avatar
    Don Gagne committed
    #if 0
            // MockLink not quite ready to handle this correctly yet
    
    Don Gagne's avatar
    Don Gagne committed
            if (strncmp(bytes, "sh /etc/init.d/rc.usb\n", cBytes) == 0) {
                // This is the mavlink start command
                _mavlinkStarted = true;
            }
    
    Don Gagne's avatar
    Don Gagne committed
    #endif
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    /// @brief Handle incoming bytes which are meant to be handled by the mavlink protocol
    void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes)
    {
        mavlink_message_t msg;
        mavlink_status_t comm;
    
    Don Gagne's avatar
    Don Gagne committed
        for (qint64 i=0; i<cBytes; i++)
        {
    
            if (!mavlink_parse_char(_mavlinkChannel, bytes[i], &msg, &comm)) {
    
    Don Gagne's avatar
    Don Gagne committed
                continue;
            }
    
            if (_missionItemHandler.handleMessage(msg)) {
    
    Don Gagne's avatar
    Don Gagne committed
                continue;
            }
    
    Don Gagne's avatar
    Don Gagne committed
            switch (msg.msgid) {
    
            case MAVLINK_MSG_ID_HEARTBEAT:
                _handleHeartBeat(msg);
                break;
    
            case MAVLINK_MSG_ID_PARAM_REQUEST_LIST:
                _handleParamRequestList(msg);
                break;
    
            case MAVLINK_MSG_ID_SET_MODE:
                _handleSetMode(msg);
                break;
    
            case MAVLINK_MSG_ID_PARAM_SET:
                _handleParamSet(msg);
                break;
    
            case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
                _handleParamRequestRead(msg);
                break;
    
            case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
                _handleFTP(msg);
                break;
    
            case MAVLINK_MSG_ID_COMMAND_LONG:
                _handleCommandLong(msg);
                break;
    
            case MAVLINK_MSG_ID_MANUAL_CONTROL:
                _handleManualControl(msg);
                break;
    
            case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
                _handleLogRequestList(msg);
                break;
    
            case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
                _handleLogRequestData(msg);
                break;
    
    
            default:
                break;
    
    Don Gagne's avatar
    Don Gagne committed
            }
        }
    }
    
    void MockLink::_handleHeartBeat(const mavlink_message_t& msg)
    {
        Q_UNUSED(msg);
    
        qCDebug(MockLinkLog) << "Heartbeat";
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void MockLink::_handleSetMode(const mavlink_message_t& msg)
    {
        mavlink_set_mode_t request;
        mavlink_msg_set_mode_decode(&msg, &request);
    
        Q_ASSERT(request.target_system == _vehicleSystemId);
    
        _mavBaseMode = request.base_mode;
        _mavCustomMode = request.custom_mode;
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void MockLink::_handleManualControl(const mavlink_message_t& msg)
    {
        mavlink_manual_control_t manualControl;
        mavlink_msg_manual_control_decode(&msg, &manualControl);
    
    
    Gus Grubba's avatar
    Gus Grubba committed
        qCDebug(MockLinkLog) << "MANUAL_CONTROL" << manualControl.x << manualControl.y << manualControl.z << manualControl.r;
    
    void MockLink::_setParamFloatUnionIntoMap(int componentId, const QString& paramName, float paramFloat)
    
    {
        mavlink_param_union_t   valueUnion;
    
        Q_ASSERT(_mapParamName2Value.contains(componentId));
        Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
    
    Don Gagne's avatar
     
    Don Gagne committed
        Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
    
        valueUnion.param_float = paramFloat;
    
    Don Gagne's avatar
     
    Don Gagne committed
        MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
    
        QVariant paramVariant;
    
        switch (paramType) {
    
        case MAV_PARAM_TYPE_REAL32:
            paramVariant = QVariant::fromValue(valueUnion.param_float);
            break;
    
        case MAV_PARAM_TYPE_UINT32:
            paramVariant = QVariant::fromValue(valueUnion.param_uint32);
            break;
    
        case MAV_PARAM_TYPE_INT32:
            paramVariant = QVariant::fromValue(valueUnion.param_int32);
            break;
    
        case MAV_PARAM_TYPE_UINT16:
            paramVariant = QVariant::fromValue(valueUnion.param_uint16);
            break;
    
        case MAV_PARAM_TYPE_INT16:
            paramVariant = QVariant::fromValue(valueUnion.param_int16);
            break;
    
        case MAV_PARAM_TYPE_UINT8:
            paramVariant = QVariant::fromValue(valueUnion.param_uint8);
            break;
    
        case MAV_PARAM_TYPE_INT8:
            paramVariant = QVariant::fromValue(valueUnion.param_int8);
            break;
    
        default:
            qCritical() << "Invalid parameter type" << paramType;
            paramVariant = QVariant::fromValue(valueUnion.param_int32);
            break;
    
        qCDebug(MockLinkLog) << "_setParamFloatUnionIntoMap" << paramName << paramVariant;
    
        _mapParamName2Value[componentId][paramName] = paramVariant;
    
    /// Convert from a parameter variant to the float value from mavlink_param_union_t
    
    float MockLink::_floatUnionForParam(int componentId, const QString& paramName)
    
        mavlink_param_union_t   valueUnion;
    
        Q_ASSERT(_mapParamName2Value.contains(componentId));
        Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
    
    Don Gagne's avatar
     
    Don Gagne committed
        Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
    
    Don Gagne's avatar
     
    Don Gagne committed
        MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
    
        QVariant paramVar = _mapParamName2Value[componentId][paramName];
    
        switch (paramType) {
    
        case MAV_PARAM_TYPE_REAL32:
    
            valueUnion.param_float = paramVar.toFloat();
    
        case MAV_PARAM_TYPE_UINT32:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toUInt();
            } else {
                valueUnion.param_uint32 = paramVar.toUInt();
            }
            break;
    
        case MAV_PARAM_TYPE_INT32:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toInt();
            } else {
                valueUnion.param_int32 = paramVar.toInt();
            }
            break;
    
        case MAV_PARAM_TYPE_UINT16:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toUInt();
            } else {
                valueUnion.param_uint16 = paramVar.toUInt();
            }
            break;
    
        case MAV_PARAM_TYPE_INT16:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toInt();
            } else {
                valueUnion.param_int16 = paramVar.toInt();
            }
            break;
    
        case MAV_PARAM_TYPE_UINT8:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toUInt();
            } else {
                valueUnion.param_uint8 = paramVar.toUInt();
            }
            break;
    
        case MAV_PARAM_TYPE_INT8:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = (unsigned char)paramVar.toChar().toLatin1();
            } else {
                valueUnion.param_int8 = (unsigned char)paramVar.toChar().toLatin1();
            }
            break;
    
        default:
            if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
                valueUnion.param_float = paramVar.toInt();
            } else {
                valueUnion.param_int32 = paramVar.toInt();
            }
            qCritical() << "Invalid parameter type" << paramType;
    
        return valueUnion.param_float;
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void MockLink::_handleParamRequestList(const mavlink_message_t& msg)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        if (_failureMode == MockConfiguration::FailParamNoReponseToRequestList) {
            return;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_param_request_list_t request;
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_msg_param_request_list_decode(&msg, &request);
    
        Q_ASSERT(request.target_system == _vehicleSystemId);
    
        Q_ASSERT(request.target_component == MAV_COMP_ID_ALL);
    
    Don Gagne's avatar
    Don Gagne committed
        // Start the worker routine
        _currentParamRequestListComponentIndex = 0;
        _currentParamRequestListParamIndex = 0;
    }
    
    Don Gagne's avatar
    Don Gagne committed
    /// Sends the next parameter to the vehicle
    void MockLink::_paramRequestListWorker(void)
    {
        if (_currentParamRequestListComponentIndex == -1) {
            // Initial request complete
            return;
        }
    
    Don Gagne's avatar
    Don Gagne committed
        int componentId = _mapParamName2Value.keys()[_currentParamRequestListComponentIndex];
        int cParameters = _mapParamName2Value[componentId].count();
        QString paramName = _mapParamName2Value[componentId].keys()[_currentParamRequestListParamIndex];
    
    Don Gagne's avatar
    Don Gagne committed
        if ((_failureMode == MockConfiguration::FailMissingParamOnInitialReqest || _failureMode == MockConfiguration::FailMissingParamOnAllRequests) && paramName == _failParam) {
            qCDebug(MockLinkLog) << "Skipping param send:" << paramName;
        } else {
    
    Don Gagne's avatar
    Don Gagne committed
            char paramId[MAVLINK_MSG_ID_PARAM_VALUE_LEN];
            mavlink_message_t responseMsg;
    
    Don Gagne's avatar
    Don Gagne committed
            Q_ASSERT(_mapParamName2Value[componentId].contains(paramName));
    
    Don Gagne's avatar
     
    Don Gagne committed
            Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramName));
    
    Don Gagne's avatar
     
    Don Gagne committed
            MAV_PARAM_TYPE paramType = _mapParamName2MavParamType[componentId][paramName];
    
    Don Gagne's avatar
    Don Gagne committed
            Q_ASSERT(paramName.length() <= MAVLINK_MSG_ID_PARAM_VALUE_LEN);
            strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_ID_PARAM_VALUE_LEN);
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(MockLinkLog) << "Sending msg_param_value" << componentId << paramId << paramType << _mapParamName2Value[componentId][paramId];
    
    
            mavlink_msg_param_value_pack_chan(_vehicleSystemId,
                                              componentId,                                   // component id
    
                                              _mavlinkChannel,
    
                                              &responseMsg,                                  // Outgoing message
                                              paramId,                                       // Parameter name
                                              _floatUnionForParam(componentId, paramName),   // Parameter value
                                              paramType,                                     // MAV_PARAM_TYPE
                                              cParameters,                                   // Total number of parameters
                                              _currentParamRequestListParamIndex);           // Index of this parameter
    
    Don Gagne's avatar
    Don Gagne committed
            respondWithMavlinkMessage(responseMsg);
    
    Don Gagne's avatar
    Don Gagne committed
        // Move to next param index
        if (++_currentParamRequestListParamIndex >= cParameters) {
            // We've sent the last parameter for this component, move to next component
            if (++_currentParamRequestListComponentIndex >= _mapParamName2Value.keys().count()) {
                // We've finished sending the last parameter for the last component, request is complete
                _currentParamRequestListComponentIndex = -1;
            } else {
                _currentParamRequestListParamIndex = 0;
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    void MockLink::_handleParamSet(const mavlink_message_t& msg)
    {
        mavlink_param_set_t request;
        mavlink_msg_param_set_decode(&msg, &request);
    
        Q_ASSERT(request.target_system == _vehicleSystemId);
    
        int componentId = request.target_component;
    
        // Param may not be null terminated if exactly fits
        char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1];
    
    Don Gagne's avatar
    Don Gagne committed
        paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN] = 0;
    
        strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
    
    Don Gagne's avatar
    Don Gagne committed
        qCDebug(MockLinkLog) << "_handleParamSet" << componentId << paramId << request.param_type;
    
        Q_ASSERT(_mapParamName2Value.contains(componentId));
    
    Don Gagne's avatar
     
    Don Gagne committed
        Q_ASSERT(_mapParamName2MavParamType.contains(componentId));
    
        Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
    
    Don Gagne's avatar
     
    Don Gagne committed
        Q_ASSERT(request.param_type == _mapParamName2MavParamType[componentId][paramId]);
    
        // Save the new value
    
        _setParamFloatUnionIntoMap(componentId, paramId, request.param_value);
    
        // Respond with a param_value to ack
        mavlink_message_t responseMsg;
    
        mavlink_msg_param_value_pack_chan(_vehicleSystemId,
                                          componentId,                                               // component id
    
                                          _mavlinkChannel,
    
                                          &responseMsg,                                              // Outgoing message
                                          paramId,                                                   // Parameter name
                                          request.param_value,                                       // Send same value back
                                          request.param_type,                                        // Send same type back
                                          _mapParamName2Value[componentId].count(),                  // Total number of parameters
                                          _mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter
    
        respondWithMavlinkMessage(responseMsg);
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    void MockLink::_handleParamRequestRead(const mavlink_message_t& msg)
    {
    
        mavlink_message_t   responseMsg;
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink_param_request_read_t request;
        mavlink_msg_param_request_read_decode(&msg, &request);
    
    Don Gagne's avatar
    Don Gagne committed
        const QString paramName(QString::fromLocal8Bit(request.param_id, strnlen(request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN)));
    
        int componentId = request.target_component;
    
    
        // special case for magic _HASH_CHECK value
    
    Don Gagne's avatar
    Don Gagne committed
        if (request.target_component == MAV_COMP_ID_ALL && paramName == "_HASH_CHECK") {
    
            mavlink_param_union_t   valueUnion;
            valueUnion.type = MAV_PARAM_TYPE_UINT32;
            valueUnion.param_uint32 = 0;
            // Special case of magic hash check value
    
            mavlink_msg_param_value_pack_chan(_vehicleSystemId,
                                              componentId,
    
                                              _mavlinkChannel,
    
                                              &responseMsg,
                                              request.param_id,
                                              valueUnion.param_float,
                                              MAV_PARAM_TYPE_UINT32,
                                              0,
                                              -1);
    
            respondWithMavlinkMessage(responseMsg);
            return;
        }
    
    
        Q_ASSERT(_mapParamName2Value.contains(componentId));
    
    Don Gagne's avatar
    Don Gagne committed
        char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1];
        paramId[0] = 0;
    
        Q_ASSERT(request.target_system == _vehicleSystemId);
    
        if (request.param_index == -1) {
            // Request is by param name. Param may not be null terminated if exactly fits
            strncpy(paramId, request.param_id, MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
    
    Don Gagne's avatar
    Don Gagne committed
        } else {
    
            // Request is by index
    
            Q_ASSERT(request.param_index >= 0 && request.param_index < _mapParamName2Value[componentId].count());
    
            QString key = _mapParamName2Value[componentId].keys().at(request.param_index);
    
            Q_ASSERT(key.length() <= MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
            strcpy(paramId, key.toLocal8Bit().constData());
    
        Q_ASSERT(_mapParamName2Value[componentId].contains(paramId));
    
    Don Gagne's avatar
     
    Don Gagne committed
        Q_ASSERT(_mapParamName2MavParamType[componentId].contains(paramId));
    
    Don Gagne's avatar
    Don Gagne committed
        if (_failureMode == MockConfiguration::FailMissingParamOnAllRequests && strcmp(paramId, _failParam) == 0) {
            qCDebug(MockLinkLog) << "Ignoring request read for " << _failParam;
            // Fail to send this param no matter what
            return;
        }
    
    
        mavlink_msg_param_value_pack_chan(_vehicleSystemId,
                                          componentId,                                               // component id
    
                                          _mavlinkChannel,
    
                                          &responseMsg,                                              // Outgoing message
                                          paramId,                                                   // Parameter name
                                          _floatUnionForParam(componentId, paramId),                 // Parameter value
    
    Don Gagne's avatar
     
    Don Gagne committed
                                          _mapParamName2MavParamType[componentId][paramId],          // Parameter type
    
                                          _mapParamName2Value[componentId].count(),                  // Total number of parameters
                                          _mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter
    
        respondWithMavlinkMessage(responseMsg);
    
    void MockLink::emitRemoteControlChannelRawChanged(int channel, uint16_t raw)
    {
        uint16_t chanRaw[18];
    
        for (int i=0; i<18; i++) {
            chanRaw[i] = UINT16_MAX;
        }
        chanRaw[channel] = raw;
    
        mavlink_message_t responseMsg;
    
        mavlink_msg_rc_channels_pack_chan(_vehicleSystemId,
                                          _vehicleComponentId,
    
                                          _mavlinkChannel,
    
                                          &responseMsg,          // Outgoing message
                                          0,                     // time since boot, ignored
                                          18,                    // channel count
                                          chanRaw[0],            // channel raw value
    
                chanRaw[1],            // channel raw value
                chanRaw[2],            // channel raw value
                chanRaw[3],            // channel raw value
                chanRaw[4],            // channel raw value
                chanRaw[5],            // channel raw value
                chanRaw[6],            // channel raw value
                chanRaw[7],            // channel raw value
                chanRaw[8],            // channel raw value
                chanRaw[9],            // channel raw value
                chanRaw[10],           // channel raw value
                chanRaw[11],           // channel raw value
                chanRaw[12],           // channel raw value
                chanRaw[13],           // channel raw value
                chanRaw[14],           // channel raw value
                chanRaw[15],           // channel raw value
                chanRaw[16],           // channel raw value
                chanRaw[17],           // channel raw value
                0);                    // rss
    
        respondWithMavlinkMessage(responseMsg);
    }
    
    void MockLink::_handleFTP(const mavlink_message_t& msg)
    {
        Q_ASSERT(_fileServer);
        _fileServer->handleFTPMessage(msg);
    
    
    void MockLink::_handleCommandLong(const mavlink_message_t& msg)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        static bool firstCmdUser3 = true;
        static bool firstCmdUser4 = true;
    
    
        mavlink_command_long_t request;
    
        uint8_t commandResult = MAV_RESULT_UNSUPPORTED;
    
        mavlink_msg_command_long_decode(&msg, &request);
    
    
        switch (request.command) {
        case MAV_CMD_COMPONENT_ARM_DISARM:
    
            if (request.param1 == 0.0f) {
                _mavBaseMode &= ~MAV_MODE_FLAG_SAFETY_ARMED;
            } else {
                _mavBaseMode |= MAV_MODE_FLAG_SAFETY_ARMED;
            }
    
            commandResult = MAV_RESULT_ACCEPTED;
    
            break;
        case MAV_CMD_PREFLIGHT_CALIBRATION:
    
            _handlePreFlightCalibration(request);
            commandResult = MAV_RESULT_ACCEPTED;
            break;
    
        case MAV_CMD_PREFLIGHT_STORAGE:
            commandResult = MAV_RESULT_ACCEPTED;
            break;
    
        case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
            commandResult = MAV_RESULT_ACCEPTED;
            _respondWithAutopilotVersion();
            break;
    
        case MAV_CMD_USER_1:
            // Test command which always returns MAV_RESULT_ACCEPTED
            commandResult = MAV_RESULT_ACCEPTED;
            break;
        case MAV_CMD_USER_2:
            // Test command which always returns MAV_RESULT_FAILED
            commandResult = MAV_RESULT_FAILED;
            break;
        case MAV_CMD_USER_3:
            // Test command which returns MAV_RESULT_ACCEPTED on second attempt
            if (firstCmdUser3) {
    
                firstCmdUser3 = false;
                return;
    
            } else {
                firstCmdUser3 = true;
                commandResult = MAV_RESULT_ACCEPTED;
            }
            break;
        case MAV_CMD_USER_4:
            // Test command which returns MAV_RESULT_FAILED on second attempt
            if (firstCmdUser4) {
    
                firstCmdUser4 = false;
                return;
    
            } else {
                firstCmdUser4 = true;
                commandResult = MAV_RESULT_FAILED;
            }
            break;
        case MAV_CMD_USER_5:
            // No response
            return;
            break;
    
    
        mavlink_message_t commandAck;
    
        mavlink_msg_command_ack_pack_chan(_vehicleSystemId,
                                          _vehicleComponentId,
    
                                          _mavlinkChannel,
    
                                          &commandAck,
                                          request.command,
    
    DonLakeFlyer's avatar
    DonLakeFlyer committed
                                          commandResult,
    
    Gus Grubba's avatar
    Gus Grubba committed
                                          0,    // progress
                                          0,    // result_param2
                                          0,    // target_system
                                          0);   // target_component
    
        respondWithMavlinkMessage(commandAck);
    
    void MockLink::_respondWithAutopilotVersion(void)
    {
        mavlink_message_t msg;
    
    
    Don Gagne's avatar
    Don Gagne committed
        uint8_t customVersion[8] = { };
        uint32_t flightVersion = 0;
    
    Don Gagne's avatar
    Don Gagne committed
        if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
    
    DonLakeFlyer's avatar
     
    DonLakeFlyer committed
            if (_vehicleType == MAV_TYPE_FIXED_WING) {
                flightVersion |= 9 << (8*2);
            } else if (_vehicleType == MAV_TYPE_SUBMARINE ) {
                flightVersion |= 5 << (8*2);
            } else if (_vehicleType == MAV_TYPE_GROUND_ROVER ) {
                flightVersion |= 5 << (8*2);
            } else {
                flightVersion |= 6 << (8*2);
            }
            flightVersion |= 3 << (8*3);    // Major
            flightVersion |= 0 << (8*1);    // Patch
    
    Don Gagne's avatar
    Don Gagne committed
            flightVersion |= FIRMWARE_VERSION_TYPE_DEV << (8*0);
        } else if (_firmwareType == MAV_AUTOPILOT_PX4) {
    
    Don Gagne's avatar
    Don Gagne committed
            flightVersion |= 1 << (8*3);
            flightVersion |= 4 << (8*2);
            flightVersion |= 1 << (8*1);
            flightVersion |= FIRMWARE_VERSION_TYPE_DEV << (8*0);
    
        mavlink_msg_autopilot_version_pack_chan(_vehicleSystemId,
                                                _vehicleComponentId,
    
                                                _mavlinkChannel,