diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml new file mode 100644 index 0000000..0922058 --- /dev/null +++ b/.github/workflows/tools.yml @@ -0,0 +1,17 @@ +name: Build tools + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ master ] + +jobs: + csv_to_ulog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build csv_to_ulog + run: cd tools/csv_to_ulog && mkdir build && cd build && cmake .. && make + - name: Test run csv_to_ulog + run: tools/csv_to_ulog/build/csv_to_ulog diff --git a/.gitignore b/.gitignore index 0ee1924..f7a56be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.hex *.elf -gazebo/build/ +build/ tools/log/ .dependencies diff --git a/tools/csv_to_ulog/CMakeLists.txt b/tools/csv_to_ulog/CMakeLists.txt new file mode 100644 index 0000000..3e094b3 --- /dev/null +++ b/tools/csv_to_ulog/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) +project(csv_to_ulog) +include(FetchContent) +set(CMAKE_CXX_STANDARD 17) + +FetchContent_Declare( + ulog_cpp + GIT_REPOSITORY https://github.com/PX4/ulog_cpp.git + GIT_TAG cf24ec6 +) + +FetchContent_Declare( + rapidcsv + GIT_REPOSITORY https://github.com/d99kris/rapidcsv.git + GIT_TAG v8.82 +) + +FetchContent_MakeAvailable(ulog_cpp) +FetchContent_MakeAvailable(rapidcsv) + +add_executable(csv_to_ulog csv_to_ulog.cpp) +target_link_libraries(csv_to_ulog PUBLIC ulog_cpp::ulog_cpp) +target_include_directories(csv_to_ulog PUBLIC ${rapidcsv_SOURCE_DIR}/src) diff --git a/tools/csv_to_ulog/csv_to_ulog.cpp b/tools/csv_to_ulog/csv_to_ulog.cpp new file mode 100644 index 0000000..b91f199 --- /dev/null +++ b/tools/csv_to_ulog/csv_to_ulog.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Oleg Kalachev +// Repository: https://github.com/okalachev/flix + +// Tool for conversion CSV log file to ULog format + +#include +#include +#include +#include "rapidcsv.h" + +using std::vector; + +struct Data { + uint64_t timestamp; + float values[30]; +}; + +int main(int argc, char** argv) +{ + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + // check input file exists + if (!std::filesystem::exists(argv[1])) { + printf("Input file \"%s\" does not exist\n", argv[1]); + return -1; + } + + // open csv file + rapidcsv::Document csv(argv[1]); + auto columns = csv.GetColumnNames(); + + const char* msg_name = "state"; + + ulog_cpp::SimpleWriter writer(argv[2], 0); + writer.writeInfo("sys_name", "flix"); + + vector fields; + fields.push_back(ulog_cpp::Field("uint64_t", "timestamp")); + columns.erase(columns.begin()); // remove timestamp column + for (auto& column : columns) { + std::replace(column.begin(), column.end(), '.', '_'); // replace dots with underscores + std::transform(column.begin(), column.end(), column.begin(), [](unsigned char c) { return std::tolower(c); }); // lowercase column name + fields.push_back(ulog_cpp::Field("float", column)); + } + + writer.writeMessageFormat(msg_name, fields); + writer.headerComplete(); + + const uint16_t msg_id = writer.writeAddLoggedMessage(msg_name); + + for (size_t i = 0; i < csv.GetRowCount(); i++) { + Data data; + data.timestamp = csv.GetCell(0, i) * 1000000.0; + for (size_t j = 1; j < columns.size(); j++) { + data.values[j] = csv.GetCell(j, i); + } + writer.writeData(msg_id, data); + } +}