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

mavlink / MAVSDK / 12228184175

09 Dec 2024 03:09AM UTC coverage: 38.637% (-0.07%) from 38.708%
12228184175

push

github

web-flow
Merge pull request #2461 from mavlink/pr-connection-subscription

Add connection error subscription

13 of 93 new or added lines in 6 files covered. (13.98%)

12 existing lines in 5 files now uncovered.

12093 of 31299 relevant lines covered (38.64%)

242.86 hits per line

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

0.0
/src/mavsdk/core/serial_connection.cpp
1
#include "serial_connection.h"
2
#include "log.h"
3

4
#if defined(APPLE) || defined(LINUX)
5
#include <unistd.h>
6
#include <fcntl.h>
7
#include <termios.h>
8
#include <poll.h>
9

10
#include <utility>
11
#endif
12

13
#include <sstream>
14

15
namespace mavsdk {
16

17
#ifndef WINDOWS
18
#define GET_ERROR() strerror(errno)
19
#else
20
#define GET_ERROR() GetLastErrorStdStr()
21
// Taken from:
22
// https://coolcowstudio.wordpress.com/2012/10/19/getlasterror-as-stdstring/
23
std::string GetLastErrorStdStr()
24
{
25
    DWORD error = GetLastError();
26
    if (error) {
27
        LPVOID lpMsgBuf;
28
        DWORD bufLen = FormatMessage(
29
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
30
                FORMAT_MESSAGE_IGNORE_INSERTS,
31
            NULL,
32
            error,
33
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
34
            (LPTSTR)&lpMsgBuf,
35
            0,
36
            NULL);
37
        if (bufLen) {
38
            LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
39
            std::string result(lpMsgStr, lpMsgStr + bufLen);
40

41
            LocalFree(lpMsgBuf);
42

43
            return result;
44
        }
45
    }
46
    return std::string();
47
}
48
#endif
49

50
SerialConnection::SerialConnection(
×
51
    Connection::ReceiverCallback receiver_callback,
52
    std::string path,
53
    int baudrate,
54
    bool flow_control,
55
    ForwardingOption forwarding_option) :
×
56
    Connection(std::move(receiver_callback), forwarding_option),
×
57
    _serial_node(std::move(path)),
×
58
    _baudrate(baudrate),
×
59
    _flow_control(flow_control)
×
60
{}
×
61

62
SerialConnection::~SerialConnection()
×
63
{
64
    // If no one explicitly called stop before, we should at least do it.
65
    stop();
×
66
}
×
67

68
ConnectionResult SerialConnection::start()
×
69
{
70
    if (!start_mavlink_receiver()) {
×
71
        return ConnectionResult::ConnectionsExhausted;
×
72
    }
73

74
    ConnectionResult ret = setup_port();
×
75
    if (ret != ConnectionResult::Success) {
×
76
        return ret;
×
77
    }
78

79
    start_recv_thread();
×
80

81
    return ConnectionResult::Success;
×
82
}
83

84
ConnectionResult SerialConnection::setup_port()
×
85
{
86
#if defined(LINUX) || defined(APPLE)
87
    // open() hangs on macOS or Linux devices(e.g. pocket beagle) unless you give it O_NONBLOCK
88
    _fd = open(_serial_node.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
×
89
    if (_fd == -1) {
×
90
        LogErr() << "open failed: " << GET_ERROR();
×
91
        return ConnectionResult::ConnectionError;
×
92
    }
93
    // We need to clear the O_NONBLOCK again because we can block while reading
94
    // as we do it in a separate thread.
95
    if (fcntl(_fd, F_SETFL, 0) == -1) {
×
96
        LogErr() << "fcntl failed: " << GET_ERROR();
×
97
        return ConnectionResult::ConnectionError;
×
98
    }
99
#elif defined(WINDOWS)
100
    // Required for COM ports > 9.
101
    const auto full_serial_path = "\\\\.\\" + _serial_node;
102

103
    _handle = CreateFile(
104
        full_serial_path.c_str(),
105
        GENERIC_READ | GENERIC_WRITE,
106
        0, // exclusive-access
107
        NULL, //  default security attributes
108
        OPEN_EXISTING,
109
        0, //  not overlapped I/O
110
        NULL); //  hTemplate must be NULL for comm devices
111

112
    if (_handle == INVALID_HANDLE_VALUE) {
113
        LogErr() << "CreateFile failed with: " << GET_ERROR();
114
        return ConnectionResult::ConnectionError;
115
    }
116
#endif
117

118
#if defined(LINUX) || defined(APPLE)
119
    struct termios tc;
×
120
    bzero(&tc, sizeof(tc));
×
121

122
    if (tcgetattr(_fd, &tc) != 0) {
×
123
        LogErr() << "tcgetattr failed: " << GET_ERROR();
×
124
        close(_fd);
×
125
        return ConnectionResult::ConnectionError;
×
126
    }
127
#endif
128

129
#if defined(LINUX) || defined(APPLE)
130
    tc.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
×
131
    tc.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST);
×
132
    tc.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG | TOSTOP);
×
133
    tc.c_cflag &= ~(CSIZE | PARENB | CRTSCTS);
×
134
    tc.c_cflag |= CS8;
×
135

136
    tc.c_cc[VMIN] = 0; // We are ok with 0 bytes.
×
137
    tc.c_cc[VTIME] = 10; // Timeout after 1 second.
×
138

139
    if (_flow_control) {
×
140
        tc.c_cflag |= CRTSCTS;
×
141
    }
142
#endif
143

144
#if defined(LINUX) || defined(APPLE)
145
    tc.c_cflag |= CLOCAL; // Without this a write() blocks indefinitely.
×
146

147
#if defined(LINUX)
148
    const int baudrate_or_define = define_from_baudrate(_baudrate);
×
149
#elif defined(APPLE)
150
    const int baudrate_or_define = _baudrate;
151
#endif
152

153
    if (baudrate_or_define == -1) {
×
154
        return ConnectionResult::BaudrateUnknown;
×
155
    }
156

157
    if (cfsetispeed(&tc, baudrate_or_define) != 0) {
×
158
        LogErr() << "cfsetispeed failed: " << GET_ERROR();
×
159
        close(_fd);
×
160
        return ConnectionResult::ConnectionError;
×
161
    }
162

163
    if (cfsetospeed(&tc, baudrate_or_define) != 0) {
×
164
        LogErr() << "cfsetospeed failed: " << GET_ERROR();
×
165
        close(_fd);
×
166
        return ConnectionResult::ConnectionError;
×
167
    }
168

169
    if (tcsetattr(_fd, TCSANOW, &tc) != 0) {
×
170
        LogErr() << "tcsetattr failed: " << GET_ERROR();
×
171
        close(_fd);
×
172
        return ConnectionResult::ConnectionError;
×
173
    }
174
#endif
175

176
#if defined(WINDOWS)
177
    DCB dcb;
178
    SecureZeroMemory(&dcb, sizeof(DCB));
179
    dcb.DCBlength = sizeof(DCB);
180

181
    if (!GetCommState(_handle, &dcb)) {
182
        LogErr() << "GetCommState failed with error: " << GET_ERROR();
183
        return ConnectionResult::ConnectionError;
184
    }
185

186
    dcb.BaudRate = _baudrate;
187
    dcb.ByteSize = 8;
188
    dcb.Parity = NOPARITY;
189
    dcb.StopBits = ONESTOPBIT;
190
    if (_flow_control) {
191
        dcb.fOutxCtsFlow = TRUE;
192
        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
193
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
194
    } else {
195
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
196
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
197
    }
198
    dcb.fOutX = FALSE;
199
    dcb.fInX = FALSE;
200
    dcb.fBinary = TRUE;
201
    dcb.fNull = FALSE;
202
    dcb.fDsrSensitivity = FALSE;
203

204
    if (!SetCommState(_handle, &dcb)) {
205
        LogErr() << "SetCommState failed with error: " << GET_ERROR();
206
        return ConnectionResult::ConnectionError;
207
    }
208

209
    COMMTIMEOUTS timeout = {0, 0, 0, 0, 0};
210
    timeout.ReadIntervalTimeout = 1;
211
    timeout.ReadTotalTimeoutConstant = 1;
212
    timeout.ReadTotalTimeoutMultiplier = 1;
213
    timeout.WriteTotalTimeoutConstant = 1;
214
    timeout.WriteTotalTimeoutMultiplier = 1;
215
    SetCommTimeouts(_handle, &timeout);
216

217
    if (!SetCommTimeouts(_handle, &timeout)) {
218
        LogErr() << "SetCommTimeouts failed with error: " << GET_ERROR();
219
        return ConnectionResult::ConnectionError;
220
    }
221

222
#endif
223

224
    return ConnectionResult::Success;
×
225
}
226

