Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nice data access API #2

Merged
merged 12 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading