RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
data_recorder.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 Georgia Institute of Technology, University of Utah,
3  Weill Cornell Medical College
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 #ifndef DATA_RECORDER_H
21 #define DATA_RECORDER_H
22 
23 #include <QComboBox>
24 #include <QListWidget>
25 #include <QMutex>
26 #include <QSpinBox>
27 #include <QTime>
28 #include <mutex>
29 #include <optional>
30 #include <utility>
31 #include <vector>
32 
33 #include <hdf5_hl.h>
34 #include <time.h>
35 
36 #include "event.hpp"
37 #include "io.hpp"
38 #include "widgets.hpp"
39 
40 namespace DataRecorder
41 {
42 
43 typedef struct data_token_t
44 {
45  int64_t time;
46  double value;
48 
49 constexpr size_t DEFAULT_BUFFER_SIZE = 10000 * sizeof(data_token_t);
50 constexpr std::string_view MODULE_NAME = "Data Recorder";
51 
52 inline std::vector<Widgets::Variable::Info> get_default_vars()
53 {
54  return {};
55 }
56 
57 inline std::vector<IO::channel_t> get_default_channels()
58 {
59  return {{"Recording Channel",
60  "This is the channel used by the Data Recorder to record on other "
61  "inputs and "
62  "output ports",
63  IO::INPUT}};
64 }
65 
66 typedef struct record_channel
67 {
68  std::string name;
71  bool operator==(const record_channel& rhs) const
72  {
73  return (this->endpoint == rhs.endpoint)
74  && (this->data_source == rhs.data_source);
75  }
76  bool operator!=(const record_channel& rhs) const { return !operator==(rhs); }
78 
80 {
81 public:
82  Component(Widgets::Plugin* hplugin, const std::string& probe_name);
83  void execute() override;
85 
86 private:
87  std::unique_ptr<RT::OS::Fifo> m_fifo;
88 };
89 
90 class Panel : public Widgets::Panel
91 {
92  Q_OBJECT
93 
94 public:
95  Panel(const Panel&) = delete;
96  Panel(Panel&&) = delete;
97  Panel& operator=(const Panel&) = delete;
98  Panel& operator=(Panel&&) = delete;
99  Panel(QMainWindow* mwindow, Event::Manager* ev_manager);
100  ~Panel() override = default;
101 
102 signals:
104 
105 public slots:
106  void startRecordClicked();
107  void stopRecordClicked();
108  void updateDownsampleRate(size_t rate);
109  void removeRecorders(IO::Block* block);
110 
111 private slots:
112  void buildBlockList();
113  void buildChannelList();
114  void changeDataFile();
115  void insertChannel();
116  void removeChannel();
117  void addNewTag();
118  void processData();
119  void syncEnableRecordingButtons(const QString& /*unused*/);
120 
121 private:
122  size_t m_buffer_size = DEFAULT_BUFFER_SIZE;
123  size_t downsample_rate {1};
124  std::vector<std::string> dataTags;
125 
126  QGroupBox* channelGroup = nullptr;
127  QGroupBox* stampGroup = nullptr;
128  QGroupBox* sampleGroup = nullptr;
129  QGroupBox* fileGroup = nullptr;
130  QGroupBox* buttonGroup = nullptr;
131  QGroupBox* listGroup = nullptr;
132 
133  QComboBox* blockList = nullptr;
134  QComboBox* channelList = nullptr;
135  QComboBox* typeList = nullptr;
136  QListWidget* selectionBox = nullptr;
137  QLabel* recordStatus = nullptr;
138  QPushButton* addRecorderButton = nullptr;
139  QPushButton* removeRecorderButton = nullptr;
140  QPushButton* addTag = nullptr;
141 
142  QSpinBox* downsampleSpin = nullptr;
143 
144  QLineEdit* fileNameEdit = nullptr;
145  QLineEdit* timeStampEdit = nullptr;
146  QLineEdit* fileFormatEdit = nullptr;
147  QLabel* fileSizeLbl = nullptr;
148  QLabel* fileSize = nullptr;
149  QLabel* trialLengthLbl = nullptr;
150  QLabel* trialLength = nullptr;
151  QLabel* trialNumLbl = nullptr;
152  QLabel* trialNum = nullptr;
153  QTimer* recording_timer = nullptr;
154 
155  QPushButton* startRecordButton = nullptr;
156  QPushButton* stopRecordButton = nullptr;
157  QPushButton* closeButton = nullptr;
158 
159  QTime starting_record_time;
160 }; // class Panel
161 
162 class Plugin : public Widgets::Plugin
163 {
164 public:
165  Plugin(const Plugin&) = delete;
166  Plugin(Plugin&&) = delete;
167  Plugin& operator=(const Plugin&) = delete;
168  Plugin& operator=(Plugin&&) = delete;
169  explicit Plugin(Event::Manager* ev_manager);
170  ~Plugin() override;
171 
172  void receiveEvent(Event::Object* event) override;
173  void startRecording();
174  void stopRecording();
175  void openFile(const std::string& file_name);
176  void closeFile();
177  void change_file(const std::string& file_name);
183  std::vector<record_channel> get_recording_channels();
184  int apply_tag(const std::string& tag);
185  void process_data_worker();
186  std::string getOpenFilename() const { return this->hdf5_filename; }
187  bool isFileOpen() { return this->open_file.load(); }
188  bool isRecording() { return this->recording.load(); }
189  int getTrialCount() const { return this->trial_count; }
190 
191 private:
192  std::atomic<bool> recording;
193  void append_new_trial();
194  void close_trial_group();
195  void open_trial_group();
196  static void save_data(hid_t data_id,
197  const std::vector<data_token_t>& data,
198  size_t packet_count);
199  hsize_t m_data_chunk_size = static_cast<hsize_t>(1000);
200  int m_compression_factor = 5;
201  struct hdf5_handles
202  {
203  hid_t file_handle = H5I_INVALID_HID;
204  hid_t trial_group_handle = H5I_INVALID_HID;
205  hid_t attribute_handle = H5I_INVALID_HID;
206  hid_t sync_group_handle = H5I_INVALID_HID;
207  hid_t async_group_handle = H5I_INVALID_HID;
208  hid_t sys_data_group_handle = H5I_INVALID_HID;
209  hid_t channel_datatype_handle = H5I_INVALID_HID;
210  } hdf5_handles;
211 
212  struct recorder_t
213  {
214  recorder_t(record_channel chan,
215  std::unique_ptr<DataRecorder::Component> comp,
216  hid_t handle)
217  : channel(std::move(chan))
218  , component(std::move(comp))
219  , hdf5_data_handle(handle)
220  {
221  }
222  record_channel channel;
223  std::unique_ptr<DataRecorder::Component> component;
224  hid_t hdf5_data_handle;
225  };
226 
227  int trial_count = 0;
228  std::string hdf5_filename;
229  std::vector<recorder_t> m_recording_channels_list;
230  std::shared_mutex m_channels_list_mut;
231  std::atomic<bool> open_file = false;
232 }; // class Plugin
233 
234 std::unique_ptr<Widgets::Plugin> createRTXIPlugin(Event::Manager* ev_manager);
235 
236 Widgets::Panel* createRTXIPanel(QMainWindow* mwindow,
237  Event::Manager* ev_manager);
238 
239 std::unique_ptr<Widgets::Component> createRTXIComponent(
240  Widgets::Plugin* host_plugin);
241 
243 
244 } // namespace DataRecorder
245 
246 #endif /* DATA_RECORDER_H */
RT::OS::Fifo * get_fifo()
Component(Widgets::Plugin *hplugin, const std::string &probe_name)
Panel(Panel &&)=delete
Panel & operator=(const Panel &)=delete
Panel & operator=(Panel &&)=delete
void updateDownsampleRate(size_t rate)
Panel(const Panel &)=delete
~Panel() override=default
void removeRecorders(IO::Block *block)
void destroy_component(IO::endpoint endpoint)
int apply_tag(const std::string &tag)
Plugin & operator=(const Plugin &)=delete
Plugin & operator=(Plugin &&)=delete
void openFile(const std::string &file_name)
std::string getRecorderName(IO::endpoint endpoint)
void change_file(const std::string &file_name)
RT::OS::Fifo * getFifo(IO::endpoint endpoint)
int create_component(IO::endpoint endpoint)
DataRecorder::Component * getRecorderPtr(IO::endpoint endpoint)
Plugin(Plugin &&)=delete
void receiveEvent(Event::Object *event) override
Plugin(const Plugin &)=delete
std::string getOpenFilename() const
std::vector< record_channel > get_recording_channels()
Definition: io.hpp:79
constexpr size_t DEFAULT_BUFFER_SIZE
constexpr std::string_view MODULE_NAME
Widgets::FactoryMethods getFactories()
struct DataRecorder::record_channel record_channel
std::unique_ptr< Widgets::Plugin > createRTXIPlugin(Event::Manager *ev_manager)
struct DataRecorder::data_token_t data_token_t
std::vector< Widgets::Variable::Info > get_default_vars()
std::unique_ptr< Widgets::Component > createRTXIComponent(Widgets::Plugin *host_plugin)
Widgets::Panel * createRTXIPanel(QMainWindow *mwindow, Event::Manager *ev_manager)
std::vector< IO::channel_t > get_default_channels()
@ INPUT
Definition: io.hpp:54
struct IO::endpoint endpoint
bool operator!=(const record_channel &rhs) const
bool operator==(const record_channel &rhs) const