227
void SerialConnection::start_recv_thread()
×
228
{
229
    _recv_thread = std::make_unique<std::thread>(&SerialConnection::receive, this);
×
230
}
×
231

232
ConnectionResult SerialConnection::stop()
×
233
{
234
    _should_exit = true;
×
235

236
    if (_recv_thread) {
×
237
        _recv_thread->join();
×
238
        _recv_thread.reset();
×
239
    }
240

241
#if defined(LINUX) || defined(APPLE)
242
    close(_fd);
×
243
#elif defined(WINDOWS)
244
    CloseHandle(_handle);
245
#endif
246

247
    // We need to stop this after stopping the receive thread, otherwise
248
    // it can happen that we interfere with the parsing of a message.
249
    stop_mavlink_receiver();
×
250

251
    return ConnectionResult::Success;
×
252
}
253

NEW
254
std::pair<bool, std::string> SerialConnection::send_message(const mavlink_message_t& message)
×
255
{
NEW
256
    std::pair<bool, std::string> result;
×
257

258
    if (_serial_node.empty()) {
×
259
        LogErr() << "Dev Path unknown";
×
NEW
260
        result.first = false;
×
NEW
261
        result.second = "Dev Path unknown";
×
NEW
262
        return result;
×
263
    }
264

265
    if (_baudrate == 0) {
×
NEW
266
        result.first = false;
×
NEW
267
        result.second = "Baudrate unknown";
×
NEW
268
        return result;
×
269
    }
270

271
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
×
272
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
273

274
    int send_len;
275
#if defined(LINUX) || defined(APPLE)
276
    send_len = static_cast<int>(write(_fd, buffer, buffer_len));
×
277
#else
278
    if (!WriteFile(_handle, buffer, buffer_len, LPDWORD(&send_len), NULL)) {
279
        LogErr() << "WriteFile failure: " << GET_ERROR();
280
        return false;
281
    }
282
#endif
283

284
    if (send_len != buffer_len) {
×
NEW
285
        result.first = false;
×
NEW
286
        result.second = "Baudrate unknown";
×
287

NEW
288
        std::stringstream ss;
×
NEW
289
        ss << "write failure: " << GET_ERROR();
×
NEW
290
        LogErr() << ss.str();
×
NEW
291
        result.first = false;
×
NEW
292
        result.second = ss.str();
×
NEW
293
        return result;
×
UNCOV
294
    }
×
295

NEW
296
    result.first = true;
×
NEW
297
    return result;
×
298
}
299

