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

mavlink / MAVSDK / 11452150858

22 Oct 2024 02:32AM UTC coverage: 37.403% (+0.02%) from 37.381%
11452150858

push

github

web-flow
camera_server: prevent double ack+message (#2430)

It turns out we were sending the ack and message for storage information
as well as capture status twice, once directly in the request handler
callback, and once the MAVSDK user would call the respond function.

We should only call it in the respond function, not in the callback.

11105 of 29690 relevant lines covered (37.4%)

255.6 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
namespace mavsdk {
14

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

39
            LocalFree(lpMsgBuf);
40

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

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

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

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

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

77
    start_recv_thread();
×
78

79
    return ConnectionResult::Success;
×
80
}
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

220
#endif
221

222
    return ConnectionResult::Success;
×
223
}
224

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

230
ConnectionResult SerialConnection::stop()
×
231
{
232
    _should_exit = true;
×
233

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

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

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

249
    return ConnectionResult::Success;
×
250
}
251

252
bool SerialConnection::send_message(const mavlink_message_t& message)
×
253
{
254
    if (_serial_node.empty()) {
×
255
        LogErr() << "Dev Path unknown";
×
256
        return false;
×
257
    }
258

259
    if (_baudrate == 0) {
×
260
        LogErr() << "Baudrate unknown";
×
261
        return false;
×
262
    }
263

264
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
×
265
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
266

267
    int send_len;
268
#if defined(LINUX) || defined(APPLE)
269
    send_len = static_cast<int>(write(_fd, buffer, buffer_len));
×
270
#else
271
    if (!WriteFile(_handle, buffer, buffer_len, LPDWORD(&send_len), NULL)) {
272
        LogErr() << "WriteFile failure: " << GET_ERROR();
273
        return false;
274
    }
275
#endif
276

277
    if (send_len != buffer_len) {
×
278
        LogErr() << "write failure: " << GET_ERROR();
×
279
        return false;
×
280
    }
281

282
    return true;
×
283
}
284

285
void SerialConnection::receive()
×
286
{
287
    // Enough for MTU 1500 bytes.
288
    char buffer[2048];
×
289

290
#if defined(LINUX) || defined(APPLE)
291
    struct pollfd fds[1];
×
292
    fds[0].fd = _fd;
×
293
    fds[0].events = POLLIN;
×
294
#endif
295

296
    while (!_should_exit) {
×
297
        int recv_len;
298
#if defined(LINUX) || defined(APPLE)
299
        int pollrc = poll(fds, 1, 1000);
×
300
        if (pollrc == 0 || !(fds[0].revents & POLLIN)) {
×
301
            continue;
×
302
        } else if (pollrc == -1) {
×
303
            LogErr() << "read poll failure: " << GET_ERROR();
×
304
        }
305
        // We enter here if (fds[0].revents & POLLIN) == true
306
        recv_len = static_cast<int>(read(_fd, buffer, sizeof(buffer)));
×
307
        if (recv_len < -1) {
×
308
            LogErr() << "read failure: " << GET_ERROR();
×
309
        }
310
#else
311
        if (!ReadFile(_handle, buffer, sizeof(buffer), LPDWORD(&recv_len), NULL)) {
312
            LogErr() << "ReadFile failure: " << GET_ERROR();
313
            continue;
314
        }
315
#endif
316
        if (recv_len > static_cast<int>(sizeof(buffer)) || recv_len == 0) {
×
317
            continue;
×
318
        }
319
        _mavlink_receiver->set_new_datagram(buffer, recv_len);
×
320
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
321
        while (_mavlink_receiver->parse_message()) {
×
322
            receive_message(_mavlink_receiver->get_last_message(), this);
×
323
        }
324
    }
325
}
×
326

327
#if defined(LINUX)
328
int SerialConnection::define_from_baudrate(int baudrate)
×
329
{
330
    switch (baudrate) {
×
331
        case 9600:
×
332
            return B9600;
×
333
        case 19200:
×
334
            return B19200;
×
335
        case 38400:
×
336
            return B38400;
×
337
        case 57600:
×
338
            return B57600;
×
339
        case 115200:
×
340
            return B115200;
×
341
        case 230400:
×
342
            return B230400;
×
343
        case 460800:
×
344
            return B460800;
×
345
        case 500000:
×
346
            return B500000;
×
347
        case 576000:
×
348
            return B576000;
×
349
        case 921600:
×
350
            return B921600;
×
351
        case 1000000:
×
352
            return B1000000;
×
353
        case 1152000:
×
354
            return B1152000;
×
355
        case 1500000:
×
356
            return B1500000;
×
357
        case 2000000:
×
358
            return B2000000;
×
359
        case 2500000:
×
360
            return B2500000;
×
361
        case 3000000:
×
362
            return B3000000;
×
363
        case 3500000:
×
364
            return B3500000;
×
365
        case 4000000:
×
366
            return B4000000;
×
367
        default: {
×
368
            LogErr() << "Unknown baudrate";
×
369
            return -1;
×
370
        }
371
    }
372
}
373
#endif
374

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