Skip to content

Commit 71946e6

Browse files
committed
core: add raw connection
This adds a "raw connection" which accepts and delivers raw bytes. This can be handy when a custom transport other than the included UDP/TCP/serial needs to be used.
1 parent cafb83c commit 71946e6

File tree

12 files changed

+407
-1
lines changed

12 files changed

+407
-1
lines changed

src/mavsdk/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ target_sources(mavsdk
8080
param_value.cpp
8181
ping.cpp
8282
plugin_impl_base.cpp
83+
raw_connection.cpp
8384
serial_connection.cpp
8485
server_component.cpp
8586
server_component_impl.cpp

src/mavsdk/core/cli_arg.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ bool CliArg::parse(const std::string& uri)
5959
std::string_view(uri).substr(serial_flowcontrol.size() + delimiter.size()), true);
6060
}
6161

62+
const std::string raw = "raw";
63+
if (uri.find(raw + delimiter) == 0) {
64+
return parse_raw(std::string_view(uri).substr(raw.size() + delimiter.size()));
65+
}
66+
6267
LogErr() << "Unknown protocol";
6368
return false;
6469
}
@@ -293,4 +298,16 @@ bool CliArg::parse_serial(const std::string_view rest, bool flow_control_enabled
293298
return true;
294299
}
295300

301+
bool CliArg::parse_raw(const std::string_view rest)
302+
{
303+
// raw:// connection has no parameters
304+
if (!rest.empty()) {
305+
LogErr() << "raw:// connection should not have parameters";
306+
return false;
307+
}
308+
309+
protocol = Raw{};
310+
return true;
311+
}
312+
296313
} // namespace mavsdk

src/mavsdk/core/cli_arg.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ class CliArg {
3737
bool flow_control_enabled{false};
3838
};
3939

40-
using Protocol = std::variant<std::monostate, Udp, Tcp, Serial>;
40+
struct Raw {
41+
// No parameters needed for raw connection
42+
};
43+
44+
using Protocol = std::variant<std::monostate, Udp, Tcp, Serial, Raw>;
4145

4246
bool parse(const std::string& uri);
4347

@@ -55,6 +59,7 @@ class CliArg {
5559
bool parse_tcpout(const std::string_view rest);
5660

5761
bool parse_serial(const std::string_view rest, bool flow_control_enabled);
62+
bool parse_raw(const std::string_view rest);
5863
};
5964

6065
} // namespace mavsdk

src/mavsdk/core/cli_arg_test.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,3 +381,22 @@ TEST(CliArg, SerialWrong)
381381
EXPECT_FALSE(ca.parse("serial://COM3:-1"));
382382
EXPECT_FALSE(ca.parse("serial://COM3"));
383383
}
384+
385+
TEST(CliArg, RawConnection)
386+
{
387+
CliArg ca;
388+
389+
EXPECT_TRUE(ca.parse("raw://"));
390+
auto raw = std::get_if<CliArg::Raw>(&ca.protocol);
391+
ASSERT_TRUE(raw);
392+
}
393+
394+
TEST(CliArg, RawConnectionWrong)
395+
{
396+
CliArg ca;
397+
398+
// raw:// should not have any parameters
399+
EXPECT_FALSE(ca.parse("raw://something"));
400+
EXPECT_FALSE(ca.parse("raw://localhost"));
401+
EXPECT_FALSE(ca.parse("raw://127.0.0.1"));
402+
}

