diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4005b9a..e4442ff 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable(example_mcap_writer example_mcap_writer.cpp) add_executable(example_native_binary_reader example_native_binary_reader.cpp) add_executable(example_txth_reader example_txth_reader.cpp) +add_executable(example_txth_writer example_txth_writer.cpp) add_executable(convert_osi2mcap convert_osi2mcap.cpp) # Link against OSI made available by parent CMakeLists.txt @@ -9,11 +10,13 @@ if(LINK_WITH_SHARED_OSI) target_link_libraries(example_mcap_writer PRIVATE open_simulation_interface) target_link_libraries(example_native_binary_reader PRIVATE open_simulation_interface) target_link_libraries(example_txth_reader PRIVATE open_simulation_interface) + target_link_libraries(example_txth_writer PRIVATE open_simulation_interface) target_link_libraries(convert_osi2mcap PRIVATE open_simulation_interface) else() target_link_libraries(example_mcap_writer PRIVATE open_simulation_interface_pic) target_link_libraries(example_native_binary_reader PRIVATE open_simulation_interface_pic) target_link_libraries(example_txth_reader PRIVATE open_simulation_interface_pic) + target_link_libraries(example_txth_writer PRIVATE open_simulation_interface_pic) target_link_libraries(convert_osi2mcap PRIVATE open_simulation_interface_pic) include_directories(${OSI_INCLUDE_DIR}) endif() @@ -22,10 +25,12 @@ endif() target_link_libraries(example_mcap_writer PRIVATE OSIUtilities) target_link_libraries(example_native_binary_reader PRIVATE OSIUtilities) target_link_libraries(example_txth_reader PRIVATE OSIUtilities) +target_link_libraries(example_txth_writer PRIVATE OSIUtilities) target_link_libraries(convert_osi2mcap PRIVATE OSIUtilities) # Specify the public headers for the library for clean access target_include_directories(example_mcap_writer PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(example_native_binary_reader PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(example_txth_reader PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_include_directories(example_txth_writer PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(convert_osi2mcap PUBLIC ${PROJECT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/examples/convert_osi2mcap.cpp b/examples/convert_osi2mcap.cpp index f517f57..ddaa891 100644 --- a/examples/convert_osi2mcap.cpp +++ b/examples/convert_osi2mcap.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 // -#include -#include +#include +#include #include diff --git a/examples/example_mcap_writer.cpp b/examples/example_mcap_writer.cpp index 442f3e6..3e9eef1 100644 --- a/examples/example_mcap_writer.cpp +++ b/examples/example_mcap_writer.cpp @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MPL-2.0 // -#include +#include #include diff --git a/examples/example_txth_writer.cpp b/examples/example_txth_writer.cpp new file mode 100644 index 0000000..c949351 --- /dev/null +++ b/examples/example_txth_writer.cpp @@ -0,0 +1,66 @@ +// +// Copyright (c) 2024, Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// SPDX-License-Identifier: MPL-2.0 +// +#include + +#include + +#include "osi_sensordata.pb.h" +#include "osi_version.pb.h" + +std::string GenerateTempFilePath() { + const auto path = std::filesystem::temp_directory_path() / "example_txth_writer.txth"; + return path.string(); +} + +int main(int argc, const char** argv) { + std::cout << "Starting TXTH Writer example:" << std::endl; + + // Create writer and open file + auto tracefile_writer = osi3::txthTraceFileWriter(); + const auto tracefile_path = GenerateTempFilePath(); + std::cout << "Creating tracefile at " << tracefile_path << std::endl; + tracefile_writer.Open(tracefile_path); + + // create OSI data to store + const auto osi_version = osi3::InterfaceVersion::descriptor()->file()->options().GetExtension(osi3::current_interface_version); + + osi3::SensorView sensor_view; + sensor_view.mutable_version()->CopyFrom(osi_version); + sensor_view.mutable_sensor_id()->set_value(0); + + auto* const ground_truth = sensor_view.mutable_global_ground_truth(); + ground_truth->mutable_version()->CopyFrom(osi_version); + + auto* const host_vehicle = ground_truth->mutable_moving_object()->Add(); + host_vehicle->mutable_id()->set_value(12); + host_vehicle->mutable_vehicle_classification()->set_type(osi3::MovingObject_VehicleClassification_Type_TYPE_SMALL_CAR); + host_vehicle->mutable_base()->mutable_dimension()->set_length(5); + host_vehicle->mutable_base()->mutable_dimension()->set_width(2); + host_vehicle->mutable_base()->mutable_dimension()->set_height(1.5); + host_vehicle->mutable_base()->mutable_velocity()->set_x(10.0); + + // write the data continuously in a loop + constexpr double kTimeStepSizeS = 0.1; // NOLINT + for (int i = 0; i < 10; ++i) { + // manipulate the data so not every message is the same + auto timestamp = sensor_view.timestamp().seconds() * 1000000000 + sensor_view.timestamp().nanos(); + timestamp += 100000000; + sensor_view.mutable_timestamp()->set_nanos(timestamp % 1000000000); + sensor_view.mutable_timestamp()->set_seconds(timestamp / 1000000000); + ground_truth->mutable_timestamp()->set_nanos(timestamp % 1000000000); + ground_truth->mutable_timestamp()->set_seconds(timestamp / 1000000000); + const auto old_position = host_vehicle->base().position().x(); + const auto new_position = old_position + host_vehicle->base().velocity().x() * kTimeStepSizeS; + host_vehicle->mutable_base()->mutable_position()->set_x(new_position); + + // write the data + tracefile_writer.WriteMessage(sensor_view); + } + + tracefile_writer.Close(); + + std::cout << "Finished TXTH Writer example" << std::endl; + return 0; +} \ No newline at end of file diff --git a/include/osi-utilities/tracefile/Writer.h b/include/osi-utilities/tracefile/Writer.h index 4984a43..d251423 100644 --- a/include/osi-utilities/tracefile/Writer.h +++ b/include/osi-utilities/tracefile/Writer.h @@ -47,6 +47,15 @@ class TraceFileWriter { */ virtual bool Open(const std::string& filename) = 0; + /** + * @brief Writes a protobuf message to the file + * @tparam T Type of the protobuf message + * @param top_level_message The message to write + * @return true if successful, false otherwise + */ + template + bool WriteMessage(T top_level_message) = delete; + /** * @brief Writes a protobuf message to the file * @tparam T Type of the protobuf message @@ -63,7 +72,7 @@ class TraceFileWriter { * @param metadata_entries Key-value pairs of metadata * @return true if successful, false otherwise */ - virtual bool SetMetadata(const std::string& name, const std::unordered_map& metadata_entries) { return false; } + virtual bool SetMetadata(const std::string& name, const std::unordered_map& metadata_entries) = 0; /** * @brief Closes the trace file diff --git a/include/osi-utilities/tracefile/writer/txthTraceFileWriter.h b/include/osi-utilities/tracefile/writer/txthTraceFileWriter.h new file mode 100644 index 0000000..75c2ae8 --- /dev/null +++ b/include/osi-utilities/tracefile/writer/txthTraceFileWriter.h @@ -0,0 +1,31 @@ +// +// Copyright (c) 2024, Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// SPDX-License-Identifier: MPL-2.0 +// + + +#ifndef OSIUTILITIES_TRACEFILE_WRITER_TXTHTRACEFILEWRITER_H_ +#define OSIUTILITIES_TRACEFILE_WRITER_TXTHTRACEFILEWRITER_H_ + +#include "../Writer.h" +#include +#include + +namespace osi3 { + +class txthTraceFileWriter final : public TraceFileWriter { +public: + bool Open(const std::string& filename) override; + void Close() override; + bool SetMetadata(const std::string& name, const std::unordered_map& metadata_entries) { return false; } + + template + bool WriteMessage(T top_level_message); + +private: + std::ofstream trace_file_; + bool file_open_{false}; +}; + +} // namespace osi3 +#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8706ae..07b68af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ set(OSIUtilities_SRCS tracefile/MCAPImplementation.cpp tracefile/writer/Writer.cpp tracefile/writer/MCAPTraceFileWriter.cpp + tracefile/writer/txthTraceFileWriter.cpp tracefile/reader/Reader.cpp tracefile/reader/NativeBinaryTraceFileReader.cpp tracefile/reader/txthTraceFileReader.cpp diff --git a/src/tracefile/writer/txthTraceFileWriter.cpp b/src/tracefile/writer/txthTraceFileWriter.cpp new file mode 100644 index 0000000..cfaf930 --- /dev/null +++ b/src/tracefile/writer/txthTraceFileWriter.cpp @@ -0,0 +1,69 @@ +// +// Copyright (c) 2024, Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// SPDX-License-Identifier: MPL-2.0 +// + +#include "osi-utilities/tracefile/writer/txthTraceFileWriter.h" + +#include "osi_groundtruth.pb.h" +#include "osi_hostvehicledata.pb.h" +#include "osi_motionrequest.pb.h" +#include "osi_sensordata.pb.h" +#include "osi_sensorview.pb.h" +#include "osi_streamingupdate.pb.h" +#include "osi_trafficcommand.pb.h" +#include "osi_trafficcommandupdate.pb.h" +#include "osi_trafficupdate.pb.h" + +namespace osi3 { + +bool txthTraceFileWriter::Open(const std::string& filename) { + if (filename.substr(filename.length() - 5) != ".txth") { + std::cerr << "Error: Filename must end with .txth extension\n"; + return false; + } + + trace_file_.open(filename); + if (trace_file_.is_open()) { + file_open_ = true; + return true; + } + return false; +} + +void txthTraceFileWriter::Close() { + if (file_open_) { + trace_file_.close(); + file_open_ = false; + } +} + +template +bool txthTraceFileWriter::WriteMessage(T top_level_message) { + if (!file_open_) { + std::cerr << "Error: Cannot write message, file is not open\n"; + return false; + } + + std::string text_output; + if (!google::protobuf::TextFormat::PrintToString(top_level_message, &text_output)) { + std::cerr << "Error: Failed to convert message to text format\n"; + return false; + } + + trace_file_ << text_output; + return true; +} + +// Template instantiations for allowed OSI top-level messages +template bool txthTraceFileWriter::WriteMessage(osi3::GroundTruth); +template bool txthTraceFileWriter::WriteMessage(osi3::SensorData); +template bool txthTraceFileWriter::WriteMessage(osi3::SensorView); +template bool txthTraceFileWriter::WriteMessage(osi3::HostVehicleData); +template bool txthTraceFileWriter::WriteMessage(osi3::TrafficCommand); +template bool txthTraceFileWriter::WriteMessage(osi3::TrafficCommandUpdate); +template bool txthTraceFileWriter::WriteMessage(osi3::TrafficUpdate); +template bool txthTraceFileWriter::WriteMessage(osi3::MotionRequest); +template bool txthTraceFileWriter::WriteMessage(osi3::StreamingUpdate); + +} // namespace osi3 \ No newline at end of file