• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 16247112396

13 Jul 2025 08:18AM UTC coverage: 46.221% (+1.0%) from 45.212%
16247112396

Pull #2610

github

web-flow
Merge a15009557 into 6c112e71f
Pull Request #2610: Integrate parts of libmav into MAVSDK and add MavlinkDirect plugin

727 of 980 new or added lines in 15 files covered. (74.18%)

27 existing lines in 5 files now uncovered.

16217 of 35086 relevant lines covered (46.22%)

148063.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.48
/src/mavsdk/core/udp_connection.cpp
1
#include "udp_connection.h"
2
#include "log.h"
3

4
#ifdef WINDOWS
5
#include <winsock2.h>
6
#include <Ws2tcpip.h> // For InetPton
7
#undef SOCKET_ERROR // conflicts with ConnectionResult::SocketError
8
#ifndef MINGW
9
#pragma comment(lib, "Ws2_32.lib") // Without this, Ws2_32.lib is not included in static library.
10
#endif
11
#else
12
#include <netinet/in.h>
13
#include <sys/socket.h>
14
#include <sys/time.h>
15
#include <arpa/inet.h>
16
#include <errno.h>
17
#endif
18

19
#include <algorithm>
20
#include <utility>
21
#include <sstream>
22

23
#ifdef WINDOWS
24
#define GET_ERROR(_x) WSAGetLastError()
25
#else
26
#define GET_ERROR(_x) strerror(_x)
27
#endif
28