src/mavsdk/core/include/mavsdk/mavsdk.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,50 @@ class Mavsdk {
463463
*/
464464
void intercept_outgoing_messages_async(std::function<bool(mavlink_message_t&)> callback);
465465

466+
/**
467+
* @brief Callback type for raw bytes subscriptions.
468+
*/
469+
using RawBytesCallback = std::function<void(const char* bytes, size_t length)>;
470+
471+
/**
472+
* @brief Handle type for raw bytes subscriptions.
473+
*/
474+
using RawBytesHandle = Handle<const char*, size_t>;
475+
476+
/**
477+
* @brief Send raw MAVLink bytes.
478+
*
479+
* This allows sending raw MAVLink message bytes through all connections.
480+
* The bytes can contain one or more MAVLink messages.
481+
*
482+
* @note When this API is first used, heartbeats will be automatically enabled.
483+
*
484+
* @param bytes Pointer to raw MAVLink message bytes.
485+
* @param length Number of bytes to send.
486+
* @return true if bytes were successfully sent, false otherwise.
487+
*/
488+
bool send_raw_bytes(const char* bytes, size_t length);
489+
490+
/**
491+
* @brief Subscribe to all outgoing raw bytes.
492+
*
493+
* This allows monitoring all MAVLink bytes that are sent out through any connection.
494+
* The callback will be called with raw bytes after they are successfully sent.
495+
*
496+
* @note When this API is first used, heartbeats will be automatically enabled.
497+
*
498+
* @param callback Callback to be called with outgoing raw bytes.
499+
* @return Handle to unsubscribe again.
500+
*/
501+
RawBytesHandle subscribe_raw_bytes(RawBytesCallback callback);
502+
503+
/**
504+
* @brief Unsubscribe from raw bytes.
505+
*
506+
* @param handle Handle from subscribe_raw_bytes.
507+
*/
508+
void unsubscribe_raw_bytes(RawBytesHandle handle);
509+
466510
private:
467511
static constexpr int DEFAULT_SYSTEM_ID_AUTOPILOT = 1;
468512
static constexpr int DEFAULT_COMPONENT_ID_AUTOPILOT = MAV_COMP_ID_AUTOPILOT1;

src/mavsdk/core/mavsdk.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@ void Mavsdk::intercept_outgoing_messages_async(std::function<bool(mavlink_messag
234234
_impl->intercept_outgoing_messages_async(callback);
235235
}
236236

237+
bool Mavsdk::send_raw_bytes(const char* bytes, size_t length)
238+
{
239+
return _impl->send_raw_bytes(bytes, length);
240+
}
241+
242+
Mavsdk::RawBytesHandle Mavsdk::subscribe_raw_bytes(RawBytesCallback callback)
243+
{
244+
return _impl->subscribe_raw_bytes(callback);
245+
}
246+
247+
void Mavsdk::unsubscribe_raw_bytes(RawBytesHandle handle)
248+
{
249+
_impl->unsubscribe_raw_bytes(handle);
250+
}
251+
237252
Mavsdk::ConnectionErrorHandle
238253
Mavsdk::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
239254
{

src/mavsdk/core/mavsdk_impl.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "tcp_client_connection.h"
1010
#include "tcp_server_connection.h"
1111
#include "udp_connection.h"
12+
#include "raw_connection.h"
1213
#include "system.h"
1314
#include "system_impl.h"
1415
#include "serial_connection.h"
@@ -789,6 +790,9 @@ std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_any_connec
789790
[this, forwarding_option](const CliArg::Serial& serial) {
790791
return add_serial_connection(
791792
serial.path, serial.baudrate, serial.flow_control_enabled, forwarding_option);
793+
},
794+
[this, forwarding_option](const CliArg::Raw&) {
795+
return add_raw_connection(forwarding_option);
792796
}},
793797
cli_arg.protocol);
794798
}
@@ -929,6 +933,44 @@ std::pair<ConnectionResult, Mavsdk::ConnectionHandle> MavsdkImpl::add_serial_con
929933
}
930934
}
931935

936+
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
937+
MavsdkImpl::add_raw_connection(ForwardingOption forwarding_option)
938+
{
939+
// Check if a raw connection already exists
940+
if (find_raw_connection() != nullptr) {
941+
LogErr() << "Raw connection already exists. Only one raw connection is allowed.";
942+
return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
943+
}
944+
945+
auto new_conn = std::make_unique<RawConnection>(
946+
[this](mavlink_message_t& message, Connection* connection) {
947+
receive_message(message, connection);
948+
},
949+
[this](const Mavsdk::MavlinkMessage& message, Connection* connection) {
950+
receive_libmav_message(message, connection);
951+
},
952+
*this,
953+
forwarding_option);
954+
955+
if (!new_conn) {
956+
return {ConnectionResult::ConnectionError, Mavsdk::ConnectionHandle{}};
957+
}
958+
959+
ConnectionResult ret = new_conn->start();
960+
if (ret != ConnectionResult::Success) {
961+
return {ret, Mavsdk::ConnectionHandle{}};
962+
}
963+
964+
auto handle = add_connection(std::move(new_conn));
965+
966+
// Enable heartbeats for raw connection
967+
auto new_configuration = get_configuration();
968+
new_configuration.set_always_send_heartbeats(true);
969+
set_configuration(new_configuration);
970+
971+
return {ConnectionResult::Success, handle};
972+
}
973+
932974
Mavsdk::ConnectionHandle MavsdkImpl::add_connection(std::unique_ptr<Connection>&& new_connection)
933975
{
934976
std::lock_guard lock(_mutex);
@@ -1299,6 +1341,58 @@ void MavsdkImpl::unsubscribe_outgoing_messages_json(Mavsdk::InterceptJsonHandle
12991341
}
13001342
}
13011343

