diff --git a/include/mdf/ichannel.h b/include/mdf/ichannel.h index f3f899b..dd66eb1 100644 --- a/include/mdf/ichannel.h +++ b/include/mdf/ichannel.h @@ -515,6 +515,19 @@ class IChannel : public IBlock { [[nodiscard]] bool CalculateMasterTime() const { return calculate_master_time_; } + + /** \brief Sets the array size for the channel. + * + * This is an internal function that speed up the reads of values. + * + * @param array_size Size of the array. + */ + void ArraySize(uint64_t array_size) { channel_array_size_ = array_size;} + /** \brief Returns 1 if no array and > 1 if it is an array. + * + * @return Returns 1 for normal channel and array size for array channels + */ + [[nodiscard]] uint64_t ArraySize() const {return channel_array_size_;} protected: @@ -571,6 +584,7 @@ class IChannel : public IBlock { mutable uint64_t vlsd_record_id_ = 0; ///< Used to fix the VLSD CG block. bool calculate_master_time_ = true; ///< If true, the master time channel will be calculated. + uint64_t channel_array_size_ = 1; ///< Value set when reading configuration }; template diff --git a/include/mdf/idatagroup.h b/include/mdf/idatagroup.h index c895420..516a536 100644 --- a/include/mdf/idatagroup.h +++ b/include/mdf/idatagroup.h @@ -10,6 +10,7 @@ #pragma once #include #include +#include #include "mdf/iblock.h" @@ -133,8 +134,16 @@ class IDataGroup : public IBlock { [[nodiscard]] bool IsSubscribingOnChannelVlsd(const IChannel& channel) const; protected: mutable std::vector observer_list_; ///< List of observers. + + /** \brief The fast observer list is used internally when reading data. + * + */ + std::map > fast_observer_list_; + ~IDataGroup() override = default; ///< Default destructor + void InitFastObserverList(); + private: mutable bool mark_as_read_ = false; ///< True if the data block has been read. diff --git a/include/mdf/mdffile.h b/include/mdf/mdffile.h index f130585..d82e405 100644 --- a/include/mdf/mdffile.h +++ b/include/mdf/mdffile.h @@ -216,6 +216,15 @@ class MdfFile { /** \brief Returns true if the file is finalized. */ [[nodiscard]] virtual bool IsFinalized(uint16_t& standard_flags, uint16_t& custom_flags) const = 0; + + /** Returns true if the finalize was done. + * + * The function may be used to check on not finalized file to + * verify that the file have been finalized. + * @return True if the finalization was successful. + */ + [[nodiscard]] virtual bool IsFinalizedDone() const; + /** \brief Returns a parent data group (DG) depending a channel. */ [[nodiscard]] virtual IDataGroup* FindParentDataGroup( const IChannel &channel) const = 0; diff --git a/include/mdf/mdfreader.h b/include/mdf/mdfreader.h index c56a5d9..8aea563 100644 --- a/include/mdf/mdfreader.h +++ b/include/mdf/mdfreader.h @@ -46,7 +46,20 @@ void CreateChannelObserverForChannelGroup(const IDataGroup& data_group, const IChannelGroup& group, ChannelObserverList& dest); - +/** \brief Creates channel observers for all channels within a data group. + * + * This function generates channel observers for all channels within the + * a data group. This function may a little but dangerous as it may allocate + * a lot of primary memory. An hint is to check how many bytes there are + * in the DG/DT blocks. If there is more than 1GByte, maybe you should do + * some partial read instead. + * + * Note that channel groups without samples or data, are skipped. + * @param data_group Reference to the data group. + * @param dest_list The subscriber list which is appended with channel observers. + */ +void CreateChannelObserverForDataGroup(const IDataGroup& data_group, + ChannelObserverList& dest_list); /** \class MdfReader mdfreader.h "mdf/mdfreader.h" * \brief Reader interface to an MDF file. * @@ -84,6 +97,14 @@ class MdfReader { /// \return True if the file was read OK. [[nodiscard]] bool IsOk() const { return static_cast(instance_); } + /** \brief Return true if the file is marked as finalized + * + * This function returns true if the file is marked as finalized. This + * is done by checking the ID block. + * @return True if the file is marked as finalized. + */ + [[nodiscard]] bool IsFinalized() const; + /// Returns a pointer to the MDF file. This file holds references to the MDF /// blocks. \return Pointer to the MDF file object. Note it may return a null /// pointer. diff --git a/mdflib/src/cg4block.cpp b/mdflib/src/cg4block.cpp index 16de956..7214fac 100644 --- a/mdflib/src/cg4block.cpp +++ b/mdflib/src/cg4block.cpp @@ -379,52 +379,20 @@ ISourceInformation *Cg4Block::SourceInformation() const { return si_block_ ? si_block_.get() : nullptr; } -size_t Cg4Block::UpdateCycleCounter(std::FILE *file) { - size_t count = 0; - if (Flags() & CgFlag::VlsdChannel) { - // This is normally used for string and the CG block only include one signal - uint32_t length = 0; - count += ReadNumber(file, length); - std::vector record(length, 0); - if (length > 0) { - count += StepFilePosition(file, length); - } - ++nof_samples_; - } else { - // Normal fixed length records - const size_t record_size = nof_data_bytes_ + nof_invalid_bytes_; - count += StepFilePosition(file, record_size); - ++nof_samples_; - } - return count; +void Cg4Block::UpdateCycleCounter(uint64_t nof_samples) { + nof_samples_ += nof_samples; } -size_t Cg4Block::UpdateVlsdSize(std::FILE *file) { - - size_t count = 0; +void Cg4Block::UpdateVlsdSize(uint64_t nof_data_bytes) { if (Flags() & CgFlag::VlsdChannel) { uint64_t vlsd_size = nof_invalid_bytes_; vlsd_size <<= 32; vlsd_size += nof_data_bytes_; - // This is normally used for string and the CG block only include one signal - uint32_t length = 0; - count += ReadNumber(file, length); - std::vector record(length, 0); - if (length > 0) { - count += StepFilePosition(file, length); - } - vlsd_size += length; + vlsd_size += nof_data_bytes; nof_data_bytes_ = static_cast(vlsd_size & 0xFFFFFFFF); nof_invalid_bytes_ = static_cast(vlsd_size >> 32); - ++nof_samples_; - } else { - // Normal fixed length records - const size_t record_size = nof_data_bytes_ + nof_invalid_bytes_; - count += StepFilePosition(file, record_size); - ++nof_samples_; } - return count; } size_t Cg4Block::StepRecord(std::FILE *file) const { diff --git a/mdflib/src/cg4block.h b/mdflib/src/cg4block.h index eb7013b..007226c 100644 --- a/mdflib/src/cg4block.h +++ b/mdflib/src/cg4block.h @@ -75,8 +75,8 @@ class Cg4Block : public MdfBlock, public IChannelGroup { ISourceInformation* CreateSourceInformation() override; ISourceInformation* SourceInformation() const override; - size_t UpdateCycleCounter(std::FILE *file); - size_t UpdateVlsdSize(std::FILE *file); + void UpdateCycleCounter(uint64_t nof_samples); + void UpdateVlsdSize(uint64_t nof_data_bytes); size_t StepRecord(std::FILE *file) const; [[nodiscard]] IMetaData* CreateMetaData() override { diff --git a/mdflib/src/channelobserver.h b/mdflib/src/channelobserver.h index b8c080a..af5f1e4 100644 --- a/mdflib/src/channelobserver.h +++ b/mdflib/src/channelobserver.h @@ -70,8 +70,16 @@ class ChannelObserver : public IChannelObserver { bool OnSample(uint64_t sample, uint64_t record_id, const std::vector& record) override { - const auto* channel_array = channel_.ChannelArray(); - auto array_size = channel_array != nullptr ? channel_array->NofArrayValues() : 1; + bool parse_record = record_id == record_id_; + if (channel_.VlsdRecordId() > 0 && record_id == channel_.VlsdRecordId()) { + parse_record = true; + } + if (!parse_record) { + return true; + } + + // const auto* channel_array = channel_.ChannelArray(); + const auto array_size = channel_.ArraySize(); T value{}; bool valid; diff --git a/mdflib/src/dg3block.cpp b/mdflib/src/dg3block.cpp index 37493dd..d54ba1f 100644 --- a/mdflib/src/dg3block.cpp +++ b/mdflib/src/dg3block.cpp @@ -178,6 +178,7 @@ void Dg3Block::ReadData(std::FILE *file) { if (file == nullptr) { throw std::invalid_argument("File pointer is null"); } + InitFastObserverList(); std::FILE *data_file = nullptr; size_t data_size = DataSize(); SetFilePosition(file, Link(kIndexData)); @@ -198,6 +199,7 @@ void Dg3Block::ReadRangeData(std::FILE *file, DgRange& range) { if (file == nullptr) { throw std::invalid_argument("File pointer is null"); } + InitFastObserverList(); std::FILE *data_file = nullptr; size_t data_size = DataSize(); SetFilePosition(file, Link(kIndexData)); diff --git a/mdflib/src/dg4block.cpp b/mdflib/src/dg4block.cpp index db14be4..b8d19c6 100644 --- a/mdflib/src/dg4block.cpp +++ b/mdflib/src/dg4block.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include "dl4block.h" #include "dt4block.h" @@ -23,6 +23,12 @@ constexpr size_t kIndexData = 2; constexpr size_t kIndexMd = 3; constexpr size_t kIndexNext = 0; +struct CgCounter { + mdf::detail::Cg4Block* CgBlock = nullptr; + uint64_t NofSamples = 0; + uint64_t NofBytes = 0; +}; + ///< Helper function that recursively copies all data bytes to a /// destination file. size_t CopyDataToFile( @@ -123,11 +129,11 @@ size_t Dg4Block::Write(std::FILE* file) { bytes += WriteNumber(file, rec_id_size_); bytes += WriteBytes(file, 7); UpdateBlockSize(file, bytes); - // Need to update the signal data link for any chaannels referencing - // VLSD CG bloks. + // Need to update the signal data link for any channels referencing + // VLSD CG blocks. for (auto& cg4 : cg_list_) { if (cg4 && (cg4->Flags() & CgFlag::VlsdChannel) ) { - // The previous CG block have a channel which need to update its + // The previous CG block has a channel which needs to update its // signal data link. auto* other_cg4 = FindCgRecordId(cg4->RecordId() -1); auto* cn4 = other_cg4 != nullptr ? @@ -159,7 +165,7 @@ void Dg4Block::ReadData(std::FILE* file) { if (block_list.empty()) { return; } - + InitFastObserverList(); // First scan through all CN blocks and set up any VLSD CG or MLSD channel // relations. @@ -182,7 +188,7 @@ void Dg4Block::ReadData(std::FILE* file) { if (channel == nullptr) { continue; } - // Check if channel is in the subscription + // Check if the channel is in the subscription if (!IsSubscribingOnChannel(*channel) ) { continue; } @@ -190,8 +196,9 @@ void Dg4Block::ReadData(std::FILE* file) { if (cn_block == nullptr) { continue; } - // Fetch the channels referenced data block. Note that some type of - // data blocks are own by this channel as SD block but some are only + // Fetch the channels referenced data block. + // Note that some types of + // data blocks are owned by this channel as a SD block, but some are only // references to block own by another block. Of interest is VLSD CG block // and MLSD channel. const auto data_link = cn_block->DataLink(); @@ -230,12 +237,13 @@ void Dg4Block::ReadData(std::FILE* file) { /* // Convert everything to a samples in a file single DT block can be read - // directly but remaining block types are streamed to a temporary file. The - // main reason is that linked data blocks,is not aligned to a record or even + // directly, but the remaining block types are streamed to a temporary file. + // The + // main reason is that linked data blocks are not aligned to a record or even // worse a channel value bytes. Converting everything to a simple DT block // solves that problem. - bool close_data_file = false; // If DT block do not close the data file. + bool close_data_file = false; // If a DT block does not close the data file. std::FILE* data_file = nullptr; size_t data_size = 0; if (block_list.size() == 1 && block_list[0] && @@ -295,6 +303,8 @@ void Dg4Block::ReadRangeData(std::FILE* file, DgRange& range) { return; } + InitFastObserverList(); + // First scan through all CN blocks and set up any VLSD CG or MLSD channel // relations. for (const auto& cg4 : cg_list_) { @@ -307,7 +317,7 @@ void Dg4Block::ReadRangeData(std::FILE* file, DgRange& range) { if (!cg_used) { continue; } - // Fetch all data from sample reduction blocks (SR) but only if the + // Fetch all data from sample reduction blocks (SR), but only if the // channel group data is subscribed. for (const auto& sr4 : cg4->Sr4()) { if (!sr4) { @@ -322,7 +332,7 @@ void Dg4Block::ReadRangeData(std::FILE* file, DgRange& range) { if (channel == nullptr) { continue; } - // Check if channel is in the subscription + // Check if the channel is in the subscription if (!IsSubscribingOnChannel(*channel) ) { continue; } @@ -330,8 +340,8 @@ void Dg4Block::ReadRangeData(std::FILE* file, DgRange& range) { if (cn_block == nullptr) { continue; } - // Fetch the channels referenced data block. Note that some type of - // data blocks are own by this channel as SD block but some are only + // Fetch the channels referenced data block. Note that some types of + // data blocks are owned by this channel as an SD block, but some are only // references to block own by another block. Of interest is VLSD CG block // and MLSD channel. const auto data_link = cn_block->DataLink(); @@ -399,8 +409,8 @@ void Dg4Block::ReadVlsdData(std::FILE* file,Cn4Block& channel, throw std::invalid_argument("File pointer is null"); } - // Fetch the channels referenced data block. Note that some type of - // data blocks are own by this channel as SD block but some are only + // Fetch the channels referenced data block. Note that some types of + // data blocks are owned by this channel as an SD block, but some are only // references to block own by another block. Of interest is VLSD CG block // and MLSD channel. @@ -408,6 +418,7 @@ void Dg4Block::ReadVlsdData(std::FILE* file,Cn4Block& channel, if (data_link == 0) { return; // No data to read into the system } + InitFastObserverList(); auto* block = Find(data_link); if (block == nullptr) { throw std::runtime_error( @@ -599,7 +610,7 @@ void Dg4Block::RecordIdSize(uint8_t id_size) { rec_id_size_ = id_size; } uint8_t Dg4Block::RecordIdSize() const { return rec_id_size_; } -bool Dg4Block::UpdateDtBlocks(std::FILE *file) { +bool Dg4Block::FinalizeDtBlocks(std::FILE *file) { auto& block_list = DataBlockList(); if (block_list.empty()) { // No data blocks to update @@ -621,10 +632,10 @@ bool Dg4Block::UpdateDtBlocks(std::FILE *file) { return true; } -// Update the unfinished payload data (DT) block. This function update the +// Update the unfinished payload data (DT) block. This function updates the // channel group (CG) and a CG-VLSD channel group regarding cycle count // and offsets (VLSD) -bool Dg4Block::UpdateCgAndVlsdBlocks(std::FILE* file, bool update_cg, +bool Dg4Block::FinalizeCgAndVlsdBlocks(std::FILE* file, bool update_cg, bool update_vlsd) { auto& block_list = DataBlockList(); if (block_list.empty()) { @@ -644,24 +655,67 @@ bool Dg4Block::UpdateCgAndVlsdBlocks(std::FILE* file, bool update_cg, MDF_ERROR() << "Invalid DT block type-cast."; return false; } - mdf::detail::SetFilePosition(file, dt_block->DataPosition()); + // Create a simple map that speeds up the counting. + std::map counter_list; + for (const auto& cg4 : Cg4()) { + if (!cg4) { + continue; + } + CgCounter counter; + counter.CgBlock = cg4.get(); + counter_list.emplace(cg4->RecordId(), counter); + } + if (counter_list.empty()) { + return true; // Nothing to update + } + + + SetFilePosition(file, dt_block->DataPosition()); size_t count = 0; while (count < dt_block->DataSize()) { uint64_t record_id = 0; count += ReadRecordId(file,record_id); - const auto* cg_block = FindCgRecordId(record_id); - if (cg_block == nullptr) { + auto cg_find = counter_list.size() == 1 ? + counter_list.begin() : counter_list.find(record_id); + if (cg_find == counter_list.end()) { MDF_DEBUG() << "Failed to find the CG block. Record ID: " << record_id; break; } - auto* cg4 = const_cast(cg_block); - const auto vlsd = (cg4->Flags() & CgFlag::VlsdChannel) != 0; - if (!vlsd && update_cg) { - count += cg4->UpdateCycleCounter(file); // Increment the cycle counter - } else if (vlsd && update_vlsd) { - count += cg4->UpdateVlsdSize(file); // Update size and offset + auto& counter = cg_find->second; + auto* cg_block = counter.CgBlock; + size_t count_bytes = 0; + if (cg_block == nullptr) { + MDF_ERROR() << "CG block pointer is null. Internal error."; + return false; + } + if ((cg_block->Flags() & CgFlag::VlsdChannel) != 0) { + // This is normally used for string, + // and the CG block only includes one signal + uint32_t length = 0; + count_bytes += ReadNumber(file, length); + //std::vector record(length, 0); + if (length > 0) { + count_bytes += StepFilePosition(file, length); + } } else { - count += cg4->StepRecord(file); // + // Normal fixed length records + const size_t record_size = cg_block->NofDataBytes() + cg_block->NofInvalidBytes(); + count_bytes += StepFilePosition(file, record_size); + } + ++counter.NofSamples; + counter.NofBytes += count_bytes; + count += count_bytes; + } + for (const auto& [record_id_1, counter1] : counter_list) { + Cg4Block* cg_block = counter1.CgBlock; + if (cg_block == nullptr) { + continue; + } + if (update_cg) { + cg_block->UpdateCycleCounter(counter1.NofSamples); + } + if (update_vlsd) { + cg_block->UpdateVlsdSize(counter1.NofBytes); } } return true; diff --git a/mdflib/src/dg4block.h b/mdflib/src/dg4block.h index b23f158..c3dd7e7 100644 --- a/mdflib/src/dg4block.h +++ b/mdflib/src/dg4block.h @@ -53,8 +53,8 @@ class Dg4Block : public DataListBlock, public IDataGroup { size_t Write(std::FILE* file) override; size_t DataSize() const override; - bool UpdateDtBlocks(std::FILE *file); - bool UpdateCgAndVlsdBlocks(std::FILE *file, bool update_cg, bool update_vlsd); + bool FinalizeDtBlocks(std::FILE *file); + bool FinalizeCgAndVlsdBlocks(std::FILE *file, bool update_cg, bool update_vlsd); [[nodiscard]] IChannelGroup *FindParentChannelGroup( const IChannel &channel) const override; diff --git a/mdflib/src/hd4block.cpp b/mdflib/src/hd4block.cpp index 4a12cac..4f9f638 100644 --- a/mdflib/src/hd4block.cpp +++ b/mdflib/src/hd4block.cpp @@ -498,22 +498,22 @@ size_t Hd4Block::Write(std::FILE* file) { return bytes; } -bool Hd4Block::UpdateDtBlocks(std::FILE *file) { +bool Hd4Block::FinalizeDtBlocks(std::FILE *file) { if (dg_list_.empty()) { return true; } - auto* last_dg = dg_list_.back().get(); - return last_dg != nullptr && last_dg->UpdateDtBlocks(file); + Dg4Block* last_dg = dg_list_.back().get(); + return last_dg != nullptr && last_dg->FinalizeDtBlocks(file); } -bool Hd4Block::UpdateCgAndVlsdBlocks(std::FILE* file, bool update_cg, +bool Hd4Block::FinalizeCgAndVlsdBlocks(std::FILE* file, bool update_cg, bool update_vlsd) { if (dg_list_.empty()) { return true; } auto* last_dg = dg_list_.back().get(); - return last_dg != nullptr && last_dg->UpdateCgAndVlsdBlocks(file,update_cg, - update_vlsd); + return last_dg != nullptr && + last_dg->FinalizeCgAndVlsdBlocks(file, update_cg, update_vlsd); } const IMdfTimestamp* Hd4Block::StartTimestamp() const { diff --git a/mdflib/src/hd4block.h b/mdflib/src/hd4block.h index a291167..a39cc75 100644 --- a/mdflib/src/hd4block.h +++ b/mdflib/src/hd4block.h @@ -122,8 +122,8 @@ class Hd4Block : public MdfBlock, public IHeader { const override; IDataGroup *CreateDataGroup() override; - bool UpdateDtBlocks(std::FILE* file); - bool UpdateCgAndVlsdBlocks(std::FILE* file, bool update_cg, bool update_vlsd); + bool FinalizeDtBlocks(std::FILE* file); + bool FinalizeCgAndVlsdBlocks(std::FILE* file, bool update_cg, bool update_vlsd); bool UpdateVlsdBlocks(std::FILE* file); private: Mdf4Timestamp timestamp_; diff --git a/mdflib/src/ichannelobserver.cpp b/mdflib/src/ichannelobserver.cpp index fc86deb..7299826 100644 --- a/mdflib/src/ichannelobserver.cpp +++ b/mdflib/src/ichannelobserver.cpp @@ -27,10 +27,14 @@ IChannelObserver::IChannelObserver(const IDataGroup& data_group, const IChannel channel_(channel) { // Subscribe on all channel groups - record_id_list_.clear(); + const auto cg_list = data_group.ChannelGroups(); + // A channel observer only needs its own observer and its VLSD record ID. + // Note that the property that disable the reading of VLSD data is called + // after this constructor + record_id_list_.clear(); record_id_list_.insert(channel_.RecordId()); - if (channel_.VlsdRecordId() > 0 && read_vlsd_data_) { + if (channel_.VlsdRecordId() > 0) { record_id_list_.insert(channel_.VlsdRecordId()); } diff --git a/mdflib/src/idatagroup.cpp b/mdflib/src/idatagroup.cpp index 92ee940..9b3e0f5 100644 --- a/mdflib/src/idatagroup.cpp +++ b/mdflib/src/idatagroup.cpp @@ -5,6 +5,7 @@ #include "mdf/idatagroup.h" #include +#include #include "mdf/ichannel.h" #include "mdf/ichannelgroup.h" @@ -33,14 +34,37 @@ void IDataGroup::DetachAllSampleObservers() const { observer_list_.clear(); } bool IDataGroup::NotifySampleObservers(size_t sample, uint64_t record_id, const std::vector &record) const { - for (auto *observer : observer_list_) { - if (observer != nullptr) { + if (fast_observer_list_.empty()) { + return false; // No meaning to continue reading + } + + if ( fast_observer_list_.size() == 1) { + for (ISampleObserver* observer : observer_list_) { + if (observer == nullptr) { + continue; + } const bool continue_reading = observer->OnSample(sample, record_id, record); if (!continue_reading) { return false; } } + return true; + } + + auto itr = fast_observer_list_.find(record_id); + if (itr == fast_observer_list_.end()) { + return true; } + auto& observer_list = itr->second; + for (ISampleObserver* observer : observer_list ) { + if (observer != nullptr) { + const bool continue_reading = observer->OnSample(sample, record_id, record); + if (!continue_reading) { + return false; + } + } + }; + return true; } @@ -150,4 +174,27 @@ bool IDataGroup::IsSubscribingOnChannelVlsd(const IChannel& channel) const { return false; }); } + +void IDataGroup::InitFastObserverList() { + fast_observer_list_.clear(); + const auto cg_list = ChannelGroups(); + for (const auto* channel_group : cg_list ) { + if (channel_group == nullptr) { + continue; + } + uint64_t record_id = channel_group->RecordId(); + std::vector temp_list; + for (auto* observer : observer_list_) { + if (observer == nullptr) { + continue; + } + if (observer->IsRecordIdNeeded(record_id)) { + temp_list.emplace_back(observer); + } + } + if (!temp_list.empty()) { + fast_observer_list_.emplace(record_id, std::move(temp_list)); + } + } +} } // namespace mdf diff --git a/mdflib/src/isampleobserver.cpp b/mdflib/src/isampleobserver.cpp index cc0ea69..6d0491b 100644 --- a/mdflib/src/isampleobserver.cpp +++ b/mdflib/src/isampleobserver.cpp @@ -12,6 +12,7 @@ ISampleObserver::ISampleObserver(const IDataGroup &data_group) ISampleObserver::AttachObserver(); // Subscribe on all channel groups const auto cg_list = data_group.ChannelGroups(); + record_id_list_.clear(); for (const auto* channel_group : cg_list) { if ( channel_group != nullptr) { record_id_list_.insert(channel_group->RecordId()); diff --git a/mdflib/src/mdf4file.cpp b/mdflib/src/mdf4file.cpp index 114fbe0..0770946 100644 --- a/mdflib/src/mdf4file.cpp +++ b/mdflib/src/mdf4file.cpp @@ -35,14 +35,7 @@ void Mdf4File::ReadHeader(std::FILE *file) { hd_block_->Read(file); } - uint16_t standard_flags = 0; - uint16_t custom_flags = 0; - if (!finalized_done_ && id_block_ && - !id_block_->IsFinalized(standard_flags, custom_flags)) { - finalized_done_ = true; - // Try to finalize the last DG block. - const auto finalize = FinalizeFile(file); - } + } void Mdf4File::ReadMeasurementInfo(std::FILE *file) { @@ -59,8 +52,18 @@ void Mdf4File::ReadEverythingButData(std::FILE *file) { hd_block_->ReadEverythingButData(file); } // Now when all blocks are read in, it is time to read - // in all referenced block. This is mainly done for the channel array blocks. + // in all referenced blocks. + // This is mainly done for the channel array blocks. FindAllReferences(file); + + // Check if file needs to be finalized + uint16_t standard_flags = 0; + uint16_t custom_flags = 0; + if (!finalized_done_ && id_block_ && + !id_block_->IsFinalized(standard_flags, custom_flags)) { + // Try to finalize the last DG block. + finalized_done_ = FinalizeFile(file); + } } bool Mdf4File::IsMdf4() const { return true; } @@ -196,6 +199,7 @@ bool Mdf4File::IsFinalized(uint16_t &standard_flags, } bool Mdf4File::FinalizeFile(std::FILE* file) { + uint16_t standard_flags = 0; uint16_t custom_flags = 0; const auto finalized = IsFinalized(standard_flags, custom_flags); @@ -205,7 +209,6 @@ bool Mdf4File::FinalizeFile(std::FILE* file) { << Name(); return false; } - ReadEverythingButData(file); // 1. Update DL block // 2. Update DT block @@ -220,18 +223,29 @@ bool Mdf4File::FinalizeFile(std::FILE* file) { const bool update_vlsd_offset = (standard_flags & 0x40) != 0; bool updated = true; if (update_dl_blocks) { + MDF_ERROR() << "Finalizing DL block is not supported. File: " << Name(); updated = false; } - if (update_dt_blocks && !hd_block_->UpdateDtBlocks(file)) { - updated = false; + if (update_dt_blocks) { + // Update the number of bytes in the DT block. + const bool update_dt = hd_block_->FinalizeDtBlocks(file); + if (!update_dt) { + MDF_ERROR() << "Fail to finalize last DT block. File: " << Name(); + updated = false; + } } if (update_rd_blocks) { + MDF_ERROR() << "Finalizing RD block is not supported. File: " << Name(); updated = false; } - if ((update_cg_blocks || update_vlsd_bytes || update_vlsd_offset ) && - !hd_block_->UpdateCgAndVlsdBlocks(file, update_cg_blocks, - update_vlsd_bytes || update_vlsd_offset)) { - updated = false; + + if (update_cg_blocks || update_vlsd_bytes || update_vlsd_offset ) { + const bool update_nof_samples = hd_block_->FinalizeCgAndVlsdBlocks( + file, update_cg_blocks, update_vlsd_bytes || update_vlsd_offset); + if (!update_nof_samples) { + MDF_ERROR() << "Failed to update number of samples and VLSD block sizes. File: " << Name(); + updated = false; + } } return updated; } @@ -255,6 +269,8 @@ void Mdf4File::FindAllReferences(std::FILE *file) { if (!hd_block_) { return; } + + // Find all channel array for (auto& dg4 : hd_block_->Dg4()) { if (!dg4) { continue; @@ -284,6 +300,35 @@ void Mdf4File::FindAllReferences(std::FILE *file) { } } } + + // Now set the size of channel array for all channels + for (auto& dg4_block : hd_block_->Dg4()) { + if (!dg4_block) { + continue; + } + for (auto &cg4_block : dg4_block->Cg4()) { + if (!cg4_block) { + continue; + } + auto channel_list = cg4_block->Channels(); + for (IChannel* channel : channel_list) { + if (channel == nullptr) { + continue; + } + const auto* channel_array = channel->ChannelArray(); + if (channel_array == nullptr) { + channel->ArraySize(1); + } else { + channel->ArraySize(channel_array->NofArrayValues()); + } + + } + } + } +} + +bool Mdf4File::IsFinalizedDone() const { + return finalized_done_; } } // namespace mdf::detail \ No newline at end of file diff --git a/mdflib/src/mdf4file.h b/mdflib/src/mdf4file.h index 0e6e4a3..dad54b1 100644 --- a/mdflib/src/mdf4file.h +++ b/mdflib/src/mdf4file.h @@ -45,6 +45,7 @@ class Mdf4File : public MdfFile { uint16_t custom_flags) override; [[nodiscard]] bool IsFinalized(uint16_t& standard_flags, uint16_t& custom_flags) const override; + [[nodiscard]] bool IsFinalizedDone() const override; void ReadHeader(std::FILE* file) override; void ReadMeasurementInfo(std::FILE* file) override; diff --git a/mdflib/src/mdffile.cpp b/mdflib/src/mdffile.cpp index 26ca18e..977d3e1 100644 --- a/mdflib/src/mdffile.cpp +++ b/mdflib/src/mdffile.cpp @@ -59,5 +59,6 @@ void MdfFile::FileName(const std::string& filename) { } } } +bool MdfFile::IsFinalizedDone() const { return false; } } // namespace mdf diff --git a/mdflib/src/mdfreader.cpp b/mdflib/src/mdfreader.cpp index c95cfda..b22486b 100644 --- a/mdflib/src/mdfreader.cpp +++ b/mdflib/src/mdfreader.cpp @@ -30,12 +30,19 @@ namespace fs = std::filesystem; #endif using namespace std::chrono_literals; +using namespace mdf::detail; namespace mdf { bool IsMdfFile(const std::string &filename) { FILE* file = nullptr; - Platform::fileopen(&file, filename.c_str(), "rb"); + + try { + Platform::fileopen(&file, filename.c_str(), "rb"); + } catch (const std::exception&) { + return false; + } + if (file == nullptr) { return false; } @@ -205,6 +212,25 @@ void CreateChannelObserverForChannelGroup(const IDataGroup &data_group, } } +void CreateChannelObserverForDataGroup(const IDataGroup &data_group, + ChannelObserverList &dest) { + const auto cg_list = data_group.ChannelGroups(); + for (const IChannelGroup* group : cg_list) { + if (group == nullptr || group->NofSamples() == 0) { + continue; + } + + const auto cn_list = group->Channels(); + for (const auto *channel : cn_list) { + if (channel == nullptr) { + continue; + } + auto channel_observer = CreateChannelObserver(data_group, *group, *channel); + dest.emplace_back( std::move(channel_observer)); + + } + } +} MdfReader::MdfReader(const std::string &filename) : filename_(filename) { // Need to create MDF3 of MDF4 file @@ -281,7 +307,7 @@ bool MdfReader::ReadHeader() { MDF_ERROR() << "No instance created. File: " << filename_; return false; } - // If file is not open then open and close the file in this call + // If the file is not open, then open and close the file in this call bool shall_close = file_ == nullptr && Open(); if (file_ == nullptr) { MDF_ERROR() << "File is not open. File: " << filename_; @@ -549,4 +575,14 @@ bool MdfReader::ReadVlsdData(IDataGroup &data_group, return !error; } +bool MdfReader::IsFinalized() const { + if (!instance_) { + return false; + } + uint16_t standard_flags = 0; + uint16_t custom_flags = 0; + const bool finalized = instance_->IsFinalized(standard_flags, custom_flags); + return finalized || instance_->IsFinalizedDone(); +} + } // namespace mdf diff --git a/mdflib/src/readcache.cpp b/mdflib/src/readcache.cpp index d797491..2a1b0ae 100644 --- a/mdflib/src/readcache.cpp +++ b/mdflib/src/readcache.cpp @@ -6,6 +6,8 @@ #include "readcache.h" #include +#include "mdf/mdflogstream.h" + namespace mdf::detail { ReadCache::ReadCache( MdfBlock* block, FILE* file) @@ -16,9 +18,13 @@ ReadCache::ReadCache( MdfBlock* block, FILE* file) dg4_block_ = dynamic_cast(block); if (dg4_block_ != nullptr) { - const auto cg_list = dg4_block_->ChannelGroups(); - for (const auto* channel_group : cg_list) { - if (channel_group != nullptr && dg4_block_->IsSubscribingOnRecord(channel_group->RecordId())) { + const auto& cg_list = dg4_block_->Cg4(); + for (const auto& channel_group : cg_list) { + if (!channel_group) { + continue; + } + available_cg_list_.emplace(channel_group->RecordId(), channel_group.get()); + if (dg4_block_->IsSubscribingOnRecord(channel_group->RecordId())) { record_id_list_.insert(channel_group->RecordId()); } } @@ -30,8 +36,7 @@ ReadCache::ReadCache( MdfBlock* block, FILE* file) max_data_count_ = data_block_->DataSize(); } - // Make at list of data blocks that simplify the stepping of data blocks, - // The block_index + // Make a list of data blocks that simplify the stepping of data blocks. if (data_list_ != nullptr) { data_list_->GetDataBlockList(block_list_); } else if (data_block_ != nullptr) { @@ -50,12 +55,17 @@ bool ReadCache::ParseRecord() { bool continue_reading = true; try { const auto record_id = ParseRecordId(); - const auto *channel_group = dg4_block_->FindCgRecordId(record_id); + const auto itr_group = available_cg_list_.size() == 1 ? + available_cg_list_.begin() : available_cg_list_.find(record_id); + const auto* channel_group = itr_group == available_cg_list_.cend() ? + nullptr : itr_group->second; + // const auto *channel_group = dg4_block_->FindCgRecordId(record_id); if (channel_group == nullptr) { throw std::runtime_error("No channel group found."); } if (channel_group->Flags() & CgFlag::VlsdChannel) { - // This is normally used for string and the CG block only include one signal + // This is normally used for the string, + // and the CG block only includes one signal std::vector temp(4, 0); GetArray(temp); const LittleBuffer length(temp, 0); @@ -74,7 +84,7 @@ bool ReadCache::ParseRecord() { continue_reading = dg4_block_->NotifySampleObservers(sample, channel_group->RecordId(), record); channel_group->IncrementSample(); - }; + } } } else { @@ -98,7 +108,8 @@ bool ReadCache::ParseRecord() { } } } - } catch (const std::exception &) { + } catch (const std::exception &err) { + MDF_ERROR() << "Parse of record failed. Error: " << err.what(); return false; } @@ -126,7 +137,8 @@ bool ReadCache::ParseRangeRecord(DgRange& range) { const size_t next_sample = sample + 1; if (channel_group->Flags() & CgFlag::VlsdChannel) { - // This is normally used for string and the CG block only include one signal + // This is normally used for string, + // and the CG block only includes one signal std::vector temp(4, 0); GetArray(temp); const LittleBuffer length(temp, 0); @@ -155,7 +167,7 @@ bool ReadCache::ParseRangeRecord(DgRange& range) { continue_reading = dg4_block_->NotifySampleObservers(sample, record_id, record); channel_group->IncrementSample(); - }; + } } } else { const size_t record_size = channel_group->NofDataBytes() @@ -253,7 +265,8 @@ bool ReadCache::ParseVlsdCgData() { throw std::runtime_error("No channel group found."); } if (channel_group->Flags() & CgFlag::VlsdChannel) { - // This is normally used for string and the CG block only include one signal + // This is normally used for string, + // and the CG block only includes one signal std::vector temp(4, 0); GetArray(temp); const LittleBuffer length(temp, 0); @@ -297,7 +310,7 @@ bool ReadCache::GetNextByte(uint8_t &input) { } if ( file_index_ < data_size_) { if (file_buffer_.empty()) { - // Read direct from file + // Read direct from the file const auto nof_bytes = fread(&input,1,1,file_); if (nof_bytes != 1) { return false; @@ -332,11 +345,11 @@ bool ReadCache::GetNextByte(uint8_t &input) { size_t temp_index = 0; current_block->CopyDataToBuffer(file_,file_buffer_, temp_index); } else { - // Read from file directly + // Read from the file directly SetFilePosition(file_, current_block->DataPosition()); - file_buffer_.clear(); // Indicate that we read from file directly + file_buffer_.clear(); // Indicate that we read from the file directly } - // Read in the data form file to + // Read in the data form file to if (file_index_ >= data_size_) { return false; } diff --git a/mdflib/src/readcache.h b/mdflib/src/readcache.h index 18b3c23..35be996 100644 --- a/mdflib/src/readcache.h +++ b/mdflib/src/readcache.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "dg4block.h" @@ -29,9 +30,9 @@ class ReadCache { record_id_list_.insert(record_id); } - void SetOffsetFilter(const std::vector& offset_list); + void SetOffsetFilter(const std::vector& offset_list); - void SetCallback(const std::function&)>& callback ); + void SetCallback(const std::function&)>& callback ); private: FILE* file_; @@ -49,6 +50,7 @@ class ReadCache { size_t block_index_ = 0; std::vector block_list_; std::set record_id_list_; + std::map available_cg_list_; uint64_t offset_ = 0; std::set offset_filter_; diff --git a/mdflib_test/src/testread.cpp b/mdflib_test/src/testread.cpp index 3ee1e6d..ff9b38b 100644 --- a/mdflib_test/src/testread.cpp +++ b/mdflib_test/src/testread.cpp @@ -23,12 +23,10 @@ using namespace mdf; using namespace mdf::detail; using namespace util::log; using namespace std::chrono_literals; - +using namespace std::chrono; namespace { const std::string mdf_source_dir = "k:/test/mdf"; // Where all source MDF files exist -const std::string log_root_dir = "o:/test"; -const std::string log_file = "mdfread.log"; constexpr std::string_view kLargeLocalSsdFile = "c:/temp/Zeekr_PR60126_BX1E_583_R08A04_dia_left_5kph_0.5m_NO_08_01_psd_1_2023_12_21_041519#CANape_log_056_TDA4GMSL.mf4"; @@ -41,13 +39,15 @@ constexpr std::string_view kLargeTime = "t"; constexpr std::string_view kLargeFrameCounter = "FrameCounter_VC0"; constexpr std::string_view kLargeFrameData = "VideoRawdata_VC0"; constexpr std::string_view kBenchMarkFile = "K:/test/mdf/net/test.mf4"; +constexpr std::string_view kCssTestFile = "css_test"; + using MdfList = std::map; -MdfList mdf_list; +MdfList kMdfList; std::string GetMdfFile(const std::string &name) { - auto itr = mdf_list.find(name); - return itr == mdf_list.cend() ? std::string() : itr->second; + auto itr = kMdfList.find(name); + return itr == kMdfList.cend() ? std::string() : itr->second; } double ConvertToMs(uint64_t diff) { @@ -66,28 +66,27 @@ double ConvertToSec(uint64_t diff) { namespace mdf::test { void TestRead::SetUpTestSuite() { + // Set up the log file to log to console util::log::LogConfig &log_config = util::log::LogConfig::Instance(); - log_config.RootDir(log_root_dir); - log_config.BaseName(log_file); - log_config.Type(util::log::LogType::LogToFile); + log_config.Type(util::log::LogType::LogToConsole); log_config.CreateDefaultLogger(); - mdf_list.clear(); + kMdfList.clear(); try { for (const auto &entry : recursive_directory_iterator(mdf_source_dir)) { if (!entry.is_regular_file()) { continue; } - const auto &p = entry.path(); - - if (IsMdfFile(p.string())) { - LOG_INFO() << "Found MDF file. File: " << p.stem().string(); - mdf_list.emplace(p.stem().string(), p.string()); + const auto &fullname = entry.path(); + const auto stem = fullname.stem().string(); + if (kMdfList.find(stem) == kMdfList.cend() && IsMdfFile(fullname.string())) { + std::cout << "Found MDF file. File: " << stem << std::endl; + kMdfList.emplace(stem, fullname.string()); } } } catch (const std::exception &error) { - LOG_ERROR() << "Failed to fetch the MDF test files. Error: " - << error.what(); + std::cout << "Failed to fetch the MDF test files. Error: " + << error.what() << std::endl; } } @@ -96,37 +95,61 @@ void TestRead::TearDownTestSuite() { log_config.DeleteLogChain(); } -TEST_F(TestRead, MdfReader) // NOLINT +TEST_F(TestRead, Read) // NOLINT { - for (const auto &itr : mdf_list) { - MdfReader oRead(itr.second); - EXPECT_TRUE(oRead.IsOk()) << itr.second; - EXPECT_TRUE(oRead.GetFile() != nullptr) << itr.second; - EXPECT_TRUE(oRead.ReadMeasurementInfo()) << itr.second; + if (kMdfList.empty()) { + GTEST_SKIP_("No MDF files found." ); + } + + for (const auto& [name, filename] : kMdfList) { + const auto start1 = steady_clock::now(); + + MdfReader oRead(filename); + EXPECT_TRUE(oRead.IsOk()) << name; + const auto* mdf_file = oRead.GetFile(); + + EXPECT_TRUE(mdf_file != nullptr) << name; + EXPECT_TRUE(oRead.ReadEverythingButData()) << name; + + const auto stop1 = steady_clock::now(); + const auto diff1 = duration_cast(stop1 - start1); + + auto* dg_group = oRead.GetDataGroup(0); + if (dg_group == nullptr) { + continue; + } + const auto start2 = steady_clock::now(); + ISampleObserver observer(*dg_group); + oRead.ReadData(*dg_group); + + const auto stop2 = steady_clock::now(); + const auto diff2 = duration_cast(stop2 - start2); + std::cout << "File: " << name << " (" << diff1.count() + << "/" << diff2.count() << " ms)" << std::endl; } } TEST_F(TestRead, MdfFile) // NOLINT { - if (mdf_list.empty()) { - GTEST_SKIP(); + if (kMdfList.empty()) { + GTEST_SKIP_("No MDF files found."); } - for (const auto &itr : mdf_list) { - MdfReader oRead(itr.second); - EXPECT_TRUE(oRead.ReadMeasurementInfo()) << itr.second; - const auto *f = oRead.GetFile(); - EXPECT_TRUE(f != nullptr) << itr.second; - if (f->IsMdf4()) { - const auto *f4 = dynamic_cast(f); - EXPECT_TRUE(f4 != nullptr); - const auto &hd = f4->Hd(); - const auto &dg_list = hd.Dg4(); - LOG_INFO() << " File: " << itr.first + for (const auto &[name, filename] : kMdfList) { + MdfReader oRead(filename); + EXPECT_TRUE(oRead.ReadMeasurementInfo()) << name; + const auto *mdf_file = oRead.GetFile(); + ASSERT_TRUE(mdf_file != nullptr) << name; + if (mdf_file->IsMdf4()) { + const auto *file4 = dynamic_cast(mdf_file); + ASSERT_TRUE(file4 != nullptr); + const auto &hd4 = file4->Hd(); + const auto &dg_list = hd4.Dg4(); + LOG_INFO() << " File: " << name << ", Nof Measurement: " << dg_list.size(); - EXPECT_FALSE(dg_list.empty()) << itr.second; + EXPECT_FALSE(dg_list.empty()) << name; } else { - const auto *f3 = dynamic_cast(f); - EXPECT_TRUE(f3 != nullptr); + const auto *file3 = dynamic_cast(mdf_file); + EXPECT_TRUE(file3 != nullptr); } } } @@ -134,9 +157,9 @@ TEST_F(TestRead, MdfFile) // NOLINT TEST_F(TestRead, IdBlock) // NOLINT { const std::string file = GetMdfFile("Vector_CustomExtensions_CNcomment"); - // Check that the file exist. If not skip the test + // Check that the file exists. If not, skip the test if (file.empty()) { - GTEST_SKIP(); + GTEST_SKIP_("Comment file doesn't exist."); } MdfReader oRead(file); EXPECT_TRUE(oRead.IsOk()) << file; @@ -153,13 +176,13 @@ TEST_F(TestRead, IdBlock) // NOLINT TEST_F(TestRead, HeaderBlock) // NOLINT { - if (mdf_list.empty()) { + if (kMdfList.empty()) { GTEST_SKIP(); } int count = 0; - for (const auto &itr : mdf_list) { - MdfReader oRead(itr.second); - EXPECT_TRUE(oRead.ReadEverythingButData()) << itr.second; + for (const auto &[name, filename] : kMdfList) { + MdfReader oRead(filename); + EXPECT_TRUE(oRead.ReadEverythingButData()) << name; const auto *mdf_file = oRead.GetFile(); if (!mdf_file->IsMdf4()) { @@ -197,15 +220,15 @@ TEST_F(TestRead, Benchmark) { { MdfReader oRead(kBenchMarkFile.data()); - const auto start = std::chrono::steady_clock::now(); + const auto start = steady_clock::now(); oRead.ReadMeasurementInfo(); - const auto stop = std::chrono::steady_clock::now(); - std::chrono::duration diff = stop - start; - std::cout << "Read Measurement Info TrueNas: " << diff.count() * 1000 + const auto stop = steady_clock::now(); + std::chrono::duration diff = duration_cast(stop - start); + std::cout << "Read Measurement Info TrueNas: " << diff.count() << " ms" << std::endl; } { - const auto start = std::chrono::steady_clock::now(); + const auto start = steady_clock::now(); MdfReader oRead(kBenchMarkFile.data()); oRead.ReadEverythingButData(); const auto *file = oRead.GetFile(); @@ -230,9 +253,9 @@ TEST_F(TestRead, Benchmark) { } - const auto stop = std::chrono::steady_clock::now(); - std::chrono::duration diff = stop - start; - std::cout << "Everything + Conversion (TrueNas): " << diff.count() * 1000 + const auto stop = steady_clock::now(); + std::chrono::duration diff = duration_cast(stop - start); + std::cout << "Everything + Conversion (TrueNas): " << diff.count() << " ms" << std::endl; } } @@ -313,7 +336,7 @@ TEST_F(TestRead, TestLargeFile) { std::cout << "BENCHMARK Large (" << storage_type << ") Storage ReadData(with VLSD data)" << std::endl; const auto start_open = MdfHelper::NowNs(); - MdfReader oRead(test_file.data()); + MdfReader oRead(test_file); const auto stop_open = MdfHelper::NowNs(); std::cout << "Open File (ms): " << ConvertToMs(stop_open - start_open) << std::endl; @@ -407,7 +430,7 @@ TEST_F(TestRead, TestExampleCrash) { // In this example, we read in all sample data and fetch all values. for (auto* dg4 : dg_list) { - // Subscribers holds the sample data for a channel. + // Subscribers hold the sample data for a channel. // You should normally only subscribe on some channels. // We need a list to hold them. ChannelObserverList subscriber_list; @@ -423,7 +446,7 @@ TEST_F(TestRead, TestExampleCrash) { } // Now it is time to read in all samples - EXPECT_TRUE(reader.ReadData(*dg4)); // Read raw data from file + EXPECT_TRUE(reader.ReadData(*dg4)); // Read raw data from the file. std::string channel_value; // Channel value (no scaling) std::string eng_value; // Engineering value for (auto& obs : subscriber_list) { @@ -438,9 +461,6 @@ TEST_F(TestRead, TestExampleCrash) { std::cout << std::endl; } - // Not needed in this example as we delete the subscribers, - // but it is good practise to remove samples data from memory - // when it is no longer needed. dg4->ClearData(); } reader.Close(); // Close the file @@ -517,4 +537,101 @@ TEST_F(TestRead, TestPartialRead) { data_observer.reset(); } +TEST_F(TestRead, TestNotFinalized) { + if (kMdfList.empty()) { + GTEST_SKIP_("No MDF files found."); + } + + // Find all files that are not finalized. + MdfList test_list; + for (const auto& [name, filename] : kMdfList ) { + MdfReader reader(filename); + if (!reader.IsFinalized()) { + test_list.emplace(name, filename); + } + } + EXPECT_FALSE(test_list.empty()); + for (const auto& [name1, filename1] : test_list) { + MdfReader reader(filename1); + EXPECT_TRUE(reader.ReadEverythingButData()); + EXPECT_TRUE(reader.IsFinalized()) << name1; + std::cout << "Finalized File: " << name1 << std::endl; + + } + +} + +TEST_F(TestRead, TestCssChannelObservers) { + const std::string& test_file = GetMdfFile(kCssTestFile.data()); + const auto find_test_file = kMdfList.find(kCssTestFile.data()); + if (find_test_file == kMdfList.cend()) { + GTEST_SKIP_("No CSS test file found."); + } + + const auto start1 = steady_clock::now(); + + MdfReader reader(test_file); + ASSERT_TRUE(reader.IsOk()); + reader.ReadEverythingButData(); + const auto stop1 = steady_clock::now(); + const auto diff1 = duration_cast(stop1 - start1); + + const auto* mdf_file = reader.GetFile(); + ASSERT_TRUE(mdf_file != nullptr); + DataGroupList dg_list; + mdf_file->DataGroups(dg_list); + for (IDataGroup* data_group : dg_list) { + if (data_group == nullptr) { + continue; + } + ChannelObserverList observer_list; + CreateChannelObserverForDataGroup(*data_group, observer_list); + reader.ReadData(*data_group); + data_group->ClearData(); + } + reader.Close(); + + const auto stop2 = steady_clock::now(); + const auto diff2 = duration_cast(stop2 - stop1); + + std:: cout << "File: " << kCssTestFile << " (" << diff1.count() + << "/" << diff2.count() << " ms)" << std::endl; + +} + +TEST_F(TestRead, TestCssSampleObserver) { + const std::string& test_file = GetMdfFile(kCssTestFile.data()); + const auto find_test_file = kMdfList.find(kCssTestFile.data()); + if (find_test_file == kMdfList.cend()) { + GTEST_SKIP_("No CSS test file found."); + } + + const auto start1 = steady_clock::now(); + + MdfReader reader(test_file); + ASSERT_TRUE(reader.IsOk()); + reader.ReadEverythingButData(); + const auto stop1 = steady_clock::now(); + const auto diff1 = duration_cast(stop1 - start1); + + const auto* mdf_file = reader.GetFile(); + ASSERT_TRUE(mdf_file != nullptr); + DataGroupList dg_list; + mdf_file->DataGroups(dg_list); + for (IDataGroup* data_group : dg_list) { + if (data_group == nullptr) { + continue; + } + ISampleObserver observer(*data_group); + reader.ReadData(*data_group); + } + reader.Close(); + + const auto stop2 = steady_clock::now(); + const auto diff2 = duration_cast(stop2 - stop1); + + std:: cout << "File: " << kCssTestFile << " (" << diff1.count() + << "/" << diff2.count() << " ms)" << std::endl; + +} } // namespace mdf::test diff --git a/mdflib_test/src/testxtensor.cpp b/mdflib_test/src/testxtensor.cpp index a7fb59f..ca20fbe 100644 --- a/mdflib_test/src/testxtensor.cpp +++ b/mdflib_test/src/testxtensor.cpp @@ -18,7 +18,7 @@ TEST(XTensor, FirstExample) { xt::xarray arr2 {5.0, 6.0, 7.0}; - xt::xarray res = xt::view(arr1, 1) + arr2; + xt::xarray res = xt::view(arr1, 1.0) + arr2; std::cout << res << std::endl; diff --git a/script/boost.cmake b/script/boost.cmake index b346088..29b2ca0 100644 --- a/script/boost.cmake +++ b/script/boost.cmake @@ -9,7 +9,7 @@ if (NOT Boost_FOUND) set(Boost_DEBUG OFF) if (COMP_DIR) - set(BOOST_ROOT ${COMP_DIR}/boost/latest) + set(Boost_ROOT ${COMP_DIR}/boost/latest) endif() find_package(Boost REQUIRED COMPONENTS filesystem system locale program_options)