-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathdata_logger_thread.h
82 lines (62 loc) · 2.73 KB
/
data_logger_thread.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef CACTUS_RT_EXAMPLES_MESSAGE_PASSING_EXAMPLE_DATA_LOGGER_H_
#define CACTUS_RT_EXAMPLES_MESSAGE_PASSING_EXAMPLE_DATA_LOGGER_H_
#include <cactus_rt/experimental/lockless.h>
#include <cactus_rt/rt.h>
#include <readerwriterqueue.h>
#include <cstdint>
#include <fstream>
#include <vector>
using moodycamel::ReaderWriterQueue;
using cactus_rt::Thread;
struct OutputData {
double timestamp = 0;
double output_value = 0.0;
OutputData() = default;
OutputData(double t, double o) : timestamp(t), output_value(o) {}
};
class DataLogger : public Thread {
constexpr static int kQueueCapacity = 8 * 1024; // This is over 8 seconds of data while we write every 1 second. Should never be full.
int64_t period_ns_;
int64_t write_data_interval_ns_;
ReaderWriterQueue<OutputData> queue_;
// Want to ensure that the message count is always updated in a single
// operation? Maybe this is not necessary tho, but this is still a cool
// technique to pass data around if you can fit in a word.
// Also see: https://stackoverflow.com/questions/56275443/why-is-it-not-possible-to-instantiate-an-atomic-pair
using CountData = struct {
uint32_t successful_messages;
uint32_t total_messages;
};
using AtomicMessageCount = cactus_rt::experimental::lockless::AtomicMessage<CountData>;
AtomicMessageCount message_count_;
std::vector<OutputData> data_buffer_; // A simple buffer to hold the data in non-realtime thread so batch writing can occur
std::ofstream file_;
public:
/**
* Creates a new data logger thread.
*
* @param data_file_path The path to the CSV file to be saved.
* @param period_ns The period in us at which the data logger thread will run
* at. Default: 10ms. No good reason for this
* value other than a guess that it might reduce scheduler
* load? Probably not as this thread is a normal thread.
* @param write_data_interval_ns The interval in seconds at which the
* data is written to the CSV file.
* Default: 1 second.
*/
DataLogger(const std::string& data_file_path, int64_t period_ns = 10'000'000, int64_t write_data_interval_ns = 1'000'000'000);
~DataLogger() override;
// TODO: make this class a template on T and use perfect forwarding here...
/**
* This method should only be called by one consumer (thread). It pushes data
* to be logged into a file for later consumption.
*/
bool EmplaceData(double timestamp, double output_value) noexcept;
protected:
void Run() final;
private:
void WriteAndEmptyDataFromBuffer() noexcept;
void ReadAndLogMessageCount() noexcept;
void IncrementMessageCount(uint32_t successful_message_count) noexcept;
};
#endif