Skip to content

Commit

Permalink
Merge pull request #2 from ThomasDebrunner/read-api
Browse files Browse the repository at this point in the history
Add nice data access API
  • Loading branch information
bkueng authored Nov 14, 2023
2 parents b1b9159 + ebb4a98 commit 2e8552e
Show file tree
Hide file tree
Showing 15 changed files with 1,673 additions and 201 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,4 @@ pyrightconfig.json
.ionide

# End of https://www.toptal.com/developers/gitignore/api/c++,meson,sonar,python,sonarqube,visualstudiocode,clion
.idea
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.24 FATAL_ERROR)
cmake_minimum_required(VERSION 3.22 FATAL_ERROR)

project(ulog_cpp LANGUAGES CXX)

Expand Down
82 changes: 63 additions & 19 deletions examples/ulog_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <string>
#include <ulog_cpp/data_container.hpp>
#include <ulog_cpp/reader.hpp>
#include <variant>

int main(int argc, char** argv)
{
Expand Down Expand Up @@ -44,26 +43,71 @@ int main(int argc, char** argv)
}

// Read out some data
// TODO: create a simpler API for this
const std::string message = "multirotor_motor_limits";
printf("%s timestamps: ", message.c_str());
for (const auto& sub : data_container->subscriptions()) {
if (sub.second.add_logged_message.messageName() == message) {
const auto& fields = data_container->messageFormats().at(message).fields();
// Expect the first field to be the timestamp
if (fields[0].name != "timestamp") {
printf("Error: first field is not 'timestamp'\n");
return -1;
}
for (const auto& data : sub.second.data) {
auto value = ulog_cpp::Value(
fields[0],
std::vector<uint8_t>(data.data().begin(), data.data().begin() + sizeof(uint64_t)));
printf("%lu, ", std::get<uint64_t>(value.data()));
}

// List all subscription names
auto subscription_names = data_container->subscriptionNames();
for (const auto& sub : subscription_names) {
std::cout << sub << "\n";
}

// Get a particular subscription
if (subscription_names.find("vehicle_status") != subscription_names.end()) {
const auto& subscription = data_container->subscription("vehicle_status");

// Get message format of subscription
auto message_format = subscription->format();
std::cout << "Message format: " << message_format->name() << "\n";

// List all field names
std::cout << "Field names: "
<< "\n";
for (const std::string& field : subscription->fieldNames()) {
std::cout << field << "\n";
}

// Get particular field
auto nav_state_field = subscription->field("nav_state");

// Iterate over all samples
std::cout << "nav_state values: \n";
for (const auto& sample : *subscription) {
// always correctly extracts the type as defined in the message definition,
// gets cast to the value you put in int.
// This also works for arrays and strings.
auto nav_state = sample[nav_state_field].as<int>();
std::cout << nav_state << ", ";
}
std::cout << "\n";

// get a specific sample
auto sample_12 = subscription->at(12);

// access values by name
auto timestamp = sample_12["timestamp"].as<uint64_t>();

std::cout << timestamp << "\n";
} else {
std::cout << "No vehicle_status subscription found\n";
}

if (data_container->messageFormats().find("esc_status") !=
data_container->messageFormats().end()) {
const auto& message_format = data_container->messageFormats().at("esc_status");
std::cout << "Message format: " << message_format->name() << "\n";
for (const auto& field_name : message_format->fieldNames()) {
std::cout << field_name << "\n";
}
} else {
std::cout << "No esc_status message format found\n";
}
printf("\n");

if (subscription_names.find("esc_status") != subscription_names.end()) {
auto esc_status = data_container->subscription("esc_status");
for (const auto& sample : *esc_status) {
std::cout << "esc_power: " << sample["esc"][7]["esc_power"].as<int>() << "\n";
}
} else {
std::cout << "No esc_status subscription found\n";
}
return 0;
}
62 changes: 25 additions & 37 deletions examples/ulog_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,28 @@ int main(int argc, char** argv)
printf("Dropouts: %zu, total duration: %i ms\n", dropouts.size(), total_dropouts_ms);

auto print_value = [](const std::string& name, const ulog_cpp::Value& value) {
if (const auto* const str_ptr(std::get_if<std::string>(&value.data())); str_ptr) {
printf(" %s: %s\n", name.c_str(), str_ptr->c_str());
} else if (const auto* const int_ptr(std::get_if<int32_t>(&value.data())); int_ptr) {
printf(" %s: %i\n", name.c_str(), *int_ptr);
} else if (const auto* const uint_ptr(std::get_if<uint32_t>(&value.data())); uint_ptr) {
printf(" %s: %u\n", name.c_str(), *uint_ptr);
} else if (const auto* const float_ptr(std::get_if<float>(&value.data())); float_ptr) {
printf(" %s: %.3f\n", name.c_str(), static_cast<double>(*float_ptr));
} else {
printf(" %s: <data>\n", name.c_str());
}
std::visit(
[&name](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::string>) {
printf(" %s: %s\n", name.c_str(), arg.c_str());
} else if constexpr (std::is_same_v<T, int32_t>) {
printf(" %s: %i\n", name.c_str(), arg);
} else if constexpr (std::is_same_v<T, uint32_t>) {
printf(" %s: %u\n", name.c_str(), arg);
} else if constexpr (std::is_same_v<T, float>) {
printf(" %s: %.3f\n", name.c_str(), static_cast<double>(arg));
} else {
printf(" %s: <data>\n", name.c_str());
}
},
value.asNativeTypeVariant());
};

