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

mavlink / MAVSDK / 16288680008

15 Jul 2025 08:53AM UTC coverage: 46.345% (+1.2%) from 45.192%
16288680008

Pull #2610

github

web-flow
Merge b48a3b508 into e73dfe79e
Pull Request #2610: Integrate parts of libmav into MAVSDK and add MavlinkDirect plugin

764 of 987 new or added lines in 14 files covered. (77.41%)

13 existing lines in 2 files now uncovered.

16261 of 35087 relevant lines covered (46.34%)

124716.17 hits per line

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

75.69
/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

31
UdpConnection::UdpConnection(
98✔
32
    Connection::ReceiverCallback receiver_callback,
33
    Connection::LibmavReceiverCallback libmav_receiver_callback,
34
    ::mav::MessageSet& message_set,
35
    std::string local_ip,
36
    int local_port_number,
37
    ForwardingOption forwarding_option) :
98✔
38
    Connection(
39
        std::move(receiver_callback),
98✔
40
        std::move(libmav_receiver_callback),
98✔
41
        message_set,
42
        forwarding_option),
43
    _local_ip(std::move(local_ip)),
98✔
44
    _local_port_number(local_port_number)
392✔
45
{}
98✔
46

47
UdpConnection::~UdpConnection()
196✔
48
{
49
    // If no one explicitly called stop before, we should at least do it.
50
    stop();
98✔
51
}
196✔
52

53
ConnectionResult UdpConnection::start()
98✔
54
{
55
    if (!start_mavlink_receiver()) {
98✔
56
        return ConnectionResult::ConnectionsExhausted;
×
57
    }
58

59
    if (!start_libmav_receiver()) {
98✔
NEW
60
        return ConnectionResult::ConnectionsExhausted;
×
61
    }
62

63
    ConnectionResult ret = setup_port();
98✔
64
    if (ret != ConnectionResult::Success) {
98✔
65
        return ret;
×
66
    }
67

68
    start_recv_thread();
98✔
69

70
    return ConnectionResult::Success;
98✔
71
}
72

73
ConnectionResult UdpConnection::setup_port()
98✔
74
{
75
#ifdef WINDOWS
76
    WSADATA wsa;
77
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
78
        LogErr() << "Error: Winsock failed, error: %d", WSAGetLastError();
79
        return ConnectionResult::SocketError;
80
    }
81
#endif
82

83
    _socket_fd.reset(socket(AF_INET, SOCK_DGRAM, 0));
98✔
84

85
    if (_socket_fd.empty()) {
98✔
86
        LogErr() << "socket error" << GET_ERROR(errno);
×
87
        return ConnectionResult::SocketError;
×
88
    }
89

90
    struct sockaddr_in addr {};
98✔
91
    addr.sin_family = AF_INET;
98✔
92
    if (inet_pton(AF_INET, _local_ip.c_str(), &(addr.sin_addr)) != 1) {
98✔
93
        LogErr() << "inet_pton failure for address: " << _local_ip;
×
94
        return ConnectionResult::SocketError;
×
95
    }
96
    addr.sin_port = htons(_local_port_number);
98✔
97