300
void SerialConnection::receive()
×
301
{
302
    // Enough for MTU 1500 bytes.
303
    char buffer[2048];
×
304

305
#if defined(LINUX) || defined(APPLE)
306
    struct pollfd fds[1];
×
307
    fds[0].fd = _fd;
×
308
    fds[0].events = POLLIN;
×
309
#endif
310

311
    while (!_should_exit) {
×
312
        int recv_len;
313
#if defined(LINUX) || defined(APPLE)
314
        int pollrc = poll(fds, 1, 1000);
×
315
        if (pollrc == 0 || !(fds[0].revents & POLLIN)) {
×
316
            continue;
×
317
        } else if (pollrc == -1) {
×
318
            LogErr() << "read poll failure: " << GET_ERROR();
×
319
        }
320
        // We enter here if (fds[0].revents & POLLIN) == true
321
        recv_len = static_cast<int>(read(_fd, buffer, sizeof(buffer)));
×
322
        if (recv_len < -1) {
×
323
            LogErr() << "read failure: " << GET_ERROR();
×
324
        }
325
#else
326
        if (!ReadFile(_handle, buffer, sizeof(buffer), LPDWORD(&recv_len), NULL)) {
327
            LogErr() << "ReadFile failure: " << GET_ERROR();
328
            continue;
329
        }
330
#endif
331
        if (recv_len > static_cast<int>(sizeof(buffer)) || recv_len == 0) {
×
332
            continue;
×
333
        }
334
        _mavlink_receiver->set_new_datagram(buffer, recv_len);
×
335
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
336
        while (_mavlink_receiver->parse_message()) {
×
337
            receive_message(_mavlink_receiver->get_last_message(), this);
×
338
        }
339
    }
340
}
×
341

342
#if defined(LINUX)
343
int SerialConnection::define_from_baudrate(int baudrate)
×
344
{
345
    switch (baudrate) {
×
346
        case 9600:
×
347
            return B9600;
×
348
        case 19200:
×
349
            return B19200;
×
350
        case 38400:
×
351
            return B38400;
×
352
        case 57600:
×
353
            return B57600;
×
354
        case 115200:
×
355
            return B115200;
×
356
        case 230400:
×
357
            return B230400;
×
358
        case 460800:
×
359
            return B460800;
×
360
        case 500000:
×
361
            return B500000;
×
362
        case 576000:
×
363
            return B576000;
×
364
        case 921600:
×
365
            return B921600;
×
366
        case 1000000:
×
367
            return B1000000;
×
368
        case 1152000:
×
369
            return B1152000;
×
370
        case 1500000:
×
371
            return B1500000;
×
372
        case 2000000:
×
373
            return B2000000;
×
374
        case 2500000:
×
375
            return B2500000;
×
376
        case 3000000:
×
377
            return B3000000;
×
378
        case 3500000:
×
379
            return B3500000;
×
380
        case 4000000:
×
381
            return B4000000;
×
382
        default: {
×
383
            LogErr() << "Unknown baudrate";
×
384
            return -1;
×
385
        }
386
    }
387
}
388
#endif
389

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