looking for an example of using ReadVisdData to read CAN/CAN FD messages。 #110
-
|
Dear ihedvall, Hello, I am currently able to use your ReadData example to read signals. However, when it comes to reading CAN(FD) messages, I'm not sure how to utilize ReadVisdData. Could you kindly provide an example for reading messages, similar to the ReadData example? Thank you. Best regards. |
Beta Was this translation helpful? Give feedback.
Replies: 56 comments 13 replies
-
|
@windfenggg Just to confuse everything, there exist a so-called VLSD channel group (CG). This channel group is used to store the bytes while channel group with the VLSD channel only stores an offset into that VLSD channel group. The VLSD group cannot have any channels. By this trick, the samples can be appended to the MDF file. The If you have a large file (> 10GB), the read may fail due to out-of-memory. Instead of read everything, you need to read the file with some partial read. There exist some Instead of The As you notice the There are some (Google) unit tests that shows the usages (mdflib_test/src/testread.cpp):
Ingemar Hedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg After the You need to keep some track of what type of data the channel holds. For example You can start optimizing the read of the file in a second step. For example you may check if the channel group has any samples and if it doesn't have any samples, not subscribing of its channels. You can also add a single sample observer instead of a bunch of channel observers. You can inherit or add a callback to the observer. The callback will be called for each channel group and sample. This method reduce the memory requirements and is in theory faster. This method require that you are responsible for the caching of samples so if the channel observers working OK for you, continue to use them. Ingemar Hedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg If you look closer on the CG (CAN_DataFrame). It consist of 2 channel time and frame data. The frame data is packed byte array with CN ID, DLC and data bytes, packed into a byte array. Note that the frame data channel have sub-channel. These sub-channel describes how the frame data is packed. It's these sub-channels that are of interest for you. It seems to be some error regarding of the CAN_Frame data. It is strange error as the sub-channel data is OK. I will check if this a presentation error or something else. The below is snapshots from the MDF Viewer. You can fetch the Windows executable in the GitHub release area. If you have a simple MDF example file that you can send over, it would simply the fault tracing. |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg For you in the sample observer OnSample() function, The record ID(1) reports all the CAN frames flags and the timestamp but not the CAN data bytes. The record(2) reports the CAN data bytes. The channel observers seems to solve this cross-reference why they gives a simple solution. See also ChannelObserver::OnSample() (channelobserver.cpp). Your log file may use some other configuration so we need to figure out how to parse the file. Ingemar Hedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
If you look at the CAN file, it uses something called MLSD (Max Length Signal Data). The CAN_DataFrame normally all CAN message flags + an offset into signal data. The offset is a 8 byte (uint64_t) value into a signal data blob. This blob can be stored in a CG VLSD block or in an external SD block. The MLSD configuration stores the data bytes instead of the offset thus there is no need for an external CG-VLSD/SD block. Number of bytes is fetched from the length. When the MDF file now shall store more than 8 bytes, it has to store the CAN data bytes in either a CG-VLSD block or a SD block. The CAN_DataFrame.DataBytes seems to have the offset data (uint64_t) value instead of the actual data value. My suspect is that a SD block is used to store the data bytes. If you have the possibility to open the MDF file in the MDF Viewer and check the CAN_DataFrame.DataBytes channel configuration. It is the flag and its reference index to Signal Data that is of concern. |
Beta Was this translation helpful? Give feedback.
-
|
I have several files with MLSD and CG-VLSD data storage but I have no file by some other tool that uses SD storage. I test the MDF SD storage against my own writer, which isn't optimal. It should be the configuration of the CAN_DataFrame.DataBytes channel that controls this. I don't have any ASC to MDF converters. Just need a hint in the right direction. I append the CAN SD configuration that my unit test uses. |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg Output: |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall Hello, I see that the current system already supports storing CAN/CANFD/LIN/ETH bus messages into MF4 files. Have you considered adding support for FlexRay bus data recording into MF4? |
Beta Was this translation helpful? Give feedback.
-
|
@sy950915 |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg Your 20MS/s problem is very close what is possible to stream onto a mechanical disc which in turn is so-to-say single threaded. What I am trying to say that doing multi-threaded writing to a single disc is a bad idea. The multi-DG MDF storage have only one write thread but one in-memory queue. I don't know your application but I suspect it's similar to what I did for a power plant. The requirement is to record all 4000 signals at 100 Hz 24/7.
The MDF is perfect choice for the long term storage. In my problem, the I/O signal configuration is fairly static so using MDF for the 3 day disc cache is little bit overkill but in principle the MDF could work there as well. There are a lot of other problem but I stop here as I don't know your requirements. 100kHz sample rate is very high if you want to store 24/7. If it is in burst mode, there might be a simpler solution. Ingemar Hedvall |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg It is possible to save samples fast and also read them fast, if the following conditions are met.
With the above requirements, the CG record have a fixed byte width. The data block is now actually a type of matrix where the reader know where each value is stored. The storage is still row storage but with a specialized reader, the reading will be faster. Normally the writer saves the data block in one DT block. This can only be done if there is only one DG block. If there are multiple DG block storage, the data block is divided into smaller linked data blocks typical using the HL-DL-DT/DZ block technique. If the above requirements are met, the LD-DV storage technique can be used. The LD block is similar to the DL block but the LD block stores the time stamp of the first sample in the DV block. It should be fairly simple to add the LD-DV storage to the existing readers by adding a new Storage Type enumeration. Currently the Fixed Length, VLSD or MLSD enumerations exist but adding "Column" storage and some new Sample Queue handling will solve that problem. The big issue will be if the third-party reader will handle this. I suppose newer Vector tools will handle this new 4.2 storage. The MDF lib has only one generic reader which read the above data samples. The LD block contains number of samples and also the first sample time for each data block may generate new "Read Data" interfaces as "Read (All) Samples for a CG block" or similar call range based. These calls will be much faster than existing ones. The above is related to the Batch writer type but I leave that to another comment. |
Beta Was this translation helpful? Give feedback.
-
|
@windfenggg The main problem is not that it take some time to write the values to the disc. The main problem if saving the samples to the internal sample queue. You want a Save Sample interface with 3 input argument, signal reference, signal value and timestamp. It would be more general to use a Save Sample interface with 2 input argument, the CG reference and a value array. The value array is added in the signal order in the channel group, typical with the time value first followed by the other signal values. The Save Sample call must return fast so there is no time for any mutex or new calls. Instead I propose to use the DMA (Direct Memory Access) technique. This requires 2 or more fixed arrays. One array is the active and the other are the inactive ones. The active one is what the Save Sample function uses and when it has filled the active one it switch to the next fixed array and mark the previous one at full. Another thread is responsible to move the samples to another place and mark the array as empty. Typical only 2 buffers are needed but if you chicken out more than 2 buffers can be used. The above techniques require that the buffers are allocated and of fixed size which is similar to the column storage requirements. In principle the Save Sample can be optimized away but require that you simply add the sample values directly to the active buffer as the CG reference the active buffer. The disadvantage is that clearing the full buffer before it becomes active again, then these samples are lost. The full buffer needs to converted into MDF format and that is saved onto the disc similar to the current Sample Queue. Having a one-to-one relation between the buffer and a LD-DV or (HL)-DL-DT storage, optimize the write. I think the end-user needs to set up the buffer size in number of samples. A 2-3 seconds buffer would be enough. The Save Sample call thread/task should within a RTOS task but this is the end-user's problem. Please give some comments about this (DMA) solution proposal. |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall Previously, the main issue I encountered was that my self-maintained queue was being consumed too slowly. I have since modified the application layer to use multithreaded queue consumption. After several hours of testing, I observed that stopping the measurement is now much smoother, and the number of accumulated signals in the queue remains within an acceptable range. At this stage, I suspect that the remaining inefficiencies may be related to my thread scheduling strategy. I am currently running stress tests to verify whether any data points are being lost. If the performance bottleneck ultimately lies in the A batch interface could potentially improve this situation, for example by allowing me to pass a DG address together with a timestamp array pointer and a signal value array pointer, where the timestamp and signal value arrays are guaranteed to be aligned. I am curious whether this would be feasible to implement within the library. Since the generated MF4 files must remain compatible with third-party tools, the priority for adopting MDF 4.2 LD/DV storage is relatively low at this stage, though I understand it may be more suitable for columnar signal storage in the future. Regarding the double-buffer (DMA-like) queue technique you proposed, if it can be configured in such a way that no data is lost while still supporting high-frequency writes at the application level, I would be strongly inclined toward this approach. To summarize: I am continuing performance tests to determine whether the actual bottleneck is in the library’s Best regards! |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall Currently, I allocate one queue and one thread per signal. The same thread handles both storing the signal and writing to the MF4 file. The downside is that CPU cores are limited, but the number of threads can get quite large, since I may need to save multiple MF4 files simultaneously, and each file can contain dozens of signals that are continuously receiving data. My main goals are to ensure no data loss and to keep measurement shutdown fast. At the moment, I have two questions: 1)Suppose I have 50 different signals that need to be written in real time, each with different timestamps. Should I store them as: 50 DGs, one DG per signal, each DG with one CG, and each CG with two CNs? Or as 1 DG with 50 CGs, each CG with two CNs? Or as 1 DG with 1 CG and 51 CNs, with timestamps forcibly synchronized? 2)What’s the purpose of setting a pre-trigger time for event-triggered signals? In your test code, after setting a pre-trigger time, you were able to save a batch of samples between init and start. My current high-frequency writes are mainly DAQ event-triggered, with the fastest rate being 10 µs. Best Regards! |
Beta Was this translation helpful? Give feedback.
-
|
@ihedvall |
Beta Was this translation helpful? Give feedback.
-
|
Hello, I’ve recently encountered a performance issue and would like your advice. I’m trying to merge two MF4 files (1.mf4 and 2.mf4) into a third file (3.mf4). I’ve tested two approaches: Approach 1: Use a single thread to read 1.mf4 first and then 2.mf4. The data is stored in a Approach 2: Use two threads to read 1.mf4 and 2.mf4 in parallel, store the results into the same map, then perform parallel sorting, and finally use a thread pool to concurrently write each signal into separate DGs of 3.mf4. In my tests, Approach 1 is faster. So my question is: when merging MF4 files, is single-threaded read/write actually more efficient, since (as you mentioned before) only one writing thread is effectively active? Also, do you have suggestions for reducing the overall merge time? I noticed that when I choose Best Regards! |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for your suggestion. I will try the callback-based application layer approach. The scenario involves merging around a hundred MF4 files into a single MF4 file. Each MF4 contains only one DG that stores a single signal. Ideally, each MF4 file should be just a few hundred MB in size. |
Beta Was this translation helpful? Give feedback.
-
|
Hello, I would like to ask whether MdfReader is thread-safe. Can different DG blocks of an MF4 file be read in parallel? I created a thread for each DG block with callbacks, but I found that the data received in the multi-threaded If MdfReader is not thread-safe, are there any techniques to speed up reading large MF4 files (around 10 GB, not video or audio data)? Best Regards! |
Beta Was this translation helpful? Give feedback.










@windfenggg
Added a unit test for the CAN FD file. Just printed out the first 5 samples.