From ab362f8c4b79c21a33ff24dcb7405c3c75fb8a59 Mon Sep 17 00:00:00 2001 From: KerstinKeller Date: Wed, 4 Dec 2024 13:32:48 +0100 Subject: [PATCH] [measurement] HDF5 v6, HDF5 API v3 (#1375) 2 Hdf5 file format versions supported for writing (v5, v6). 2 API versions available (v2, v3). V6 Supports storing multiple channels with the same name. All eCAL tools still write file format version v5, using API version v2. --- app/meas_cutter/CMakeLists.txt | 2 +- app/meas_cutter/src/measurement_exporter.cpp | 11 +- app/meas_cutter/src/measurement_exporter.h | 11 +- app/meas_cutter/src/measurement_importer.cpp | 6 +- app/meas_cutter/src/measurement_importer.h | 14 +- app/meas_cutter/src/utils.h | 6 +- app/play/play_core/CMakeLists.txt | 4 +- app/play/play_core/src/ecal_play.cpp | 16 +- .../play_core/src/measurement_container.cpp | 6 +- .../play_core/src/measurement_container.h | 8 +- app/play/play_core/src/play_thread.cpp | 4 +- app/play/play_core/src/play_thread.h | 4 +- app/rec/rec_client_core/CMakeLists.txt | 4 +- .../src/job/hdf5_writer_thread.cpp | 9 +- .../src/job/hdf5_writer_thread.h | 6 +- contrib/ecalhdf5/CMakeLists.txt | 20 +- contrib/ecalhdf5/include/ecalhdf5/eh5_defs.h | 10 +- contrib/ecalhdf5/include/ecalhdf5/eh5_meas.h | 321 +------- .../include/ecalhdf5/eh5_meas_api_v2.h | 351 ++++++++ .../include/ecalhdf5/eh5_meas_api_v3.h | 312 ++++++++ contrib/ecalhdf5/include/ecalhdf5/eh5_types.h | 32 +- contrib/ecalhdf5/src/datatype_helper.cpp | 60 ++ contrib/ecalhdf5/src/datatype_helper.h | 34 + contrib/ecalhdf5/src/eh5_meas_api_v2.cpp | 283 +++++++ .../src/{eh5_meas.cpp => eh5_meas_api_v3.cpp} | 127 ++- contrib/ecalhdf5/src/eh5_meas_dir.cpp | 171 ++-- contrib/ecalhdf5/src/eh5_meas_dir.h | 66 +- contrib/ecalhdf5/src/eh5_meas_file_v1.cpp | 50 +- contrib/ecalhdf5/src/eh5_meas_file_v1.h | 41 +- contrib/ecalhdf5/src/eh5_meas_file_v2.cpp | 121 +-- contrib/ecalhdf5/src/eh5_meas_file_v2.h | 53 +- contrib/ecalhdf5/src/eh5_meas_file_v3.cpp | 8 +- contrib/ecalhdf5/src/eh5_meas_file_v3.h | 6 +- contrib/ecalhdf5/src/eh5_meas_file_v4.cpp | 8 +- contrib/ecalhdf5/src/eh5_meas_file_v4.h | 6 +- contrib/ecalhdf5/src/eh5_meas_file_v5.cpp | 8 +- contrib/ecalhdf5/src/eh5_meas_file_v5.h | 6 +- contrib/ecalhdf5/src/eh5_meas_file_v6.cpp | 128 +++ contrib/ecalhdf5/src/eh5_meas_file_v6.h | 75 ++ .../ecalhdf5/src/eh5_meas_file_writer_v5.cpp | 38 +- .../ecalhdf5/src/eh5_meas_file_writer_v5.h | 38 +- .../ecalhdf5/src/eh5_meas_file_writer_v6.cpp | 339 ++++++++ .../ecalhdf5/src/eh5_meas_file_writer_v6.h | 318 ++++++++ contrib/ecalhdf5/src/eh5_meas_impl.h | 64 +- contrib/ecalhdf5/src/escape.cpp | 14 +- contrib/ecalhdf5/src/escape.h | 5 +- contrib/ecalhdf5/src/hdf5_helper.cpp | 274 +++++++ contrib/ecalhdf5/src/hdf5_helper.h | 68 ++ .../include/ecal/measurement/base/reader.h | 43 +- .../include/ecal/measurement/base/types.h | 94 ++- .../include/ecal/measurement/base/writer.h | 7 +- .../include/ecal/measurement/imeasurement.h | 47 +- .../include/ecal/measurement/measurement.h | 4 +- .../include/ecal/measurement/omeasurement.h | 37 +- .../include/ecal/measurement/hdf5/reader.h | 40 +- .../include/ecal/measurement/hdf5/writer.h | 15 +- contrib/measurement/hdf5/src/reader.cpp | 76 +- contrib/measurement/hdf5/src/writer.cpp | 50 +- lang/python/ecalhdf5/src/ecalhdf5_wrap.cxx | 8 +- .../cpp/measurement/benchmark/src/main.cpp | 4 +- .../measurement_read/src/measurement_read.cpp | 19 +- .../contrib/ecalhdf5/hdf5_test/CMakeLists.txt | 2 + .../ecalhdf5/hdf5_test/src/hdf5_test.cpp | 749 +++++++++++++++--- 63 files changed, 3673 insertions(+), 1088 deletions(-) create mode 100644 contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v2.h create mode 100644 contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v3.h create mode 100644 contrib/ecalhdf5/src/datatype_helper.cpp create mode 100644 contrib/ecalhdf5/src/datatype_helper.h create mode 100644 contrib/ecalhdf5/src/eh5_meas_api_v2.cpp rename contrib/ecalhdf5/src/{eh5_meas.cpp => eh5_meas_api_v3.cpp} (58%) create mode 100644 contrib/ecalhdf5/src/eh5_meas_file_v6.cpp create mode 100644 contrib/ecalhdf5/src/eh5_meas_file_v6.h create mode 100644 contrib/ecalhdf5/src/eh5_meas_file_writer_v6.cpp create mode 100644 contrib/ecalhdf5/src/eh5_meas_file_writer_v6.h create mode 100644 contrib/ecalhdf5/src/hdf5_helper.cpp create mode 100644 contrib/ecalhdf5/src/hdf5_helper.h diff --git a/app/meas_cutter/CMakeLists.txt b/app/meas_cutter/CMakeLists.txt index 637b0783c8..2c41c73ab6 100644 --- a/app/meas_cutter/CMakeLists.txt +++ b/app/meas_cutter/CMakeLists.txt @@ -55,7 +55,7 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} yaml-cpp::yaml-cpp tclap::tclap eCAL::ecal-utils - eCAL::measurement_hdf5 + eCAL::hdf5 Threads::Threads) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) diff --git a/app/meas_cutter/src/measurement_exporter.cpp b/app/meas_cutter/src/measurement_exporter.cpp index 32743aac5f..115c454cb2 100644 --- a/app/meas_cutter/src/measurement_exporter.cpp +++ b/app/meas_cutter/src/measurement_exporter.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,9 @@ */ #include "measurement_exporter.h" -#include MeasurementExporter::MeasurementExporter(): - _writer(std::make_unique()) + _writer(std::make_unique()) { } @@ -29,7 +28,7 @@ void MeasurementExporter::setPath(const std::string& path, const std::string& ba { _root_output_path = EcalUtils::Filesystem::CleanPath(path); _output_path = EcalUtils::Filesystem::CleanPath(_root_output_path + EcalUtils::Filesystem::NativeSeparator(EcalUtils::Filesystem::OsStyle::Current) + eCALMeasCutterUtils::kDefaultFolderOutput, EcalUtils::Filesystem::OsStyle::Current); - if (!_writer->Open(_output_path)) + if (!_writer->Open(_output_path, eCAL::eh5::v2::eAccessType::CREATE)) { throw ExporterException("Unable to create HDF5 protobuf output path " + path + "."); } @@ -76,10 +75,10 @@ void MeasurementExporter::setData(eCALMeasCutterUtils::Timestamp timestamp, cons const auto sender_timestamp = (iter != meta_data.end()) ? iter->second.sender_timestamp : static_cast(0); iter = meta_data.find(eCALMeasCutterUtils::MetaDatumKey::SENDER_ID); - const auto sender_id = (iter != meta_data.end()) ? iter->second.sender_id : static_cast(0); + const auto sender_id = (iter != meta_data.end()) ? iter->second.sender_id : 0; iter = meta_data.find(eCALMeasCutterUtils::MetaDatumKey::SENDER_CLOCK); - const auto sender_clock = (iter != meta_data.end()) ? iter->second.sender_clock : static_cast(0); + const auto sender_clock = (iter != meta_data.end()) ? iter->second.sender_clock : 0; if (!_writer->AddEntryToFile(payload.data(), payload.size(), sender_timestamp, timestamp, _current_channel_name, sender_id, sender_clock)) { diff --git a/app/meas_cutter/src/measurement_exporter.h b/app/meas_cutter/src/measurement_exporter.h index 3d8c51334d..eea8290381 100644 --- a/app/meas_cutter/src/measurement_exporter.h +++ b/app/meas_cutter/src/measurement_exporter.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,14 @@ */ #pragma once + #include -#include #include +#include +#include +#include -#include +#include #include #include "utils.h" @@ -43,7 +46,7 @@ class MeasurementExporter std::string getRootOutputPath() const; private: - std::unique_ptr _writer; + std::unique_ptr _writer; std::string _current_channel_name; std::string _output_path; std::string _root_output_path; diff --git a/app/meas_cutter/src/measurement_importer.cpp b/app/meas_cutter/src/measurement_importer.cpp index 8394ddb582..62ab0eb102 100644 --- a/app/meas_cutter/src/measurement_importer.cpp +++ b/app/meas_cutter/src/measurement_importer.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ */ #include "measurement_importer.h" -#include +#include MeasurementImporter::MeasurementImporter() : - _reader(std::make_unique()), + _reader(std::make_unique()), _current_opened_channel_data() { } diff --git a/app/meas_cutter/src/measurement_importer.h b/app/meas_cutter/src/measurement_importer.h index 0c20f3f5af..bd40390780 100644 --- a/app/meas_cutter/src/measurement_importer.h +++ b/app/meas_cutter/src/measurement_importer.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,16 @@ */ #pragma once -#include #include -#include #include -#include +#include +#include +#include +#include +#include #include -#include +#include #include "utils.h" @@ -53,7 +55,7 @@ class MeasurementImporter private: bool isEcalMeasFile(const std::string& path); bool isProtoChannel(const eCAL::experimental::measurement::base::DataTypeInformation& channel_info); - std::unique_ptr _reader; + std::unique_ptr _reader; eCALMeasCutterUtils::ChannelData _current_opened_channel_data; std::string _loaded_path; eCALMeasCutterUtils::ChannelNameSet _channel_names; diff --git a/app/meas_cutter/src/utils.h b/app/meas_cutter/src/utils.h index 9d40dee26f..48736c8f9b 100644 --- a/app/meas_cutter/src/utils.h +++ b/app/meas_cutter/src/utils.h @@ -312,10 +312,10 @@ namespace eCALMeasCutterUtils { Timestamp receiver_timestamp; Timestamp sender_timestamp; - uint64_t sender_id; - uint64_t sender_clock; + int64_t sender_id; + int64_t sender_clock; - std::array __union_size; + std::array __union_size; }; typedef std::unordered_map MetaData; diff --git a/app/play/play_core/CMakeLists.txt b/app/play/play_core/CMakeLists.txt index 29de1b037d..f99d6aed54 100644 --- a/app/play/play_core/CMakeLists.txt +++ b/app/play/play_core/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC eCAL::core_protobuf eCAL::app_pb eCAL::ecaltime_pb - eCAL::measurement_hdf5 + eCAL::hdf5 ) target_link_libraries(${PROJECT_NAME} PRIVATE diff --git a/app/play/play_core/src/ecal_play.cpp b/app/play/play_core/src/ecal_play.cpp index 45d9608688..9bda0456c8 100644 --- a/app/play/play_core/src/ecal_play.cpp +++ b/app/play/play_core/src/ecal_play.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,16 @@ #include "ecal_play.h" -#include +#include +#include #include +#include +#include #include -#include -#include #include "ecal_play_logger.h" #include "play_thread.h" -#include - +#include #include #include #include @@ -58,7 +58,7 @@ bool EcalPlay::LoadMeasurement(const std::string& path) { EcalPlayLogger::Instance()->info("Loading measurement..."); - std::shared_ptr measurement(std::make_shared()); + std::shared_ptr measurement(std::make_shared()); std::string meas_dir; // The directory of the measurement std::string path_to_load; // The actual path we load the measurement from. May be a directory or a .hdf5 file @@ -129,7 +129,7 @@ bool EcalPlay::LoadMeasurement(const std::string& path) void EcalPlay::CloseMeasurement() { description_ = ""; - play_thread_->SetMeasurement(std::shared_ptr(nullptr)); + play_thread_->SetMeasurement(std::shared_ptr(nullptr)); measurement_path_ = ""; clearScenariosPath(); channel_mapping_path_ = ""; diff --git a/app/play/play_core/src/measurement_container.cpp b/app/play/play_core/src/measurement_container.cpp index fbd80a6d51..71df78431a 100644 --- a/app/play/play_core/src/measurement_container.cpp +++ b/app/play/play_core/src/measurement_container.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,13 @@ #include "measurement_container.h" #include -#include +#include #include #include #include -MeasurementContainer::MeasurementContainer(std::shared_ptr hdf5_meas, const std::string& meas_dir, bool use_receive_timestamp) +MeasurementContainer::MeasurementContainer(std::shared_ptr hdf5_meas, const std::string& meas_dir, bool use_receive_timestamp) : hdf5_meas_ (hdf5_meas) , meas_dir_ (meas_dir) , use_receive_timestamp_ (use_receive_timestamp) diff --git a/app/play/play_core/src/measurement_container.h b/app/play/play_core/src/measurement_container.h index fa5cd691ff..abc07936f1 100644 --- a/app/play/play_core/src/measurement_container.h +++ b/app/play/play_core/src/measurement_container.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,14 +24,14 @@ #include #include -#include +#include #include "continuity_report.h" class MeasurementContainer { public: - MeasurementContainer(std::shared_ptr hdf5_meas, const std::string& meas_dir = "", bool use_receive_timestamp = true); + MeasurementContainer(std::shared_ptr hdf5_meas, const std::string& meas_dir = "", bool use_receive_timestamp = true); ~MeasurementContainer(); void CreatePublishers(); @@ -108,7 +108,7 @@ class MeasurementContainer PublisherInfo* publisher_info_; }; - std::shared_ptr hdf5_meas_; + std::shared_ptr hdf5_meas_; std::string meas_dir_; bool use_receive_timestamp_; diff --git a/app/play/play_core/src/play_thread.cpp b/app/play/play_core/src/play_thread.cpp index 3bf3a3f2c1..e4a00b0127 100644 --- a/app/play/play_core/src/play_thread.cpp +++ b/app/play/play_core/src/play_thread.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -576,7 +576,7 @@ void PlayThread::LogChannelMapping(const std::map& cha //// Measurement //// //////////////////////////////////////////////////////////////////////////////// -void PlayThread::SetMeasurement(const std::shared_ptr& measurement, const std::string& path) +void PlayThread::SetMeasurement(const std::shared_ptr& measurement, const std::string& path) { std::unique_ptr new_measurment_container; diff --git a/app/play/play_core/src/play_thread.h b/app/play/play_core/src/play_thread.h index 5e782f3b0e..f789421270 100644 --- a/app/play/play_core/src/play_thread.h +++ b/app/play/play_core/src/play_thread.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,7 +79,7 @@ class PlayThread : public InterruptibleThread * @param measurement The new measurement * @param path The (optional) path from where the measurement was loaded */ - void SetMeasurement(const std::shared_ptr& measurement, const std::string& path = ""); + void SetMeasurement(const std::shared_ptr& measurement, const std::string& path = ""); /** * @brief Returns whether a measurement has successfully been loaded diff --git a/app/rec/rec_client_core/CMakeLists.txt b/app/rec/rec_client_core/CMakeLists.txt index f0eda371be..0037e5ce0b 100644 --- a/app/rec/rec_client_core/CMakeLists.txt +++ b/app/rec/rec_client_core/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -98,7 +98,7 @@ target_link_libraries(${PROJECT_NAME} eCAL::core_pb eCAL::app_pb PRIVATE - eCAL::measurement_hdf5 + eCAL::hdf5 ThreadingUtils Threads::Threads eCAL::ecal-utils diff --git a/app/rec/rec_client_core/src/job/hdf5_writer_thread.cpp b/app/rec/rec_client_core/src/job/hdf5_writer_thread.cpp index a84d47e3f5..10065714cb 100644 --- a/app/rec/rec_client_core/src/job/hdf5_writer_thread.cpp +++ b/app/rec/rec_client_core/src/job/hdf5_writer_thread.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ */ #include "hdf5_writer_thread.h" -#include #include "rec_client_core/ecal_rec_logger.h" @@ -42,7 +41,7 @@ namespace eCAL , new_topic_info_map_available_(true) , flushing_ (false) { - hdf5_writer_ = std::make_unique(); + hdf5_writer_ = std::make_unique(); } Hdf5WriterThread::~Hdf5WriterThread() @@ -186,7 +185,7 @@ namespace eCAL frame->data_.size(), std::chrono::duration_cast(frame->ecal_publish_time_.time_since_epoch()).count(), std::chrono::duration_cast(frame->ecal_receive_time_.time_since_epoch()).count(), - frame->topic_name_, + frame->topic_name_, frame->id_, frame->clock_ )) @@ -265,7 +264,7 @@ namespace eCAL #endif // NDEBUG std::unique_lock hdf5_writer_lock(hdf5_writer_mutex_); - if (hdf5_writer_->Open(hdf5_dir)) + if (hdf5_writer_->Open(hdf5_dir, eCAL::eh5::v2::eAccessType::CREATE)) { #ifndef NDEBUG EcalRecLogger::Instance()->debug("Hdf5WriterThread::Open(): Successfully opened HDF5-Writer with path \"" + hdf5_dir + "\""); diff --git a/app/rec/rec_client_core/src/job/hdf5_writer_thread.h b/app/rec/rec_client_core/src/job/hdf5_writer_thread.h index 042b64eeba..91e3982c42 100644 --- a/app/rec/rec_client_core/src/job/hdf5_writer_thread.h +++ b/app/rec/rec_client_core/src/job/hdf5_writer_thread.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include #include @@ -94,7 +94,7 @@ namespace eCAL mutable RecHdf5JobStatus last_status_; mutable std::mutex hdf5_writer_mutex_; - std::unique_ptr hdf5_writer_; + std::unique_ptr hdf5_writer_; std::atomic flushing_; diff --git a/contrib/ecalhdf5/CMakeLists.txt b/contrib/ecalhdf5/CMakeLists.txt index d5285101c1..37b87df48f 100644 --- a/contrib/ecalhdf5/CMakeLists.txt +++ b/contrib/ecalhdf5/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,7 +29,10 @@ else() endif() set(ecalhdf5_src - src/eh5_meas.cpp + src/datatype_helper.cpp + src/datatype_helper.h + src/eh5_meas_api_v2.cpp + src/eh5_meas_api_v3.cpp src/eh5_meas_dir.cpp src/eh5_meas_dir.h src/eh5_meas_file_v1.cpp @@ -42,9 +45,15 @@ set(ecalhdf5_src src/eh5_meas_file_v4.h src/eh5_meas_file_v5.cpp src/eh5_meas_file_v5.h + src/eh5_meas_file_v6.cpp + src/eh5_meas_file_v6.h src/eh5_meas_file_writer_v5.cpp src/eh5_meas_file_writer_v5.h + src/eh5_meas_file_writer_v6.cpp + src/eh5_meas_file_writer_v6.h src/eh5_meas_impl.h + src/hdf5_helper.h + src/hdf5_helper.cpp src/escape.cpp src/escape.h ) @@ -52,6 +61,8 @@ set(ecalhdf5_src set(ecalhdf5_header_base include/ecalhdf5/eh5_defs.h include/ecalhdf5/eh5_meas.h + include/ecalhdf5/eh5_meas_api_v2.h + include/ecalhdf5/eh5_meas_api_v3.h include/ecalhdf5/eh5_types.h ) @@ -67,7 +78,10 @@ target_include_directories(${PROJECT_NAME} target_compile_definitions(${PROJECT_NAME} PRIVATE - $<$:_UNICODE>) + $<$:_UNICODE> + PUBLIC + ECAL_EH5_API_VERSION=2 + ) target_compile_options(${PROJECT_NAME} PRIVATE diff --git a/contrib/ecalhdf5/include/ecalhdf5/eh5_defs.h b/contrib/ecalhdf5/include/ecalhdf5/eh5_defs.h index 3208ffc1fe..a05f6c027b 100644 --- a/contrib/ecalhdf5/include/ecalhdf5/eh5_defs.h +++ b/contrib/ecalhdf5/include/ecalhdf5/eh5_defs.h @@ -1,6 +1,6 @@ ;/* ========================= eCAL LICENSE ================================= ; * -; * Copyright (C) 2016 - 2019 Continental Corporation +; * Copyright (C) 2016 - 2024 Continental Corporation ; * ; * Licensed under the Apache License, Version 2.0 (the "License"); ; * you may not use this file except in compliance with the License. @@ -25,13 +25,13 @@ #pragma once ;/* version parsed out into numeric values */ -#define ECAL_HDF5_VERSION_MAJOR 2 //!< eCAL HDF5 major version -#define ECAL_HDF5_VERSION_MINOR 1 //!< eCAL HDF5 minor version +#define ECAL_HDF5_VERSION_MAJOR 3 //!< eCAL HDF5 major version +#define ECAL_HDF5_VERSION_MINOR 0 //!< eCAL HDF5 minor version #define ECAL_HDF5_VERSION_PATCH 0 //!< eCAL HDF5 patch version ;/* version as string */ -#define ECAL_HDF5_VERSION "v.2.1.0.20180329" //!< eCAL HDF5 version string -#define ECAL_HDF5_DATE "29.03.2018" //!< eCAL HDF5 version date +#define ECAL_HDF5_VERSION "v.3.0.0.20241203" //!< eCAL HDF5 version string +#define ECAL_HDF5_DATE "03.12.2024" //!< eCAL HDF5 version date ;/* name as string */ #define ECAL_HDF5_NAME "eCALHDF5" //!< eCAL HDF5 library name diff --git a/contrib/ecalhdf5/include/ecalhdf5/eh5_meas.h b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas.h index 4b3c64df96..13e0771166 100644 --- a/contrib/ecalhdf5/include/ecalhdf5/eh5_meas.h +++ b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -31,315 +32,11 @@ #include "eh5_types.h" -namespace eCAL -{ - namespace eh5 - { - class HDF5MeasImpl; +#if defined(ECAL_EH5_NO_DEPRECATION_WARNINGS) +#define ECAL_EH5_DEPRECATE(__message__) //!< Don't print deprecation warnigns +#else +#define ECAL_EH5_DEPRECATE(__message__) [[deprecated(__message__)]] //!< Deprecate the following function +#endif - /** - * @brief eCAL HDF5 measurement API - **/ - class HDF5Meas - { - public: - /** - * @brief Constructor - **/ - HDF5Meas(); - - /** - * @brief Constructor - * - * @param path Input file path / measurement directory path (see meas directory structure description bellow, in Open method). - * @param access Access type - * - **/ - explicit HDF5Meas(const std::string& path, eAccessType access = RDONLY); - - /** - * @brief Destructor - **/ - ~HDF5Meas(); - - /** - * @brief Copy constructor deleted - **/ - HDF5Meas(const HDF5Meas& other) = delete; - /** - * @brief Move assignemnt deleted - **/ - HDF5Meas& operator=(const HDF5Meas& other) = delete; - - /** - * @brief Move constructor - **/ - HDF5Meas(HDF5Meas&&) = default; - /** - * @brief Move assignment - **/ - HDF5Meas& operator=(HDF5Meas&&) = default; - - /** - * @brief Open file - * - * @param path Input file path / measurement directory path. - * - * Default measurement directory structure: - * - root directory e.g.: M:\measurement_directory\measurement01 - * - documents directory: |_doc - * - hosts directories: |_Host1 (e.g.: CARPC01) - * |_Host2 (e.g.: CARPC02) - * - * File path as input (eAccessType::RDONLY): - * - root directory (e.g.: M:\measurement_directory\measurement01) in this case all hosts subdirectories will be iterated, - * - host directory (e.g.: M:\measurement_directory\measurement01\CARPC01), - * - file path, path to file from measurement (e.g.: M:\measurement_directory\measurement01\CARPC01\meas01_05.hdf5). - * - * File path as output (eAccessType::CREATE): - * - full path to measurement directory (recommended with host name) (e.g.: M:\measurement_directory\measurement01\CARPC01), - * - to set the name of the actual hdf5 file use SetFileBaseName method. - * - * @param access Access type - * - * @return true if output (eAccessType::CREATE) measurement directory structure can be accessed/created, false otherwise. - * true if input (eAccessType::RDONLY) measurement/file path was opened, false otherwise. - **/ - bool Open(const std::string& path, eAccessType access = RDONLY); - - /** - * @brief Close file - * - * @return true if succeeds, false if it fails - **/ - bool Close(); - - /** - * @brief Checks if file/measurement is ok - * - * @return true if meas can be opened(read) or location is accessible(write), false otherwise - **/ - bool IsOk() const; - - /** - * @brief Get the File Type Version of the current opened file - * - * @return file version - **/ - std::string GetFileVersion() const; - - /** - * @brief Gets maximum allowed size for an individual file - * - * @return maximum size in MB - **/ - size_t GetMaxSizePerFile() const; - - /** - * @brief Sets maximum allowed size for an individual file - * - * @param size maximum size in MB - **/ - void SetMaxSizePerFile(size_t size); - - /** - * @brief Whether each Channel shall be writte in its own file - * - * When enabled, data is clustered by channel and each channel is written - * to its own file. The filenames will consist of the basename and the - * channel name. - * - * @return true, if one file per channel is enabled - */ - bool IsOneFilePerChannelEnabled() const; - - /** - * @brief Enable / disable the creation of one individual file per channel - * - * When enabled, data is clustered by channel and each channel is written - * to its own file. The filenames will consist of the basename and the - * channel name. - * - * @param enabled Whether one file shall be created per channel - */ - void SetOneFilePerChannelEnabled(bool enabled); - - /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names - **/ - std::set GetChannelNames() const; - - /** - * @brief Check if channel exists in measurement - * - * @param channel_name name of the channel - * - * @return true if exists, false otherwise - **/ - bool HasChannel(const std::string& channel_name) const; - - /** - * @brief Get the channel description for the given channel - * - * @param channel_name channel name - * - * @return channel description - **/ - [[deprecated("Please use GetChannelDataTypeInformation instead")]] - std::string GetChannelDescription(const std::string& channel_name) const; - - /** - * @brief Set description of the given channel - * - * @param channel_name channel name - * @param description description of the channel - **/ - [[deprecated("Please use SetChannelDataTypeInformation instead")]] - void SetChannelDescription(const std::string& channel_name, const std::string& description); - - /** - * @brief Gets the channel type of the given channel - * - * @param channel_name channel name - * - * @return channel type - **/ - [[deprecated("Please use GetChannelDataTypeInformation instead")]] - std::string GetChannelType(const std::string& channel_name) const; - - /** - * @brief Set type of the given channel - * - * @param channel_name channel name - * @param type type of the channel - **/ - [[deprecated("Please use SetChannelDataTypeInformation instead")]] - void SetChannelType(const std::string& channel_name, const std::string& type); - - /** - * @brief Get data type information of the given channel - * - * @param channel_name channel name - * - * @return channel type - **/ - DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const; - - /** - * @brief Set data type information of the given channel - * - * @param channel_name channel name - * @param info datatype info of the channel - * - * @return channel type - **/ - void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info); - - /** - * @brief Gets minimum timestamp for specified channel - * - * @param channel_name channel name - * - * @return minimum timestamp value - **/ - long long GetMinTimestamp(const std::string& channel_name) const; - - /** - * @brief Gets maximum timestamp for specified channel - * - * @param channel_name channel name - * - * @return maximum timestamp value - **/ - long long GetMaxTimestamp(const std::string& channel_name) const; - - /** - * @brief Gets the header info for all data entries for the given channel - * Header = timestamp + entry id - * - * @param [in] channel_name channel name - * @param [out] entries header info for all data entries - * - * @return true if succeeds, false if it fails - **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const; - - /** - * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) - * Header = timestamp + entry id - * - * @param [in] channel_name channel name - * @param [in] begin time range begin timestamp - * @param [in] end time range end timestamp - * @param [out] entries header info for data entries in given range - * - * @return true if succeeds, false if it fails - **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const; - - /** - * @brief Gets data size of a specific entry - * - * @param [in] entry_id Entry ID - * @param [out] size Entry data size - * - * @return true if succeeds, false if it fails - **/ - bool GetEntryDataSize(long long entry_id, size_t& size) const; - - /** - * @brief Gets data from a specific entry - * - * @param [in] entry_id Entry ID - * @param [out] data Entry data - * - * @return true if succeeds, false if it fails - **/ - bool GetEntryData(long long entry_id, void* data) const; - - /** - * @brief Set measurement file base name (desired name for the actual hdf5 files that will be created) - * - * @param base_name Name of the hdf5 files that will be created. - **/ - void SetFileBaseName(const std::string& base_name); - - /** - * @brief Add entry to file - * - * @param data data to be added - * @param size size of the data - * @param snd_timestamp send time stamp - * @param rcv_timestamp receive time stamp - * @param channel_name channel name - * @param id message id - * @param clock message clock - * - * @return true if succeeds, false if it fails - **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock); - - /** - * @brief Callback function type for pre file split notification - **/ - typedef std::function CallbackFunction; - - /** - * @brief Connect callback for pre file split notification - * - * @param cb callback function - **/ - void ConnectPreSplitCallback(CallbackFunction cb); - - /** - * @brief Disconnect pre file split callback - **/ - void DisconnectPreSplitCallback(); - - private: - std::unique_ptr hdf_meas_impl_; - }; - } // namespace eh5 -} // namespace eCAL +#include "eh5_meas_api_v2.h" +#include "eh5_meas_api_v3.h" diff --git a/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v2.h b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v2.h new file mode 100644 index 0000000000..ccba86ad64 --- /dev/null +++ b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v2.h @@ -0,0 +1,351 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file eh5_meas.h + * @brief eCALHDF5 measurement class +**/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "eh5_types.h" + +namespace eCAL +{ + namespace eh5 + { + inline namespace v3 + { + class HDF5Meas; + } + + namespace v2 + { + /** + * @brief eCAL HDF5 measurement API + **/ + class HDF5Meas + { + public: + /** + * @brief Constructor + **/ + HDF5Meas(); + + /** + * @brief Constructor + * + * @param path Input file path / measurement directory path (see meas directory structure description bellow, in Open method). + * @param access Access type + * + **/ + explicit HDF5Meas(const std::string& path, eAccessType access = RDONLY); + + /** + * @brief Destructor + **/ + ~HDF5Meas(); + + /** + * @brief Copy constructor deleted + **/ + HDF5Meas(const HDF5Meas& other) = delete; + /** + * @brief Move assignemnt deleted + **/ + HDF5Meas& operator=(const HDF5Meas& other) = delete; + + /** + * @brief Move constructor + **/ + HDF5Meas(HDF5Meas&&) = default; + /** + * @brief Move assignment + **/ + HDF5Meas& operator=(HDF5Meas&&) = default; + + /** + * @brief Open file + * + * @param path Input file path / measurement directory path. + * + * Default measurement directory structure: + * - root directory e.g.: M:\measurement_directory\measurement01 + * - documents directory: |_doc + * - hosts directories: |_Host1 (e.g.: CARPC01) + * |_Host2 (e.g.: CARPC02) + * + * File path as input (eAccessType::RDONLY): + * - root directory (e.g.: M:\measurement_directory\measurement01) in this case all hosts subdirectories will be iterated, + * - host directory (e.g.: M:\measurement_directory\measurement01\CARPC01), + * - file path, path to file from measurement (e.g.: M:\measurement_directory\measurement01\CARPC01\meas01_05.hdf5). + * + * File path as output (eAccessType::CREATE): + * - full path to measurement directory (recommended with host name) (e.g.: M:\measurement_directory\measurement01\CARPC01), + * - to set the name of the actual hdf5 file use SetFileBaseName method. + * + * @param access Access type + * + * @return true if output (eAccessType::CREATE) measurement directory structure can be accessed/created, false otherwise. + * true if input (eAccessType::RDONLY) measurement/file path was opened, false otherwise. + **/ + bool Open(const std::string& path, eAccessType access = RDONLY); + + /** + * @brief Close file + * + * @return true if succeeds, false if it fails + **/ + bool Close(); + + /** + * @brief Checks if file/measurement is ok + * + * @return true if meas can be opened(read) or location is accessible(write), false otherwise + **/ + bool IsOk() const; + + /** + * @brief Get the File Type Version of the current opened file + * + * @return file version + **/ + std::string GetFileVersion() const; + + /** + * @brief Gets maximum allowed size for an individual file + * + * @return maximum size in MB + **/ + size_t GetMaxSizePerFile() const; + + /** + * @brief Sets maximum allowed size for an individual file + * + * @param size maximum size in MB + **/ + void SetMaxSizePerFile(size_t size); + + /** + * @brief Whether each Channel shall be writte in its own file + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @return true, if one file per channel is enabled + */ + bool IsOneFilePerChannelEnabled() const; + + /** + * @brief Enable / disable the creation of one individual file per channel + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @param enabled Whether one file shall be created per channel + */ + void SetOneFilePerChannelEnabled(bool enabled); + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names + **/ + std::set GetChannelNames() const; + + /** + * @brief Check if channel exists in measurement + * + * @param channel_name name of the channel + * + * @return true if exists, false otherwise + **/ + bool HasChannel(const std::string& channel_name) const; + + /** + * @brief Get the channel description for the given channel + * + * @param channel_name channel name + * + * @return channel description + **/ + std::string GetChannelDescription(const std::string& channel_name) const; + + /** + * @brief Set description of the given channel + * + * @param channel_name channel name + * @param description description of the channel + **/ + void SetChannelDescription(const std::string& channel_name, const std::string& description); + + /** + * @brief Gets the channel type of the given channel + * + * @param channel_name channel name + * + * @return channel type + **/ + std::string GetChannelType(const std::string& channel_name) const; + + /** + * @brief Set type of the given channel + * + * @param channel_name channel name + * @param type type of the channel + **/ + void SetChannelType(const std::string& channel_name, const std::string& type); + + /** + * @brief Get data type information of the given channel + * + * @param channel_name channel name + * + * @return channel type + **/ + DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const; + + /** + * @brief Set data type information of the given channel + * + * @param channel_name channel name + * @param info datatype info of the channel + * + * @return channel type + **/ + void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info); + + /** + * @brief Gets minimum timestamp for specified channel + * + * @param channel_name channel name + * + * @return minimum timestamp value + **/ + long long GetMinTimestamp(const std::string& channel_name) const; + + /** + * @brief Gets maximum timestamp for specified channel + * + * @param channel_name channel name + * + * @return maximum timestamp value + **/ + long long GetMaxTimestamp(const std::string& channel_name) const; + + /** + * @brief Gets the header info for all data entries for the given channel + * Header = timestamp + entry id + * + * @param [in] channel_name channel name + * @param [out] entries header info for all data entries + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const; + + /** + * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) + * Header = timestamp + entry id + * + * @param [in] channel_name channel name + * @param [in] begin time range begin timestamp + * @param [in] end time range end timestamp + * @param [out] entries header info for data entries in given range + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const; + + /** + * @brief Gets data size of a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] size Entry data size + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryDataSize(long long entry_id, size_t& size) const; + + /** + * @brief Gets data from a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] data Entry data + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryData(long long entry_id, void* data) const; + + /** + * @brief Set measurement file base name (desired name for the actual hdf5 files that will be created) + * + * @param base_name Name of the hdf5 files that will be created. + **/ + void SetFileBaseName(const std::string& base_name); + + /** + * @brief Add entry to file + * + * @param data data to be added + * @param size size of the data + * @param snd_timestamp send time stamp + * @param rcv_timestamp receive time stamp + * @param channel_name channel name + * @param id message id + * @param clock message clock + * + * @return true if succeeds, false if it fails + **/ + bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock); + + /** + * @brief Callback function type for pre file split notification + **/ + typedef std::function CallbackFunction; + + /** + * @brief Connect callback for pre file split notification + * + * @param cb callback function + **/ + void ConnectPreSplitCallback(CallbackFunction cb); + + /** + * @brief Disconnect pre file split callback + **/ + void DisconnectPreSplitCallback(); + + private: + std::unique_ptr hdf_meas_impl_; + // this map saves all datatype infos that have been set, so that the api can still + // support setting type and descriptor separately + std::map data_type_info_map; + }; + } // namespace v1 + } // namespace eh5 +} // namespace eCAL diff --git a/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v3.h b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v3.h new file mode 100644 index 0000000000..80ec6a6ee0 --- /dev/null +++ b/contrib/ecalhdf5/include/ecalhdf5/eh5_meas_api_v3.h @@ -0,0 +1,312 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file eh5_meas.h + * @brief eCALHDF5 measurement class +**/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "eh5_types.h" + + +namespace eCAL +{ + namespace eh5 + { + class HDF5MeasImpl; + + inline namespace v3{ + + /** + * @brief eCAL HDF5 measurement API + **/ + class HDF5Meas + { + public: + /** + * @brief Constructor + **/ + HDF5Meas(); + + /** + * @brief Constructor + * + * @param path Input file path / measurement directory path (see meas directory structure description bellow, in Open method). + * @param access Access type + * + **/ + explicit HDF5Meas(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); + + /** + * @brief Destructor + **/ + ~HDF5Meas(); + + /** + * @brief Copy constructor deleted + **/ + HDF5Meas(const HDF5Meas& other) = delete; + /** + * @brief Move assignemnt deleted + **/ + HDF5Meas& operator=(const HDF5Meas& other) = delete; + + /** + * @brief Move constructor + **/ + HDF5Meas(HDF5Meas&&) = default; + /** + * @brief Move assignment + **/ + HDF5Meas& operator=(HDF5Meas&&) = default; + + /** + * @brief Open file + * + * @param path Input file path / measurement directory path. + * + * Default measurement directory structure: + * - root directory e.g.: M:\measurement_directory\measurement01 + * - documents directory: |_doc + * - hosts directories: |_Host1 (e.g.: CARPC01) + * |_Host2 (e.g.: CARPC02) + * + * File path as input (eAccessType::RDONLY): + * - root directory (e.g.: M:\measurement_directory\measurement01) in this case all hosts subdirectories will be iterated, + * - host directory (e.g.: M:\measurement_directory\measurement01\CARPC01), + * - file path, path to file from measurement (e.g.: M:\measurement_directory\measurement01\CARPC01\meas01_05.hdf5). + * + * File path as output (eAccessType::CREATE): + * - full path to measurement directory (recommended with host name) (e.g.: M:\measurement_directory\measurement01\CARPC01), + * - to set the name of the actual hdf5 file use SetFileBaseName method. + * + * @param access Access type + * + * @return true if output (eAccessType::CREATE) measurement directory structure can be accessed/created, false otherwise. + * true if input (eAccessType::RDONLY) measurement/file path was opened, false otherwise. + **/ + bool Open(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); + + /** + * @brief Close file + * + * @return true if succeeds, false if it fails + **/ + bool Close(); + + /** + * @brief Checks if file/measurement is ok + * + * @return true if meas can be opened(read) or location is accessible(write), false otherwise + **/ + bool IsOk() const; + + /** + * @brief Get the File Type Version of the current opened file + * + * @return file version + **/ + std::string GetFileVersion() const; + + /** + * @brief Gets maximum allowed size for an individual file + * + * @return maximum size in MB + **/ + size_t GetMaxSizePerFile() const; + + /** + * @brief Sets maximum allowed size for an individual file + * + * @param size maximum size in MB + **/ + void SetMaxSizePerFile(size_t size); + + /** + * @brief Whether each Channel shall be writte in its own file + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @return true, if one file per channel is enabled + */ + bool IsOneFilePerChannelEnabled() const; + + /** + * @brief Enable / disable the creation of one individual file per channel + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @param enabled Whether one file shall be created per channel + */ + void SetOneFilePerChannelEnabled(bool enabled); + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return Channels (channel name & id) + **/ + std::set GetChannels() const; + + /** + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise + **/ + bool HasChannel(const eCAL::eh5::SChannel& channel) const; + + /** + * @brief Get data type information of the given channel + * + * @param channel_name channel name + * + * @return channel type + **/ + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const; + + /** + * @brief Set data type information of the given channel + * + * @param channel_name channel name + * @param info datatype info of the channel + * + * @return channel type + **/ + void SetChannelDataTypeInformation(const SChannel& channel, const DataTypeInformation& info); + + /** + * @brief Gets minimum timestamp for specified channel + * + * @param channel channel (name & id) + * + * @return minimum timestamp value + **/ + long long GetMinTimestamp(const SChannel& channel) const; + + /** + * @brief Gets maximum timestamp for specified channel + * + * @param channel channel (name & id) + * + * @return maximum timestamp value + **/ + long long GetMaxTimestamp(const SChannel& channel) const; + + /** + * @brief Gets the header info for all data entries for the given channel + * Header = timestamp + entry id + * + * @param [in] channel channel (name & id) + * @param [out] entries header info for all data entries + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const; + + /** + * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) + * Header = timestamp + entry id + * + * @param [in] channel channel (name & id) + * @param [in] begin time range begin timestamp + * @param [in] end time range end timestamp + * @param [out] entries header info for data entries in given range + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const; + + /** + * @brief Gets data size of a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] size Entry data size + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryDataSize(long long entry_id, size_t& size) const; + + /** + * @brief Gets data from a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] data Entry data + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryData(long long entry_id, void* data) const; + + /** + * @brief Set measurement file base name (desired name for the actual hdf5 files that will be created) + * + * @param base_name Name of the hdf5 files that will be created. + **/ + void SetFileBaseName(const std::string& base_name); + + /** + * @brief Add entry to file + * + * @param data data to be added + * @param size size of the data + * @param snd_timestamp send time stamp + * @param rcv_timestamp receive time stamp + * @param channel channel channel (name & id) + * @param id message id + * @param clock message clock + * + * @return true if succeeds, false if it fails + **/ + bool AddEntryToFile(const SWriteEntry& entry); + + /** + * @brief Callback function type for pre file split notification + **/ + typedef std::function CallbackFunction; + + /** + * @brief Connect callback for pre file split notification + * + * @param cb callback function + **/ + void ConnectPreSplitCallback(CallbackFunction cb); + + /** + * @brief Disconnect pre file split callback + **/ + void DisconnectPreSplitCallback(); + + private: + std::unique_ptr hdf_meas_impl_; + }; + } + } // namespace eh5 +} // namespace eCAL diff --git a/contrib/ecalhdf5/include/ecalhdf5/eh5_types.h b/contrib/ecalhdf5/include/ecalhdf5/eh5_types.h index 91351e56b5..1ec43591fa 100644 --- a/contrib/ecalhdf5/include/ecalhdf5/eh5_types.h +++ b/contrib/ecalhdf5/include/ecalhdf5/eh5_types.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,18 +38,40 @@ namespace eCAL const std::string kChnNameAttribTitle ("Channel Name"); const std::string kChnDescAttrTitle ("Channel Description"); const std::string kChnTypeAttrTitle ("Channel Type"); + const std::string kChnIdTypename ("TypeName"); + const std::string kChnIdEncoding ("TypeEncoding"); + const std::string kChnIdDescriptor ("TypeDescriptor"); + const std::string kChnIdData ("DataTable"); const std::string kFileVerAttrTitle ("Version"); const std::string kTimestampAttrTitle ("Timestamps"); const std::string kChnAttrTitle ("Channels"); // Remove @eCAL6 -> backwards compatibility with old interface! + using SChannel = eCAL::experimental::measurement::base::Channel; using SEntryInfo = eCAL::experimental::measurement::base::EntryInfo; using EntryInfoSet = eCAL::experimental::measurement::base::EntryInfoSet; using EntryInfoVect = eCAL::experimental::measurement::base::EntryInfoVect; - using eAccessType = eCAL::experimental::measurement::base::AccessType; - using eCAL::experimental::measurement::base::RDONLY; - using eCAL::experimental::measurement::base::CREATE; - + using SWriteEntry = eCAL::experimental::measurement::base::WriteEntry; + + namespace v2 + { + enum eAccessType + { + RDONLY, //!< ReadOnly - the measurement can only be read + CREATE, //!< Create - a new measurement will be created + }; + } + + inline namespace v3 + { + enum class eAccessType + { + RDONLY, //!< ReadOnly - the measurement can only be read + CREATE, //!< Create - a new measurement will be created + CREATE_V5 //!< Create a legacy V5 hdf5 measurement (For testing purpose only!) + }; + } + using eCAL::experimental::measurement::base::DataTypeInformation; //!< @endcond } // namespace eh5 diff --git a/contrib/ecalhdf5/src/datatype_helper.cpp b/contrib/ecalhdf5/src/datatype_helper.cpp new file mode 100644 index 0000000000..63d5a8fb0a --- /dev/null +++ b/contrib/ecalhdf5/src/datatype_helper.cpp @@ -0,0 +1,60 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "datatype_helper.h" + +namespace eCAL +{ + namespace eh5 + { + DataTypeInformation CreateInfo(const std::string& combined_topic_type_, const std::string& descriptor_) + { + eCAL::eh5::DataTypeInformation info; + auto pos = combined_topic_type_.find(':'); + if (pos == std::string::npos) + { + info.name = combined_topic_type_; + info.encoding = ""; + } + else + { + info.name = combined_topic_type_.substr(pos + 1); + info.encoding = combined_topic_type_.substr(0, pos); + } + info.descriptor = descriptor_; + return info; + } + + std::pair FromInfo(const eCAL::eh5::DataTypeInformation& datatype_info_) + { + std::string combined_topic_type; + if (datatype_info_.encoding.empty()) + { + combined_topic_type = datatype_info_.name; + } + else + { + combined_topic_type = datatype_info_.encoding + ":" + datatype_info_.name; + } + + return std::make_pair(combined_topic_type, datatype_info_.descriptor); + } + + } +} \ No newline at end of file diff --git a/contrib/ecalhdf5/src/datatype_helper.h b/contrib/ecalhdf5/src/datatype_helper.h new file mode 100644 index 0000000000..cb1a1e3d03 --- /dev/null +++ b/contrib/ecalhdf5/src/datatype_helper.h @@ -0,0 +1,34 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include +#include +#include + + +namespace eCAL +{ + namespace eh5 + { + DataTypeInformation CreateInfo(const std::string& combined_topic_type_, const std::string& descriptor_); + std::pair FromInfo(const DataTypeInformation& datatype_info_); + } +} \ No newline at end of file diff --git a/contrib/ecalhdf5/src/eh5_meas_api_v2.cpp b/contrib/ecalhdf5/src/eh5_meas_api_v2.cpp new file mode 100644 index 0000000000..6f699a716b --- /dev/null +++ b/contrib/ecalhdf5/src/eh5_meas_api_v2.cpp @@ -0,0 +1,283 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCALHDF5 measurement class +**/ + +#include + +#include + +#include +#include "datatype_helper.h" + + +namespace { + using namespace eCAL::eh5; + + v3::eAccessType convert(v2::eAccessType v2_access) + { + switch (v2_access) + { + case v2::RDONLY: + return v3::eAccessType::RDONLY; + case v2::CREATE: + return v3::eAccessType::CREATE_V5; + default: + return v3::eAccessType::RDONLY; + } + } + + SChannel createChannel(const std::string& channel_name) + { + return SChannel(channel_name, 0); + } + + + std::set GetChannelsWithName(const std::unique_ptr& hdf_meas_impl_, const std::string& channel_name) + { + const auto all_channels = hdf_meas_impl_->GetChannels(); + std::set named_channels; + for (const auto& channel : all_channels) + { + if (channel.name == channel_name) + { + named_channels.insert(channel); + } + } + return named_channels; + } +} + +using namespace eCAL::eh5::v2; + +eCAL::eh5::v2::HDF5Meas::HDF5Meas() +: hdf_meas_impl_(std::make_unique()) +{} + +// TODO restrict to V5 due to API +eCAL::eh5::v2::HDF5Meas::HDF5Meas(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +: hdf_meas_impl_(std::make_unique(path, convert(access))) +{} + +eCAL::eh5::v2::HDF5Meas::~HDF5Meas() += default; + +bool eCAL::eh5::v2::HDF5Meas::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +{ + return hdf_meas_impl_->Open(path, convert(access)); +} + +bool eCAL::eh5::v2::HDF5Meas::Close() +{ + return hdf_meas_impl_->Close(); +} + +bool eCAL::eh5::v2::HDF5Meas::IsOk() const +{ + return hdf_meas_impl_->IsOk(); +} + +std::string eCAL::eh5::v2::HDF5Meas::GetFileVersion() const +{ + return hdf_meas_impl_->GetFileVersion(); +} + +size_t eCAL::eh5::v2::HDF5Meas::GetMaxSizePerFile() const +{ + return hdf_meas_impl_->GetMaxSizePerFile(); +} + +void eCAL::eh5::v2::HDF5Meas::SetMaxSizePerFile(size_t size) +{ + return hdf_meas_impl_->SetMaxSizePerFile(size); +} + +bool eCAL::eh5::v2::HDF5Meas::IsOneFilePerChannelEnabled() const +{ + return hdf_meas_impl_->IsOneFilePerChannelEnabled(); +} + +void eCAL::eh5::v2::HDF5Meas::SetOneFilePerChannelEnabled(bool enabled) +{ + return hdf_meas_impl_->SetOneFilePerChannelEnabled(enabled); +} + +std::set eCAL::eh5::v2::HDF5Meas::GetChannelNames() const +{ + auto channels = hdf_meas_impl_->GetChannels(); + std::set channel_names; + for (const auto& channel : channels) { + channel_names.insert(channel.name); + } + return channel_names; +} + +bool eCAL::eh5::v2::HDF5Meas::HasChannel(const std::string& channel_name) const +{ + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + bool has_channel = false; + for (const auto& channel : named_channels) + { + has_channel |= hdf_meas_impl_->HasChannel(channel); + } + return has_channel; +} + +std::string eCAL::eh5::v2::HDF5Meas::GetChannelDescription(const std::string& channel_name) const +{ + auto datatype_info = GetChannelDataTypeInformation(channel_name); + return datatype_info.descriptor; +} + +void eCAL::eh5::v2::HDF5Meas::SetChannelDescription(const std::string& channel_name, const std::string& description) +{ + const auto& channel = createChannel(channel_name); + auto& current_info = data_type_info_map[channel_name]; + current_info.descriptor = description; + hdf_meas_impl_->SetChannelDataTypeInformation(channel, current_info); +} + +std::string eCAL::eh5::v2::HDF5Meas::GetChannelType(const std::string& channel_name) const +{ + std::string ret_val; + auto datatype_info = GetChannelDataTypeInformation(channel_name); + std::tie(ret_val, std::ignore) = FromInfo(datatype_info); + return ret_val; +} + +void eCAL::eh5::v2::HDF5Meas::SetChannelType(const std::string& channel_name, const std::string& type) +{ + const auto& channel = createChannel(channel_name); + auto& current_info = data_type_info_map[channel_name]; + current_info = CreateInfo(type, current_info.descriptor); + hdf_meas_impl_->SetChannelDataTypeInformation(channel, current_info); +} + +// This function "has" to loose information if there are multople channels in the measurement with the same name +eCAL::eh5::DataTypeInformation eCAL::eh5::v2::HDF5Meas::GetChannelDataTypeInformation(const std::string& channel_name) const +{ + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + if (named_channels.size() > 0) + { + return hdf_meas_impl_->GetChannelDataTypeInformation(*named_channels.begin()); + } + return DataTypeInformation{}; +} + +void eCAL::eh5::v2::HDF5Meas::SetChannelDataTypeInformation(const std::string& channel_name, const eCAL::eh5::DataTypeInformation& info) +{ + data_type_info_map[channel_name] = info; + return hdf_meas_impl_->SetChannelDataTypeInformation(createChannel(channel_name), info); +} + +long long eCAL::eh5::v2::HDF5Meas::GetMinTimestamp(const std::string& channel_name) const +{ + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + long long min_timestamp = std::numeric_limits::max(); + for (const auto& channel : named_channels) + { + min_timestamp = std::min(min_timestamp, hdf_meas_impl_->GetMinTimestamp(channel)); + } + if (min_timestamp == std::numeric_limits::max()) + return 0; // the old API would return 0 in this case + return min_timestamp; +} + +long long eCAL::eh5::v2::HDF5Meas::GetMaxTimestamp(const std::string& channel_name) const +{ + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + long long max_timestamp = std::numeric_limits::min(); + for (const auto& channel : named_channels) + { + max_timestamp = std::max(max_timestamp, hdf_meas_impl_->GetMaxTimestamp(channel)); + } + if (max_timestamp == std::numeric_limits::min()) + return 0; // the old API would return 0 in this case + return max_timestamp; +} + +bool eCAL::eh5::v2::HDF5Meas::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const +{ + // we need to aggregate info from all channels within the measurement with a given name. + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + entries.clear(); + bool ret_value{ true }; + for (const auto& channel : named_channels) + { + EntryInfoSet channel_entries; + ret_value &= hdf_meas_impl_->GetEntriesInfo(channel, channel_entries); + entries.insert(channel_entries.begin(), channel_entries.end()); + } + return ret_value; +} + +bool eCAL::eh5::v2::HDF5Meas::GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const +{ + // we need to aggregate info from all channels within the measurement with a given name. + auto named_channels = GetChannelsWithName(hdf_meas_impl_, channel_name); + entries.clear(); + bool ret_value{ true }; + for (const auto& channel : named_channels) + { + EntryInfoSet channel_entries; + ret_value &= hdf_meas_impl_->GetEntriesInfoRange(channel, begin, end, channel_entries); + entries.insert(channel_entries.begin(), channel_entries.end()); + } + return ret_value; +} + +bool eCAL::eh5::v2::HDF5Meas::GetEntryDataSize(long long entry_id, size_t& size) const +{ + return hdf_meas_impl_->GetEntryDataSize(entry_id, size); +} + +bool eCAL::eh5::v2::HDF5Meas::GetEntryData(long long entry_id, void* data) const +{ + return hdf_meas_impl_->GetEntryData(entry_id, data); +} + +void eCAL::eh5::v2::HDF5Meas::SetFileBaseName(const std::string& base_name) +{ + return hdf_meas_impl_->SetFileBaseName(base_name); +} + +bool eCAL::eh5::v2::HDF5Meas::AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) +{ + SWriteEntry entry; + entry.channel = createChannel(channel_name); + entry.data = data; + entry.size = size; + entry.snd_timestamp = snd_timestamp; + entry.rcv_timestamp = rcv_timestamp; + entry.sender_id = id; + entry.clock = clock; + return hdf_meas_impl_->AddEntryToFile(entry); +} + +void eCAL::eh5::v2::HDF5Meas::ConnectPreSplitCallback(CallbackFunction cb) +{ + return hdf_meas_impl_->ConnectPreSplitCallback(std::move(cb)); +} + +void eCAL::eh5::v2::HDF5Meas::DisconnectPreSplitCallback() +{ + return hdf_meas_impl_->DisconnectPreSplitCallback(); +} diff --git a/contrib/ecalhdf5/src/eh5_meas.cpp b/contrib/ecalhdf5/src/eh5_meas_api_v3.cpp similarity index 58% rename from contrib/ecalhdf5/src/eh5_meas.cpp rename to contrib/ecalhdf5/src/eh5_meas_api_v3.cpp index 28419814ce..8feff22ff5 100644 --- a/contrib/ecalhdf5/src/eh5_meas.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_api_v3.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,12 @@ * @brief eCALHDF5 measurement class **/ -#include +#include #include +#include +#include #include #include "eh5_meas_dir.h" @@ -33,33 +35,36 @@ #include "eh5_meas_file_v3.h" #include "eh5_meas_file_v4.h" #include "eh5_meas_file_v5.h" +#include "eh5_meas_file_v6.h" #include "escape.h" namespace { - const double file_version_max(5.0); + const double file_version_max(6.0); } -eCAL::eh5::HDF5Meas::HDF5Meas() +using namespace eCAL::eh5::v3; + +eCAL::eh5::v3::HDF5Meas::HDF5Meas() = default; -eCAL::eh5::HDF5Meas::HDF5Meas(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +eCAL::eh5::v3::HDF5Meas::HDF5Meas(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) { Open(path, access); } -eCAL::eh5::HDF5Meas::~HDF5Meas() +eCAL::eh5::v3::HDF5Meas::~HDF5Meas() = default; -bool eCAL::eh5::HDF5Meas::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +bool eCAL::eh5::v3::HDF5Meas::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) { if (hdf_meas_impl_) { Close(); } - if (access == eAccessType::CREATE) + if (access == eAccessType::CREATE || access == eAccessType::CREATE_V5) { EcalUtils::Filesystem::MkPath(path, EcalUtils::Filesystem::OsStyle::Current); } @@ -75,7 +80,7 @@ bool eCAL::eh5::HDF5Meas::Open(const std::string& path, eAccessType access /*= e } case EcalUtils::Filesystem::RegularFile: { - hdf_meas_impl_ = std::make_unique(path, access); + hdf_meas_impl_ = std::make_unique(path, access); if (!hdf_meas_impl_->IsOk()) { @@ -120,6 +125,10 @@ bool eCAL::eh5::HDF5Meas::Open(const std::string& path, eAccessType access /*= e { hdf_meas_impl_ = std::make_unique(path, access); } + else if (file_version_numeric >= 5.0 && file_version_numeric < 6.0) + { + hdf_meas_impl_ = std::make_unique(path, access); + } } break; case EcalUtils::Filesystem::Unknown: @@ -128,7 +137,7 @@ bool eCAL::eh5::HDF5Meas::Open(const std::string& path, eAccessType access /*= e break; } - if (access == eAccessType::CREATE) + if (access == eAccessType::CREATE || access == eAccessType::CREATE_V5) { return hdf_meas_impl_ ? EcalUtils::Filesystem::IsDir(path, EcalUtils::Filesystem::OsStyle::Current) : false; } @@ -138,7 +147,7 @@ bool eCAL::eh5::HDF5Meas::Open(const std::string& path, eAccessType access /*= e } } -bool eCAL::eh5::HDF5Meas::Close() +bool eCAL::eh5::v3::HDF5Meas::Close() { bool ret_val = false; if (hdf_meas_impl_) @@ -149,7 +158,7 @@ bool eCAL::eh5::HDF5Meas::Close() return ret_val; } -bool eCAL::eh5::HDF5Meas::IsOk() const +bool eCAL::eh5::v3::HDF5Meas::IsOk() const { bool ret_val = false; if (hdf_meas_impl_) @@ -160,7 +169,7 @@ bool eCAL::eh5::HDF5Meas::IsOk() const return ret_val; } -std::string eCAL::eh5::HDF5Meas::GetFileVersion() const +std::string eCAL::eh5::v3::HDF5Meas::GetFileVersion() const { std::string ret_val; if (hdf_meas_impl_) @@ -171,7 +180,7 @@ std::string eCAL::eh5::HDF5Meas::GetFileVersion() const return ret_val; } -size_t eCAL::eh5::HDF5Meas::GetMaxSizePerFile() const +size_t eCAL::eh5::v3::HDF5Meas::GetMaxSizePerFile() const { size_t ret_val = 0; if (hdf_meas_impl_) @@ -182,7 +191,7 @@ size_t eCAL::eh5::HDF5Meas::GetMaxSizePerFile() const return ret_val; } -void eCAL::eh5::HDF5Meas::SetMaxSizePerFile(size_t size) +void eCAL::eh5::v3::HDF5Meas::SetMaxSizePerFile(size_t size) { if (hdf_meas_impl_) { @@ -190,7 +199,7 @@ void eCAL::eh5::HDF5Meas::SetMaxSizePerFile(size_t size) } } -bool eCAL::eh5::HDF5Meas::IsOneFilePerChannelEnabled() const +bool eCAL::eh5::v3::HDF5Meas::IsOneFilePerChannelEnabled() const { if (hdf_meas_impl_ != nullptr) { @@ -199,7 +208,7 @@ bool eCAL::eh5::HDF5Meas::IsOneFilePerChannelEnabled() const return false; } -void eCAL::eh5::HDF5Meas::SetOneFilePerChannelEnabled(bool enabled) +void eCAL::eh5::v3::HDF5Meas::SetOneFilePerChannelEnabled(bool enabled) { if (hdf_meas_impl_ != nullptr) { @@ -207,128 +216,96 @@ void eCAL::eh5::HDF5Meas::SetOneFilePerChannelEnabled(bool enabled) } } -std::set eCAL::eh5::HDF5Meas::GetChannelNames() const +std::set eCAL::eh5::v3::HDF5Meas::GetChannels() const { - std::set ret_val; + std::set ret_val; if (hdf_meas_impl_) { - std::set escaped_channel_names = hdf_meas_impl_->GetChannelNames(); - for (const std::string& escaped_name : escaped_channel_names) + auto escaped_channels = hdf_meas_impl_->GetChannels(); + for (const auto& escaped_channel : escaped_channels) { - ret_val.emplace(GetUnescapedString(escaped_name)); + ret_val.emplace(GetUnescapedString(escaped_channel.name), escaped_channel.id); } } return ret_val; } -bool eCAL::eh5::HDF5Meas::HasChannel(const std::string& channel_name) const +bool eCAL::eh5::v3::HDF5Meas::HasChannel(const eCAL::eh5::SChannel& channel) const { bool ret_val = false; if (hdf_meas_impl_) { - ret_val = hdf_meas_impl_->HasChannel(GetEscapedTopicname(channel_name)); + ret_val = hdf_meas_impl_->HasChannel(GetEscapedTopicname(channel)); } return ret_val; } -// deprecated -std::string eCAL::eh5::HDF5Meas::GetChannelDescription(const std::string& channel_name) const -{ - auto datatype_info = GetChannelDataTypeInformation(channel_name); - return datatype_info.descriptor; -} - -// deprecated -void eCAL::eh5::HDF5Meas::SetChannelDescription(const std::string& channel_name, const std::string& description) -{ - auto current_info = GetChannelDataTypeInformation(channel_name); - current_info.descriptor = description; - SetChannelDataTypeInformation(channel_name, current_info); -} - -// deprecated -std::string eCAL::eh5::HDF5Meas::GetChannelType(const std::string& channel_name) const -{ - std::string ret_val; - auto datatype_info = GetChannelDataTypeInformation(channel_name); - std::tie(ret_val, std::ignore) = FromInfo(datatype_info); - return ret_val; -} - -// deprecated -void eCAL::eh5::HDF5Meas::SetChannelType(const std::string& channel_name, const std::string& type) -{ - auto current_info = GetChannelDataTypeInformation(channel_name); - auto new_info = CreateInfo(type, current_info.descriptor); - SetChannelDataTypeInformation(channel_name, new_info); -} - -eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5Meas::GetChannelDataTypeInformation(const std::string& channel_name) const +eCAL::eh5::DataTypeInformation eCAL::eh5::v3::HDF5Meas::GetChannelDataTypeInformation(const SChannel& channel) const { eCAL::eh5::DataTypeInformation ret_val; if (hdf_meas_impl_) { - ret_val = hdf_meas_impl_->GetChannelDataTypeInformation(GetEscapedTopicname(channel_name)); + ret_val = hdf_meas_impl_->GetChannelDataTypeInformation(GetEscapedTopicname(channel)); } return ret_val; } -void eCAL::eh5::HDF5Meas::SetChannelDataTypeInformation(const std::string& channel_name, const eCAL::eh5::DataTypeInformation& info) +void eCAL::eh5::v3::HDF5Meas::SetChannelDataTypeInformation(const SChannel& channel, const eCAL::eh5::DataTypeInformation& info) { if (hdf_meas_impl_) { - hdf_meas_impl_->SetChannelDataTypeInformation(GetEscapedTopicname(channel_name), info); + hdf_meas_impl_->SetChannelDataTypeInformation(GetEscapedTopicname(channel), info); } } -long long eCAL::eh5::HDF5Meas::GetMinTimestamp(const std::string& channel_name) const +long long eCAL::eh5::v3::HDF5Meas::GetMinTimestamp(const SChannel& channel) const { long long ret_val = 0; if (hdf_meas_impl_) { - ret_val = hdf_meas_impl_->GetMinTimestamp(GetEscapedTopicname(channel_name)); + ret_val = hdf_meas_impl_->GetMinTimestamp(GetEscapedTopicname(channel)); } return ret_val; } -long long eCAL::eh5::HDF5Meas::GetMaxTimestamp(const std::string& channel_name) const +long long eCAL::eh5::v3::HDF5Meas::GetMaxTimestamp(const SChannel& channel) const { long long ret_val = 0; if (hdf_meas_impl_) { - ret_val = hdf_meas_impl_->GetMaxTimestamp(GetEscapedTopicname(channel_name)); + ret_val = hdf_meas_impl_->GetMaxTimestamp(GetEscapedTopicname(channel)); } return ret_val; } -bool eCAL::eh5::HDF5Meas::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const +bool eCAL::eh5::v3::HDF5Meas::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { bool ret_val = false; if (hdf_meas_impl_) { - ret_val = hdf_meas_impl_->GetEntriesInfo(GetEscapedTopicname(channel_name), entries); + ret_val = hdf_meas_impl_->GetEntriesInfo(GetEscapedTopicname(channel), entries); } return ret_val; } -bool eCAL::eh5::HDF5Meas::GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const +bool eCAL::eh5::v3::HDF5Meas::GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const { bool ret_val = false; if (hdf_meas_impl_ && begin < end) { - ret_val = hdf_meas_impl_->GetEntriesInfoRange(GetEscapedTopicname(channel_name), begin, end, entries); + ret_val = hdf_meas_impl_->GetEntriesInfoRange(GetEscapedTopicname(channel), begin, end, entries); } return ret_val; } -bool eCAL::eh5::HDF5Meas::GetEntryDataSize(long long entry_id, size_t& size) const +bool eCAL::eh5::v3::HDF5Meas::GetEntryDataSize(long long entry_id, size_t& size) const { bool ret_val = false; if (hdf_meas_impl_) @@ -339,7 +316,7 @@ bool eCAL::eh5::HDF5Meas::GetEntryDataSize(long long entry_id, size_t& size) con return ret_val; } -bool eCAL::eh5::HDF5Meas::GetEntryData(long long entry_id, void* data) const +bool eCAL::eh5::v3::HDF5Meas::GetEntryData(long long entry_id, void* data) const { bool ret_val = false; if (hdf_meas_impl_) @@ -350,7 +327,7 @@ bool eCAL::eh5::HDF5Meas::GetEntryData(long long entry_id, void* data) const return ret_val; } -void eCAL::eh5::HDF5Meas::SetFileBaseName(const std::string& base_name) +void eCAL::eh5::v3::HDF5Meas::SetFileBaseName(const std::string& base_name) { if (hdf_meas_impl_) { @@ -358,18 +335,18 @@ void eCAL::eh5::HDF5Meas::SetFileBaseName(const std::string& base_name) } } -bool eCAL::eh5::HDF5Meas::AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) +bool eCAL::eh5::v3::HDF5Meas::AddEntryToFile(const SWriteEntry& entry) { bool ret_val = false; if (hdf_meas_impl_) { - return hdf_meas_impl_->AddEntryToFile(data, size, snd_timestamp, rcv_timestamp, GetEscapedTopicname(channel_name), id, clock); + return hdf_meas_impl_->AddEntryToFile(GetEscapedEntry(entry)); } return ret_val; } -void eCAL::eh5::HDF5Meas::ConnectPreSplitCallback(CallbackFunction cb) +void eCAL::eh5::v3::HDF5Meas::ConnectPreSplitCallback(CallbackFunction cb) { if (hdf_meas_impl_) { @@ -377,7 +354,7 @@ void eCAL::eh5::HDF5Meas::ConnectPreSplitCallback(CallbackFunction cb) } } -void eCAL::eh5::HDF5Meas::DisconnectPreSplitCallback() +void eCAL::eh5::v3::HDF5Meas::DisconnectPreSplitCallback() { if (hdf_meas_impl_) { diff --git a/contrib/ecalhdf5/src/eh5_meas_dir.cpp b/contrib/ecalhdf5/src/eh5_meas_dir.cpp index 0e8263d5ff..d53ed26460 100644 --- a/contrib/ecalhdf5/src/eh5_meas_dir.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_dir.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,31 +24,34 @@ #include "eh5_meas_dir.h" #include "escape.h" +#define NOMINMAX #ifdef WIN32 #include #else #include #endif //WIN32 -#include -#include #include +#include +#include +#include #include #include #include "eh5_meas_file_writer_v5.h" +#include "eh5_meas_file_writer_v6.h" // TODO: Test the one-file-per-channel setting with gtest constexpr unsigned int kDefaultMaxFileSizeMB = 1000; eCAL::eh5::HDF5MeasDir::HDF5MeasDir() - : access_ (RDONLY) // Temporarily set it to RDONLY, so the leading "Close()" from the Open() function will not operate on the uninitialized variable. + : access_ (v3::eAccessType::RDONLY) // Temporarily set it to RDONLY, so the leading "Close()" from the Open() function will not operate on the uninitialized variable. , one_file_per_channel_(false) , max_size_per_file_ (kDefaultMaxFileSizeMB * 1024 * 1024) , cb_pre_split_ (nullptr) {} -eCAL::eh5::HDF5MeasDir::HDF5MeasDir(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +eCAL::eh5::HDF5MeasDir::HDF5MeasDir(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : access_ (access) , one_file_per_channel_(false) , max_size_per_file_ (kDefaultMaxFileSizeMB * 1024 * 1024) @@ -66,7 +69,7 @@ eCAL::eh5::HDF5MeasDir::~HDF5MeasDir() HDF5MeasDir::Close(); } -bool eCAL::eh5::HDF5MeasDir::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +bool eCAL::eh5::HDF5MeasDir::Open(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) { // call the function via its class becase it's a virtual function that is called directly/indirectly in constructor/destructor,- // where the vtable is not created yet or it's destructed. @@ -80,10 +83,11 @@ bool eCAL::eh5::HDF5MeasDir::Open(const std::string& path, eAccessType access /* switch (access) { - case eCAL::eh5::RDONLY: + case eCAL::eh5::v3::eAccessType::RDONLY: //case eCAL::eh5::RDWR: return OpenRX(path, access); - case eCAL::eh5::CREATE: + case eCAL::eh5::v3::eAccessType::CREATE: + case eCAL::eh5::v3::eAccessType::CREATE_V5: output_dir_ = path; return true; default: @@ -97,7 +101,7 @@ bool eCAL::eh5::HDF5MeasDir::Close() { bool successfully_closed{ true }; - if (access_ == eAccessType::CREATE) + if (access_ == v3::eAccessType::CREATE || access_ == v3::eAccessType::CREATE_V5) { // Close all existing file writers for (auto& file_writer : file_writers_) @@ -135,10 +139,11 @@ bool eCAL::eh5::HDF5MeasDir::IsOk() const { switch (access_) { - case eCAL::eh5::RDONLY: + case eCAL::eh5::v3::eAccessType::RDONLY: //case eCAL::eh5::RDWR: return !file_readers_.empty() && !entries_by_id_.empty(); - case eCAL::eh5::CREATE: + case eCAL::eh5::v3::eAccessType::CREATE: + case eCAL::eh5::v3::eAccessType::CREATE_V5: return true; default: return false; @@ -182,26 +187,26 @@ void eCAL::eh5::HDF5MeasDir::SetOneFilePerChannelEnabled(bool enabled) one_file_per_channel_ = enabled; } -std::set eCAL::eh5::HDF5MeasDir::GetChannelNames() const +std::set eCAL::eh5::HDF5MeasDir::GetChannels() const { - std::set channels; + std::set channels; + for (const auto& chn : channels_info_) channels.insert(chn.first); return channels; } -bool eCAL::eh5::HDF5MeasDir::HasChannel(const std::string& channel_name) const +bool eCAL::eh5::HDF5MeasDir::HasChannel(const eCAL::eh5::SChannel& channel) const { - return channels_info_.count(channel_name) != 0; + return channels_info_.find(channel) != channels_info_.end(); } -eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasDir::GetChannelDataTypeInformation(const std::string& channel_name) const +eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasDir::GetChannelDataTypeInformation(const SChannel& channel) const { eCAL::eh5::DataTypeInformation ret_val; - const auto& found = channels_info_.find(channel_name); - + const auto& found = channels_info_.find(channel); if (found != channels_info_.end()) { ret_val = found->second.info; @@ -209,85 +214,82 @@ eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasDir::GetChannelDataTypeInforma return ret_val; } -void eCAL::eh5::HDF5MeasDir::SetChannelDataTypeInformation(const std::string& channel_name, const eCAL::eh5::DataTypeInformation& info) +// TODO: this seems fishy. Do we need to escape? +void eCAL::eh5::HDF5MeasDir::SetChannelDataTypeInformation(const SChannel& channel, const eCAL::eh5::DataTypeInformation& info) { // Get an existing writer or create a new one - auto file_writer_it = GetWriter(channel_name); - file_writer_it->second->SetChannelDataTypeInformation(channel_name, info); + auto file_writer_it = GetWriter(channel); + file_writer_it->second->SetChannelDataTypeInformation(channel, info); // Let's save them in case we need to query them - channels_info_[channel_name] = ChannelInfo(info); + channels_info_[channel] = ChannelInfo(info); } -long long eCAL::eh5::HDF5MeasDir::GetMinTimestamp(const std::string& channel_name) const +long long eCAL::eh5::HDF5MeasDir::GetMinTimestamp(const SChannel& channel) const { - long long ret_val = 0; - - const auto& found = entries_by_chn_.find(channel_name); + long long min_timestamp = std::numeric_limits::max(); + const auto& channel_entries = entries_by_chn_.find(channel); - if (found != entries_by_chn_.end()) + if (channel_entries != entries_by_chn_.end()) { - if (!found->second.empty()) + if (!channel_entries->second.empty()) { - ret_val = found->second.begin()->RcvTimestamp; + min_timestamp = channel_entries->second.begin()->RcvTimestamp; } } - return ret_val; + return min_timestamp; } -long long eCAL::eh5::HDF5MeasDir::GetMaxTimestamp(const std::string& channel_name) const +long long eCAL::eh5::HDF5MeasDir::GetMaxTimestamp(const SChannel& channel) const { - long long ret_val = 0; - - const auto& found = entries_by_chn_.find(channel_name); + long long max_timestamp = std::numeric_limits::min(); + const auto& channel_entries = entries_by_chn_.find(channel); - if (found != entries_by_chn_.end()) + if (channel_entries != entries_by_chn_.end()) { - if (!found->second.empty()) + if (!channel_entries->second.empty()) { - ret_val = found->second.rbegin()->RcvTimestamp; + max_timestamp = channel_entries->second.rbegin()->RcvTimestamp; } } - return ret_val; + return max_timestamp; } -bool eCAL::eh5::HDF5MeasDir::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasDir::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); - const auto& found = entries_by_chn_.find(channel_name); - - if (found != entries_by_chn_.end()) + const auto& channel_it = entries_by_chn_.find(channel); + if (channel_it == entries_by_chn_.end()) { - entries = found->second; + return false; } + entries = channel_it->second; + return !entries.empty(); } -bool eCAL::eh5::HDF5MeasDir::GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasDir::GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const { - bool ret_val = false; - entries.clear(); - const auto& found = entries_by_chn_.find(channel_name); - - if (found != entries_by_chn_.end()) + const auto& channel_it = entries_by_chn_.find(channel); + if (channel_it == entries_by_chn_.end()) { - if (begin == 0) begin = entries.begin()->RcvTimestamp; - if (end == 0) end = entries.rbegin()->RcvTimestamp; + return false; + } - const auto& lower = found->second.lower_bound(SEntryInfo(begin, 0, 0)); - const auto& upper = found->second.upper_bound(SEntryInfo(end, 0, 0)); + if (begin == 0) begin = entries.begin()->RcvTimestamp; + if (end == 0) end = entries.rbegin()->RcvTimestamp; - entries.insert(lower, upper); - ret_val = true; - } + const auto& lower = channel_it->second.lower_bound(SEntryInfo(begin, 0, 0)); + const auto& upper = channel_it->second.upper_bound(SEntryInfo(end, 0, 0)); - return ret_val; + entries.insert(lower, upper); + return true; } bool eCAL::eh5::HDF5MeasDir::GetEntryDataSize(long long entry_id, size_t& size) const @@ -317,9 +319,9 @@ void eCAL::eh5::HDF5MeasDir::SetFileBaseName(const std::string& base_name) base_name_ = base_name; } -bool eCAL::eh5::HDF5MeasDir::AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) +bool eCAL::eh5::HDF5MeasDir::AddEntryToFile(const SWriteEntry& entry) { - if ((access_ != CREATE) + if ((access_ == v3::eAccessType::RDONLY) || (output_dir_.empty()) || (base_name_.empty())) { @@ -327,10 +329,10 @@ bool eCAL::eh5::HDF5MeasDir::AddEntryToFile(const void* data, const unsigned lon } // Get an existing writer or create a new one - auto file_writer_it = GetWriter(channel_name); - + auto file_writer_it = GetWriter(entry.channel); + // Use the writer that was either found or created to actually write the data - return file_writer_it->second->AddEntryToFile(data, size, snd_timestamp, rcv_timestamp, channel_name, id, clock); + return file_writer_it->second->AddEntryToFile(entry); } void eCAL::eh5::HDF5MeasDir::ConnectPreSplitCallback(CallbackFunction cb) @@ -422,9 +424,9 @@ std::list eCAL::eh5::HDF5MeasDir::GetHdfFiles(const std::string& pa return paths; } -bool eCAL::eh5::HDF5MeasDir::OpenRX(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +bool eCAL::eh5::HDF5MeasDir::OpenRX(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) { - if (access != eAccessType::RDONLY /*&& access != eAccessType::RDWR*/) return false; + if (access != v3::eAccessType::RDONLY /*&& access != eAccessType::RDWR*/) return false; auto files = GetHdfFiles(path); @@ -432,30 +434,19 @@ bool eCAL::eh5::HDF5MeasDir::OpenRX(const std::string& path, eAccessType access for (const auto& file_path : files) { - auto reader = new eCAL::eh5::HDF5Meas(file_path); + auto reader = new eCAL::eh5::v3::HDF5Meas(file_path); if (reader->IsOk()) { - auto channels = reader->GetChannelNames(); + auto channels = reader->GetChannels(); for (const auto& channel : channels) { - auto escaped_name = GetEscapedTopicname(channel); - auto info = reader->GetChannelDataTypeInformation(channel); - - // What exactly does that do? what are we overwriting? - if (channels_info_.find(escaped_name) == channels_info_.end()) - { - channels_info_[escaped_name] = ChannelInfo(info); - } - else - { - if (!info.descriptor.empty()) - { - channels_info_[escaped_name].info.descriptor = info.descriptor; - } - } + auto escaped_channel = GetEscapedTopicname(channel); + auto info = reader->GetChannelDataTypeInformation(escaped_channel); - channels_info_[escaped_name].files.push_back(reader); + auto& channel_info = channels_info_[escaped_channel]; + channel_info.info = info; + channel_info.files.push_back(reader); EntryInfoSet entries; if (reader->GetEntriesInfo(channel, entries)) @@ -464,7 +455,7 @@ bool eCAL::eh5::HDF5MeasDir::OpenRX(const std::string& path, eAccessType access { entries_by_id_[id] = EntryInfo(entry.ID, reader); entry.ID = id; - entries_by_chn_[escaped_name].insert(entry); + entries_by_chn_[escaped_channel].insert(entry); id++; } } @@ -481,16 +472,24 @@ bool eCAL::eh5::HDF5MeasDir::OpenRX(const std::string& path, eAccessType access return !file_readers_.empty(); } -::eCAL::eh5::HDF5MeasDir::FileWriterMap::iterator eCAL::eh5::HDF5MeasDir::GetWriter(const std::string& channel_name) +::eCAL::eh5::HDF5MeasDir::FileWriterMap::iterator eCAL::eh5::HDF5MeasDir::GetWriter(const SChannel& channel) { + const auto& channel_name{ channel.name }; // Look for an existing writer. When creating 1 file per channel, the channel // name is used as key. Otherwise, emptystring is used as "generic" key and // the same writer is used for all channels. FileWriterMap::iterator file_writer_it = file_writers_.find(one_file_per_channel_ ? channel_name : ""); if (file_writer_it == file_writers_.end()) { - // No appropriate file writer was found. Let's create a new one! - file_writer_it = file_writers_.emplace(one_file_per_channel_ ? channel_name : "", std::make_unique<::eCAL::eh5::HDF5MeasFileWriterV5>()).first; + if (access_ == v3::eAccessType::CREATE) + { + // No appropriate file writer was found. Let's create a new one! + file_writer_it = file_writers_.emplace(one_file_per_channel_ ? channel_name : "", std::make_unique<::eCAL::eh5::HDF5MeasFileWriterV6>()).first; + } + else + { + file_writer_it = file_writers_.emplace(one_file_per_channel_ ? channel_name : "", std::make_unique<::eCAL::eh5::HDF5MeasFileWriterV5>()).first; + } // Set the current parameters to the new file writer file_writer_it->second->SetMaxSizePerFile(GetMaxSizePerFile()); diff --git a/contrib/ecalhdf5/src/eh5_meas_dir.h b/contrib/ecalhdf5/src/eh5_meas_dir.h index f6287b3e9c..f7392e3b6c 100644 --- a/contrib/ecalhdf5/src/eh5_meas_dir.h +++ b/contrib/ecalhdf5/src/eh5_meas_dir.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ namespace eCAL * * @param path input file path **/ - explicit HDF5MeasDir(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasDir(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -67,7 +67,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool Open(const std::string& path, eAccessType access = eAccessType::RDONLY) override; + bool Open(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY) override; /** * @brief Close file @@ -127,20 +127,20 @@ namespace eCAL void SetOneFilePerChannelEnabled(bool enabled) override; /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names - **/ - std::set GetChannelNames() const override; + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids + **/ + std::set GetChannels() const override; /** - * @brief Check if channel exists in measurement - * - * @param channel_name name of the channel - * - * @return true if exists, false otherwise + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise **/ - bool HasChannel(const std::string& channel_name) const override; + bool HasChannel(const eCAL::eh5::SChannel& channel) const override; /** * @brief Get data type information of the given channel @@ -149,7 +149,7 @@ namespace eCAL * * @return channel type **/ - DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const override; + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const override; /** * @brief Set data type information of the given channel @@ -159,7 +159,7 @@ namespace eCAL * * @return channel type **/ - void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info) override; + void SetChannelDataTypeInformation(const SChannel& channel, const DataTypeInformation& info) override; /** * @brief Gets minimum timestamp for specified channel @@ -168,7 +168,7 @@ namespace eCAL * * @return minimum timestamp value **/ - long long GetMinTimestamp(const std::string& channel_name) const override; + long long GetMinTimestamp(const SChannel& channel) const override; /** * @brief Gets maximum timestamp for specified channel @@ -177,7 +177,7 @@ namespace eCAL * * @return maximum timestamp value **/ - long long GetMaxTimestamp(const std::string& channel_name) const override; + long long GetMaxTimestamp(const SChannel& channel) const override; /** * @brief Gets the header info for all data entries for the given channel @@ -188,7 +188,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -201,7 +201,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const override; + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const override; /** * @brief Gets data size of a specific entry @@ -237,13 +237,13 @@ namespace eCAL * @param size size of the data * @param snd_timestamp send timestamp * @param rcv_timestamp receive timestamp - * @param channel_name channel name + * @param channel channel * @param id message id * @param clock message clock * * @return true if succeeds, false if it fails **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) override; + bool AddEntryToFile(const SWriteEntry& entry) override; typedef std::function CallbackFunction; /** @@ -266,7 +266,7 @@ namespace eCAL struct ChannelInfo { DataTypeInformation info; - std::list files; + std::list files; ChannelInfo() = default; ChannelInfo(const DataTypeInformation& info_) @@ -276,21 +276,21 @@ namespace eCAL struct EntryInfo { - long long file_id; - const eCAL::eh5::HDF5Meas* reader; + long long file_id; + const eCAL::eh5::v3::HDF5Meas* reader; EntryInfo() : file_id(0), reader(nullptr) {} - EntryInfo(long long file_id_, const eCAL::eh5::HDF5Meas* reader_) + EntryInfo(long long file_id_, const eCAL::eh5::v3::HDF5Meas* reader_) : file_id(file_id_) , reader(reader_) {} }; - typedef std::list HDF5Files; - typedef std::unordered_map ChannelInfoUMap; - typedef std::unordered_map EntriesByIdUMap; - typedef std::unordered_map EntriesByChannelUMap; + typedef std::list HDF5Files; + typedef std::unordered_map ChannelInfoUMap; + typedef std::unordered_map EntriesByIdUMap; + typedef std::unordered_map EntriesByChannelUMap; HDF5Files file_readers_; ChannelInfoUMap channels_info_; @@ -307,7 +307,7 @@ namespace eCAL typedef std::map Channels; Channels channels_; - eAccessType access_; + v3::eAccessType access_; std::list GetHdfFiles(const std::string& path) const; @@ -318,7 +318,7 @@ namespace eCAL return std::equal(end.rbegin(), end.rend(), str.rbegin()); } - bool OpenRX(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/); + bool OpenRX(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/); // ===================================================================== @@ -350,7 +350,7 @@ namespace eCAL * * @return an iterator to the writer */ - FileWriterMap::iterator GetWriter(const std::string& channel_name); + FileWriterMap::iterator GetWriter(const SChannel& channel); }; } // namespace eh5 } // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v1.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v1.cpp index 20d1381f1d..a6a227d7e7 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v1.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_v1.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include "hdf5.h" #include +#include "datatype_helper.h" #include @@ -37,7 +38,7 @@ eCAL::eh5::HDF5MeasFileV1::HDF5MeasFileV1() } -eCAL::eh5::HDF5MeasFileV1::HDF5MeasFileV1(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +eCAL::eh5::HDF5MeasFileV1::HDF5MeasFileV1(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : file_id_(-1) { #ifndef _DEBUG @@ -58,7 +59,7 @@ eCAL::eh5::HDF5MeasFileV1::~HDF5MeasFileV1() } -bool eCAL::eh5::HDF5MeasFileV1::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +bool eCAL::eh5::HDF5MeasFileV1::Open(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) { entries_.clear(); @@ -68,7 +69,7 @@ bool eCAL::eh5::HDF5MeasFileV1::Open(const std::string& path, eAccessType access if (file_id_ > 0) HDF5MeasFileV1::Close(); - if (access != eAccessType::RDONLY) + if (access != v3::eAccessType::RDONLY) { ReportUnsupportedAction(); return false; @@ -78,11 +79,11 @@ bool eCAL::eh5::HDF5MeasFileV1::Open(const std::string& path, eAccessType access if (HDF5MeasFileV1::IsOk()) { - auto channels = HDF5MeasFileV1::GetChannelNames(); + auto channels = HDF5MeasFileV1::GetChannels(); if (channels.size() == 1) { - channel_name_ = *channels.begin(); - HDF5MeasFileV1::GetEntriesInfo(channel_name_, entries_); + auto channel = *channels.begin(); + HDF5MeasFileV1::GetEntriesInfo(channel, entries_); } } @@ -143,48 +144,47 @@ void eCAL::eh5::HDF5MeasFileV1::SetOneFilePerChannelEnabled(bool /*enabled*/) ReportUnsupportedAction(); } -std::set eCAL::eh5::HDF5MeasFileV1::GetChannelNames() const +std::set eCAL::eh5::HDF5MeasFileV1::GetChannels() const { - std::set channels; + std::set channels; std::string channel_name; GetAttributeValue(file_id_, kChnNameAttribTitle, channel_name); if (!channel_name.empty()) - channels.insert(channel_name); + channels.insert(eCAL::experimental::measurement::base::CreateChannel(channel_name)); return channels; } - -bool eCAL::eh5::HDF5MeasFileV1::HasChannel(const std::string& channel_name) const +bool eCAL::eh5::HDF5MeasFileV1::HasChannel(const eCAL::eh5::SChannel& channel) const { - auto channels = GetChannelNames(); + auto channels = GetChannels(); - return std::find(channels.cbegin(), channels.cend(), channel_name) != channels.end(); + return std::find(channels.cbegin(), channels.cend(), channel) != channels.end(); } -eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV1::GetChannelDataTypeInformation(const std::string& channel_name) const +eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV1::GetChannelDataTypeInformation(const SChannel& channel) const { std::string type; - if (EcalUtils::String::Icompare(channel_name, channel_name_)) + if (EcalUtils::String::Icompare(channel.name, channel_name_)) GetAttributeValue(file_id_, kChnTypeAttrTitle, type); std::string description; - if (EcalUtils::String::Icompare(channel_name, channel_name_)) + if (EcalUtils::String::Icompare(channel.name, channel_name_)) GetAttributeValue(file_id_, kChnDescAttrTitle, description); return CreateInfo(type, description); } -void eCAL::eh5::HDF5MeasFileV1::SetChannelDataTypeInformation(const std::string& /*channel_name*/, const eCAL::eh5::DataTypeInformation& /*info*/) +void eCAL::eh5::HDF5MeasFileV1::SetChannelDataTypeInformation(const SChannel& /*channel*/, const eCAL::eh5::DataTypeInformation& /*info*/) { ReportUnsupportedAction(); } -long long eCAL::eh5::HDF5MeasFileV1::GetMinTimestamp(const std::string& /*channel_name*/) const +long long eCAL::eh5::HDF5MeasFileV1::GetMinTimestamp(const SChannel& /*channel_name*/) const { long long ret_val = 0; @@ -196,7 +196,7 @@ long long eCAL::eh5::HDF5MeasFileV1::GetMinTimestamp(const std::string& /*channe return ret_val; } -long long eCAL::eh5::HDF5MeasFileV1::GetMaxTimestamp(const std::string& /*channel_name*/) const +long long eCAL::eh5::HDF5MeasFileV1::GetMaxTimestamp(const SChannel& /*channel_name*/) const { long long ret_val = 0; @@ -208,11 +208,11 @@ long long eCAL::eh5::HDF5MeasFileV1::GetMaxTimestamp(const std::string& /*channe return ret_val; } -bool eCAL::eh5::HDF5MeasFileV1::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasFileV1::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); - if (!EcalUtils::String::Icompare(channel_name, channel_name_)) return false; + if (!EcalUtils::String::Icompare(channel.name, channel_name_)) return false; if (!HDF5MeasFileV1::IsOk()) return false; @@ -244,7 +244,7 @@ bool eCAL::eh5::HDF5MeasFileV1::GetEntriesInfo(const std::string& channel_name, return (status >= 0); } -bool eCAL::eh5::HDF5MeasFileV1::GetEntriesInfoRange(const std::string& /*channel_name*/, long long begin, long long end, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasFileV1::GetEntriesInfoRange(const SChannel& /*channel_name*/, long long begin, long long end, EntryInfoSet& entries) const { bool ret_val = false; @@ -305,7 +305,7 @@ void eCAL::eh5::HDF5MeasFileV1::SetFileBaseName(const std::string& /*base_name*/ ReportUnsupportedAction(); } -bool eCAL::eh5::HDF5MeasFileV1::AddEntryToFile(const void* /*data*/, const unsigned long long& /*size*/, const long long& /*snd_timestamp*/, const long long& /*rcv_timestamp*/, const std::string& /*channel_name*/, long long /*id*/, long long /*clock*/) +bool eCAL::eh5::HDF5MeasFileV1::AddEntryToFile(const SWriteEntry& /*entry*/) { ReportUnsupportedAction(); return false; @@ -377,5 +377,5 @@ bool eCAL::eh5::HDF5MeasFileV1::GetAttributeValue(hid_t obj_id, const std::strin void eCAL::eh5::HDF5MeasFileV1::ReportUnsupportedAction() { - std::cout << "eCALHDF5 file version bellow 2.0 support only readonly access type. Desired action not supported.\n"; + std::cout << "eCALHDF5 file version below 2.0 support only readonly access type. Desired action not supported.\n"; } diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v1.h b/contrib/ecalhdf5/src/eh5_meas_file_v1.h index 317733c80d..7b8e0565cc 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v1.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_v1.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ namespace eCAL * * @param path input file path **/ - explicit HDF5MeasFileV1(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasFileV1(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -58,7 +58,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool Open(const std::string& path, eAccessType access = eAccessType::RDONLY) override; + bool Open(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY) override; /** * @brief Close file @@ -117,22 +117,21 @@ namespace eCAL */ void SetOneFilePerChannelEnabled(bool enabled) override; - /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids **/ - std::set GetChannelNames() const override; + std::set GetChannels() const override; /** - * @brief Check if channel exists in measurement - * - * @param channel_name name of the channel - * - * @return true if exists, false otherwise + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise **/ - bool HasChannel(const std::string& channel_name) const override; + bool HasChannel(const eCAL::eh5::SChannel& channel) const override; /** * @brief Get data type information of the given channel @@ -141,7 +140,7 @@ namespace eCAL * * @return channel type **/ - DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const override; + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const override; /** * @brief Set data type information of the given channel @@ -151,7 +150,7 @@ namespace eCAL * * @return channel type **/ - void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info) override; + void SetChannelDataTypeInformation(const SChannel& channel, const DataTypeInformation& info) override; /** * @brief Gets minimum timestamp for specified channel @@ -160,7 +159,7 @@ namespace eCAL * * @return minimum timestamp value **/ - long long GetMinTimestamp(const std::string& channel_name) const override; + long long GetMinTimestamp(const SChannel& channel) const override; /** * @brief Gets maximum timestamp for specified channel @@ -169,7 +168,7 @@ namespace eCAL * * @return maximum timestamp value **/ - long long GetMaxTimestamp(const std::string& channel_name) const override; + long long GetMaxTimestamp(const SChannel& channel) const override; /** * @brief Gets the header info for all data entries for the given channel @@ -180,7 +179,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -193,7 +192,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const override; + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const override; /** * @brief Gets data size of a specific entry @@ -235,7 +234,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) override; + bool AddEntryToFile(const SWriteEntry& entry) override; typedef std::function CallbackFunction; /** diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v2.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v2.cpp index a84b15c4ba..c621311f09 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v2.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_v2.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ **/ #include "eh5_meas_file_v2.h" +#include "hdf5_helper.h" +#include "datatype_helper.h" #include "hdf5.h" #include @@ -38,7 +40,7 @@ eCAL::eh5::HDF5MeasFileV2::HDF5MeasFileV2() #endif // _DEBUG } -eCAL::eh5::HDF5MeasFileV2::HDF5MeasFileV2(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +eCAL::eh5::HDF5MeasFileV2::HDF5MeasFileV2(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : file_id_(-1) { #ifndef _DEBUG @@ -58,11 +60,11 @@ eCAL::eh5::HDF5MeasFileV2::~HDF5MeasFileV2() } -bool eCAL::eh5::HDF5MeasFileV2::Open(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) +bool eCAL::eh5::HDF5MeasFileV2::Open(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) { if (file_id_ > 0) Close(); if (path.empty()) return false; - if (access != eAccessType::RDONLY) return false; + if (access != v3::eAccessType::RDONLY) return false; file_id_ = H5Fopen(path.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); @@ -95,7 +97,7 @@ bool eCAL::eh5::HDF5MeasFileV2::IsOk() const std::string eCAL::eh5::HDF5MeasFileV2::GetFileVersion() const { std::string file_version; - GetAttributeValue(file_id_, kFileVerAttrTitle, file_version); + GetAttribute(file_id_, kFileVerAttrTitle, file_version); return file_version; } @@ -120,42 +122,41 @@ void eCAL::eh5::HDF5MeasFileV2::SetOneFilePerChannelEnabled(bool /*enabled*/) { } -std::set eCAL::eh5::HDF5MeasFileV2::GetChannelNames() const +std::set eCAL::eh5::HDF5MeasFileV2::GetChannels() const { - std::set channels_set; + std::set channels_set; - std::string channel_names; - GetAttributeValue(file_id_, kChnAttrTitle, channel_names); + std::string combined_channel_names; + GetAttribute(file_id_, kChnAttrTitle, combined_channel_names); - std::list channels; - EcalUtils::String::Split(channel_names, ",", channels); + std::list channel_name_list; + EcalUtils::String::Split(combined_channel_names, ",", channel_name_list); - for (const auto& channel : channels) - channels_set.insert(channel); + for (const auto& channel_name : channel_name_list) + channels_set.insert(eCAL::experimental::measurement::base::CreateChannel(channel_name)); return channels_set; } - -bool eCAL::eh5::HDF5MeasFileV2::HasChannel(const std::string& channel_name) const +bool eCAL::eh5::HDF5MeasFileV2::HasChannel(const eCAL::eh5::SChannel& channel) const { - auto channels = GetChannelNames(); + auto channels = GetChannels(); - return std::find(channels.cbegin(), channels.cend(), channel_name) != channels.end(); + return std::find(channels.cbegin(), channels.cend(), channel) != channels.end(); } -eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV2::GetChannelDataTypeInformation(const std::string& channel_name) const +eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV2::GetChannelDataTypeInformation(const SChannel& channel) const { std::string type; std::string description; if (this->IsOk()) { - auto dataset_id = H5Dopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + auto dataset_id = H5Dopen(file_id_, channel.name.c_str(), H5P_DEFAULT); if (dataset_id >= 0) { - GetAttributeValue(dataset_id, kChnTypeAttrTitle, type); - GetAttributeValue(dataset_id, kChnDescAttrTitle, description); + GetAttribute(dataset_id, kChnTypeAttrTitle, type); + GetAttribute(dataset_id, kChnDescAttrTitle, description); H5Dclose(dataset_id); } } @@ -163,17 +164,17 @@ eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV2::GetChannelDataTypeInfo return CreateInfo(type, description); } -void eCAL::eh5::HDF5MeasFileV2::SetChannelDataTypeInformation(const std::string& /*channel_name*/, const eCAL::eh5::DataTypeInformation& /*info*/) +void eCAL::eh5::HDF5MeasFileV2::SetChannelDataTypeInformation(const SChannel& /*channel*/ , const eCAL::eh5::DataTypeInformation& /*info*/) { } -long long eCAL::eh5::HDF5MeasFileV2::GetMinTimestamp(const std::string& channel_name) const +long long eCAL::eh5::HDF5MeasFileV2::GetMinTimestamp(const SChannel& channel) const { long long ret_val = 0; EntryInfoSet entries; - if (GetEntriesInfo(channel_name, entries) && !entries.empty()) + if (GetEntriesInfo(channel, entries) && !entries.empty()) { ret_val = entries.begin()->RcvTimestamp; } @@ -181,12 +182,12 @@ long long eCAL::eh5::HDF5MeasFileV2::GetMinTimestamp(const std::string& channel_ return ret_val; } -long long eCAL::eh5::HDF5MeasFileV2::GetMaxTimestamp(const std::string& channel_name) const +long long eCAL::eh5::HDF5MeasFileV2::GetMaxTimestamp(const SChannel& channel) const { long long ret_val = 0; EntryInfoSet entries; - if (GetEntriesInfo(channel_name, entries) && !entries.empty()) + if (GetEntriesInfo(channel, entries) && !entries.empty()) { ret_val = entries.rbegin()->RcvTimestamp; } @@ -194,13 +195,13 @@ long long eCAL::eh5::HDF5MeasFileV2::GetMaxTimestamp(const std::string& channel_ return ret_val; } -bool eCAL::eh5::HDF5MeasFileV2::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasFileV2::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); if (!this->IsOk()) return false; - auto dataset_id = H5Dopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + auto dataset_id = H5Dopen(file_id_, channel.name.c_str(), H5P_DEFAULT); if (dataset_id < 0) return false; @@ -228,13 +229,13 @@ bool eCAL::eh5::HDF5MeasFileV2::GetEntriesInfo(const std::string& channel_name, return (status >= 0); } -bool eCAL::eh5::HDF5MeasFileV2::GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const +bool eCAL::eh5::HDF5MeasFileV2::GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const { bool ret_val = false; EntryInfoSet all_entries; entries.clear(); - if (GetEntriesInfo(channel_name, all_entries) && !all_entries.empty()) + if (GetEntriesInfo(channel, all_entries) && !all_entries.empty()) { if (begin == 0) begin = entries.begin()->RcvTimestamp; if (end == 0) end = entries.rbegin()->RcvTimestamp; @@ -293,9 +294,9 @@ void eCAL::eh5::HDF5MeasFileV2::SetFileBaseName(const std::string& /*base_name*/ } -bool eCAL::eh5::HDF5MeasFileV2::AddEntryToFile(const void* /*data*/, const unsigned long long& /*size*/, const long long& /*snd_timestamp*/, const long long& /*rcv_timestamp*/, const std::string& /*channel_name*/, long long /*id*/, long long /*clock*/) +bool eCAL::eh5::HDF5MeasFileV2::AddEntryToFile(const SWriteEntry& /*entry*/) { - return false; + return false; } void eCAL::eh5::HDF5MeasFileV2::ConnectPreSplitCallback(CallbackFunction /*cb*/) @@ -304,58 +305,4 @@ void eCAL::eh5::HDF5MeasFileV2::ConnectPreSplitCallback(CallbackFunction /*cb*/) void eCAL::eh5::HDF5MeasFileV2::DisconnectPreSplitCallback() { -} - -bool eCAL::eh5::HDF5MeasFileV2::GetAttributeValue(hid_t obj_id, const std::string& name, std::string& value) -{ - bool ret_val = false; - // empty attribute value - value.clear(); - if (obj_id < 0) return false; - - // check if attribute exists - if (H5Aexists(obj_id, name.c_str()) != 0) - { - // open attribute by name, getting the attribute index - hid_t attr_id = H5Aopen_name(obj_id, name.c_str()); - // fail - attribute can not be opened - if (attr_id <= 0) return false; - - // get attribute type - hid_t attr_type = H5Aget_type(attr_id); - // get type class based on attribute type - H5T_class_t type_class = H5Tget_class(attr_type); - // get attribute content dataSize - const size_t attr_size = H5Tget_size(attr_type); - - // if attribute class is string - if (type_class == H5T_STRING) - { - hid_t attr_type_mem = H5Tget_native_type(attr_type, H5T_DIR_ASCEND); - // create buffer to store the value of the attribute - char* content_buffer = new char[attr_size]; - // get attribute value - ret_val = (H5Aread(attr_id, attr_type_mem, content_buffer) >= 0); - - // convert value to std string - value = std::string(content_buffer, attr_size); - - // free buffer - delete[]content_buffer; - } - else - { - // fail - attribute is not string type - ret_val = false; - } - // close attribute - H5Aclose(attr_id); - } - else - { - // fail - attribute name does not exist - ret_val = false; - } - // return read status - return ret_val; -} +} \ No newline at end of file diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v2.h b/contrib/ecalhdf5/src/eh5_meas_file_v2.h index 63934f88cb..17101d59eb 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v2.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_v2.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ namespace eCAL * * @param path input file path **/ - explicit HDF5MeasFileV2(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasFileV2(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -58,7 +58,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool Open(const std::string& path, eAccessType access = eAccessType::RDONLY) override; + bool Open(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY) override; /** * @brief Close file @@ -117,22 +117,21 @@ namespace eCAL */ void SetOneFilePerChannelEnabled(bool enabled) override; - /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids **/ - std::set GetChannelNames() const override; + std::set GetChannels() const override; /** - * @brief Check if channel exists in measurement - * - * @param channel_name name of the channel - * - * @return true if exists, false otherwise + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise **/ - bool HasChannel(const std::string& channel_name) const override; + bool HasChannel(const eCAL::eh5::SChannel& channel) const override; /** * @brief Get data type information of the given channel @@ -141,7 +140,7 @@ namespace eCAL * * @return channel type **/ - DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const override; + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const override; /** * @brief Set data type information of the given channel @@ -151,7 +150,7 @@ namespace eCAL * * @return channel type **/ - void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info) override; + void SetChannelDataTypeInformation(const SChannel& channel, const DataTypeInformation& info) override; /** * @brief Gets minimum timestamp for specified channel @@ -160,7 +159,7 @@ namespace eCAL * * @return minimum timestamp value **/ - long long GetMinTimestamp(const std::string& channel_name) const override; + long long GetMinTimestamp(const SChannel& channel) const override; /** * @brief Gets maximum timestamp for specified channel @@ -169,7 +168,7 @@ namespace eCAL * * @return maximum timestamp value **/ - long long GetMaxTimestamp(const std::string& channel_name) const override; + long long GetMaxTimestamp(const SChannel& channel) const override; /** * @brief Gets the header info for all data entries for the given channel @@ -180,7 +179,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -193,7 +192,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const override; + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const override; /** * @brief Gets data size of a specific entry @@ -235,7 +234,8 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) override; + bool AddEntryToFile(const SWriteEntry& entry) override; + typedef std::function CallbackFunction; /** @@ -252,17 +252,6 @@ namespace eCAL protected: hid_t file_id_; - - /** - * @brief Gets the value of a string attribute - * - * @param [in] obj_id ID of the attribute's parent - * @param [in] name Name of the attribute - * @param [out] value Value of the attribute - * - * @return true if succeeds, false if it fails - **/ - static bool GetAttributeValue(hid_t obj_id, const std::string& name, std::string& value) ; }; } // namespace eh5 diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v3.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v3.cpp index 0469d80619..36e5eec07f 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v3.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_v3.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ namespace eCAL namespace eh5 { - HDF5MeasFileV3::HDF5MeasFileV3(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) + HDF5MeasFileV3::HDF5MeasFileV3(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : HDF5MeasFileV2(path, access) { } @@ -41,13 +41,13 @@ namespace eCAL HDF5MeasFileV3::~HDF5MeasFileV3() = default; - bool HDF5MeasFileV3::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const + bool HDF5MeasFileV3::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); if (!this->IsOk()) return false; - hid_t dataset_id = H5Dopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + hid_t dataset_id = H5Dopen(file_id_, channel.name.c_str(), H5P_DEFAULT); if (dataset_id < 0) return false; diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v3.h b/contrib/ecalhdf5/src/eh5_meas_file_v3.h index 716bf00a7c..3c16b5ff32 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v3.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_v3.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ namespace eCAL * * @param path Input file path **/ - explicit HDF5MeasFileV3(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasFileV3(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -60,7 +60,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; }; } // namespace eh5 } // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v4.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v4.cpp index ae5d125d76..dd33c70497 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v4.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_v4.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ namespace eCAL namespace eh5 { - HDF5MeasFileV4::HDF5MeasFileV4(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) + HDF5MeasFileV4::HDF5MeasFileV4(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : HDF5MeasFileV2(path, access) { } @@ -41,13 +41,13 @@ namespace eCAL HDF5MeasFileV4::~HDF5MeasFileV4() = default; - bool HDF5MeasFileV4::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const + bool HDF5MeasFileV4::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); if (!this->IsOk()) return false; - auto dataset_id = H5Dopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + auto dataset_id = H5Dopen(file_id_, channel.name.c_str(), H5P_DEFAULT); if (dataset_id < 0) return false; diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v4.h b/contrib/ecalhdf5/src/eh5_meas_file_v4.h index f9d1139752..d2b0de9737 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v4.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_v4.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ namespace eCAL * * @param path Input file path **/ - explicit HDF5MeasFileV4(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasFileV4(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -60,7 +60,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; }; } // namespace eh5 } // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v5.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v5.cpp index 39960e6015..5ba1eebb8a 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v5.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_v5.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ namespace eCAL namespace eh5 { - HDF5MeasFileV5::HDF5MeasFileV5(const std::string& path, eAccessType access /*= eAccessType::RDONLY*/) + HDF5MeasFileV5::HDF5MeasFileV5(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) : HDF5MeasFileV2(path, access) { } @@ -41,13 +41,13 @@ namespace eCAL HDF5MeasFileV5::~HDF5MeasFileV5() = default; - bool HDF5MeasFileV5::GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const + bool HDF5MeasFileV5::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const { entries.clear(); if (!this->IsOk()) return false; - auto dataset_id = H5Dopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + auto dataset_id = H5Dopen(file_id_, channel.name.c_str(), H5P_DEFAULT); if (dataset_id < 0) return false; diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v5.h b/contrib/ecalhdf5/src/eh5_meas_file_v5.h index 9188616b15..ec2d7acc28 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_v5.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_v5.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ namespace eCAL * * @param path Input file path **/ - explicit HDF5MeasFileV5(const std::string& path, eAccessType access = eAccessType::RDONLY); + explicit HDF5MeasFileV5(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); /** * @brief Destructor @@ -60,7 +60,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; }; } // namespace eh5 } // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v6.cpp b/contrib/ecalhdf5/src/eh5_meas_file_v6.cpp new file mode 100644 index 0000000000..3aa8e5058a --- /dev/null +++ b/contrib/ecalhdf5/src/eh5_meas_file_v6.cpp @@ -0,0 +1,128 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCALHDF5 reader multiple channels implement +**/ + +#include "eh5_meas_file_v6.h" + +#include "hdf5.h" +#include "hdf5_helper.h" + +namespace eCAL +{ + namespace eh5 + { + + HDF5MeasFileV6::HDF5MeasFileV6(const std::string& path, v3::eAccessType access /*= eAccessType::RDONLY*/) + : HDF5MeasFileV2(path, access) + { + } + + HDF5MeasFileV6::HDF5MeasFileV6() + = default; + + HDF5MeasFileV6::~HDF5MeasFileV6() + = default; + + // Channels have to be obtained differently, the names themselves are written in the header + // for channel, id, we need to traverse the file format and get them. + std::set HDF5MeasFileV6::GetChannels() const + { + std::set channels; + // V2 Channel function will return (channel_name, 0) + // so we will take those channel_names + const auto channels_v2 = HDF5MeasFileV2::GetChannels(); + + for (const auto& channel_v2 : channels_v2) + { + const auto& channel_name = channel_v2.name; + auto group_id = H5Gopen(file_id_, channel_name.c_str(), H5P_DEFAULT); + auto groups = ListSubgroups(group_id); + H5Gclose(group_id); + + for (const auto& group : groups) + { + const auto id = parseHexID(group); + channels.insert({ channel_name, id }); + } + + } + + return channels; + } + + bool HDF5MeasFileV6::HasChannel(const eCAL::eh5::SChannel& channel) const + { + bool has_channel_name = HasGroup(file_id_, channel.name); + if (!has_channel_name) + { + return false; + } + auto group_id = H5Gopen(file_id_, channel.name.c_str(), H5P_DEFAULT); + bool has_channel_id = HasGroup(group_id, printHex(channel.id).c_str()); + H5Gclose(group_id); + + return has_channel_id; + } + + eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileV6::GetChannelDataTypeInformation(const SChannel& channel) const + { + std::string type_name; + std::string type_encoding; + std::string type_descriptor; + + auto group_id = H5Gopen(file_id_, channel.name.c_str(), H5P_DEFAULT); + auto groups = ListSubgroups(group_id); + H5Gclose(group_id); + + if (this->IsOk()) + { + std::string channel_id_key = printHex(channel.id); + + // Read Typename + auto type_name_url = v6::GetUrl(channel.name, channel_id_key, eCAL::eh5::kChnIdTypename); + ReadStringEntryAsString(file_id_, type_name_url, type_name); + + // Read Encoding + auto type_encoding_url = v6::GetUrl(channel.name, channel_id_key, eCAL::eh5::kChnIdEncoding); + ReadStringEntryAsString(file_id_, type_encoding_url, type_encoding); + + // Read Descriptor + auto type_descriptor_url = v6::GetUrl(channel.name, channel_id_key, eCAL::eh5::kChnIdDescriptor); + ReadStringEntryAsString(file_id_, type_descriptor_url, type_descriptor); + } + + return eCAL::eh5::DataTypeInformation{ type_name, type_encoding, type_descriptor }; + } + + bool eCAL::eh5::HDF5MeasFileV6::GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const + { + if (!this->IsOk()) return false; + + auto hex_id = printHex(channel.id); + EntryInfoSet channel_id_entries; + auto url = v6::GetUrl(channel.name, hex_id, kChnIdData); + GetEntryInfoVector(file_id_, url, entries); + + return true; + } + } // namespace eh5 +} // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_v6.h b/contrib/ecalhdf5/src/eh5_meas_file_v6.h new file mode 100644 index 0000000000..536e7cd0aa --- /dev/null +++ b/contrib/ecalhdf5/src/eh5_meas_file_v6.h @@ -0,0 +1,75 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * eCALHDF5 file reader multiple channels +**/ + +#pragma once + +#include + +#include "eh5_meas_file_v5.h" + +namespace eCAL +{ + namespace eh5 + { + class HDF5MeasFileV6 : virtual public HDF5MeasFileV5 + { + public: + /** + * @brief Constructor + **/ + HDF5MeasFileV6(); + + /** + * @brief Constructor + * + * @param path Input file path + **/ + explicit HDF5MeasFileV6(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY); + + /** + * @brief Destructor + **/ + ~HDF5MeasFileV6() override; + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids + **/ + std::set GetChannels() const override; + + /** + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise + **/ + bool HasChannel(const eCAL::eh5::SChannel& channel) const override; + + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const override; + + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; + }; + } // namespace eh5 +} // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.cpp b/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.cpp index 31d9f3916a..7d0562d275 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.cpp +++ b/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ #include "eh5_meas_file_writer_v5.h" #include "escape.h" +#include "datatype_helper.h" #ifdef WIN32 #include @@ -54,7 +55,7 @@ eCAL::eh5::HDF5MeasFileWriterV5::~HDF5MeasFileWriterV5() HDF5MeasFileWriterV5::Close(); } -bool eCAL::eh5::HDF5MeasFileWriterV5::Open(const std::string& output_dir, eAccessType /*access = eAccessType::RDONLY*/) +bool eCAL::eh5::HDF5MeasFileWriterV5::Open(const std::string& output_dir, v3::eAccessType /*access = eAccessType::RDONLY*/) { Close(); @@ -126,51 +127,50 @@ void eCAL::eh5::HDF5MeasFileWriterV5::SetOneFilePerChannelEnabled(bool /*enabled { } -std::set eCAL::eh5::HDF5MeasFileWriterV5::GetChannelNames() const +std::set eCAL::eh5::HDF5MeasFileWriterV5::GetChannels() const { - // UNSUPPORTED FUNCTION - return {}; + // UNSUPPORTED FUNCTIONs + return std::set(); } -bool eCAL::eh5::HDF5MeasFileWriterV5::HasChannel(const std::string& /*channel_name*/) const +bool eCAL::eh5::HDF5MeasFileWriterV5::HasChannel(const eCAL::eh5::SChannel& /*channel*/ ) const { // UNSUPPORTED FUNCTION return false; } - -eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileWriterV5::GetChannelDataTypeInformation(const std::string& /*channel_name*/) const +eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileWriterV5::GetChannelDataTypeInformation(const SChannel& /*channel*/) const { // UNSUPPORTED FUNCTION return eCAL::eh5::DataTypeInformation{}; } -void eCAL::eh5::HDF5MeasFileWriterV5::SetChannelDataTypeInformation(const std::string& channel_name, const eCAL::eh5::DataTypeInformation& info) +void eCAL::eh5::HDF5MeasFileWriterV5::SetChannelDataTypeInformation(const SChannel& channel, const eCAL::eh5::DataTypeInformation& info) { auto type_descriptor = FromInfo(info); - channels_[channel_name].Type = type_descriptor.first; - channels_[channel_name].Description = type_descriptor.second; + channels_[channel.name].Type = type_descriptor.first; + channels_[channel.name].Description = type_descriptor.second; } -long long eCAL::eh5::HDF5MeasFileWriterV5::GetMinTimestamp(const std::string& /*channel_name*/) const +long long eCAL::eh5::HDF5MeasFileWriterV5::GetMinTimestamp(const SChannel& /*channel*/) const { // UNSUPPORTED FUNCTION return -1; } -long long eCAL::eh5::HDF5MeasFileWriterV5::GetMaxTimestamp(const std::string& /*channel_name*/) const +long long eCAL::eh5::HDF5MeasFileWriterV5::GetMaxTimestamp(const SChannel& /*channel*/) const { // UNSUPPORTED FUNCTION return -1; } -bool eCAL::eh5::HDF5MeasFileWriterV5::GetEntriesInfo(const std::string& /*channel_name*/, EntryInfoSet& /*entries*/) const +bool eCAL::eh5::HDF5MeasFileWriterV5::GetEntriesInfo(const SChannel& /*channel*/, EntryInfoSet& /*entries*/) const { // UNSUPPORTED FUNCTION return false; } -bool eCAL::eh5::HDF5MeasFileWriterV5::GetEntriesInfoRange(const std::string& /*channel_name*/, long long /*begin*/, long long /*end*/, EntryInfoSet& /*entries*/) const +bool eCAL::eh5::HDF5MeasFileWriterV5::GetEntriesInfoRange(const SChannel& /*channel*/, long long /*begin*/, long long /*end*/, EntryInfoSet& /*entries*/) const { // UNSUPPORTED FUNCTION return false; @@ -193,13 +193,13 @@ void eCAL::eh5::HDF5MeasFileWriterV5::SetFileBaseName(const std::string& base_na base_name_ = base_name; } -bool eCAL::eh5::HDF5MeasFileWriterV5::AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) +bool eCAL::eh5::HDF5MeasFileWriterV5::AddEntryToFile(const SWriteEntry& entry) { if (!IsOk()) file_id_ = Create(); if (!IsOk()) return false; - hsize_t hsSize = static_cast(size); + hsize_t hsSize = static_cast(entry.size); if (!EntryFitsTheFile(hsSize)) { @@ -223,14 +223,14 @@ bool eCAL::eh5::HDF5MeasFileWriterV5::AddEntryToFile(const void* data, const uns auto dataSet = H5Dcreate(file_id_, std::to_string(entries_counter_).c_str(), H5T_NATIVE_UCHAR, dataSpace, H5P_DEFAULT, dsProperty, H5P_DEFAULT); // Write buffer to dataset - herr_t writeStatus = H5Dwrite(dataSet, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + herr_t writeStatus = H5Dwrite(dataSet, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, entry.data); // Close dataset, data space, and data set property H5Dclose(dataSet); H5Pclose(dsProperty); H5Sclose(dataSpace); - channels_[channel_name].Entries.emplace_back(SEntryInfo(rcv_timestamp, static_cast(entries_counter_), clock, snd_timestamp, id)); + channels_[entry.channel.name].Entries.emplace_back(SEntryInfo(entry.rcv_timestamp, static_cast(entries_counter_), entry.clock, entry.snd_timestamp, entry.sender_id)); entries_counter_++; diff --git a/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.h b/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.h index 579951cf70..975d1f34b4 100644 --- a/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.h +++ b/contrib/ecalhdf5/src/eh5_meas_file_writer_v5.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,7 +65,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool Open(const std::string& output_dir, eAccessType access) override; + bool Open(const std::string& output_dir, v3::eAccessType access) override; /** * @brief Close file @@ -125,20 +125,20 @@ namespace eCAL void SetOneFilePerChannelEnabled(bool enabled) override; /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids **/ - std::set GetChannelNames() const override; + std::set GetChannels() const override; /** - * @brief Check if channel exists in measurement - * - * @param channel_name name of the channel - * - * @return true if exists, false otherwise + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise **/ - bool HasChannel(const std::string& channel_name) const override; + bool HasChannel(const eCAL::eh5::SChannel & channel) const override; /** * @brief Get data type information of the given channel @@ -147,7 +147,7 @@ namespace eCAL * * @return channel type **/ - DataTypeInformation GetChannelDataTypeInformation(const std::string & channel_name) const override; + DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const override; /** * @brief Set data type information of the given channel @@ -157,7 +157,7 @@ namespace eCAL * * @return channel type **/ - void SetChannelDataTypeInformation(const std::string & channel_name, const DataTypeInformation & info) override; + void SetChannelDataTypeInformation(const SChannel& channel, const DataTypeInformation & info) override; /** * @brief Gets minimum timestamp for specified channel @@ -166,7 +166,7 @@ namespace eCAL * * @return minimum timestamp value **/ - long long GetMinTimestamp(const std::string& channel_name) const override; + long long GetMinTimestamp(const SChannel& channel) const override; /** * @brief Gets maximum timestamp for specified channel @@ -175,7 +175,7 @@ namespace eCAL * * @return maximum timestamp value **/ - long long GetMaxTimestamp(const std::string& channel_name) const override; + long long GetMaxTimestamp(const SChannel& channel) const override; /** * @brief Gets the header info for all data entries for the given channel @@ -186,7 +186,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const override; + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -199,7 +199,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const override; + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const override; /** * @brief Gets data size of a specific entry @@ -241,7 +241,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) override; + bool AddEntryToFile(const SWriteEntry& entry) override; using CallbackFunction = std::function; /** diff --git a/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.cpp b/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.cpp new file mode 100644 index 0000000000..4689073a97 --- /dev/null +++ b/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.cpp @@ -0,0 +1,339 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCALHDF5 directory reader +**/ + +#include "eh5_meas_file_writer_v6.h" +#include "escape.h" + +#ifdef WIN32 +#include +#else +#include +#endif //WIN32 + +#include +#include +#include + +#include +#include + +#include "hdf5_helper.h" + +constexpr unsigned int kDefaultMaxFileSizeMB = 1000; + +eCAL::eh5::HDF5MeasFileWriterV6::HDF5MeasFileWriterV6() + : cb_pre_split_ (nullptr) + , file_id_ (-1) + , file_split_counter_(-1) + , entries_counter_ (0) + , max_size_per_file_ (kDefaultMaxFileSizeMB * 1024 * 1024) +{} + +eCAL::eh5::HDF5MeasFileWriterV6::~HDF5MeasFileWriterV6() +{ + // call the function via its class becase it's a virtual function that is called in constructor/destructor,- + // where the vtable is not created yet or it's destructed. + HDF5MeasFileWriterV6::Close(); +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::Open(const std::string& output_dir, v3::eAccessType /*access = eAccessType::RDONLY*/) +{ + Close(); + + // Check if the given path points to a directory + if (!EcalUtils::Filesystem::IsDir(output_dir, EcalUtils::Filesystem::Current)) + return false; + + output_dir_ = output_dir; + + return true; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::Close() +{ + if (!this->IsOk()) return false; + + std::string channels_with_entries; + + for (const auto& channel_per_name : channels_) + { + for (const auto& channel_per_id : channel_per_name.second) + { + std::ignore = CreateEntriesTableOfContentsFor(channel_per_name.first, channel_per_id.first, channel_per_id.second.Info, channel_per_id.second.Entries); + } + channels_with_entries += channel_per_name.first + ","; + } + + if ((!channels_with_entries.empty()) && (channels_with_entries.back() == ',')) + channels_with_entries.pop_back(); + + SetAttribute(file_id_, kChnAttrTitle, channels_with_entries); + + for (auto& channel_per_name : channels_) + for (auto& channel_per_id : channel_per_name.second) + channel_per_id.second.Entries.clear(); + + if (H5Fclose(file_id_) >= 0) + { + file_id_ = -1; + return true ; + } + else + { + return false; + } +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::IsOk() const +{ + return (file_id_ >= 0); +} + +std::string eCAL::eh5::HDF5MeasFileWriterV6::GetFileVersion() const +{ + // UNSUPPORTED FUNCTION + return ""; +} + +size_t eCAL::eh5::HDF5MeasFileWriterV6::GetMaxSizePerFile() const +{ + return max_size_per_file_ / 1024 / 1024; +} + +void eCAL::eh5::HDF5MeasFileWriterV6::SetMaxSizePerFile(size_t max_file_size_mib) +{ + max_size_per_file_ = max_file_size_mib * 1024 * 1024; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::IsOneFilePerChannelEnabled() const +{ + return false; +} + +void eCAL::eh5::HDF5MeasFileWriterV6::SetOneFilePerChannelEnabled(bool /*enabled*/) +{ +} + +std::set eCAL::eh5::HDF5MeasFileWriterV6::GetChannels() const +{ + // UNSUPPORTED FUNCTION + return std::set(); +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::HasChannel(const eCAL::eh5::SChannel& /*channel*/) const +{ + // UNSUPPORTED FUNCTION + return false; +} + +eCAL::eh5::DataTypeInformation eCAL::eh5::HDF5MeasFileWriterV6::GetChannelDataTypeInformation(const SChannel& /*channel*/) const +{ + // UNSUPPORTED FUNCTION + return eCAL::eh5::DataTypeInformation{}; +} + +void eCAL::eh5::HDF5MeasFileWriterV6::SetChannelDataTypeInformation(const SChannel& channel , const eCAL::eh5::DataTypeInformation& info) +{ + channels_[channel.name][channel.id].Info = info; +} + + +long long eCAL::eh5::HDF5MeasFileWriterV6::GetMinTimestamp(const SChannel& /*channel_name*/) const +{ + // UNSUPPORTED FUNCTION + return -1; +} + +long long eCAL::eh5::HDF5MeasFileWriterV6::GetMaxTimestamp(const SChannel& /*channel_name*/) const +{ + // UNSUPPORTED FUNCTION + return -1; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::GetEntriesInfo(const SChannel& /*channel_name*/, EntryInfoSet& /*entries*/) const +{ + // UNSUPPORTED FUNCTION + return false; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::GetEntriesInfoRange(const SChannel& /*channel_name*/, long long /*begin*/, long long /*end*/, EntryInfoSet& /*entries*/) const +{ + // UNSUPPORTED FUNCTION + return false; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::GetEntryDataSize(long long /*entry_id*/, size_t& /*size*/) const +{ + // UNSUPPORTED FUNCTION + return false; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::GetEntryData(long long /*entry_id*/, void* /*data*/) const +{ + // UNSUPPORTED FUNCTION + return false; +} + +void eCAL::eh5::HDF5MeasFileWriterV6::SetFileBaseName(const std::string& base_name) +{ + base_name_ = base_name; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::AddEntryToFile(const SWriteEntry& entry) +{ + if (!IsOk()) file_id_ = Create(); + if (!IsOk()) + return false; + + hsize_t hsSize = static_cast(entry.size); + + if (!EntryFitsTheFile(hsSize)) + { + if (cb_pre_split_ != nullptr) + { + cb_pre_split_(); + } + + if (Create() < 0) + return false; + } + + // Create DataSpace with rank 1 and size dimension + auto dataSpace = H5Screate_simple(1, &hsSize, nullptr); + + // Create creation property for dataSpace + auto dsProperty = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_obj_track_times(dsProperty, false); + + // Create dataset in dataSpace + auto dataSet = H5Dcreate(file_id_, std::to_string(entries_counter_).c_str(), H5T_NATIVE_UCHAR, dataSpace, H5P_DEFAULT, dsProperty, H5P_DEFAULT); + + // Write buffer to dataset + herr_t writeStatus = H5Dwrite(dataSet, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, entry.data); + + // Close dataset, data space, and data set property + H5Dclose(dataSet); + H5Pclose(dsProperty); + H5Sclose(dataSpace); + + // TODO: check here about id vs channel.id + channels_[entry.channel.name][entry.channel.id].Entries.emplace_back(SEntryInfo(entry.rcv_timestamp, static_cast(entries_counter_), entry.clock, entry.snd_timestamp, entry.sender_id)); + + entries_counter_++; + + return (writeStatus >= 0); +} + +void eCAL::eh5::HDF5MeasFileWriterV6::ConnectPreSplitCallback(CallbackFunction cb) +{ + cb_pre_split_ = cb; +} + +void eCAL::eh5::HDF5MeasFileWriterV6::DisconnectPreSplitCallback() +{ + cb_pre_split_ = nullptr; +} + +hid_t eCAL::eh5::HDF5MeasFileWriterV6::Create() +{ + if (output_dir_.empty()) return -1; + + if (!EcalUtils::Filesystem::IsDir(output_dir_, EcalUtils::Filesystem::OsStyle::Current) + && !EcalUtils::Filesystem::MkPath(output_dir_, EcalUtils::Filesystem::OsStyle::Current)) + return -1; + + if (base_name_.empty()) return -1; + + if (IsOk() && !Close()) return -1; + + file_split_counter_++; + + std::string filePath = output_dir_ + "/" + base_name_; + + if (file_split_counter_ > 0) + filePath += "_" + std::to_string(file_split_counter_); + + filePath += ".hdf5"; + + // create file access property + hid_t fileAccessPropery = H5Pcreate(H5P_FILE_ACCESS); + // create file create property + hid_t fileCreateProperty = H5Pcreate(H5P_FILE_CREATE); + + // Create hdf file and get file id + file_id_ = H5Fcreate(filePath.c_str(), H5F_ACC_TRUNC, fileCreateProperty, fileAccessPropery); + + if (file_id_ >= 0) + SetAttribute(file_id_, kFileVerAttrTitle, "6.0"); + else + file_split_counter_--; + + return file_id_; +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::EntryFitsTheFile(const hsize_t& size) const +{ + hsize_t fileSize = 0; + bool status = GetFileSize(fileSize); + + // check if buffer fits the current file + return (status && ((fileSize + size) <= max_size_per_file_)); +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::GetFileSize(hsize_t& size) const +{ + if (!IsOk()) + { + size = 0; + return false; + } + else + { + return H5Fget_filesize(file_id_, &size) >= 0; + } +} + +bool eCAL::eh5::HDF5MeasFileWriterV6::CreateEntriesTableOfContentsFor(const std::string& channelName, std::uint64_t channelId, const DataTypeInformation& channelInfo, const EntryInfoVect& entries) const +{ + if (!IsOk()) return false; + +// const size_t dataSetsSize = entries.size(); +// if (dataSetsSize == 0) return false; + + std::string hex_id = printHex(channelId); + + // Create a group with the cannel name + auto group_name_id = OpenOrCreateGroup(file_id_, channelName); + auto group_id_id = OpenOrCreateGroup(group_name_id, hex_id); + + CreateStringEntryInRoot(file_id_, v6::GetUrl(channelName, hex_id, kChnIdTypename), channelInfo.name); + CreateStringEntryInRoot(file_id_, v6::GetUrl(channelName, hex_id, kChnIdEncoding), channelInfo.encoding); + CreateStringEntryInRoot(file_id_, v6::GetUrl(channelName, hex_id, kChnIdDescriptor), channelInfo.descriptor); + CreateInformationEntryInRoot(file_id_, v6::GetUrl(channelName, hex_id, kChnIdData), entries); + + H5Gclose(group_name_id); + H5Gclose(group_id_id); + + return true; +} + diff --git a/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.h b/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.h new file mode 100644 index 0000000000..8ff05339c0 --- /dev/null +++ b/contrib/ecalhdf5/src/eh5_meas_file_writer_v6.h @@ -0,0 +1,318 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * eCALHDF5 file reader single channel +**/ + +#pragma once + +#include +#include +#include +#include + +#include "eh5_meas_impl.h" + +#include "hdf5.h" + +namespace eCAL +{ + namespace eh5 + { + class HDF5MeasFileWriterV6 : virtual public HDF5MeasImpl + { + public: + /** + * @brief Constructor + **/ + HDF5MeasFileWriterV6(); + + // Copy + HDF5MeasFileWriterV6(const HDF5MeasFileWriterV6&) = delete; + HDF5MeasFileWriterV6& operator=(const HDF5MeasFileWriterV6&) = delete; + + // Move + HDF5MeasFileWriterV6& operator=(HDF5MeasFileWriterV6&&) = default; + HDF5MeasFileWriterV6(HDF5MeasFileWriterV6&&) = default; + + /** + * @brief Destructor + **/ + ~HDF5MeasFileWriterV6() override; + + /** + * @brief Open file + * + * @param output_dir Input file path / measurement directory path + * @param access Access type (IGNORED, WILL ALWAYS OPEN READ-WRITE!) + * + * @return true if succeeds, false if it fails + **/ + bool Open(const std::string& output_dir, v3::eAccessType access) override; + + /** + * @brief Close file + * + * @return true if succeeds, false if it fails + **/ + bool Close() override; + + /** + * @brief Checks if file/measurement is ok + * + * @return true if meas can be opened(read) or location is accessible(write), false otherwise + **/ + bool IsOk() const override; + + /** + * @brief Get the File Type Version of the current opened file + * + * @return file version + **/ + std::string GetFileVersion() const override; + + /** + * @brief Gets maximum allowed size for an individual file + * + * @return maximum size in MB + **/ + size_t GetMaxSizePerFile() const override; + + /** + * @brief Sets maximum allowed size for an individual file + * + * @param max_file_size_mib maximum size in MB + **/ + void SetMaxSizePerFile(size_t max_file_size_mib) override; + + /** + * @brief Whether each Channel shall be writte in its own file + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @return true, if one file per channel is enabled + */ + bool IsOneFilePerChannelEnabled() const override; + + /** + * @brief Enable / disable the creation of one individual file per channel + * + * When enabled, data is clustered by channel and each channel is written + * to its own file. The filenames will consist of the basename and the + * channel name. + * + * @param enabled Whether one file shall be created per channel + */ + void SetOneFilePerChannelEnabled(bool enabled) override; + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids + **/ + std::set GetChannels() const override; + + /** + * @brief Check if channel exists in measurement + * + * @param channel channel name & id + * + * @return true if exists, false otherwise + **/ + bool HasChannel(const eCAL::eh5::SChannel & channel) const override; + + /** + * @brief Get data type information of the given channel + * + * @param channel_name channel name + * + * @return channel type + **/ + DataTypeInformation GetChannelDataTypeInformation(const SChannel & channel) const override; + + /** + * @brief Set data type information of the given channel + * + * @param channel_name channel name + * @param info datatype info of the channel + * + * @return channel type + **/ + void SetChannelDataTypeInformation(const SChannel& channel, const eCAL::eh5::DataTypeInformation& info) override; + + /** + * @brief Gets minimum timestamp for specified channel + * + * @param channel_name channel name + * + * @return minimum timestamp value + **/ + long long GetMinTimestamp(const SChannel& channel) const override; + + /** + * @brief Gets maximum timestamp for specified channel + * + * @param channel_name channel name + * + * @return maximum timestamp value + **/ + long long GetMaxTimestamp(const SChannel& channele) const override; + + /** + * @brief Gets the header info for all data entries for the given channel + * Header = timestamp + entry id + * + * @param [in] channel_name channel name + * @param [out] entries header info for all data entries + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const override; + + /** + * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) + * Header = timestamp + entry id + * + * @param [in] channel_name channel name + * @param [in] begin time range begin timestamp + * @param [in] end time range end timestamp + * @param [out] entries header info for data entries in given range + * + * @return true if succeeds, false if it fails + **/ + bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const override; + + /** + * @brief Gets data size of a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] size Entry data size + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryDataSize(long long entry_id, size_t& size) const override; + + /** + * @brief Gets data from a specific entry + * + * @param [in] entry_id Entry ID + * @param [out] data Entry data + * + * @return true if succeeds, false if it fails + **/ + bool GetEntryData(long long entry_id, void* data) const override; + + /** + * @brief Set measurement file base name + * + * @param base_name File base name. + **/ + void SetFileBaseName(const std::string& base_name) override; + + /** + * @brief Add entry to file + * + * @param data data to be added + * @param size size of the data + * @param snd_timestamp send timestamp + * @param rcv_timestamp receive timestamp + * @param channel channel name + * @param id message id + * @param clock message clock + * + * @return true if succeeds, false if it fails + **/ + bool AddEntryToFile(const SWriteEntry& entry) override; + + using CallbackFunction = std::function; + /** + * @brief Connect callback for pre file split notification + * + * @param cb callback function + **/ + void ConnectPreSplitCallback(CallbackFunction cb) override; + + /** + * @brief Disconnect pre file split callback + **/ + void DisconnectPreSplitCallback() override; + + protected: + struct Channel + { + DataTypeInformation Info; + EntryInfoVect Entries; + }; + + using Channels = std::map>; + + std::string output_dir_; + std::string base_name_; + Channels channels_; + CallbackFunction cb_pre_split_; + hid_t file_id_; + int file_split_counter_; + unsigned long long entries_counter_; + size_t max_size_per_file_; + + /** + * @brief Creates the actual file + * + * @return file ID, file was not created if id is negative + **/ + hid_t Create(); + + /** + * @brief Checks if current file size + entry size does not exceed the maximum allowed size of the file + * + * @param size Size of the entry in bytes + * + * @return true if entry can be saved in current file, false if it can not be added to the current file + **/ + bool EntryFitsTheFile(const hsize_t& size) const; + + /** + * @brief Gets the size of the file + * + * @param size Size of the file in bytes + * + * @return true if succeeds, false if it fails + **/ + bool GetFileSize(hsize_t& size) const; + + /** + * @brief Creates the entries "table of contents" (timestamp + entry id) + * (Call it just before closing the file) + * + * @param channelName name for the dataset + * @param channelId id for the dataset (unique publisher ID) + * @param channelType type for the dataset + * @param channelDescription description for the dataset + * @param entries entries for given channel + * + * @return true if succeeds, false if it fails + **/ + bool CreateEntriesTableOfContentsFor(const std::string& channelName, std::uint64_t channelId, const DataTypeInformation& channelInfo, const EntryInfoVect& entries) const; + + }; + } // namespace eh5 +} // namespace eCAL diff --git a/contrib/ecalhdf5/src/eh5_meas_impl.h b/contrib/ecalhdf5/src/eh5_meas_impl.h index cee99fac60..97cc4fa563 100644 --- a/contrib/ecalhdf5/src/eh5_meas_impl.h +++ b/contrib/ecalhdf5/src/eh5_meas_impl.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,39 +29,6 @@ #include "ecalhdf5/eh5_types.h" -inline eCAL::eh5::DataTypeInformation CreateInfo(const std::string& combined_topic_type_, const std::string& descriptor_) -{ - eCAL::eh5::DataTypeInformation info; - auto pos = combined_topic_type_.find(':'); - if (pos == std::string::npos) - { - info.name = combined_topic_type_; - info.encoding = ""; - } - else - { - info.name = combined_topic_type_.substr(pos + 1); - info.encoding = combined_topic_type_.substr(0, pos); - } - info.descriptor = descriptor_; - return info; -} - -inline std::pair FromInfo(const eCAL::eh5::DataTypeInformation& datatype_info_) -{ - std::string combined_topic_type; - if (datatype_info_.encoding.empty()) - { - combined_topic_type = datatype_info_.name; - } - else - { - combined_topic_type = datatype_info_.encoding + ":" + datatype_info_.name; - } - - return std::make_pair(combined_topic_type, datatype_info_.descriptor); -} - namespace eCAL { namespace eh5 @@ -82,7 +49,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - virtual bool Open(const std::string& path, eAccessType access = eAccessType::RDONLY) = 0; + virtual bool Open(const std::string& path, v3::eAccessType access = v3::eAccessType::RDONLY) = 0; /** * @brief Close file @@ -141,22 +108,21 @@ namespace eCAL */ virtual void SetOneFilePerChannelEnabled(bool enabled) = 0; - /** - * @brief Get the available channel names of the current opened file / measurement - * - * @return channel names + * @brief Get the available channel names of the current opened file / measurement + * + * @return channel names & ids **/ - virtual std::set GetChannelNames() const = 0; + virtual std::set GetChannels() const = 0; /** * @brief Check if channel exists in measurement * - * @param channel_name name of the channel + * @param channel channel name & id * * @return true if exists, false otherwise **/ - virtual bool HasChannel(const std::string& channel_name) const = 0; + virtual bool HasChannel(const eCAL::eh5::SChannel& channel) const = 0; /** * @brief Get data type information of the given channel @@ -165,7 +131,7 @@ namespace eCAL * * @return channel type **/ - virtual DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const = 0; + virtual DataTypeInformation GetChannelDataTypeInformation(const SChannel& channel) const = 0; /** * @brief Set data type information of the given channel @@ -175,7 +141,7 @@ namespace eCAL * * @return channel type **/ - virtual void SetChannelDataTypeInformation(const std::string& channel_name, const DataTypeInformation& info) = 0; + virtual void SetChannelDataTypeInformation(const SChannel& channel, const eCAL::eh5::DataTypeInformation& info) = 0; /** * @brief Gets minimum timestamp for specified channel @@ -184,7 +150,7 @@ namespace eCAL * * @return minimum timestamp value **/ - virtual long long GetMinTimestamp(const std::string& channel_name) const = 0; + virtual long long GetMinTimestamp(const SChannel& channel) const = 0; /** * @brief Gets maximum timestamp for specified channel @@ -193,7 +159,7 @@ namespace eCAL * * @return maximum timestamp value **/ - virtual long long GetMaxTimestamp(const std::string& channel_name) const = 0; + virtual long long GetMaxTimestamp(const SChannel& channel) const = 0; /** * @brief Gets the header info for all data entries for the given channel @@ -204,7 +170,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - virtual bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const = 0; + virtual bool GetEntriesInfo(const SChannel& channel, EntryInfoSet& entries) const = 0; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -217,7 +183,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - virtual bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const = 0; + virtual bool GetEntriesInfoRange(const SChannel& channel, long long begin, long long end, EntryInfoSet& entries) const = 0; /** * @brief Gets data size of a specific entry @@ -259,7 +225,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - virtual bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) = 0; + virtual bool AddEntryToFile(const SWriteEntry& entry) = 0; typedef std::function CallbackFunction; /** diff --git a/contrib/ecalhdf5/src/escape.cpp b/contrib/ecalhdf5/src/escape.cpp index dfc37c75ec..cce41dfb8f 100644 --- a/contrib/ecalhdf5/src/escape.cpp +++ b/contrib/ecalhdf5/src/escape.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -576,6 +576,18 @@ namespace eCAL return GetEscapedString(non_escaped_topicname, is_reserved_topicname_); } + SChannel GetEscapedTopicname(const SChannel& input) + { + return SChannel(GetEscapedTopicname(input.name), input.id); + } + + SWriteEntry GetEscapedEntry(const SWriteEntry& input) + { + SWriteEntry escaped_entry{ input }; + escaped_entry.channel = GetEscapedTopicname(input.channel); + return escaped_entry; + } + std::string GetEscapedFilename(const std::string& non_escaped_filename) { return GetEscapedString(non_escaped_filename, is_reserved_filename_); diff --git a/contrib/ecalhdf5/src/escape.h b/contrib/ecalhdf5/src/escape.h index 4e979c7b92..cd26027964 100644 --- a/contrib/ecalhdf5/src/escape.h +++ b/contrib/ecalhdf5/src/escape.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,15 @@ #pragma once #include +#include namespace eCAL { namespace eh5 { std::string GetEscapedTopicname(const std::string& input); + SChannel GetEscapedTopicname(const SChannel& input); + SWriteEntry GetEscapedEntry(const SWriteEntry& input); std::string GetEscapedFilename(const std::string& input); std::string GetUnescapedString(const std::string& input); } diff --git a/contrib/ecalhdf5/src/hdf5_helper.cpp b/contrib/ecalhdf5/src/hdf5_helper.cpp new file mode 100644 index 0000000000..e752aefc74 --- /dev/null +++ b/contrib/ecalhdf5/src/hdf5_helper.cpp @@ -0,0 +1,274 @@ +#include "hdf5_helper.h" + +bool CreateStringEntryInRoot(hid_t root, const std::string& url, const std::string& dataset_content) +{ + // create scalar dataset + hid_t scalar_dataspace = H5Screate(H5S_SCALAR); + // create new string data type + hid_t string_data_type = H5Tcopy(H5T_C_S1); + + // if attribute's value length exists, allocate space for it + if (dataset_content.length() > 0) + H5Tset_size(string_data_type, dataset_content.length()); + // Create creation property for data_space + auto ds_property = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_obj_track_times(ds_property, false); + //H5Pset_create_intermediate_group(ds_property, 1); + + // create attribute + hid_t data_set = H5Dcreate(root, url.c_str(), string_data_type, scalar_dataspace, H5P_DEFAULT, ds_property, H5P_DEFAULT); + if (data_set < 0) return false; + + auto write_status = H5Dwrite(data_set, string_data_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, dataset_content.c_str()); + if (write_status < 0) return false; + + // close all created stuff + H5Dclose(data_set); + H5Pclose(ds_property); + H5Tclose(string_data_type); + H5Sclose(scalar_dataspace); + + return true; +} + + +bool ReadStringEntryAsString(hid_t root, const std::string& url, std::string& data) +{ + // empty attribute value + data.clear(); + if (root < 0) return false; + + auto dataset_id = H5Dopen(root, url.c_str(), H5P_DEFAULT); + if (dataset_id < 0) return false; + + auto size = H5Dget_storage_size(dataset_id); + data.resize(size); + + herr_t read_status = -1; + if (size >= 0) + { + hid_t string_data_type = H5Tcopy(H5T_C_S1); + H5Tset_size(string_data_type, size); + read_status = H5Dread(dataset_id, string_data_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, static_cast(const_cast(data.data()))); + } + + H5Dclose(dataset_id); + return (read_status >= 0); +} + +// status = H5Dread(dset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata); +bool CreateBinaryEntryInRoot(hid_t root, const std::string& url, const std::string& dataset_content) +{ + + hsize_t hs_size = static_cast(dataset_content.size()); + // Create DataSpace with rank 1 and size dimension + auto data_space = H5Screate_simple(1, &hs_size, nullptr); + // Create creation property for data_space + auto ds_property = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_obj_track_times(ds_property, false); + //H5Pset_create_intermediate_group(ds_property, 1); + // Create dataset in data_space + auto data_set = H5Dcreate(root, url.c_str(), H5T_NATIVE_UCHAR, data_space, H5P_DEFAULT, ds_property, H5P_DEFAULT); + + // Write buffer to dataset + herr_t write_status = H5Dwrite(data_set, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, dataset_content.c_str()); + + // Close dataset, data space, and data set property + H5Dclose(data_set); + H5Pclose(ds_property); + H5Sclose(data_space); + + return (write_status >= 0); +} + +bool ReadBinaryEntryAsString(hid_t root, const std::string& url, std::string& data) +{ + data.clear(); + + auto dataset_id = H5Dopen(root, url.c_str(), H5P_DEFAULT); + if (dataset_id < 0) return false; + + auto size = H5Dget_storage_size(dataset_id); + data.resize(size); + + herr_t read_status = -1; + if (size >= 0) + { + read_status = H5Dread(dataset_id, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, static_cast(const_cast(data.data()))); + } + + H5Dclose(dataset_id); + return (read_status >= 0); +} + + +bool CreateInformationEntryInRoot(hid_t root, const std::string& url, const eCAL::eh5::EntryInfoVect& entries) +{ + const size_t dataSetsSize = entries.size(); + hsize_t dims[2] = { dataSetsSize, 5 }; + // Create DataSpace with rank 2 and size dimension + auto dataSpace = H5Screate_simple(2, dims, nullptr); + // Create creation property for data_space + auto dsProperty = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_obj_track_times(dsProperty, false); + auto dataSet = H5Dcreate(root, url.c_str(), H5T_NATIVE_LLONG, dataSpace, H5P_DEFAULT, dsProperty, H5P_DEFAULT); + if (dataSet < 0) return false; + + // Write buffer to dataset + herr_t writeStatus = H5Dwrite(dataSet, H5T_NATIVE_LLONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, entries.data()); + if (writeStatus < 0) return false; + + // Close dataset, data space, and data set property + H5Dclose(dataSet); + H5Pclose(dsProperty); + H5Sclose(dataSpace); + + return true; +} + +bool GetEntryInfoVector(hid_t root, const std::string& url, eCAL::eh5::EntryInfoSet& entries) +{ + entries.clear(); + + auto dataset_id = H5Dopen(root, url.c_str(), H5P_DEFAULT); + + if (dataset_id < 0) return false; + + const size_t sizeof_ll = sizeof(long long); + hsize_t data_size = H5Dget_storage_size(dataset_id) / sizeof_ll; + + if (data_size <= 0) return false; + + std::vector data(data_size); + herr_t status = H5Dread(dataset_id, H5T_NATIVE_LLONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data[0]); + H5Dclose(dataset_id); + + for (unsigned int index = 0; index < data_size; index += 5) + { + // rec timestamp, channel id, send clock, send time stamp, send ID + entries.emplace(eCAL::eh5::SEntryInfo(data[index], data[index + 1], data[index + 2], data[index + 3], data[index + 4])); + } + + return (status >= 0); +} + +bool SetAttribute(hid_t id, const std::string& name, const std::string& value) +{ + if (id < 0) return false; + + if (H5Aexists(id, name.c_str()) > 0) + H5Adelete(id, name.c_str()); + // create scalar dataset + hid_t scalarDataset = H5Screate(H5S_SCALAR); + + // create new string data type + hid_t stringDataType = H5Tcopy(H5T_C_S1); + + // if attribute's value length exists, allocate space for it + if (value.length() > 0) + H5Tset_size(stringDataType, value.length()); + + // create attribute + hid_t attribute = H5Acreate(id, name.c_str(), stringDataType, scalarDataset, H5P_DEFAULT, H5P_DEFAULT); + + if (attribute < 0) return false; + + // write attribute value to attribute + herr_t writeStatus = H5Awrite(attribute, stringDataType, value.c_str()); + if (writeStatus < 0) return false; + + // close attribute + H5Aclose(attribute); + // close scalar dataset + H5Sclose(scalarDataset); + // close string data type + H5Tclose(stringDataType); + + return true; +} + +bool GetAttribute(hid_t id, const std::string& name, std::string& value) +{ + bool ret_val = false; + // empty attribute value + value.clear(); + if (id < 0) return false; + + // check if attribute exists + if (H5Aexists(id, name.c_str()) != 0) + { + // open attribute by name, getting the attribute index + hid_t attr_id = H5Aopen_name(id, name.c_str()); + // fail - attribute can not be opened + if (attr_id <= 0) return false; + + // get attribute type + hid_t attr_type = H5Aget_type(attr_id); + // get type class based on attribute type + H5T_class_t type_class = H5Tget_class(attr_type); + // get attribute content dataSize + const size_t attr_size = H5Tget_size(attr_type); + + // if attribute class is string + if (type_class == H5T_STRING) + { + hid_t attr_type_mem = H5Tget_native_type(attr_type, H5T_DIR_ASCEND); + // create buffer to store the value of the attribute + std::vector content_buffer(attr_size); + // get attribute value + ret_val = (H5Aread(attr_id, attr_type_mem, &content_buffer[0]) >= 0); + + // convert value to std string + value = std::string(&content_buffer[0], attr_size); + } + else + { + // fail - attribute is not string type + ret_val = false; + } + // close attribute + H5Aclose(attr_id); + } + else + { + // fail - attribute name does not exist + ret_val = false; + } + // return read status + return ret_val; +} + +bool HasGroup(hid_t root, const std::string& path) +{ + hid_t exists = H5Lexists(root, path.c_str(), H5P_DEFAULT); + return (exists > 0); +} + +hid_t OpenOrCreateGroup(hid_t root, const std::string& name) +{ + hid_t group; + if (HasGroup(root, name)) + { + group = H5Gopen(root, name.c_str(), H5P_DEFAULT); + } + else + { + group = H5Gcreate(root, name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + } + + return group; +} + +std::vector ListSubgroups(hid_t id) +{ + std::vector group_vector; + auto iterate_lambda = [](hid_t /*group*/, const char* name, const H5L_info_t* /*info*/, void* op_data)->herr_t + { + auto vec = static_cast*>(op_data); + vec->push_back(name); + return 0; + }; + + H5Literate(id, H5_INDEX_NAME, H5_ITER_INC, nullptr, iterate_lambda, (void*)&group_vector); + return group_vector; +} \ No newline at end of file diff --git a/contrib/ecalhdf5/src/hdf5_helper.h b/contrib/ecalhdf5/src/hdf5_helper.h new file mode 100644 index 0000000000..6df5544800 --- /dev/null +++ b/contrib/ecalhdf5/src/hdf5_helper.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +bool CreateStringEntryInRoot(hid_t root, const std::string& url, const std::string& dataset_content); +bool ReadStringEntryAsString(hid_t root, const std::string& url, std::string& data); + +bool CreateBinaryEntryInRoot(hid_t root, const std::string& url, const std::string& dataset_content); +bool ReadBinaryEntryAsString(hid_t root, const std::string& url, std::string& data); + +bool CreateInformationEntryInRoot(hid_t root, const std::string& url, const eCAL::eh5::EntryInfoVect& entries); +bool GetEntryInfoVector(hid_t root, const std::string& url, eCAL::eh5::EntryInfoSet& entries); + +/** +* @brief Set attribute to object(file, entry...) +* +* @param id ID of the attributes parent +* @param name Name of the attribute +* @param value Value of the attribute +* +* @return true if succeeds, false if it fails +**/ +bool SetAttribute(hid_t id, const std::string& name, const std::string& value); +/** +* @brief Gets the value of a string attribute +* +* @param [in] obj_id ID of the attribute's parent +* @param [in] name Name of the attribute +* @param [out] value Value of the attribute +* +* @return true if succeeds, false if it fails +**/ +bool GetAttribute(hid_t id, const std::string& name, std::string& value); + +bool HasGroup(hid_t root, const std::string& path); + +hid_t OpenOrCreateGroup(hid_t root, const std::string& name); + +std::vector ListSubgroups(hid_t id); + + +inline std::string printHex(eCAL::experimental::measurement::base::Channel::id_t id) +{ + std::stringstream ss; + ss << std::hex << std::setw(16) << std::setfill('0') << std::uppercase << id; + return ss.str(); +} + +inline eCAL::experimental::measurement::base::Channel::id_t parseHexID(std::string string_id) +{ + auto unsigned_value = std::stoull(string_id, 0, 16); + return static_cast(unsigned_value); +} + +namespace v6 +{ + inline std::string GetUrl(const std::string& channel_name_, const std::string& channel_id, const std::string& attribute) + { + return "/" + channel_name_ + "/" + channel_id + "/" + attribute; + //return "/" + channel_name_ + "/" + attribute; + } +} \ No newline at end of file diff --git a/contrib/measurement/base/include/ecal/measurement/base/reader.h b/contrib/measurement/base/include/ecal/measurement/base/reader.h index 557b94d887..fcc9999075 100644 --- a/contrib/measurement/base/include/ecal/measurement/base/reader.h +++ b/contrib/measurement/base/include/ecal/measurement/base/reader.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,68 +114,81 @@ namespace eCAL * * @return channel names **/ - virtual std::set GetChannelNames() const = 0; + /*virtual std::set GetChannelNames() const = 0;*/ + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return Channels (channel name & id) + **/ + virtual std::set GetChannels() const = 0; + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return Channels (channel name & id) + **/ + /*virtual std::set GetChannels(const std::string& channel_name) const = 0;*/ /** * @brief Check if channel exists in measurement * - * @param channel_name name of the channel + * @param channel The channel (channel name & id) * * @return true if exists, false otherwise **/ - virtual bool HasChannel(const std::string& channel_name) const = 0; + virtual bool HasChannel(const eCAL::experimental::measurement::base::Channel& channel) const = 0; /** * @brief Get data type information of the given channel * - * @param channel_name channel name + * @param channel (channel name & id) * * @return channel type **/ - virtual DataTypeInformation GetChannelDataTypeInformation(const std::string & channel_name) const = 0; - + virtual DataTypeInformation GetChannelDataTypeInformation(const eCAL::experimental::measurement::base::Channel& channel) const = 0; /** * @brief Gets minimum timestamp for specified channel * - * @param channel_name channel name + * @param channel (channel name & id) * * @return minimum timestamp value **/ - virtual long long GetMinTimestamp(const std::string& channel_name) const = 0; + virtual long long GetMinTimestamp(const eCAL::experimental::measurement::base::Channel& channel) const = 0; /** * @brief Gets maximum timestamp for specified channel * - * @param channel_name channel name + * @param channel (channel name & id) * * @return maximum timestamp value **/ - virtual long long GetMaxTimestamp(const std::string& channel_name) const = 0; + virtual long long GetMaxTimestamp(const eCAL::experimental::measurement::base::Channel & channel) const = 0; /** * @brief Gets the header info for all data entries for the given channel * Header = timestamp + entry id * - * @param [in] channel_name channel name + * @param [in] channel (channel name & id) * @param [out] entries header info for all data entries * * @return true if succeeds, false if it fails **/ - virtual bool GetEntriesInfo(const std::string& channel_name, EntryInfoSet& entries) const = 0; + virtual bool GetEntriesInfo(const eCAL::experimental::measurement::base::Channel & channel, EntryInfoSet& entries) const = 0; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) * Header = timestamp + entry id * - * @param [in] channel_name channel name + * @param [in] channel (channel name & id) * @param [in] begin time range begin timestamp * @param [in] end time range end timestamp * @param [out] entries header info for data entries in given range * * @return true if succeeds, false if it fails **/ - virtual bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, EntryInfoSet& entries) const = 0; + virtual bool GetEntriesInfoRange(const eCAL::experimental::measurement::base::Channel& channel, long long begin, long long end, EntryInfoSet& entries) const = 0; /** * @brief Gets data size of a specific entry diff --git a/contrib/measurement/base/include/ecal/measurement/base/types.h b/contrib/measurement/base/include/ecal/measurement/base/types.h index 0906824716..43a3b5dad7 100644 --- a/contrib/measurement/base/include/ecal/measurement/base/types.h +++ b/contrib/measurement/base/include/ecal/measurement/base/types.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,9 @@ #pragma once +#include #include +#include #include namespace eCAL @@ -42,33 +44,88 @@ namespace eCAL **/ struct DataTypeInformation { - std::string name; //!< name of the datatype - std::string encoding; //!< encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) - std::string descriptor; //!< descriptor information of the datatype (necessary for reflection) + std::string name = ""; //!< name of the datatype + std::string encoding = ""; //!< encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) + std::string descriptor = ""; //!< descriptor information of the datatype (necessary for reflection) //!< @cond bool operator==(const DataTypeInformation& other) const { - return name == other.name && encoding == other.encoding && descriptor == other.descriptor; + return std::tie(name, encoding, descriptor) == std::tie(other.name, other.encoding, other.descriptor); } bool operator!=(const DataTypeInformation& other) const { return !(*this == other); } + + bool operator<(const DataTypeInformation& other) const + { + return std::tie(name, encoding, descriptor) < std::tie(other.name, other.encoding, other.descriptor); + } //!< @endcond }; + struct Channel + { + using id_t = std::int64_t; + + std::string name = ""; + id_t id = 0; + + Channel() = default; + Channel(const std::string name_, id_t id_) : name(name_), id(id_) {}; + + //!< @cond + bool operator==(const Channel& other) const + { + return std::tie(id, name) == std::tie(other.id, other.name); + } + + bool operator!=(const Channel& other) const + { + return !(*this == other); + } + + bool operator<(const Channel& other) const + { + return std::tie(id, name) < std::tie(other.id, other.name); + } + //!< @endcond + }; + + inline Channel CreateChannel(const std::string& name) + { + return Channel{ name, 0 }; + } + + struct WriteEntry + { + // channel + Channel channel; + + // data + const void* data = nullptr; + unsigned long long size = 0; + + // metadata + long long snd_timestamp = 0; + long long rcv_timestamp = 0; + long long sender_id = 0; // Unique ID which may be set by sender + long long clock = 0; + }; + + /** * @brief Info struct for a single measurement entry **/ struct EntryInfo { long long RcvTimestamp; //!< Receive time stamp - long long ID; //!< Channel ID + long long ID; //!< Data ID - to extract corresponding data long long SndClock; //!< Send clock long long SndTimestamp; //!< Send time stamp - long long SndID; //!< Send ID + long long SndID; //!< Send ID (!= channel ID!!!!) //!< @cond EntryInfo() : RcvTimestamp(0), ID(0), SndClock(0), SndTimestamp(0), SndID(0) {} @@ -103,16 +160,21 @@ namespace eCAL **/ using EntryInfoVect = std::vector; - /** - * @brief eCAL Measurement Access types - **/ - enum AccessType - { - RDONLY, //!< ReadOnly - the measurement can only be read - CREATE //!< Create - a new measurement will be created - }; - } } } +} + +namespace std { + template <> + struct hash { + std::size_t operator()(const eCAL::experimental::measurement::base::Channel& data) const { + // Combine the hash of the string and the integer + std::size_t h1 = std::hash{}(data.name); + std::size_t h2 = std::hash{}(data.id); + + // Combine the two hashes (this is a common technique) + return h1 ^ (h2 << 1); // XOR and shift + } + }; } \ No newline at end of file diff --git a/contrib/measurement/base/include/ecal/measurement/base/writer.h b/contrib/measurement/base/include/ecal/measurement/base/writer.h index 9eb003290c..c092fcd3f5 100644 --- a/contrib/measurement/base/include/ecal/measurement/base/writer.h +++ b/contrib/measurement/base/include/ecal/measurement/base/writer.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -146,7 +146,7 @@ namespace eCAL * * @return channel type **/ - virtual void SetChannelDataTypeInformation(const std::string & channel_name, const DataTypeInformation& info) = 0; + virtual void SetChannelDataTypeInformation(const eCAL::experimental::measurement::base::Channel& channel_name, const DataTypeInformation& info) = 0; /** * @brief Set measurement file base name (desired name for the actual hdf5 files that will be created) @@ -168,8 +168,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - virtual bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) = 0; - + virtual bool AddEntryToFile(const base::WriteEntry& entry) = 0; }; } } diff --git a/contrib/measurement/base/include/ecal/measurement/imeasurement.h b/contrib/measurement/base/include/ecal/measurement/imeasurement.h index 95d89f8da1..11bc011a72 100644 --- a/contrib/measurement/base/include/ecal/measurement/imeasurement.h +++ b/contrib/measurement/base/include/ecal/measurement/imeasurement.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,11 +35,11 @@ namespace eCAL class IBinaryChannel { public: - IBinaryChannel(std::shared_ptr meas_, std::string name_) - : channel_name(name_) + IBinaryChannel(std::shared_ptr meas_, experimental::measurement::base::Channel channel_) + : channel(channel_) , meas(meas_) { - meas->GetEntriesInfo(channel_name, entry_infos); + meas->GetEntriesInfo(channel, entry_infos); } virtual BinaryFrame operator[](const experimental::measurement::base::EntryInfo& entry) @@ -53,7 +53,7 @@ namespace eCAL std::string name() { - return channel_name; + return channel.name; } class iterator /*: public std::iterator>*/ @@ -100,7 +100,7 @@ namespace eCAL mutable std::string m_msg; }; - bool operator==(const IBinaryChannel& rhs) const { return channel_name == rhs.channel_name && meas == rhs.meas; /*return it == rhs.it; */ }; + bool operator==(const IBinaryChannel& rhs) const { return channel == rhs.channel && meas == rhs.meas; /*return it == rhs.it; */ }; bool operator!=(const IBinaryChannel& rhs) const { return !(operator==(rhs)); /*return it == rhs.it; */ }; iterator begin() @@ -114,7 +114,7 @@ namespace eCAL } private: - const std::string channel_name; + const experimental::measurement::base::Channel channel; std::shared_ptr meas; mutable experimental::measurement::base::EntryInfoSet entry_infos; mutable std::string data; @@ -125,8 +125,8 @@ namespace eCAL class IChannel { public: - IChannel(std::shared_ptr meas_, std::string name_) - : binary_channel(meas_, name_) + IChannel(std::shared_ptr meas_, const experimental::measurement::base::Channel& channel_) + : binary_channel(meas_, channel_) { } @@ -219,10 +219,11 @@ namespace eCAL public: IMeasurement(const std::string& path); - ChannelSet ChannelNames() const; + ChannelSet Channels() const; + ChannelSet Channels(const std::string& channel_name) const; template - IChannel Get(const std::string& channel) const; + IChannel Get(const experimental::measurement::base::Channel& channel) const; private: std::shared_ptr meas; @@ -233,9 +234,23 @@ namespace eCAL { } - inline ChannelSet IMeasurement::ChannelNames() const + inline ChannelSet IMeasurement::Channels() const { - return meas->GetChannelNames(); + return meas->GetChannels(); + } + + // This is probably not very performant. We should check! + inline ChannelSet IMeasurement::Channels(const std::string& channel_name) const + { + ChannelSet channels_filtered_by_name; + auto all_channels = meas->GetChannels(); + for (const auto& channel : all_channels) { + if (channel.name == channel_name) + { + channels_filtered_by_name.insert(channel); + } + } + return channels_filtered_by_name; } // This will return a nullptr if channel name and @@ -243,14 +258,14 @@ namespace eCAL // a) channel does not exist in the IMeasurement // b) the registered type does not match with the descriptor in the chanenel template - inline IChannel IMeasurement::Get(const std::string& channel) const + inline IChannel IMeasurement::Get(const experimental::measurement::base::Channel& channel) const { // Assert that the channel is in the IMeasurement - auto channels = ChannelNames(); + auto channels = Channels(); if (channels.find(channel) == channels.end()) { // Throw an exception, if channel is not available? - throw std::out_of_range("The channel " + channel + " does not exist in this measurement"); + throw std::out_of_range("The channel {" + channel.name + ", " + std::to_string(channel.id) + "} does not exist in this measurement"); } // Assert that the channel type is compatible with the requested type diff --git a/contrib/measurement/base/include/ecal/measurement/measurement.h b/contrib/measurement/base/include/ecal/measurement/measurement.h index 720445d5d4..19ece11e8c 100644 --- a/contrib/measurement/base/include/ecal/measurement/measurement.h +++ b/contrib/measurement/base/include/ecal/measurement/measurement.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ namespace eCAL { namespace measurement { - using ChannelSet = std::set; + using ChannelSet = std::set; struct SenderID { diff --git a/contrib/measurement/base/include/ecal/measurement/omeasurement.h b/contrib/measurement/base/include/ecal/measurement/omeasurement.h index 09f92c3763..46fe1d0fc2 100644 --- a/contrib/measurement/base/include/ecal/measurement/omeasurement.h +++ b/contrib/measurement/base/include/ecal/measurement/omeasurement.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,36 +34,46 @@ namespace eCAL class OBinaryChannel { public: - OBinaryChannel(std::shared_ptr meas_, const std::string& name_) - : channel_name(name_) + OBinaryChannel(std::shared_ptr meas_, const std::string& name_, const eCAL::experimental::measurement::base::DataTypeInformation& datatype_info) + : channel(name_, 0) , meas(meas_) - , SenderID(0) + , id(0) , clock(0) { + meas->SetChannelDataTypeInformation(channel, datatype_info); } OBinaryChannel& operator<<(const BinaryFrame& entry_) { - meas->AddEntryToFile((void*)entry_.message.data(), entry_.message.size(), entry_.send_timestamp, entry_.receive_timestamp, channel_name, SenderID, clock); + eCAL::experimental::measurement::base::WriteEntry entry; + entry.channel = channel; + entry.data = entry_.message.data(); + entry.size = entry_.message.size(); + entry.snd_timestamp = entry_.send_timestamp; + entry.rcv_timestamp = entry_.receive_timestamp; + entry.sender_id = id; + entry.clock = clock; + + meas->AddEntryToFile(entry); ++clock; return *this; } OBinaryChannel& operator<<(const SenderID& id_) { - SenderID = id_.ID; + id = id_.ID; return *this; } - bool operator==(const OBinaryChannel& rhs) const { return channel_name == rhs.channel_name && meas == rhs.meas; /*return it == rhs.it; */ }; + bool operator==(const OBinaryChannel& rhs) const { return channel == rhs.channel && meas == rhs.meas; /*return it == rhs.it; */ }; bool operator!=(const OBinaryChannel& rhs) const { return !(operator==(rhs)); /*return it == rhs.it; */ }; private: - const std::string channel_name; + const experimental::measurement::base::Channel channel; std::shared_ptr meas; - long long SenderID; + long long id; long long clock; }; @@ -72,8 +82,8 @@ namespace eCAL class OChannel { public: - OChannel(std::shared_ptr meas_, std::string name_) - : binary_channel(meas_, name_) + OChannel(std::shared_ptr meas_, std::string name_, const eCAL::experimental::measurement::base::DataTypeInformation& datatype_info) + : binary_channel(meas_, name_, datatype_info) { } @@ -137,9 +147,8 @@ namespace eCAL eCAL::message::GetEncoding(msg), eCAL::message::GetDescription(msg) }; - meas->SetChannelDataTypeInformation(channel, datatype_info); - // Construct a channel based - return OChannel{meas, channel}; + // Construct a channel based + return OChannel{meas, channel, datatype_info}; } diff --git a/contrib/measurement/hdf5/include/ecal/measurement/hdf5/reader.h b/contrib/measurement/hdf5/include/ecal/measurement/hdf5/reader.h index c626b2efab..f5eeb21111 100644 --- a/contrib/measurement/hdf5/include/ecal/measurement/hdf5/reader.h +++ b/contrib/measurement/hdf5/include/ecal/measurement/hdf5/reader.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,20 +27,16 @@ #include #include - namespace eCAL { - namespace eh5 - { - class HDF5Meas; - } - namespace experimental { namespace measurement { namespace hdf5 { + struct ReaderImpl; + /** * @brief Hdf5 based Reader Implementation **/ @@ -124,7 +120,21 @@ namespace eCAL * * @return channel names **/ - std::set GetChannelNames() const override; + //std::set GetChannelNames() const override; + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return Channels (channel name & id) + **/ + std::set GetChannels() const override; + + /** + * @brief Get the available channel names of the current opened file / measurement + * + * @return Channels (channel name & id) + **/ + //std::set GetChannels(const std::string& channel_name) const override; /** * @brief Check if channel exists in measurement @@ -133,7 +143,7 @@ namespace eCAL * * @return true if exists, false otherwise **/ - bool HasChannel(const std::string& channel_name) const override; + bool HasChannel(const eCAL::experimental::measurement::base::Channel& channel) const override; /** * @brief Get data type information of the given channel @@ -142,7 +152,7 @@ namespace eCAL * * @return channel type **/ - base::DataTypeInformation GetChannelDataTypeInformation(const std::string& channel_name) const override; + base::DataTypeInformation GetChannelDataTypeInformation(const eCAL::experimental::measurement::base::Channel& channel) const override; /** * @brief Gets minimum timestamp for specified channel @@ -151,7 +161,7 @@ namespace eCAL * * @return minimum timestamp value **/ - long long GetMinTimestamp(const std::string& channel_name) const override; + long long GetMinTimestamp(const eCAL::experimental::measurement::base::Channel& channel) const override; /** * @brief Gets maximum timestamp for specified channel @@ -160,7 +170,7 @@ namespace eCAL * * @return maximum timestamp value **/ - long long GetMaxTimestamp(const std::string& channel_name) const override; + long long GetMaxTimestamp(const eCAL::experimental::measurement::base::Channel& channel) const override; /** * @brief Gets the header info for all data entries for the given channel @@ -171,7 +181,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfo(const std::string& channel_name, measurement::base::EntryInfoSet& entries) const override; + bool GetEntriesInfo(const eCAL::experimental::measurement::base::Channel& channel, measurement::base::EntryInfoSet& entries) const override; /** * @brief Gets the header info for data entries for the given channel included in given time range (begin->end) @@ -184,7 +194,7 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, measurement::base::EntryInfoSet& entries) const override; + bool GetEntriesInfoRange(const eCAL::experimental::measurement::base::Channel& channel, long long begin, long long end, measurement::base::EntryInfoSet& entries) const override; /** * @brief Gets data size of a specific entry @@ -207,7 +217,7 @@ namespace eCAL bool GetEntryData(long long entry_id, void* data) const override; private: - std::unique_ptr measurement; + std::unique_ptr impl; }; diff --git a/contrib/measurement/hdf5/include/ecal/measurement/hdf5/writer.h b/contrib/measurement/hdf5/include/ecal/measurement/hdf5/writer.h index 355bccc2e4..8e20c2036d 100644 --- a/contrib/measurement/hdf5/include/ecal/measurement/hdf5/writer.h +++ b/contrib/measurement/hdf5/include/ecal/measurement/hdf5/writer.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,17 +33,14 @@ namespace eCAL { - namespace eh5 - { - class HDF5Meas; - } - namespace experimental { namespace measurement { namespace hdf5 { + class WriterImpl; + /** * @brief Hdf5 based Writer implementation **/ @@ -156,7 +153,7 @@ namespace eCAL * * @return channel type **/ - void SetChannelDataTypeInformation(const std::string& channel_name, const base::DataTypeInformation& info) override; + void SetChannelDataTypeInformation(const eCAL::experimental::measurement::base::Channel& channel, const base::DataTypeInformation& info) override; /** * @brief Set measurement file base name (desired name for the actual hdf5 files that will be created) @@ -178,10 +175,10 @@ namespace eCAL * * @return true if succeeds, false if it fails **/ - bool AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) override; + bool AddEntryToFile(const base::WriteEntry& entry) override; private: - std::unique_ptr measurement; + std::unique_ptr impl; }; } // namespace hdf5 } // namespace measurement diff --git a/contrib/measurement/hdf5/src/reader.cpp b/contrib/measurement/hdf5/src/reader.cpp index 2f9f3fd18b..064d7df48d 100644 --- a/contrib/measurement/hdf5/src/reader.cpp +++ b/contrib/measurement/hdf5/src/reader.cpp @@ -5,12 +5,34 @@ using namespace eCAL::experimental::measurement::hdf5; using namespace eCAL::experimental::measurement; +namespace eCAL +{ + namespace experimental + { + namespace measurement + { + namespace hdf5 + { + struct ReaderImpl + { + eCAL::eh5::HDF5Meas measurement; + + ReaderImpl() = default; + ReaderImpl(const std::string& path) + : measurement(path, eCAL::eh5::eAccessType::RDONLY) + {} + }; + } + } + } +} + Reader::Reader() - : measurement(std::make_unique()) + : impl(std::make_unique()) {} Reader::Reader(const std::string& path) - : measurement(std::make_unique(path, eh5::eAccessType::RDONLY)) + : impl(std::make_unique(path)) {} Reader::~Reader() = default; @@ -21,65 +43,77 @@ Reader& Reader::operator=(Reader&&) noexcept = default; bool Reader::Open(const std::string& path) { - return measurement->Open(path, eh5::eAccessType::RDONLY); + return impl->measurement.Open(path, eCAL::eh5::eAccessType::RDONLY); } bool Reader::Close() { - return measurement->Close(); + return impl->measurement.Close(); } bool Reader::IsOk() const { - return measurement->IsOk(); + return impl->measurement.IsOk(); } std::string Reader::GetFileVersion() const { - return measurement->GetFileVersion(); + return impl->measurement.GetFileVersion(); } +/* std::set Reader::GetChannelNames() const { - return measurement->GetChannelNames(); + return impl->measurement.GetChannelNames(); +}*/ + +std::set eCAL::experimental::measurement::hdf5::Reader::GetChannels() const +{ + return impl->measurement.GetChannels(); } -bool Reader::HasChannel(const std::string& channel_name) const +/* +std::set eCAL::experimental::measurement::hdf5::Reader::GetChannels(const std::string& channel_name) const +{ + return impl->measurement.GetChannels(channel_name); +}*/ + +bool Reader::HasChannel(const eCAL::experimental::measurement::base::Channel& channel) const { - return measurement->HasChannel(channel_name); + return impl->measurement.HasChannel(channel); } -base::DataTypeInformation Reader::GetChannelDataTypeInformation(const std::string& channel_name) const +base::DataTypeInformation Reader::GetChannelDataTypeInformation(const base::Channel& channel) const { - return measurement->GetChannelDataTypeInformation(channel_name); + return impl->measurement.GetChannelDataTypeInformation(channel); } -long long Reader::GetMinTimestamp(const std::string& channel_name) const +long long Reader::GetMinTimestamp(const base::Channel& channel) const { - return measurement->GetMinTimestamp(channel_name); + return impl->measurement.GetMinTimestamp(channel); } -long long Reader::GetMaxTimestamp(const std::string& channel_name) const +long long Reader::GetMaxTimestamp(const base::Channel& channel) const { - return measurement->GetMaxTimestamp(channel_name); + return impl->measurement.GetMaxTimestamp(channel); } -bool Reader::GetEntriesInfo(const std::string& channel_name, base::EntryInfoSet& entries) const +bool Reader::GetEntriesInfo(const base::Channel& channel, base::EntryInfoSet& entries) const { - return measurement->GetEntriesInfo(channel_name, entries); + return impl->measurement.GetEntriesInfo(channel, entries); } -bool Reader::GetEntriesInfoRange(const std::string& channel_name, long long begin, long long end, base::EntryInfoSet& entries) const +bool Reader::GetEntriesInfoRange(const base::Channel& channel, long long begin, long long end, base::EntryInfoSet& entries) const { - return measurement->GetEntriesInfoRange(channel_name, begin, end, entries); + return impl->measurement.GetEntriesInfoRange(channel, begin, end, entries); } bool Reader::GetEntryDataSize(long long entry_id, size_t& size) const { - return measurement->GetEntryDataSize(entry_id, size); + return impl->measurement.GetEntryDataSize(entry_id, size); } bool Reader::GetEntryData(long long entry_id, void* data) const { - return measurement->GetEntryData(entry_id, data); + return impl->measurement.GetEntryData(entry_id, data); } \ No newline at end of file diff --git a/contrib/measurement/hdf5/src/writer.cpp b/contrib/measurement/hdf5/src/writer.cpp index 34fd56717e..783a73838a 100644 --- a/contrib/measurement/hdf5/src/writer.cpp +++ b/contrib/measurement/hdf5/src/writer.cpp @@ -4,12 +4,34 @@ using namespace eCAL::experimental::measurement::hdf5; using namespace eCAL::experimental::measurement; +namespace eCAL +{ + namespace experimental + { + namespace measurement + { + namespace hdf5 + { + struct WriterImpl + { + eCAL::eh5::HDF5Meas measurement; + + WriterImpl() = default; + WriterImpl(const std::string& path) + : measurement(path, eCAL::eh5::eAccessType::CREATE) + {} + }; + } + } + } +} + Writer::Writer() - : measurement(std::make_unique()) + : impl(std::make_unique()) {} Writer::Writer(const std::string& path) - : measurement(std::make_unique(path, eh5::eAccessType::CREATE)) + : impl(std::make_unique(path)) {} Writer::~Writer() = default; @@ -20,50 +42,50 @@ Writer& Writer::operator=(Writer&&) noexcept = default; bool Writer::Open(const std::string& path) { - return measurement->Open(path, eh5::eAccessType::CREATE); + return impl->measurement.Open(path, eCAL::eh5::eAccessType::CREATE); } bool Writer::Close() { - return measurement->Close(); + return impl->measurement.Close(); } bool Writer::IsOk() const { - return measurement->IsOk(); + return impl->measurement.IsOk(); } size_t Writer::GetMaxSizePerFile() const { - return measurement->GetMaxSizePerFile(); + return impl->measurement.GetMaxSizePerFile(); } void Writer::SetMaxSizePerFile(size_t size) { - return measurement->SetMaxSizePerFile(size); + return impl->measurement.SetMaxSizePerFile(size); } bool Writer::IsOneFilePerChannelEnabled() const { - return measurement->IsOneFilePerChannelEnabled(); + return impl->measurement.IsOneFilePerChannelEnabled(); } void Writer::SetOneFilePerChannelEnabled(bool enabled) { - return measurement->SetOneFilePerChannelEnabled(enabled); + return impl->measurement.SetOneFilePerChannelEnabled(enabled); } -void Writer::SetChannelDataTypeInformation(const std::string& channel_name, const base::DataTypeInformation& info) +void Writer::SetChannelDataTypeInformation(const base::Channel& channel_name, const base::DataTypeInformation& info) { - measurement->SetChannelDataTypeInformation(channel_name, info); + impl->measurement.SetChannelDataTypeInformation(channel_name, info); } void Writer::SetFileBaseName(const std::string& base_name) { - return measurement->SetFileBaseName(base_name); + return impl->measurement.SetFileBaseName(base_name); } -bool Writer::AddEntryToFile(const void* data, const unsigned long long& size, const long long& snd_timestamp, const long long& rcv_timestamp, const std::string& channel_name, long long id, long long clock) +bool Writer::AddEntryToFile(const base::WriteEntry& entry) { - return measurement->AddEntryToFile(data, size, snd_timestamp, rcv_timestamp, channel_name, id, clock); + return impl->measurement.AddEntryToFile(entry); } diff --git a/lang/python/ecalhdf5/src/ecalhdf5_wrap.cxx b/lang/python/ecalhdf5/src/ecalhdf5_wrap.cxx index 17da3f73d7..14901be703 100644 --- a/lang/python/ecalhdf5/src/ecalhdf5_wrap.cxx +++ b/lang/python/ecalhdf5/src/ecalhdf5_wrap.cxx @@ -39,7 +39,7 @@ typedef struct { PyObject_HEAD - eCAL::eh5::HDF5Meas *hdf5_meas; + eCAL::eh5::v2::HDF5Meas *hdf5_meas; } Meas; /****************************************/ @@ -52,7 +52,7 @@ static PyObject* Meas_New(PyTypeObject *type, PyObject* /*args*/, PyObject* /*kw self = (Meas *)type->tp_alloc(type, 0); if (self != NULL) { - self->hdf5_meas = new eCAL::eh5::HDF5Meas(); + self->hdf5_meas = new eCAL::eh5::v2::HDF5Meas(); } return (PyObject *)self; @@ -107,11 +107,11 @@ static PyObject* Meas_Open(Meas *self, PyObject *args) switch (access) { case 0: - open_meas = self->hdf5_meas->Open(path, eCAL::experimental::measurement::base::AccessType::RDONLY); + open_meas = self->hdf5_meas->Open(path, eCAL::eh5::v2::RDONLY); break; case 1: - open_meas = self->hdf5_meas->Open(path, eCAL::experimental::measurement::base::AccessType::CREATE); + open_meas = self->hdf5_meas->Open(path, eCAL::eh5::v2::CREATE); break; default: diff --git a/samples/cpp/measurement/benchmark/src/main.cpp b/samples/cpp/measurement/benchmark/src/main.cpp index d589e9a663..6606e8bfca 100644 --- a/samples/cpp/measurement/benchmark/src/main.cpp +++ b/samples/cpp/measurement/benchmark/src/main.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ void MeasPerf(const std::string& file_name, const size_t pkg_size, const size_t // start time auto start = std::chrono::high_resolution_clock::now(); - eCAL::eh5::HDF5Meas writer(output_dir, eCAL::eh5::CREATE); + eCAL::eh5::v2::HDF5Meas writer(output_dir, eCAL::eh5::v2::CREATE); writer.SetFileBaseName(file_name + "_hdf5"); writer.SetMaxSizePerFile(max_size_per_file); for (size_t loop = 0; loop < write_loops; ++loop) diff --git a/samples/cpp/measurement/measurement_read/src/measurement_read.cpp b/samples/cpp/measurement/measurement_read/src/measurement_read.cpp index 92fab83361..1b776752f5 100644 --- a/samples/cpp/measurement/measurement_read/src/measurement_read.cpp +++ b/samples/cpp/measurement/measurement_read/src/measurement_read.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,14 +41,17 @@ int main(int /*argc*/, char** /*argv*/) eCAL::measurement::IMeasurement meas(MEASUREMENT_PATH); // create a channel (topic name "person") - eCAL::measurement::IChannel person_channel = meas.Get("person"); - - // iterate over the messages - for (const auto& person_entry : person_channel) + auto person_channels = meas.Channels("person"); + if (person_channels.size() > 0) { - std::cout << "Person object at timestamp " << person_entry.send_timestamp << std::endl; - print_person(person_entry.message); + eCAL::measurement::IChannel person_channel = meas.Get(*person_channels.begin()); + + // iterate over the messages + for (const auto& person_entry : person_channel) + { + std::cout << "Person object at timestamp " << person_entry.send_timestamp << std::endl; + print_person(person_entry.message); + } } - return 0; } diff --git a/tests/contrib/ecalhdf5/hdf5_test/CMakeLists.txt b/tests/contrib/ecalhdf5/hdf5_test/CMakeLists.txt index a637c7e1c8..2c237272b5 100644 --- a/tests/contrib/ecalhdf5/hdf5_test/CMakeLists.txt +++ b/tests/contrib/ecalhdf5/hdf5_test/CMakeLists.txt @@ -32,7 +32,9 @@ target_link_libraries(${PROJECT_NAME} eCAL::hdf5 Threads::Threads) +target_include_directories(${PROJECT_NAME} PRIVATE $) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_EH5_NO_DEPRECATION_WARNINGS) ecal_install_gtest(${PROJECT_NAME}) diff --git a/tests/contrib/ecalhdf5/hdf5_test/src/hdf5_test.cpp b/tests/contrib/ecalhdf5/hdf5_test/src/hdf5_test.cpp index e2f19acb39..c8b775fb33 100644 --- a/tests/contrib/ecalhdf5/hdf5_test/src/hdf5_test.cpp +++ b/tests/contrib/ecalhdf5/hdf5_test/src/hdf5_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +17,18 @@ * ========================= eCAL LICENSE ================================= */ -#include - +#include #include -#include +#include #include -#include +#include +#include #include -#include // This header file is usually not available as public include! +#include +#include // This header file is usually not available as public include! +#include // This header file is usually not available as public include! using namespace eCAL::experimental::measurement::base; @@ -44,58 +46,126 @@ namespace eCAL } } } + namespace eh5 + { + void PrintTo(const SChannel& channel, std::ostream* os) { + *os << "(" << channel.name << "," << channel.id << ")"; + } + } } +using MeasAPI = eCAL::eh5::v3::HDF5Meas; +using MeasAPIAccess = eCAL::eh5::v3::eAccessType; +using LegacyAPI = eCAL::eh5::v2::HDF5Meas; +using LegacyAPIAccess = eCAL::eh5::v2::eAccessType; +using eCAL::eh5::SChannel; + namespace { struct TestingMeasEntry { - std::string channel_name = "topic"; + SChannel channel{ "topic", 1 }; std::string data = "Hello World"; long long snd_timestamp = 1001LL; long long rcv_timestamp = 2001LL; - long long id = 1LL; + long long snd_id = 0; long long clock = 11LL; }; +std::string print(const TestingMeasEntry& entry) { + std::stringstream s; + s << "(" + << entry.channel.name << ", " + << entry.channel.id << ", " + << entry.data << ", " + << entry.snd_timestamp << ", " + << entry.rcv_timestamp << ", " + << entry.snd_id << ", " + << entry.clock << ")"; + return s.str(); +} + +std::string print(const EntryInfo& info) { + std::stringstream s; + s << "( rcv: " << info.RcvTimestamp + << ", id: " << info.ID + << ", clock: " << info.SndClock + << ", snd: " << info.SndTimestamp + << ", snd_id: " << info.SndID + << ")"; + return s.str(); +} + bool MeasEntryEqualsEntryInfo(const TestingMeasEntry& meas_entry, const EntryInfo entry_info) { - return meas_entry.id == entry_info.SndID + return meas_entry.snd_id == entry_info.SndID && meas_entry.snd_timestamp == entry_info.SndTimestamp && meas_entry.rcv_timestamp == entry_info.RcvTimestamp && meas_entry.clock == entry_info.SndClock; } -bool WriteToHDF(eCAL::eh5::HDF5Meas& writer, const TestingMeasEntry& entry) +eCAL::eh5::SWriteEntry Convert(const TestingMeasEntry& entry) +{ + eCAL::eh5::SWriteEntry write_entry; + write_entry.channel = entry.channel; + write_entry.data = entry.data.data(); + write_entry.size = entry.data.size(); + write_entry.snd_timestamp = entry.snd_timestamp; + write_entry.rcv_timestamp = entry.rcv_timestamp; + write_entry.sender_id = entry.snd_id; + write_entry.clock = entry.clock; + return write_entry; +} + +bool WriteToHDF(MeasAPI& writer, const TestingMeasEntry& entry) +{ + eCAL::eh5::SWriteEntry write_entry = Convert(entry); + return writer.AddEntryToFile(write_entry); +} + +bool WriteToHDF(LegacyAPI& writer, const TestingMeasEntry& entry) { return writer.AddEntryToFile( entry.data.data(), // data entry.data.size(), // data size entry.snd_timestamp, // snd_timestamp entry.rcv_timestamp, // rcv_timestamp - entry.channel_name, // channel name - entry.id, // id + entry.channel.name, // channel_name + entry.snd_id, // channel, id (maybe we shouldn't mix them???) entry.clock // clock ); } +// Validates, that if all entries contained in TestingMeasEntry were added to a measurement, they are later contained in the read measurement. +void ValidateChannelsInMeasurementV6(MeasAPI& hdf5_reader, const std::vector& entries_in_measurement) +{ + std::set expected_channels; + std::transform(entries_in_measurement.begin(), entries_in_measurement.end(), std::inserter(expected_channels, expected_channels.begin()), + [](const TestingMeasEntry& entry) { return entry.channel; }); + + EXPECT_EQ(hdf5_reader.GetChannels(), expected_channels); +} // Validates, that if all entries contained in TestingMeasEntry were added to a measurement, they are later contained in the read measurement. -void ValidateChannelsInMeasurement(eCAL::eh5::HDF5Meas& hdf5_reader, const std::vector& entries_in_measurement) +void ValidateChannelsInMeasurementV5(MeasAPI& hdf5_reader, const std::vector& entries_in_measurement) { - std::set expected_channel_names; - std::transform(entries_in_measurement.begin(), entries_in_measurement.end(), std::inserter(expected_channel_names, expected_channel_names.begin()), - [](const TestingMeasEntry& entry) { return entry.channel_name; }); + std::set expected_channels; + std::transform(entries_in_measurement.begin(), entries_in_measurement.end(), std::inserter(expected_channels, expected_channels.begin()), + [](const TestingMeasEntry& entry) { return eCAL::eh5::SChannel(entry.channel.name, 0); }); - EXPECT_EQ(hdf5_reader.GetChannelNames(), expected_channel_names); + EXPECT_EQ(hdf5_reader.GetChannels(), expected_channels); } // Finds an EntryInfo corresponding to the TestingMeasEntry in the EntryInfoSet // Return default EntryInfo if it cannot be Found EntryInfo FindInSet(const eCAL::eh5::EntryInfoSet& info_set, const TestingMeasEntry& to_find) { - auto it = std::find_if(info_set.begin(), info_set.end(), [&to_find](const EntryInfo& entry) { return entry.SndClock == to_find.clock; }); + auto it = std::find_if(info_set.begin(), info_set.end(), [&to_find](const EntryInfo& entry) { + return entry.SndClock == to_find.clock && + entry.SndID == to_find.snd_id && + entry.SndTimestamp == to_find.snd_timestamp && + entry.RcvTimestamp == to_find.rcv_timestamp; }); if (it != info_set.end()) { return *it; @@ -105,22 +175,23 @@ EntryInfo FindInSet(const eCAL::eh5::EntryInfoSet& info_set, const TestingMeasEn } } -// Validate, that the TestingMeasEntry can be read from the measurement -void ValidateDataInMeasurement(eCAL::eh5::HDF5Meas& hdf5_reader, const TestingMeasEntry& entry) +template +void ValidateDataInMeasurementGeneric(Reader& hdf5_reader, const TestingMeasEntry& entry, std::function entry_info_function) { eCAL::eh5::EntryInfoSet entries_info_set; - EXPECT_TRUE(hdf5_reader.GetEntriesInfo(entry.channel_name, entries_info_set)); - // Get Entry info from measurement and see that it checks out + EXPECT_TRUE(entry_info_function(entries_info_set)); + + // Get Entry info1 from measurement and see that it checks out EntryInfo info = FindInSet(entries_info_set, entry); - // entry was not in the dataset + // entry1 was not in the dataset if (info == EntryInfo{}) { - FAIL() << "Entry info not found in set / measurement"; + FAIL() << "Entry info for not found in set / measurement\n" << print(entry); return; } - EXPECT_TRUE(MeasEntryEqualsEntryInfo(entry, info)); + EXPECT_TRUE(MeasEntryEqualsEntryInfo(entry, info)) << print(entry) << " != " << print(info); size_t data_size; EXPECT_TRUE(hdf5_reader.GetEntryDataSize(info.ID, data_size)); @@ -131,50 +202,76 @@ void ValidateDataInMeasurement(eCAL::eh5::HDF5Meas& hdf5_reader, const TestingMe EXPECT_EQ(data_read, entry.data); } + +// Validate, that the TestingMeasEntry can be read from the measurement +void ValidateDataInMeasurement(MeasAPI& hdf5_reader, const TestingMeasEntry& entry) +{ + auto get_entries_info = [&hdf5_reader, &entry](eCAL::eh5::EntryInfoSet& entries) -> bool + { + return hdf5_reader.GetEntriesInfo(entry.channel, entries); + }; + + ValidateDataInMeasurementGeneric(hdf5_reader, entry, get_entries_info); +} + +void ValidateDataInMeasurement(LegacyAPI& hdf5_reader, const TestingMeasEntry& entry) +{ + auto get_entries_info = [&hdf5_reader, &entry](eCAL::eh5::EntryInfoSet& entries) -> bool + { + return hdf5_reader.GetEntriesInfo(entry.channel.name, entries); + }; + + ValidateDataInMeasurementGeneric(hdf5_reader, entry, get_entries_info); +} + + TestingMeasEntry m1{ - "topic / with / slash", + {"topic / with / slash", 1}, + //{"t1", 1}, "Hello World", 1001LL, 2001LL, - 1LL, + 0, 11LL, }; TestingMeasEntry m2{ - "another,topic", + {"another,topic", 2}, + //{"t2", 2}, "", 1002LL, 2002LL, - 2LL, + 0, 12LL }; TestingMeasEntry m3{ - " ASCII and beyond!\a\b\t\n\v\f\r\"#$%&\'()*+,-./0123456789:,<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~üöäÜÖÄâÂôÔûÛáàÁÀúÙ", + {" ASCII and beyond!\a\b\t\n\v\f\r\"#$%&\'()*+,-./0123456789:,<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~üöäÜÖÄâÂôÔûÛáàÁÀúÙ", 3}, + //{"t3", 3}, "o.O", 1003LL, 2003LL, - 3LL, + 0, 13LL }; TestingMeasEntry topic_1 { - "topic_1", + {"topic_1", 1}, "topic1: test data", 1001LL, 2001LL, - 1LL, + 0, 11LL }; TestingMeasEntry topic_2 { - "topic_2", + {"topic_2", 2}, "topic2: test data", 1002LL, 2002LL, - 2LL, + 1000, 12LL, }; @@ -185,6 +282,20 @@ TestingMeasEntry topic_2 std::vector data; } +template +void CreateMeasurement(Meas& hdf5_writer, std::string root_dir, std::string base_name, AccessType access_type = AccessType::CREATE) +{ + if (hdf5_writer.Open(root_dir, access_type)) + { + hdf5_writer.SetFileBaseName(base_name); + hdf5_writer.SetMaxSizePerFile(max_size_per_file); + } + else + { + FAIL() << "Failed to open HDF5 Writer"; + } +} + TEST(contrib, HDF5_EscapeUnescape) { std::string test_string = "This string contains commata,slashes/ spaces, a percent sign (%), a bell\a, nasty line endings\r\n and a german letter oe from the Latin-1 codepage: \xF8"; @@ -213,17 +324,8 @@ TEST(contrib, HDF5_WriteReadIntegrity) // Write HDF5 file { - eCAL::eh5::HDF5Meas hdf5_writer; - - if (hdf5_writer.Open(meas_root_dir, eCAL::eh5::eAccessType::CREATE)) - { - hdf5_writer.SetFileBaseName(base_name); - hdf5_writer.SetMaxSizePerFile(max_size_per_file); - } - else - { - FAIL() << "Failed to open HDF5 Writer"; - } + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); for (const auto& entry : meas_entries) { @@ -233,12 +335,17 @@ TEST(contrib, HDF5_WriteReadIntegrity) EXPECT_TRUE(hdf5_writer.Close()); } + std::set expected_channels{ m1.channel, m2.channel, m3.channel }; + // Read HDF5 file { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir + "/" + base_name + ".hdf5")); - ValidateChannelsInMeasurement(hdf5_reader, meas_entries); + auto actual_channels = hdf5_reader.GetChannels(); + EXPECT_EQ(actual_channels, expected_channels); + + ValidateChannelsInMeasurementV6(hdf5_reader, meas_entries); for (const auto& entry : meas_entries) { ValidateDataInMeasurement(hdf5_reader, entry); @@ -247,10 +354,13 @@ TEST(contrib, HDF5_WriteReadIntegrity) // Read entries with HDF5 dir API { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); - ValidateChannelsInMeasurement(hdf5_reader, meas_entries); + auto actual_channels = hdf5_reader.GetChannels(); + EXPECT_EQ(actual_channels, expected_channels); + + ValidateChannelsInMeasurementV6(hdf5_reader, meas_entries); for (const auto& entry : meas_entries) { ValidateDataInMeasurement(hdf5_reader, entry); @@ -263,12 +373,16 @@ TEST(contrib, HDF5_ReadWrite) std::string file_name = "meas_readwrite"; // Create writer instance - eCAL::eh5::HDF5Meas writer(output_dir, eCAL::eh5::CREATE); + MeasAPI writer(output_dir, MeasAPIAccess::CREATE); writer.SetFileBaseName(file_name); writer.SetMaxSizePerFile(max_size_per_file); data.resize(1024); - EXPECT_TRUE(writer.AddEntryToFile(static_cast(data.data()), data.size(), 0, 0, "myChannel", 0, 0)); + eCAL::eh5::SWriteEntry entry; + entry.channel = CreateChannel("myChannel"); + entry.data = static_cast(data.data()); + entry.size = data.size(); + EXPECT_TRUE(writer.AddEntryToFile(entry)); EXPECT_TRUE(writer.Close()); } @@ -276,10 +390,10 @@ TEST(contrib, HDF5_ReadWrite) TEST(contrib, HDF5_IsOneFilePerChannelEnabled) { - eCAL::eh5::HDF5Meas hdf5_writer; + MeasAPI hdf5_writer; std::string base_name = "output"; - if (hdf5_writer.Open(output_dir, eCAL::eh5::eAccessType::CREATE)) + if (hdf5_writer.Open(output_dir, MeasAPIAccess::CREATE)) { hdf5_writer.SetFileBaseName(base_name); hdf5_writer.SetMaxSizePerFile(max_size_per_file); @@ -302,9 +416,9 @@ TEST(contrib, HDF5_SetOneFilePerChannelEnabled) // Write HDF5 file { - eCAL::eh5::HDF5Meas hdf5_writer; + MeasAPI hdf5_writer; - if (hdf5_writer.Open(output_dir, eCAL::eh5::eAccessType::CREATE)) + if (hdf5_writer.Open(output_dir, MeasAPIAccess::CREATE)) { hdf5_writer.SetFileBaseName(base_name); hdf5_writer.SetMaxSizePerFile(max_size_per_file); @@ -325,20 +439,20 @@ TEST(contrib, HDF5_SetOneFilePerChannelEnabled) // Read HDF5 topic 1 file { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; - EXPECT_TRUE(hdf5_reader.Open(output_dir + "/" + base_name + "_" + topic_1.channel_name + ".hdf5")); - ValidateChannelsInMeasurement(hdf5_reader, { topic_1 }); + EXPECT_TRUE(hdf5_reader.Open(output_dir + "/" + base_name + "_" + topic_1.channel.name + ".hdf5")); + ValidateChannelsInMeasurementV6(hdf5_reader, { topic_1 }); ValidateDataInMeasurement(hdf5_reader, topic_1); } // Read HDF5 topic 2 file { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; - EXPECT_TRUE(hdf5_reader.Open(output_dir + "/" + base_name + "_" + topic_2.channel_name + ".hdf5")); + EXPECT_TRUE(hdf5_reader.Open(output_dir + "/" + base_name + "_" + topic_2.channel.name + ".hdf5")); - ValidateChannelsInMeasurement(hdf5_reader, { topic_2 }); + ValidateChannelsInMeasurementV6(hdf5_reader, { topic_2 }); ValidateDataInMeasurement(hdf5_reader, topic_2); } @@ -348,15 +462,15 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) { // Define data that will be written to the file TestingMeasEntry normal_ascii = topic_1; - normal_ascii.channel_name = "NormalAscii"; + normal_ascii.channel.name = "NormalAscii"; const std::string normal_ascii_escaped_filename = "NormalAscii"; TestingMeasEntry escape_ascii = topic_1; - escape_ascii.channel_name = "Escape these: \\/*\"<>|%"; + escape_ascii.channel.name = "Escape these: \\/*\"<>|%"; const std::string escape_ascii_escaped_filename = "Escape these%3A %5C%2F%2A%22%3C%3E%7C%25"; TestingMeasEntry escape_ascii_2 = topic_1; - escape_ascii_2.channel_name = "Don't escape these +~#,;.²³🍺"; + escape_ascii_2.channel.name = "Don't escape these +~#,;.²³🍺"; const std::string escape_ascii_2_escaped_filename = "Don't escape these +~#,;.²³\xF0\x9F\x8D\xBA"; std::string base_name = "escape_filename_meas"; @@ -364,9 +478,9 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) // Write HDF5 file { - eCAL::eh5::HDF5Meas hdf5_writer; + MeasAPI hdf5_writer; - if (hdf5_writer.Open(meas_root_dir, eCAL::eh5::eAccessType::CREATE)) + if (hdf5_writer.Open(meas_root_dir, MeasAPIAccess::CREATE)) { hdf5_writer.SetFileBaseName(base_name); hdf5_writer.SetMaxSizePerFile(max_size_per_file); @@ -386,7 +500,7 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) // Read File 1 { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir + "/" + base_name + "_" + normal_ascii_escaped_filename + ".hdf5")); ValidateDataInMeasurement(hdf5_reader, normal_ascii); @@ -394,7 +508,7 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) // Read File 2 { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir + "/" + base_name + "_" + escape_ascii_escaped_filename + ".hdf5")); ValidateDataInMeasurement(hdf5_reader, escape_ascii); @@ -402,7 +516,7 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) // Read File 3 { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir + "/" + base_name + "_" + escape_ascii_2_escaped_filename + ".hdf5")); ValidateDataInMeasurement(hdf5_reader, escape_ascii_2); @@ -410,7 +524,7 @@ TEST(contrib, HDF5_EscapeFilenamesForOneFilePerChannel) // Read entries with HDF5 dir API { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); ValidateDataInMeasurement(hdf5_reader, normal_ascii); @@ -424,6 +538,7 @@ TEST(contrib, HDF5_WriteReadTopicTypeInformation) { // Define data that will be written to the file TestingMeasEntry entry; + const auto& channel = entry.channel; DataTypeInformation info{ "mytype", "myencoding", "mydescriptor" }; std::string base_name = "datatypeinformation_meas"; @@ -431,19 +546,10 @@ TEST(contrib, HDF5_WriteReadTopicTypeInformation) // Write HDF5 file { - eCAL::eh5::HDF5Meas hdf5_writer; - - if (hdf5_writer.Open(meas_root_dir, eCAL::eh5::eAccessType::CREATE)) - { - hdf5_writer.SetFileBaseName(base_name); - hdf5_writer.SetMaxSizePerFile(max_size_per_file); - } - else - { - FAIL() << "Failed to open HDF5 Writer"; - } + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); - hdf5_writer.SetChannelDataTypeInformation(entry.channel_name, info); + hdf5_writer.SetChannelDataTypeInformation(channel, info); EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); EXPECT_TRUE(hdf5_writer.Close()); @@ -451,16 +557,77 @@ TEST(contrib, HDF5_WriteReadTopicTypeInformation) // Read entries with HDF5 dir API { - eCAL::eh5::HDF5Meas hdf5_reader; + MeasAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(channel), info); + } +} + +/* +* This test verifies that you save different descriptors for the same channel +* And later read them from the measurement +*/ +TEST(HDF5, WriteReadMultipleTopicTypeInformation) +{ + TestingMeasEntry entry1; + entry1.channel.id = 1; + TestingMeasEntry entry2; + entry2.channel.id = 2; + TestingMeasEntry entry3; + entry3.channel.id = 3; + + const auto& channel1 = entry1.channel; + const auto& channel2 = entry2.channel; + const auto& channel3 = entry3.channel; + + DataTypeInformation info1{ "mytype", "myencoding", "mydescriptor" }; + DataTypeInformation info2{ "mytype2", "myencoding2", "mydescriptor2" }; + // take one info whith \0 character, as protobuf descriptors may contain those + DataTypeInformation info3{ "mytype3", "myencoding3", std::string("my\0\ndescriptor3", 15)}; + EXPECT_EQ(info3.descriptor.size(), 15); + + std::string base_name = "datatypeinformation_multiple"; + std::string meas_root_dir = output_dir + "/" + base_name; + + // Write HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + + hdf5_writer.SetChannelDataTypeInformation(channel1, info1); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry1)); + + hdf5_writer.SetChannelDataTypeInformation(channel2, info2); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry2)); + + hdf5_writer.SetChannelDataTypeInformation(channel3, info3); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry3)); + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Read entries with HDF5 dir API + { + MeasAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); - EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(entry.channel_name), info); + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(channel1), info1); + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(channel2), info2); + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(channel3), info3); + ValidateDataInMeasurement(hdf5_reader, entry1); + ValidateDataInMeasurement(hdf5_reader, entry2); + ValidateDataInMeasurement(hdf5_reader, entry3); } } +// This test checks two cases for the old API. +// We write values with ID 0, and with ID != 0 +// Both descriptors should be read from the measurement successfully TEST(contrib, HDF5_WriteReadTopicTypeInformationDeprecated) { // Define data that will be written to the file - TestingMeasEntry entry; + TestingMeasEntry entry_zero{ {"topic_zero",0}, "topic2: test data", 1001, 1002, 1000, 0 }; + TestingMeasEntry entry_nonzero{ {"topic_nonzerozero",0xAAAA}, "topic2: test data", 1001, 1002, 2000, 0 }; + std::string type = "myencoding:mytype"; std::string descriptor = "mydescriptor"; DataTypeInformation info{ "mytype", "myencoding", "mydescriptor" }; @@ -470,32 +637,414 @@ TEST(contrib, HDF5_WriteReadTopicTypeInformationDeprecated) // Write HDF5 file { - eCAL::eh5::HDF5Meas hdf5_writer; + LegacyAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + + hdf5_writer.SetChannelType(entry_zero.channel.name, type); + hdf5_writer.SetChannelDescription(entry_zero.channel.name, descriptor); + + hdf5_writer.SetChannelType(entry_nonzero.channel.name, type); + hdf5_writer.SetChannelDescription(entry_nonzero.channel.name, descriptor); + + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry_zero)); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry_nonzero)); + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Read entries with HDF5 dir API + { + LegacyAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + EXPECT_EQ(hdf5_reader.GetChannelType(entry_zero.channel.name), type); + EXPECT_EQ(hdf5_reader.GetChannelDescription(entry_zero.channel.name), descriptor); + EXPECT_EQ(hdf5_reader.GetChannelType(entry_nonzero.channel.name), type); + EXPECT_EQ(hdf5_reader.GetChannelDescription(entry_nonzero.channel.name), descriptor); - if (hdf5_writer.Open(meas_root_dir, eCAL::eh5::eAccessType::CREATE)) + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation({ entry_zero.channel.name, 0 }), info); + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation({ entry_nonzero.channel.name, 0 }), info); + } +} + +// This tests confirms if you write with the new API, you can read the information with the old API +TEST(HDF5, WriteReadTopicTypeInformationDeprecated2) +{ + // Define data that will be written to the file + TestingMeasEntry entry_topic_1{ {"topic_1", 1}, "topic2: test data", 1001, 1002, 0xAAAA, 0 }; + const auto& channel_topic_1 = entry_topic_1.channel; + + TestingMeasEntry entry_topic_2{ {"topic_2", 2}, "topic2: test data", 1001, 1002, 0xAAAA, 0 }; + const auto& channel_topic_2 = entry_topic_2.channel; + + std::string type_1 = "myencoding:mytype"; + std::string descriptor_1 = "mydescriptor"; + DataTypeInformation info_1{ "mytype", "myencoding", "mydescriptor" }; + + std::string type_2 = "myencoding2:mytype2"; + std::string descriptor_2 = "mydescriptor2"; + DataTypeInformation info_2{ "mytype2", "myencoding2", "mydescriptor2" }; + + std::string base_name = "datatypeinformation_meas_deprecated_2"; + std::string meas_root_dir = output_dir + "/" + base_name; + + // Write HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + + hdf5_writer.SetChannelDataTypeInformation(channel_topic_1, info_1); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry_topic_1)); + + hdf5_writer.SetChannelDataTypeInformation(channel_topic_2, info_2); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry_topic_2)); + + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Read entries with HDF5 dir API + { + LegacyAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + EXPECT_EQ(hdf5_reader.GetChannelType(channel_topic_1.name), type_1); + EXPECT_EQ(hdf5_reader.GetChannelDescription(channel_topic_1.name), descriptor_1); + + EXPECT_EQ(hdf5_reader.GetChannelType(channel_topic_2.name), type_2); + EXPECT_EQ(hdf5_reader.GetChannelDescription(channel_topic_2.name), descriptor_2); + } +} + + +// This tests confirms if you write with the new API, you can read the information with the old API +TEST(HDF5, MinMaxTimestamps) +{ + Channel::id_t id_1 = 0xAAAA; + Channel::id_t id_2 = 0x00AA; + std::string topic_name = "topic"; + + eCAL::eh5::SChannel channel_1{ topic_name, id_1 }; + eCAL::eh5::SChannel channel_2{ topic_name, id_2 }; + + // Define data that will be written to the file + std::vector meas_entries = { + TestingMeasEntry{ {topic_name, id_1}, "topic2: test data", 1001, 1002, 0, 0 }, + TestingMeasEntry{ {topic_name, id_1}, "topic2: test data", 2001, 2002, 0, 1 }, + TestingMeasEntry{ {topic_name, id_2}, "topic2: test data", 2051, 2052, 0, 0 }, + TestingMeasEntry{ {topic_name, id_1}, "topic2: test data", 3001, 3002, 0, 2 }, + TestingMeasEntry{ {topic_name, id_2}, "topic2: test data", 3051, 3052, 0, 1 }, + TestingMeasEntry{ {topic_name, id_1}, "topic2: test data", 4001, 4002, 0, 3 }, + TestingMeasEntry{ {topic_name, id_1}, "topic2: test data", 5001, 5002, 0, 4 }, + }; + + std::string base_name = "min_max_timestamps"; + std::string meas_root_dir = output_dir + "/" + base_name; + + // Write HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + + for (const auto& entry : meas_entries) { - hdf5_writer.SetFileBaseName(base_name); - hdf5_writer.SetMaxSizePerFile(max_size_per_file); + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); } - else + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Read entries with HDF5 dir API + { + MeasAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + // New API + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel_1), 1002) << "Min channel 1 should be 1002"; + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel_2), 2052) << "Min channel 2 should be 2052"; + + // New API + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel_1), 5002) << "Max channel 1 should be 5002"; + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel_2), 3052) << "Max channel 2 should be 3052"; + + for (const auto& entry : meas_entries) { - FAIL() << "Failed to open HDF5 Writer"; + ValidateDataInMeasurement(hdf5_reader, entry); + } + } + + // Read entries with HDF5 dir API + { + LegacyAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMinTimestamp(topic_name), 1002) << "Min topic should be 1002"; + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(topic_name), 5002) << "Min topic should be 5002"; + + // TODO This won't work, I guess + for (const auto& entry : meas_entries) + { + ValidateDataInMeasurement(hdf5_reader, entry); } + } - hdf5_writer.SetChannelType(entry.channel_name, type); - hdf5_writer.SetChannelDescription(entry.channel_name, descriptor); +} - EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); +/* +* This test checks that we can correctly merge multiple measurements +* +*/ +TEST(HDF5, MergedMeasurements) +{ + Channel::id_t id_1 = 0xAAAA; + Channel::id_t id_2 = 0x00AA; + Channel::id_t id_3 = 0x0001; + std::string topic_name_1_2 = "topic"; + std::string topic_name_3 = "topic_3"; + + eCAL::eh5::SChannel channel_1{ topic_name_1_2, id_1 }; + eCAL::eh5::SChannel channel_2{ topic_name_1_2, id_2 }; + eCAL::eh5::SChannel channel_3{ topic_name_3, id_3 }; + + DataTypeInformation info_1{ "mytype1", "myencoding1", "mydescriptor1" }; + DataTypeInformation info_2{ "mytype2", "myencoding2", "mydescriptor2" }; + DataTypeInformation info_3{ "mytype3", "myencoding3", "mydescriptor3" }; + + // Define data that will be written to the file + std::vector entries_meas_1 = { + TestingMeasEntry{ {topic_name_1_2, id_1}, "topic2: test data", 0001, 0002, 0 , 0 }, + TestingMeasEntry{ {topic_name_1_2, id_1}, "topic2: test data", 2001, 2002, 0 , 1 }, + TestingMeasEntry{ {topic_name_1_2, id_1}, "topic2: test data", 3001, 3002, 0 , 2 }, + TestingMeasEntry{ {topic_name_1_2, id_1}, "topic2: test data", 4001, 4002, 0 , 3 }, + TestingMeasEntry{ {topic_name_1_2, id_1}, "topic2: test data", 5001, 5002, 0 , 4 }, + }; + + std::vector entries_meas_2 = { + TestingMeasEntry{ {topic_name_3 , id_3 }, "topic2: test data", 1001, 1002, 0 , 0 }, + TestingMeasEntry{ {topic_name_3 , id_3 }, "topic2: test data", 2001, 2002, 0 , 1 }, + TestingMeasEntry{ {topic_name_1_2, id_2 }, "topic2: test data", 2051, 2052, 0 , 0 }, + TestingMeasEntry{ {topic_name_3 , id_3 }, "topic2: test data", 3001, 3002, 0 , 2 }, + TestingMeasEntry{ {topic_name_1_2, id_2 }, "topic2: test data", 3051, 3052, 0 , 1 }, + TestingMeasEntry{ {topic_name_3 , id_3 }, "topic2: test data", 6001, 6002, 0 , 3 }, + TestingMeasEntry{ {topic_name_3 , id_3 }, "topic2: test data", 7001, 7002, 0 , 4 }, + }; + + std::string base_dir = output_dir + "/merged_measurement"; + std::string meas_dir_1 = base_dir + "/meas1"; + std::string meas_dir_2 = base_dir + "/meas2"; + + // Write first HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_dir_1, "meas1"); + + for (const auto& entry : entries_meas_1) + { + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); + } + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Write second HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_dir_1, "meas2"); + + for (const auto& entry : entries_meas_2) + { + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); + } + + EXPECT_TRUE(hdf5_writer.Close()); + } + + { + MeasAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(base_dir)); + + std::set expected_channels{ channel_1, channel_2, channel_3 }; + EXPECT_EQ(hdf5_reader.GetChannels(), expected_channels); + + // New API + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel_1), 0002); + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel_2), 2052); + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel_3), 1002); + + // New API + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel_1), 5002); + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel_2), 3052); + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel_3), 7002); + + // Check that all entries are present in the measurement + for (const auto& entry : entries_meas_1) + { + ValidateDataInMeasurement(hdf5_reader, entry); + } + for (const auto& entry : entries_meas_2) + { + ValidateDataInMeasurement(hdf5_reader, entry); + } + } + + { + LegacyAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(base_dir)); + + std::set expected_channels{ topic_name_1_2, topic_name_3 }; + EXPECT_EQ(hdf5_reader.GetChannelNames(), expected_channels); + + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMinTimestamp(topic_name_1_2), 0002); + EXPECT_EQ(hdf5_reader.GetMinTimestamp(topic_name_3), 1002); + + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(topic_name_1_2), 5002); + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(topic_name_3), 7002); + + for (const auto& entry : entries_meas_1) + { + ValidateDataInMeasurement(hdf5_reader, entry); + } + for (const auto& entry : entries_meas_2) + { + ValidateDataInMeasurement(hdf5_reader, entry); + } + } + +} + +// We don't write empty measurements. +// If we change the implementation, we can reactivate this test +TEST(HDF5, DISABLED_WriteReadEmptyMeasurement) +{ + eCAL::eh5::SChannel channel{ "topic", 0}; + DataTypeInformation info{ "mytype", "myencoding", "mydescriptor" }; + + std::string base_name = "empty_measurement"; + std::string meas_root_dir = output_dir + "/" + base_name; + + // Write HDF5 file + { + MeasAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + hdf5_writer.SetChannelDataTypeInformation(channel, info); + + EXPECT_TRUE(hdf5_writer.Close()); + } + + // Read entries with HDF5 dir API + { + MeasAPI hdf5_reader; + EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); + + EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(channel), info); + + std::set channels{ channel }; + EXPECT_EQ(hdf5_reader.GetChannels(), channels); + + EXPECT_EQ(hdf5_reader.GetMinTimestamp(channel), 0); + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(channel), 0); + } +} + +// This tests confirms if you write with the old API, you can read the information both old and new api +TEST(HDF5, TestReaderWriterV5) +{ + Channel::id_t id_1 = 0xAAAA; + Channel::id_t id_2 = 0x00AA; + std::string topic_name = "topic"; + + eCAL::eh5::SChannel channel_1{ topic_name, id_1 }; + eCAL::eh5::SChannel channel_2{ topic_name, id_2 }; + + // Define data that will be written to the file + std::vector meas_entries = { + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 1001, 1002, 0, 0 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 2001, 2002, 0, 1 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 2051, 2052, 0, 0 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 3001, 3002, 0, 2 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 3051, 3052, 0, 1 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 4001, 4002, 0, 3 }, + TestingMeasEntry{ {topic_name, 0}, "topic: test data", 5001, 5002, 0, 4 }, + }; + + std::string base_name = "read_write_v5"; + std::string meas_root_dir = output_dir + "/" + base_name; + + // Write HDF5 file + { + LegacyAPI hdf5_writer; + CreateMeasurement(hdf5_writer, meas_root_dir, base_name); + + for (const auto& entry : meas_entries) + { + EXPECT_TRUE(WriteToHDF(hdf5_writer, entry)); + } EXPECT_TRUE(hdf5_writer.Close()); } // Read entries with HDF5 dir API { - eCAL::eh5::HDF5Meas hdf5_reader; + LegacyAPI hdf5_reader; EXPECT_TRUE(hdf5_reader.Open(meas_root_dir)); - EXPECT_EQ(hdf5_reader.GetChannelType(entry.channel_name), type); - EXPECT_EQ(hdf5_reader.GetChannelDescription(entry.channel_name), descriptor); - EXPECT_EQ(hdf5_reader.GetChannelDataTypeInformation(entry.channel_name), info); + EXPECT_EQ(hdf5_reader.GetFileVersion(), "5.0"); + + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMinTimestamp(topic_name), 1002); + + // Deprecated API + EXPECT_EQ(hdf5_reader.GetMaxTimestamp(topic_name), 5002); + + + for (const auto& entry : meas_entries) + { + ValidateDataInMeasurement(hdf5_reader, entry); + } } -} \ No newline at end of file +} + + + + +TEST(HDF5, ParsePrintHex) +{ + std::vector hex_values = + { + "0000000000000000", + "0000000000000001", + "000000000000000F", + "ABCDEF0123456789", + "EFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFF" + }; + + for (const auto& value : hex_values) + { + auto int_value = parseHexID(value); + auto string_value = printHex(int_value); + EXPECT_EQ(string_value, value); + } +} + +TEST(HDF5, PrintParseHex) +{ + std::vector numeric_values = + { + 0, + -1, + 1, + std::numeric_limits::min(), + std::numeric_limits::max() + }; + + for (const auto& value : numeric_values) + { + auto string_value = printHex(value); + auto int_value = parseHexID(string_value); + + EXPECT_EQ(int_value, value); + } +} + +