98
    if (bind(_socket_fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != 0) {
98✔
99
        LogErr() << "bind error: " << GET_ERROR(errno);
×
100
        return ConnectionResult::BindError;
×
101
    }
102

103
    // Set receive timeout cross-platform
104
    const unsigned timeout_ms = 500;
98✔
105

106
#if defined(WINDOWS)
107
    setsockopt(
108
        _socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
109
#else
110
    struct timeval tv;
98✔
111
    tv.tv_sec = 0;
98✔
112
    tv.tv_usec = timeout_ms * 1000;
98✔
113
    setsockopt(_socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
98✔
114
#endif
115

116
    return ConnectionResult::Success;
98✔
117
}
118

119
void UdpConnection::start_recv_thread()
98✔
120
{
121
    _recv_thread = std::make_unique<std::thread>(&UdpConnection::receive, this);
98✔
122
}
98✔
123

124
ConnectionResult UdpConnection::stop()
98✔
125
{
126
    _should_exit = true;
98✔
127

128
    if (_recv_thread) {
98✔
129
        _recv_thread->join();
98✔
130
        _recv_thread.reset();
98✔
131
    }
132

133
    _socket_fd.close();
98✔
134

135
    // We need to stop this after stopping the receive thread, otherwise
136
    // it can happen that we interfere with the parsing of a message.
137
    stop_mavlink_receiver();
98✔
138

139
    return ConnectionResult::Success;
98✔
140
}
141

142
std::pair<bool, std::string> UdpConnection::send_message(const mavlink_message_t& message)
2,132✔
143
{
144
    std::pair<bool, std::string> result;
2,132✔
145

146
    std::lock_guard<std::mutex> lock(_remote_mutex);
2,133✔
147

148
    // Remove inactive remotes before sending messages
149
    auto now = std::chrono::steady_clock::now();
2,132✔
150

151
    _remotes.erase(
8,523✔
152
        std::remove_if(
4,260✔
153
            _remotes.begin(),
154
            _remotes.end(),
155
            [&now, this](const Remote& remote) {
4,266✔
156
                const auto elapsed = now - remote.last_activity;
2,133✔
157
                const bool inactive = elapsed > REMOTE_TIMEOUT;
2,131✔
158

159
                const bool should_remove = inactive && remote.remote_option == RemoteOption::Found;
2,133✔
160

161
                // We can cleanup old/previous remotes if we have
162
                if (should_remove) {
2,133✔
163
                    LogInfo() << "Removing inactive remote: " << remote.ip << ":"
×
164
                              << remote.port_number;
×
165
                }
166

167
                return should_remove;
2,133✔
168
            }),
169
        _remotes.end());
4,261✔
170

171
    if (_remotes.size() == 0) {
2,129✔
172
        result.first = false;
×
173
        result.second = "no remotes";
×
174
        return result;
×
175
    }
176

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

183
    // For multiple remotes, we ignore errors, for just one, we bubble it up.
184
    result.first = true;
2,130✔
185

186
    for (auto& remote : _remotes) {
4,263✔
187
        struct sockaddr_in dest_addr {};
2,131✔
188
        dest_addr.sin_family = AF_INET;
2,131✔
189

190
        if (inet_pton(AF_INET, remote.ip.c_str(), &dest_addr.sin_addr.s_addr) != 1) {
2,131✔
191
            std::stringstream ss;
×
192
            ss << "inet_pton failure for: " << remote.ip << ":" << remote.port_number;
×
193
            LogErr() << ss.str();
×
194
            result.first = false;
×
195
            if (!result.second.empty()) {
×
196
                result.second += ", ";
×
197
            }
198
            result.second += ss.str();
×
199
            continue;
×
200
        }
×
201
        dest_addr.sin_port = htons(remote.port_number);
2,128✔
202

203
        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
2,128✔
204
        uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
2,132✔
205

206
        const auto send_len = sendto(
2,133✔
207
            _socket_fd.get(),
208
            reinterpret_cast<char*>(buffer),
209
            buffer_len,
210
            0,
211
            reinterpret_cast<const sockaddr*>(&dest_addr),
212
            sizeof(dest_addr));
213

214
        if (send_len != buffer_len) {
2,133✔
215
            std::stringstream ss;
×
216
            ss << "sendto failure: " << GET_ERROR(errno) << " for: " << remote.ip << ":"
×
217
               << remote.port_number;
×
218
            LogErr() << ss.str();
×
219
            result.first = false;
×
220
            if (!result.second.empty()) {
×
221
                result.second += ", ";
×
222
            }
223
            result.second += ss.str();
×
224
            continue;
×
225
        }
×
226
    }
227

228
    return result;
229
}
2,132✔
230

231
void UdpConnection::add_remote_to_keep(const std::string& remote_ip, const int remote_port)
49✔
232
{
233
    add_remote_impl(remote_ip, remote_port, 0, RemoteOption::Fixed);
49✔
234
}
49✔
235

236
void UdpConnection::add_remote_impl(
2,178✔
237
    const std::string& remote_ip,
238
    const int remote_port,
239
    const uint8_t remote_sysid,
240
    RemoteOption remote_option)
241
{
242
    std::lock_guard<std::mutex> lock(_remote_mutex);
2,178✔
243
    Remote new_remote;
2,178✔
244
    new_remote.ip = remote_ip;
2,180✔
245
    new_remote.port_number = remote_port;
2,179✔
246
    new_remote.last_activity = std::chrono::steady_clock::now();
2,179✔
247
    new_remote.remote_option = remote_option;
2,179✔
248

249
    auto existing_remote =
2,179✔
250
        std::find_if(_remotes.begin(), _remotes.end(), [&new_remote](Remote& remote) {
2,180✔
251
            return remote == new_remote;
2,079✔
252
        });
253

254
    if (existing_remote == _remotes.end()) {
2,178✔
255
        // System with sysid 0 is a bit special: it is a placeholder for a connection initiated
256
        // by MAVSDK. As such, it should not be advertised as a newly discovered system.
257
        if (static_cast<int>(remote_sysid) != 0) {
98✔
258
            LogInfo() << "New system on: " << new_remote.ip << ":" << new_remote.port_number
98✔
259
                      << " (with system ID: " << static_cast<int>(remote_sysid) << ")";
98✔
260
        }
261
        _remotes.push_back(new_remote);
98✔
262
    } else {
263
        // Update the timestamp for the existing remote
264
        existing_remote->last_activity = std::chrono::steady_clock::now();
2,079✔
265
    }
266
}
2,179✔
267

268
void UdpConnection::receive()
98✔
269
{
270
    // Enough for MTU 1500 bytes.
271
    char buffer[2048];
98✔
272

273
    while (!_should_exit) {
2,478✔
274
        struct sockaddr_in src_addr = {};
2,392✔
275
        socklen_t src_addr_len = sizeof(src_addr);
2,392✔
276
        const auto recv_len = recvfrom(
2,392✔
277
            _socket_fd.get(),
278
            buffer,
279
            sizeof(buffer),
280
            0,
281
            reinterpret_cast<struct sockaddr*>(&src_addr),
282
            &src_addr_len);
283

284
        if (recv_len == 0) {
2,392✔
285
            // This can happen when shutdown is called on the socket,
286
            // therefore we check _should_exit again.
287
            continue;
×
288
        }
289

290
        if (recv_len < 0) {
2,392✔
291
            // This happens on destruction when _socket_fd.close() is called,
292
            // therefore be quiet.
293
            // LogErr() << "recvfrom error: " << GET_ERROR(errno);
294
            continue;
259✔
295
        }
296

297
        _mavlink_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
2,133✔
298

299
        // Parse all mavlink messages in one datagram. Once exhausted, we'll exit while.
300
        while (_mavlink_receiver->parse_message()) {
4,260✔
301
            const uint8_t sysid = _mavlink_receiver->get_last_message().sysid;
2,131✔
302

303
            if (sysid != 0) {
2,125✔
304
                char ip_str[INET_ADDRSTRLEN];
2,125✔
305
                if (inet_ntop(AF_INET, &src_addr.sin_addr, ip_str, INET_ADDRSTRLEN) != nullptr) {
2,125✔
306
                    add_remote_impl(ip_str, ntohs(src_addr.sin_port), sysid, RemoteOption::Found);
2,130✔
307
                } else {
308
                    LogErr() << "inet_ntop failure for: " << strerror(errno);
×
309
                }
310
            }
311

312
            receive_message(_mavlink_receiver->get_last_message(), this);
2,129✔
313
        }
314

315
        // Also parse with libmav if available
316
        if (_libmav_receiver) {
2,133✔
317
            _libmav_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
2,133✔
318

319
            while (_libmav_receiver->parse_message()) {
4,264✔
320
                receive_libmav_message(_libmav_receiver->get_last_message(), this);
2,132✔
321
            }
322
        }
323
    }
324
}
98✔
325

326
} // 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