// Info messages
printf("Info Messages:\n");
for (const auto& info_msg : data_container->messageInfo()) {
print_value(info_msg.second.field().name, info_msg.second.value());
print_value(info_msg.second.field().name(), info_msg.second.value());
}
// Info multi messages
printf("Info Multiple Messages:");
Expand All @@ -82,35 +87,18 @@ int main(int argc, char** argv)
// Messages
printf("\n");
printf("Name (multi id) - number of data points\n");

// Sort by name & multi id
const auto& subscriptions = data_container->subscriptions();
std::vector<uint16_t> sorted_subscription_ids(subscriptions.size());
std::transform(subscriptions.begin(), subscriptions.end(), sorted_subscription_ids.begin(),
[](const auto& pair) { return pair.first; });
std::sort(sorted_subscription_ids.begin(), sorted_subscription_ids.end(),
[&subscriptions](const uint16_t a, const uint16_t b) {
const auto& add_logged_a = subscriptions.at(a).add_logged_message;
const auto& add_logged_b = subscriptions.at(b).add_logged_message;
if (add_logged_a.messageName() == add_logged_b.messageName()) {
return add_logged_a.multiId() < add_logged_b.multiId();
}
return add_logged_a.messageName() < add_logged_b.messageName();
});
for (const auto& subscription_id : sorted_subscription_ids) {
const auto& subscription = subscriptions.at(subscription_id);
const int multi_instance = subscription.add_logged_message.multiId();
const std::string message_name = subscription.add_logged_message.messageName();
printf(" %s (%i) - %zu\n", message_name.c_str(), multi_instance, subscription.data.size());
for (const auto& item : data_container->subscriptionsByNameAndMultiId()) {
printf(" %s (%i) - %zu\n", item.first.name.c_str(), item.first.multi_id,
item.second->size());
}

printf("Formats:\n");
for (const auto& msg_format : data_container->messageFormats()) {
std::string format_fields;
for (const auto& field : msg_format.second.fields()) {
format_fields += field.encode() + ", ";
for (const auto& field : msg_format.second->fields()) {
format_fields += field->encode() + ", ";
}
printf(" %s: %s\n", msg_format.second.name().c_str(), format_fields.c_str());
printf(" %s: %s\n", msg_format.second->name().c_str(), format_fields.c_str());
}

// logging
Expand All @@ -127,11 +115,11 @@ int main(int argc, char** argv)
// Params (init, after, defaults)
printf("Default Params:\n");
for (const auto& default_param : data_container->defaultParameters()) {
print_value(default_param.second.field().name, default_param.second.value());
print_value(default_param.second.field().name(), default_param.second.value());
}
printf("Initial Params:\n");
for (const auto& default_param : data_container->initialParameters()) {
print_value(default_param.second.field().name, default_param.second.value());
print_value(default_param.second.field().name(), default_param.second.value());
}

return 0;
Expand Down
1 change: 0 additions & 1 deletion examples/ulog_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
****************************************************************************/

#include <chrono>
#include <cstdint>
#include <string>
#include <thread>
#include <ulog_cpp/simple_writer.hpp>
Expand Down
16 changes: 12 additions & 4 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@


include(FetchContent)

set(SYSTEM_ARG SYSTEM)
# cmake < 3.25 fails with system attribute
if (CMAKE_VERSION VERSION_LESS "3.25")
set(SYSTEM_ARG "")
endif ()
FetchContent_Declare(
DocTest
GIT_REPOSITORY "https://github.com/doctest/doctest.git"
GIT_TAG "v2.4.11"
SYSTEM
DocTest
GIT_REPOSITORY "https://github.com/doctest/doctest.git"
GIT_TAG "v2.4.11"
${SYSTEM_ARG}
)


FetchContent_MakeAvailable(DocTest)


add_executable(tests
main.cpp
ulog_parsing_test.cpp
read_api_test.cpp
)

target_link_libraries(tests PUBLIC
Expand Down
Loading

0 comments on commit 2e8552e

Please sign in to comment.