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

mavlink / MAVSDK / 16983693748

15 Aug 2025 05:30AM UTC coverage: 47.227%. First build
16983693748

Pull #2638

github

web-flow
Merge ee902ea22 into e5b8bb7bb
Pull Request #2638: Add new JSON based interception API

379 of 417 new or added lines in 12 files covered. (90.89%)

16690 of 35340 relevant lines covered (47.23%)

447.39 hits per line

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

58.65
/src/mavsdk/plugins/mavlink_direct/mavlink_direct_impl.cpp
1
#include "mavlink_direct_impl.h"
2
#include <mav/Message.h>
3
#include <mav/MessageSet.h>
4
#include <variant>
5
#include <json/json.h>
6
#include "log.h"
7
#include "connection.h"
8
#include "callback_list.tpp"
9

10
namespace mavsdk {
11

12
template class CallbackList<MavlinkDirect::MavlinkMessage>;
13

14
MavlinkDirectImpl::MavlinkDirectImpl(System& system) : PluginImplBase(system)
×
15
{
16
    if (const char* env_p = std::getenv("MAVSDK_MAVLINK_DIRECT_DEBUGGING")) {
×
17
        if (std::string(env_p) == "1") {
×
18
            _debugging = true;
×
19
        }
20
    }
21
    _system_impl->register_plugin(this);
×
22
}
×
23

24
MavlinkDirectImpl::MavlinkDirectImpl(std::shared_ptr<System> system) :
16✔
25
    PluginImplBase(std::move(system))
16✔
26
{
27
    if (const char* env_p = std::getenv("MAVSDK_MAVLINK_DIRECT_DEBUGGING")) {
16✔
28
        if (std::string(env_p) == "1") {
×
29
            _debugging = true;
×
30
        }
31
    }
32
    _system_impl->register_plugin(this);
16✔
33
}
16✔
34

35
MavlinkDirectImpl::~MavlinkDirectImpl()
32✔
36
{
37
    _system_impl->unregister_plugin(this);
16✔
38
}
32✔
39

40
void MavlinkDirectImpl::init()
16✔
41
{
42
    // No need to initialize MessageSet here - LibmavReceiver handles that
43
    // No need to register for mavlink messages anymore - we'll use libmav subscription system
44
}
16✔
45

46
void MavlinkDirectImpl::deinit()
16✔
47
{
48
    // Clean up all libmav message handlers registered by this plugin
49
    _system_impl->unregister_all_libmav_message_handlers(this);
16✔
50

51
    // Clear internal state
52
    _handle_to_message_name.clear();
16✔
53
    _message_subscriptions.clear();
16✔
54
}
16✔
55

56
void MavlinkDirectImpl::enable() {}
16✔
57

58
void MavlinkDirectImpl::disable() {}
16✔
59

60
MavlinkDirect::Result MavlinkDirectImpl::send_message(MavlinkDirect::MavlinkMessage message)
10✔
61
{
62
    // Get access to the MessageSet through the system
63
    auto& message_set = _system_impl->get_message_set();
10✔
64

65
    // Create libmav message from the message name
66
    auto libmav_message_opt = message_set.create(message.message_name);
10✔
67
    if (!libmav_message_opt) {
10✔
68
        LogErr() << "Failed to create message: " << message.message_name;
×
69
        return MavlinkDirect::Result::InvalidMessage; // Message type not found
×
70
    }
71

72
    if (_debugging) {
10✔
73
        LogDebug() << "Created message " << message.message_name
×
74
                   << " with ID: " << libmav_message_opt.value().id();
×
75
    }
76

77
    auto libmav_message = libmav_message_opt.value();
10✔
78

79
    // Convert JSON fields to libmav message fields
80
    if (!json_to_libmav_message(message.fields_json, libmav_message)) {
10✔
81
        LogErr() << "Failed to convert JSON fields to libmav message for " << message.message_name;
×
82
        return MavlinkDirect::Result::InvalidField; // JSON conversion failed
×
83
    }
84

85
    if (_debugging) {
10✔
86
        LogDebug() << "Successfully populated fields for " << message.message_name;
×
87
    }
88

89
    // Set target system/component if specified
90
    if (message.target_system_id != 0) {
10✔
91
        // For messages that have target_system field, set it
NEW
92
        libmav_message.set("target_system", static_cast<uint8_t>(message.target_system_id));
×
93
    }
94
    if (message.target_component_id != 0) {
10✔
95
        // For messages that have target_component field, set it
NEW
96
        libmav_message.set(
×
NEW
97
            "target_component_id", static_cast<uint8_t>(message.target_component_id));
×
98
    }
99

100
    if (_debugging) {
10✔
101
        LogDebug() << "Sending " << message.message_name << " via unified libmav API";
×
102
    }
103

104
    _system_impl->queue_message([&](MavlinkAddress mavlink_address, uint8_t channel) {
10✔
105
        mavlink_message_t mavlink_message;
106

107
        // Use clean libmav helper methods to get payload data
108
        auto payload_view = libmav_message.getPayloadView();
10✔
109
        const uint8_t payload_length = libmav_message.getPayloadLength();
10✔
110

111
        // Set up the mavlink_message_t structure
112
        mavlink_message.msgid = libmav_message.id();
10✔
113
        mavlink_message.len = payload_length;
10✔
114
        memcpy(mavlink_message.payload64, payload_view.first, payload_length);
10✔
115

116
        mavlink_finalize_message_chan(
20✔
117
            &mavlink_message,
118
            mavlink_address.system_id,
10✔
119
            mavlink_address.component_id,
10✔
120
            channel,
121
            payload_length,
122
            libmav_message.type().maxPayloadSize(),
10✔
123
            libmav_message.type().crcExtra());
10✔
124

125
        return mavlink_message;
10✔
126
    });
127

128
    if (_debugging) {
10✔
129
        LogDebug() << "Successfully sent " << message.message_name << " as raw data";
×
130
    }
131

132
    return MavlinkDirect::Result::Success;
10✔
133
}
134

135
MavlinkDirect::MessageHandle MavlinkDirectImpl::subscribe_message(
8✔
136
    std::string message_name, const MavlinkDirect::MessageCallback& callback)
137
{
138
    // Subscribe using CallbackList pattern - this handles thread safety internally
139
    auto handle = _message_subscriptions.subscribe(callback);
8✔
140

141
    // Store message name for this handle (CallbackList protects this too)
142
    _handle_to_message_name[handle] = message_name;
8✔
143

144
    // Register with SystemImpl - no lock-order-inversion because CallbackList handles
145
    // synchronization
146
    _system_impl->register_libmav_message_handler(
8✔
147
        message_name,
148
        [this](const Mavsdk::MavlinkMessage& message) {
20✔
149
            // Use CallbackList::queue to safely call all subscribed callbacks
150
            // Convert Mavsdk::MavlinkMessage to MavlinkDirect::MavlinkMessage (they're identical)
151
            MavlinkDirect::MavlinkMessage direct_message;
10✔
152
            direct_message.message_name = message.message_name;
10✔
153
            direct_message.system_id = message.system_id;
10✔
154
            direct_message.component_id = message.component_id;
10✔
155
            direct_message.target_system_id = message.target_system_id;
10✔
156
            direct_message.target_component_id = message.target_component_id;
10✔
157
            direct_message.fields_json = message.fields_json;
10✔
158

159
            _message_subscriptions.queue(direct_message, [this](const auto& func) {
10✔
160
                _system_impl->call_user_callback(func);
10✔
161
            });
10✔
162
        },
10✔
163
        &handle // Use handle address as cookie for specific unregistration
164
    );
165

166
    return handle;
8✔
167
}
168

169
void MavlinkDirectImpl::unsubscribe_message(MavlinkDirect::MessageHandle handle)
8✔
170
{
171
    // Get the message name for unregistration
172
    auto name_it = _handle_to_message_name.find(handle);
8✔
173
    if (name_it == _handle_to_message_name.end()) {
8✔
174
        return; // Handle not found, nothing to unsubscribe
×
175
    }
176
    std::string message_name = name_it->second;
8✔
177

178
    // Unregister from SystemImpl - no lock-order-inversion with CallbackList pattern
179
    _system_impl->unregister_libmav_message_handler(message_name, &handle);
8✔
180

181
    // Remove from CallbackList and message name map - CallbackList handles thread safety
182
    _message_subscriptions.unsubscribe(handle);
8✔
183
    _handle_to_message_name.erase(handle);
8✔
184
}
8✔
185

186
std::optional<uint32_t> MavlinkDirectImpl::message_name_to_id(const std::string& name) const
×
187
{
188
    // Get MessageSet to access message definitions
189
    auto& message_set = _system_impl->get_message_set();
×
190

191
    // Use MessageSet's message name to ID conversion
192
    auto id_opt = message_set.idForMessage(name);
×
193
    if (id_opt.has_value()) {
×
194
        return static_cast<uint32_t>(id_opt.value());
×
195
    }
196

197
    return std::nullopt;
×
198
}
199

200
std::optional<std::string> MavlinkDirectImpl::message_id_to_name(uint32_t id) const
×
201
{
202
    // Get MessageSet to access message definitions
203
    auto& message_set = _system_impl->get_message_set();
×
204

205
    // Use MessageSet's message ID to name conversion
206
    auto message_def = message_set.getMessageDefinition(static_cast<int>(id));
×
207
    if (message_def) {
×
208
        return message_def.get().name();
×
209
    }
210
    return std::nullopt;
×
211
}
212

213
Json::Value MavlinkDirectImpl::libmav_to_json(const mav::Message& msg) const
×
214
{
215
    (void)msg;
216
    // TODO: Implement field iteration once libmav noexcept API is stable
217
    return Json::Value();
×
218
}
219

220
bool MavlinkDirectImpl::json_to_libmav(const Json::Value& json, mav::Message& msg) const
×
221
{
222
    (void)json;
223
    (void)msg;
224
    // TODO: Implement once libmav noexcept API set methods are stable
225
    return false;
×
226
}
227

228
bool MavlinkDirectImpl::json_to_libmav_message(
10✔
229
    const std::string& json_string, mav::Message& msg) const
230
{
231
    Json::Value json;
10✔
232
    Json::Reader reader;
10✔
233

234
    if (!reader.parse(json_string, json)) {
10✔
235
        LogErr() << "Failed to parse JSON: " << json_string;
×
236
        return false;
×
237
    }
238

239
    // Iterate through all JSON fields and set them in the libmav message
240
    for (const auto& field_name : json.getMemberNames()) {
87✔
241
        const Json::Value& field_value = json[field_name];
77✔
242

243
        // Convert JSON values to appropriate types and set in message
244
        if (field_value.isInt()) {
77✔
245
            auto result = msg.set(field_name, static_cast<int32_t>(field_value.asInt()));
66✔
246
            if (result != ::mav::MessageResult::Success) {
66✔
247
                // Try as other integer types
248
                if (msg.set(field_name, static_cast<uint32_t>(field_value.asUInt())) !=
×
249
                        ::mav::MessageResult::Success &&
×
250
                    msg.set(field_name, static_cast<int16_t>(field_value.asInt())) !=
×
251
                        ::mav::MessageResult::Success &&
×
252
                    msg.set(field_name, static_cast<uint16_t>(field_value.asUInt())) !=
×
253
                        ::mav::MessageResult::Success &&
×
254
                    msg.set(field_name, static_cast<int8_t>(field_value.asInt())) !=
×
255
                        ::mav::MessageResult::Success &&
×
256
                    msg.set(field_name, static_cast<uint8_t>(field_value.asUInt())) !=
×
257
                        ::mav::MessageResult::Success) {
258
                    LogWarn() << "Failed to set integer field " << field_name << " = "
×
259
                              << field_value.asInt();
×
260
                }
261
            }
262
        } else if (field_value.isUInt()) {
11✔
263
            auto result = msg.set(field_name, static_cast<uint32_t>(field_value.asUInt()));
×
264
            if (result != ::mav::MessageResult::Success) {
×
265
                // Try as other unsigned integer types
266
                if (msg.set(field_name, static_cast<uint64_t>(field_value.asUInt64())) !=
×
267
                        ::mav::MessageResult::Success &&
×
268
                    msg.set(field_name, static_cast<uint16_t>(field_value.asUInt())) !=
×
269
                        ::mav::MessageResult::Success &&
×
270
                    msg.set(field_name, static_cast<uint8_t>(field_value.asUInt())) !=
×
271
                        ::mav::MessageResult::Success) {
272
                    LogWarn() << "Failed to set unsigned integer field " << field_name << " = "
×
273
                              << field_value.asUInt();
×
274
                }
275
            }
276
        } else if (field_value.isDouble()) {
11✔
277
            auto result = msg.set(field_name, static_cast<float>(field_value.asFloat()));
×
278
            if (result != ::mav::MessageResult::Success) {
×
279
                // Try as double
280
                if (msg.set(field_name, field_value.asDouble()) != ::mav::MessageResult::Success) {
×
281
                    LogWarn() << "Failed to set float/double field " << field_name << " = "
×
282
                              << field_value.asDouble();
×
283
                }
284
            }
285
        } else if (field_value.isString()) {
11✔
286
            auto result = msg.setString(field_name, field_value.asString());
1✔
287
            if (result != ::mav::MessageResult::Success) {
1✔
288
                LogWarn() << "Failed to set string field " << field_name << " = "
×
289
                          << field_value.asString();
×
290
            }
291
        } else if (field_value.isArray()) {
10✔
292
            // Handle array fields
293
            auto array_size = field_value.size();
10✔
294

295
            // Try different vector types based on typical MAVLink array field types
296
            std::vector<uint8_t> uint8_vec;
10✔
297
            std::vector<uint16_t> uint16_vec;
10✔
298
            std::vector<uint32_t> uint32_vec;
10✔
299
            std::vector<int8_t> int8_vec;
10✔
300
            std::vector<int16_t> int16_vec;
10✔
301
            std::vector<int32_t> int32_vec;
10✔
302
            std::vector<float> float_vec;
10✔
303
            std::vector<double> double_vec;
10✔
304

305
            // Convert JSON array to vectors of different types
306
            uint8_vec.reserve(array_size);
10✔
307
            uint16_vec.reserve(array_size);
10✔
308
            uint32_vec.reserve(array_size);
10✔
309
            int8_vec.reserve(array_size);
10✔
310
            int16_vec.reserve(array_size);
10✔
311
            int32_vec.reserve(array_size);
10✔
312
            float_vec.reserve(array_size);
10✔
313
            double_vec.reserve(array_size);
10✔
314

315
            for (Json::ArrayIndex i = 0; i < array_size; ++i) {
210✔
316
                const auto& elem = field_value[i];
200✔
317
                if (elem.isNumeric()) {
200✔
318
                    uint8_vec.push_back(static_cast<uint8_t>(elem.asUInt()));
200✔
319
                    uint16_vec.push_back(static_cast<uint16_t>(elem.asUInt()));
200✔
320
                    uint32_vec.push_back(static_cast<uint32_t>(elem.asUInt()));
200✔
321
                    int8_vec.push_back(static_cast<int8_t>(elem.asInt()));
200✔
322
                    int16_vec.push_back(static_cast<int16_t>(elem.asInt()));
200✔
323
                    int32_vec.push_back(static_cast<int32_t>(elem.asInt()));
200✔
324
                    float_vec.push_back(static_cast<float>(elem.asFloat()));
200✔
325
                    double_vec.push_back(elem.asDouble());
200✔
326
                } else {
327
                    // Default to 0 for non-numeric values
328
                    uint8_vec.push_back(0);
×
329
                    uint16_vec.push_back(0);
×
330
                    uint32_vec.push_back(0);
×
331
                    int8_vec.push_back(0);
×
332
                    int16_vec.push_back(0);
×
333
                    int32_vec.push_back(0);
×
334
                    float_vec.push_back(0.0f);
×
335
                    double_vec.push_back(0.0);
×
336
                }
337
            }
338

339
            // Try to set the array field with different vector types
340
            if (msg.set(field_name, uint8_vec) == ::mav::MessageResult::Success ||
10✔
341
                msg.set(field_name, uint16_vec) == ::mav::MessageResult::Success ||
×
342
                msg.set(field_name, uint32_vec) == ::mav::MessageResult::Success ||
×
343
                msg.set(field_name, int8_vec) == ::mav::MessageResult::Success ||
×
344
                msg.set(field_name, int16_vec) == ::mav::MessageResult::Success ||
×
345
                msg.set(field_name, int32_vec) == ::mav::MessageResult::Success ||
×
346
                msg.set(field_name, float_vec) == ::mav::MessageResult::Success ||
10✔
347
                msg.set(field_name, double_vec) == ::mav::MessageResult::Success) {
×
348
                // Successfully set the array field
349
            } else {
350
                LogWarn() << "Failed to set array field " << field_name;
×
351
            }
352
        } else {
10✔
353
            LogWarn() << "Unsupported JSON field type for " << field_name;
×
354
        }
355
    }
10✔
356

357
    return true;
10✔
358
}
10✔
359

360
MavlinkDirect::Result MavlinkDirectImpl::load_custom_xml(const std::string& xml_content)
4✔
361
{
362
    if (_debugging) {
4✔
363
        LogDebug() << "Loading custom XML definitions...";
×
364
    }
365

366
    // Load the custom XML into the MessageSet with thread safety
367
    // This uses the thread-safe method that protects against concurrent read operations
368
    bool success = _system_impl->load_custom_xml_to_message_set(xml_content);
4✔
369

370
    if (success) {
4✔
371
        if (_debugging) {
4✔
372
            LogDebug() << "Successfully loaded custom XML definitions";
×
373
        }
374
        return MavlinkDirect::Result::Success;
4✔
375
    } else {
376
        LogErr() << "Failed to load custom XML definitions";
×
377
        return MavlinkDirect::Result::Error;
×
378
    }
379
}
380

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