Replies: 18 comments 4 replies
-
|
@sy950915 The end-user should only subscribe on the CAN_DataFrame channel and of course the timestamp. Maybe a special subscriber type that inherit from the normal channel observer and have a new "GetCanMessage" function with sample index as input. Note that no subscription should be needed for the composite channel as they points into the CAN_DataFrame (the CG record actually). I suppose the same requirement would be for the LIN, FlexRay, MOST and ETH message wrapper. The new subscriber needs to optimize how to get message data out of the CAN_DataFrame byte array. This should minimize the memory consumption. |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall Yes, there is indeed a requirement for reading LIN, FR, and ETH data. Recently, I have been using mdflib to read and write MDF files. The progress has been relatively smooth, but I have encountered a few issues, such as: When writing CAN error frames, after saving the MF4 file and importing it into CANoe 16, CANoe reports an error and does not recognize this information. When writing ETH_Frame, CANoe also fails to recognize it. |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 The ETH and LIN are new protocols that may not be tested on so many reader platforms. I have noticed that the Vector MDF Validator complains on some issues. |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall Additionally, I'd like to ask: Can MDF files be read in chronological order? After all, when writing data, it's always sequential based on timestamps. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 I checked with my test code and that code works OK. It's little bit of bad timing as I'm changing the code regarding the configuration but I don't think I accidentally corrected this bug. In the code below, the second if statement should be true. I suspect that you is running multiple DG threads (LIN, CAN and normal) at the same time. The Storage Type is set in the writer. I suspect there is a design issue when introducing multiple DG threads. The Storage Type belong to the DG block not globally to the writer object. This design bug was introduced with the new multiple DG storage option. const bool mandatory_only = writer_.MandatoryMembersOnly();
const IChannel* cn_data_byte = nullptr; // Need to update the VLSD Record ID
if (IChannelGroup* cg_data_frame = dg_block.CreateChannelGroup(
MakeGroupName("DataFrame"));
cg_data_frame != nullptr) {
cg_data_frame->PathSeparator('.');
cg_data_frame->Flags(CgFlag::PlainBusEvent | CgFlag::BusEvent);
CreateTimeChannel(*cg_data_frame,"t");
CreateCanDataFrameChannel(*cg_data_frame);
cn_data_byte = cg_data_frame->GetChannel("CAN_DataFrame.DataBytes");
CreateSourceInformation(*cg_data_frame);
}
if (writer_.StorageType() == MdfStorageType::VlsdStorage && cn_data_byte != nullptr) {
// Need to add a special CG group for the data samples
if (auto* cg_samples_frame = dg_block.CreateChannelGroup("");
cg_samples_frame != nullptr) {
cg_samples_frame->Flags(CgFlag::VlsdChannel);
cn_data_byte->VlsdRecordId(cg_samples_frame->RecordId());
}
}
|
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 I'm not sure, but where did you find the get_channel_group_value() function? |
Beta Was this translation helpful? Give feedback.
-
|
Ah, I see. The get_channel_group_value was just a hypothetical example I used. What I really meant is to treat the MDF file as a data repository and retrieve the data row by row (in a sequential, line-by-line manner). |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915
Both method uses minimum of memory, well at least it's not the reader problem. The first method is fast and simple if you know the storage format but for general read you might do all the tricks as the Channel Observer do. There is a partial read function as well. The MDF must be read sequential i.e. row-by-row. If you want to read column-by-column, then you need to convert the MDF file to something as Parquet files. This is a Data Lake application but this is a story for another day. |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
I took a code snippets from a unit test. It starts as a normal channel observer read but before calling the ReadData() function, set the frame channel to not read any VLSD data bytes(frame_observer->ReadVlsdData(false)). This read will be fast as you don't need to read most of the data bytes. Note that the callback function below, doesn't do anything useful. Next is to fetch the sample index to VLSD offset list. Make a callback function that is called for each offset and CAN data bytes in the ReadVlsdData. This method will do the job but you need some knowledge about the MDF file. This function is not so fast as you read the file twice. It very useful for ultra-large files with VLSD data blobs. MdfReader oRead(test_file);
EXPECT_TRUE(oRead.ReadEverythingButData()) << oRead.ShortName();
const auto *header = oRead.GetHeader();
ASSERT_TRUE(header != nullptr);
auto *last_dg = header->LastDataGroup();
ASSERT_TRUE(last_dg != nullptr);
auto channel_groups = last_dg->ChannelGroups();
ASSERT_FALSE(channel_groups.empty());
auto *channel_group = channel_groups[0];
ASSERT_TRUE(channel_group != nullptr);
auto *time_channel = channel_group->GetChannel(kLargeTime);
ASSERT_TRUE(time_channel != nullptr);
auto *counter_channel = channel_group->GetChannel(kLargeFrameCounter);
ASSERT_TRUE(counter_channel != nullptr);
auto *frame_channel = channel_group->GetChannel(kLargeFrameData);
ASSERT_TRUE(frame_channel != nullptr);
auto time_observer = CreateChannelObserver(*last_dg,
*channel_group, *time_channel);
auto counter_observer = CreateChannelObserver(*last_dg,
*channel_group, *counter_channel);
auto frame_observer = CreateChannelObserver(*last_dg,
*channel_group, *frame_channel);
frame_observer->ReadVlsdData(false);
EXPECT_TRUE(oRead.ReadData(*last_dg)) << oRead.ShortName();
auto offset_list = frame_observer->GetOffsetList();
// size_t sample_count = 0;
std::function callback = [&](uint64_t offset, const std::vector<uint8_t> &buffer) {
const bool offset_exist = std::any_of(offset_list.cbegin(),offset_list.cend(),
[&] (uint64_t off) {
return off == offset;
});
EXPECT_TRUE(offset_exist) << "Offset: " << offset << std::endl;
};
EXPECT_TRUE(oRead.ReadVlsdData(*last_dg, *frame_channel, offset_list, callback)) << oRead.ShortName(); |
Beta Was this translation helpful? Give feedback.
-
|
Thank you. When using the following code, I noticed that the RecordId() of CAN_DataFrame.DataBytes is the same as the RecordId() of other CAN_DataFrame entries, which prevents me from correctly retrieving the CAN_DataFrame.DataBytes data in the callback function. TSAPI(s32) mdf_test(const char* mdffilename) } |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 Now to your real problem. The Data Bytes channel is actual marked as a Variant Length Signal Data so even if its data type is a Byte Array, it actually stores and 64-bit offset into the Signal Data. I haven't checked your file but I assume that it uses the CG VLSD option. This means that the data bytes are stored in another Channel Group with another Record ID . The IChannel object have a property called VlsdRecordId() that points to the CG VLSD Record ID. To summarize, the Channel Observer first check if the Record ID match the VLSD Record ID and if not, basically run your for loop but skipping the Data Byte channel. If it match the VLSD Record ID, the record bytes includes number of bytes followed by the number of bytes. Yes, it's a mess but that's how the MDF works. Getting the sample value in 2 separate callback calls, you need to rethink your design. You may spy on the ChannelObserver::OnSample() callback function. |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 |
Beta Was this translation helpful? Give feedback.


Uh oh!
There was an error while loading. Please reload this page.
-
Hello, I noticed in your library that when writing to an MDF file, we can directly pass the CanMessage type. I was wondering if it's also possible to directly read CanMessage types from the MDF file?
Beta Was this translation helpful? Give feedback.
All reactions