1344+
RawConnection* MavsdkImpl::find_raw_connection()
1345+
{
1346+
std::lock_guard lock(_mutex);
1347+
1348+
for (auto& entry : _connections) {
1349+
auto* raw_conn = dynamic_cast<RawConnection*>(entry.connection.get());
1350+
if (raw_conn != nullptr) {
1351+
return raw_conn;
1352+
}
1353+
}
1354+
return nullptr;
1355+
}
1356+
1357+
bool MavsdkImpl::send_raw_bytes(const char* bytes, size_t length)
1358+
{
1359+
auto* raw_conn = find_raw_connection();
1360+
if (raw_conn == nullptr) {
1361+
LogErr()
1362+
<< "No raw connection available. Please add one using add_any_connection(\"raw://\")";
1363+
return false;
1364+
}
1365+
1366+
raw_conn->receive(bytes, length);
1367+
return true;
1368+
}
1369+
1370+
Mavsdk::RawBytesHandle MavsdkImpl::subscribe_raw_bytes(const Mavsdk::RawBytesCallback& callback)
1371+
{
1372+
if (find_raw_connection() == nullptr) {
1373+
LogWarn() << "No raw connection available. Subscription will only receive bytes after you "
1374+
"add a connection using add_any_connection(\"raw://\")";
1375+
}
1376+
return _raw_bytes_subscriptions.subscribe(callback);
1377+
}
1378+
1379+
void MavsdkImpl::unsubscribe_raw_bytes(Mavsdk::RawBytesHandle handle)
1380+
{
1381+
_raw_bytes_subscriptions.unsubscribe(handle);
1382+
}
1383+
1384+
bool MavsdkImpl::notify_raw_bytes_sent(const char* bytes, size_t length)
1385+
{
1386+
if (_raw_bytes_subscriptions.empty()) {
1387+
return false;
1388+
}
1389+
1390+
_raw_bytes_subscriptions.queue(
1391+
bytes, length, [this](const std::function<void()>& func) { call_user_callback(func); });
1392+
1393+
return true;
1394+
}
1395+
13021396
Mavsdk::ConnectionErrorHandle
13031397
MavsdkImpl::subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback)
13041398
{

src/mavsdk/core/mavsdk_impl.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class BufferParser;
4141

4242
namespace mavsdk {
4343

44+
class RawConnection; // Forward declaration
45+
4446
class MavsdkImpl {
4547
public:
4648
MavsdkImpl(const Mavsdk::Configuration& configuration);
@@ -99,6 +101,12 @@ class MavsdkImpl {
99101
subscribe_connection_errors(Mavsdk::ConnectionErrorCallback callback);
100102
void unsubscribe_connection_errors(Mavsdk::ConnectionErrorHandle handle);
101103

104+
// Raw bytes API
105+
bool send_raw_bytes(const char* bytes, size_t length);
106+
Mavsdk::RawBytesHandle subscribe_raw_bytes(const Mavsdk::RawBytesCallback& callback);
107+
void unsubscribe_raw_bytes(Mavsdk::RawBytesHandle handle);
108+
bool notify_raw_bytes_sent(const char* bytes, size_t length);
109+
102110
std::shared_ptr<ServerComponent> server_component(unsigned instance = 0);
103111

104112
std::shared_ptr<ServerComponent>
@@ -151,6 +159,8 @@ class MavsdkImpl {
151159
int baudrate,
152160
bool flow_control,
153161
ForwardingOption forwarding_option);
162+
std::pair<ConnectionResult, Mavsdk::ConnectionHandle>
163+
add_raw_connection(ForwardingOption forwarding_option);
154164

155165
Mavsdk::ConnectionHandle add_connection(std::unique_ptr<Connection>&& connection);
156166
void make_system_with_component(uint8_t system_id, uint8_t component_id);
@@ -183,6 +193,9 @@ class MavsdkImpl {
183193
std::vector<std::pair<Mavsdk::InterceptJsonHandle, Mavsdk::InterceptJsonCallback>>&
184194
callback_list);
185195

196+
// Find the raw connection in the connections list
197+
RawConnection* find_raw_connection();
198+
186199
mutable std::recursive_mutex _mutex{};
187200

188201
// Message set for libmav message handling (shared across all connections)
@@ -244,6 +257,9 @@ class MavsdkImpl {
244257
mutable std::mutex _json_subscriptions_mutex{};
245258
HandleFactory<bool(Mavsdk::MavlinkMessage)> _json_handle_factory{};
246259

260+
// Raw bytes subscriptions
261+
CallbackList<const char*, size_t> _raw_bytes_subscriptions{};
262+
247263
std::atomic<double> _timeout_s{DEFAULT_TIMEOUT_S};
248264

249265
struct ReceivedMessage {

0 commit comments

Comments
 (0)