29
namespace mavsdk {
30

UNCOV
31
UdpConnection::UdpConnection(
×
32
    Connection::ReceiverCallback receiver_callback,
33
    std::string local_ip,
34
    int local_port_number,
UNCOV
35
    ForwardingOption forwarding_option) :
×
UNCOV
36
    Connection(std::move(receiver_callback), forwarding_option),
×
UNCOV
37
    _local_ip(std::move(local_ip)),
×
UNCOV
38
    _local_port_number(local_port_number)
×
UNCOV
39
{}
×
40

41
UdpConnection::UdpConnection(
96✔
42
    Connection::ReceiverCallback receiver_callback,
43
    Connection::LibmavReceiverCallback libmav_receiver_callback,
44
    std::string local_ip,
45
    int local_port_number,
46
    ForwardingOption forwarding_option) :
96✔
47
    Connection(
48
        std::move(receiver_callback), std::move(libmav_receiver_callback), forwarding_option),
192✔
49
    _local_ip(std::move(local_ip)),
96✔
50
    _local_port_number(local_port_number)
384✔
51
{}
96✔
52

53
UdpConnection::~UdpConnection()
192✔
54
{
55
    // If no one explicitly called stop before, we should at least do it.
56
    stop();
96✔
57
}
192✔
58

59
ConnectionResult UdpConnection::start()
96✔
60
{
61
    if (!start_mavlink_receiver()) {
96✔
62
        return ConnectionResult::ConnectionsExhausted;
×
63
    }
64

65
    if (!start_libmav_receiver()) {
96✔
NEW
66
        return ConnectionResult::ConnectionsExhausted;
×
67
    }
68

69
    ConnectionResult ret = setup_port();
96✔
70
    if (ret != ConnectionResult::Success) {
96✔
71
        return ret;
×
72
    }
73

74
    start_recv_thread();
96✔
75

76
    return ConnectionResult::Success;
96✔
77
}
78

79
ConnectionResult UdpConnection::setup_port()
96✔
80
{
81
#ifdef WINDOWS
82
    WSADATA wsa;
83
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
84
        LogErr() << "Error: Winsock failed, error: %d", WSAGetLastError();
85
        return ConnectionResult::SocketError;
86
    }
87
#endif
88

89
    _socket_fd.reset(socket(AF_INET, SOCK_DGRAM, 0));
96✔
90

91
    if (_socket_fd.empty()) {
96✔
92
        LogErr() << "socket error" << GET_ERROR(errno);
×
93
        return ConnectionResult::SocketError;
×
94
    }
95

96
    struct sockaddr_in addr {};
96✔
97
    addr.sin_family = AF_INET;
96✔
98
    if (inet_pton(AF_INET, _local_ip.c_str(), &(addr.sin_addr)) != 1) {
96✔
99
        LogErr() << "inet_pton failure for address: " << _local_ip;
×
100
        return ConnectionResult::SocketError;
×
101
    }
102
    addr.sin_port = htons(_local_port_number);
96✔
103

104
    if (bind(_socket_fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != 0) {
96✔
105
        LogErr() << "bind error: " << GET_ERROR(errno);
×
106
        return ConnectionResult::BindError;
×
107
    }
108

109
    // Set receive timeout cross-platform
110
    const unsigned timeout_ms = 500;
96✔
111

112
#if defined(WINDOWS)
113
    setsockopt(
114
        _socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
115
#else
116
    struct timeval tv;
96✔
117
    tv.tv_sec = 0;
96✔
118
    tv.tv_usec = timeout_ms * 1000;
96✔
119
    setsockopt(_socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
96✔
120
#endif
121

122
    return ConnectionResult::Success;
96✔
123
}
124

125
void UdpConnection::start_recv_thread()
96✔
126
{
127
    _recv_thread = std::make_unique<std::thread>(&UdpConnection::receive, this);
96✔
128
}
96✔
129

130
ConnectionResult UdpConnection::stop()
96✔
131
{
132
    _should_exit = true;
96✔
133

134
    if (_recv_thread) {
96✔
135
        _recv_thread->join();
96✔
136
        _recv_thread.reset();
96✔
137
    }
138

139
    _socket_fd.close();
96✔
140

141
    // We need to stop this after stopping the receive thread, otherwise
142
    // it can happen that we interfere with the parsing of a message.
143
    stop_mavlink_receiver();
96✔
144

145
    return ConnectionResult::Success;
96✔
146
}
147

148
std::pair<bool, std::string> UdpConnection::send_message(const mavlink_message_t& message)
2,090✔
149
{
150
    std::pair<bool, std::string> result;
2,090✔
151

152
    std::lock_guard<std::mutex> lock(_remote_mutex);
2,090✔
153

154
    // Remove inactive remotes before sending messages
155
    auto now = std::chrono::steady_clock::now();
2,089✔
156

157
    _remotes.erase(
8,352✔
158
        std::remove_if(
4,175✔
159
            _remotes.begin(),
160
            _remotes.end(),
161
            [&now, this](const Remote& remote) {
4,174✔
162
                const auto elapsed = now - remote.last_activity;
2,087✔
163
                const bool inactive = elapsed > REMOTE_TIMEOUT;
2,088✔
164

165
                const bool should_remove = inactive && remote.remote_option == RemoteOption::Found;
2,089✔
166

167
                // We can cleanup old/previous remotes if we have
168
                if (should_remove) {
2,089✔
169
                    LogInfo() << "Removing inactive remote: " << remote.ip << ":"
×
170
                              << remote.port_number;
×
171
                }
172

173
                return should_remove;
2,089✔
174
            }),
175
        _remotes.end());
4,177✔
176

177
    if (_remotes.size() == 0) {
2,089✔
178
        result.first = false;
×
179
        result.second = "no remotes";
×
180
        return result;
×
181
    }
182

183
    // Send the message to all the remotes. A remote is a UDP endpoint
184
    // identified by its <ip, port>. This means that if we have two
185
    // systems on two different endpoints, then messages directed towards
186
    // only one system will be sent to both remotes. The systems are
187
    // then expected to ignore messages that are not directed to them.
188

189
    // For multiple remotes, we ignore errors, for just one, we bubble it up.
190
    result.first = true;
2,087✔
191

192
    for (auto& remote : _remotes) {
4,175✔
193
        struct sockaddr_in dest_addr {};
2,086✔
194
        dest_addr.sin_family = AF_INET;
2,086✔
195

196
        if (inet_pton(AF_INET, remote.ip.c_str(), &dest_addr.sin_addr.s_addr) != 1) {
2,086✔
197
            std::stringstream ss;
×
198
            ss << "inet_pton failure for: " << remote.ip << ":" << remote.port_number;
×
199
            LogErr() << ss.str();
×
200
            result.first = false;
×
201
            if (!result.second.empty()) {
×
202
                result.second += ", ";
×
203
            }
204
            result.second += ss.str();
×
205
            continue;
×
206
        }
×
207
        dest_addr.sin_port = htons(remote.port_number);
2,090✔
208

209
        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
2,090✔
210
        uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
2,090✔
211

212
        const auto send_len = sendto(
2,089✔
213
            _socket_fd.get(),
214
            reinterpret_cast<char*>(buffer),
215
            buffer_len,
216
            0,
217
            reinterpret_cast<const sockaddr*>(&dest_addr),
218
            sizeof(dest_addr));
219

220
        if (send_len != buffer_len) {
2,090✔
221
            std::stringstream ss;
×
222
            ss << "sendto failure: " << GET_ERROR(errno) << " for: " << remote.ip << ":"
×
223
               << remote.port_number;
×
224
            LogErr() << ss.str();
×
225
            result.first = false;
×
226
            if (!result.second.empty()) {
×
227
                result.second += ", ";
×
228
            }
229
            result.second += ss.str();
×
230
            continue;
×
231
        }
×
232
    }
233

234
    return result;
235
}
2,090✔
236

237
void UdpConnection::add_remote_to_keep(const std::string& remote_ip, const int remote_port)
48✔
238
{
239
    add_remote_impl(remote_ip, remote_port, 0, RemoteOption::Fixed);
48✔
240
}
48✔
241

242
void UdpConnection::add_remote_impl(
2,132✔
243
    const std::string& remote_ip,
244
    const int remote_port,
245
    const uint8_t remote_sysid,
246
    RemoteOption remote_option)
247
{
248
    std::lock_guard<std::mutex> lock(_remote_mutex);
2,132✔
249
    Remote new_remote;
2,133✔
250
    new_remote.ip = remote_ip;
2,136✔
251
    new_remote.port_number = remote_port;
2,136✔
252
    new_remote.last_activity = std::chrono::steady_clock::now();
2,136✔
253
    new_remote.remote_option = remote_option;
2,136✔
254

255
    auto existing_remote =
2,136✔
256
        std::find_if(_remotes.begin(), _remotes.end(), [&new_remote](Remote& remote) {
2,137✔
257
            return remote == new_remote;
2,037✔
258
        });
259

260
    if (existing_remote == _remotes.end()) {
2,134✔
261
        // System with sysid 0 is a bit special: it is a placeholder for a connection initiated
262
        // by MAVSDK. As such, it should not be advertised as a newly discovered system.
263
        if (static_cast<int>(remote_sysid) != 0) {
96✔
264
            LogInfo() << "New system on: " << new_remote.ip << ":" << new_remote.port_number
96✔
265
                      << " (with system ID: " << static_cast<int>(remote_sysid) << ")";
96✔
266
        }
267
        _remotes.push_back(new_remote);
96✔
268
    } else {
269
        // Update the timestamp for the existing remote
270
        existing_remote->last_activity = std::chrono::steady_clock::now();
2,036✔
271
    }
272
}
2,133✔
273

274
void UdpConnection::receive()
96✔
275
{
276
    // Enough for MTU 1500 bytes.
277
    char buffer[2048];
96✔
278

279
    while (!_should_exit) {
2,439✔
280
        struct sockaddr_in src_addr = {};
2,347✔
281
        socklen_t src_addr_len = sizeof(src_addr);
2,347✔
282
        const auto recv_len = recvfrom(
2,347✔
283
            _socket_fd.get(),
284
            buffer,
285
            sizeof(buffer),
286
            0,
287
            reinterpret_cast<struct sockaddr*>(&src_addr),
288
            &src_addr_len);
289

290
        if (recv_len == 0) {
2,346✔
291
            // This can happen when shutdown is called on the socket,
292
            // therefore we check _should_exit again.
293
            continue;
×
294
        }
295

296
        if (recv_len < 0) {
2,346✔
297
            // This happens on destruction when _socket_fd.close() is called,
298
            // therefore be quiet.
299
            // LogErr() << "recvfrom error: " << GET_ERROR(errno);
300
            continue;
257✔
301
        }
302

303
        _mavlink_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
2,089✔
304

305
        // Parse all mavlink messages in one datagram. Once exhausted, we'll exit while.
306
        while (_mavlink_receiver->parse_message()) {
4,174✔
307
            const uint8_t sysid = _mavlink_receiver->get_last_message().sysid;
2,089✔
308

309
            if (sysid != 0) {
2,083✔
310
                char ip_str[INET_ADDRSTRLEN];
2,083✔
311
                if (inet_ntop(AF_INET, &src_addr.sin_addr, ip_str, INET_ADDRSTRLEN) != nullptr) {
2,083✔
312
                    add_remote_impl(ip_str, ntohs(src_addr.sin_port), sysid, RemoteOption::Found);
2,089✔
313
                } else {
314
                    LogErr() << "inet_ntop failure for: " << strerror(errno);
×
315
                }
316
            }
317

318
            receive_message(_mavlink_receiver->get_last_message(), this);
2,089✔
319
        }
320

321
        // Also parse with libmav if available
322
        if (_libmav_receiver) {
2,090✔
323
            _libmav_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
2,088✔
324

325
            while (_libmav_receiver->parse_message()) {
4,176✔
326
                receive_libmav_message(_libmav_receiver->get_last_message(), this);
2,085✔
327
            }
328
        }
329
    }
330
}
96✔
331

332
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc