MatLogger2  1.0.0
Library for logging of numeric data to HDF5 MAT-files, which is RT-safe and multithreaded.
matlogger2.cpp
Go to the documentation of this file.
2 #include <iostream>
3 #include <boost/algorithm/string.hpp>
4 
5 #include "thread.h"
6 #include "matlogger2_backend.h"
7 
8 
9 using namespace XBot::matlogger2;
10 
11 using namespace XBot;
12 
13 namespace
14 {
15  template <typename Func>
16  double measure_sec(Func f)
17  {
18  auto tic = std::chrono::high_resolution_clock::now();
19 
20  f();
21 
22  auto toc = std::chrono::high_resolution_clock::now();
23 
24  return std::chrono::duration_cast<std::chrono::nanoseconds>(toc-tic).count()*1e-9;
25  }
26 }
27 
29 {
30 public:
31 
33  {
34  return _mutex;
35  }
36 
37 private:
38 
39  matlogger2::MutexType _mutex;
40 };
41 
42 const std::string& VariableBuffer::get_name() const
43 {
44  return _name;
45 }
46 
48  enable_compression(false),
49  default_buffer_size(1e4)
50 {
51 }
52 
53 
54 namespace{
55 
56  std::string get_file_extension(std::string file)
57  {
58  std::vector<std::string> token_list;
59  boost::split(token_list, file, [](char c){return c == '.';});
60 
61  if(token_list.size() > 1)
62  {
63  return token_list.back();
64  }
65 
66  return "";
67  }
68 
69  std::string date_time_as_string()
70  {
71  time_t rawtime;
72  struct tm * timeinfo;
73  char buffer [80];
74  memset(buffer, 0, 80*sizeof(buffer[0]));
75 
76  std::time(&rawtime);
77  timeinfo = localtime(&rawtime);
78 
79  strftime(buffer, 80, "%Y_%m_%d__%H_%M_%S", timeinfo);
80 
81  return std::string(buffer);
82  }
83 
84 }
85 
86 MatLogger2::MatLogger2(std::string file, Options opt):
87  _file_name(file),
88  _vars_mutex(new MutexImpl),
90  _opt(opt)
91 {
92  // get the file extension, or empty string if there is none
93  std::string extension = get_file_extension(file);
94 
95  if(extension == "") // no extension, append date/time + .mat
96  {
97  _file_name += "__" + date_time_as_string() + ".mat";
98  }
99  else if(extension != "mat") // extension different from .mat, error
100  {
101  throw std::invalid_argument("MAT-file name should either have .mat \
102 extension, or no extension at all");
103  }
104 
105  // create mat file (erase if existing already)
106  _backend = Backend::MakeInstance("matio");
107 
108  if(!_backend)
109  {
110  throw std::runtime_error("MatLogger2: unable to create backend");
111  }
112 
113  if(!_backend->init(_file_name, _opt.enable_compression))
114  {
115  throw std::runtime_error("MatLogger2: unable to initialize backend");
116  }
117 }
118 
119 const std::string& MatLogger2::get_filename() const
120 {
121  return _file_name;
122 }
123 
125 {
126  return _opt;
127 }
128 
130 {
131  std::lock_guard<MutexType> lock(_vars_mutex->get());
132 
133  for(auto& p : _vars)
134  {
135  p.second.set_on_block_available(callback);
136  }
137 
138  _on_block_available = callback;
139 }
140 
142 {
143  std::lock_guard<MutexType> lock(_vars_mutex->get());
144 
145  for(auto& p : _vars)
146  {
147  p.second.set_buffer_mode(buffer_mode);
148  }
149 
150  _buffer_mode = buffer_mode;
151 }
152 
153 
154 bool MatLogger2::create(const std::string& var_name, int rows, int cols, int buffer_size)
155 {
156  if(buffer_size == -1)
157  {
158  buffer_size = _opt.default_buffer_size;
159  }
160 
161  if(!(rows > 0 && cols > 0 && buffer_size > 0))
162  {
163  fprintf(stderr, "Unable to create variable '%s': invalid parameters \
164 (rows=%d, cols=%d, buf_size=%d)\n",
165  var_name.c_str(), rows, cols, buffer_size);
166  return false;
167  }
168 
169  std::lock_guard<MutexType> lock(_vars_mutex->get());
170 
171  // check if variable is already defined (in which case, return false)
172  auto it = _vars.find(var_name);
173 
174  if(it != _vars.end())
175  {
176  fprintf(stderr, "Variable '%s' already exists\n", var_name.c_str());
177  return false;
178  }
179 
180  // compute block size from required buffer_size and number of blocks in
181  // queue
182  int block_size = std::max(1, buffer_size / VariableBuffer::NumBlocks());
183 
184  printf("Created variable '%s' (%d blocks, %d elem each)\n",
185  var_name.c_str(), VariableBuffer::NumBlocks(), block_size);
186 
187  // insert VariableBuffer object inside the _vars map
188  _vars.emplace(std::piecewise_construct,
189  std::forward_as_tuple(var_name),
190  std::forward_as_tuple(var_name, rows, cols, block_size));
191 
192  // set callback: this will be called whenever a new data block is
193  // available in the variable queue
194  _vars.at(var_name).set_on_block_available(_on_block_available);
195  _vars.at(var_name).set_buffer_mode(_buffer_mode);
196 
197  return true;
198 }
199 
200 bool MatLogger2::add(const std::string& var_name, double scalar)
201 {
202  // turn scalar into a 1x1 matrix
203  Eigen::Matrix<double, 1, 1> data(scalar);
204 
205  VariableBuffer * vbuf = find_or_create(var_name, data.rows(), data.cols());
206 
207  return vbuf && vbuf->add_elem(data);
208 }
209 
210 
212 {
213  // number of flushed bytes is returned on exit
214  int bytes = 0;
215 
216  // acquire exclusive access to the _vars object
217  std::lock_guard<MutexType> lock(_vars_mutex->get());
218  for(auto& p : _vars)
219  {
220  Eigen::MatrixXd block;
221  int valid_elems = 0;
222 
223  // while there are blocks available for reading..
224  while(p.second.read_block(block, valid_elems))
225  {
226  // get rows & cols for a single sample
227  auto dims = p.second.get_dimension();
228 
229  int rows = -1;
230  int cols = -1;
231  int slices = -1;
232  bool is_vector = false;
233 
234 
235  if(dims.second == 1) // the variable is a vector
236  {
237  rows = dims.first;
238  cols = valid_elems;
239  slices = 1;
240  is_vector = true;
241  }
242  else // the variable is a matrix
243  {
244  rows = dims.first;
245  cols = dims.second;
246  slices = valid_elems;
247  is_vector = false;
248  }
249 
250  // write block
251  _backend->write(p.second.get_name().c_str(),
252  block.data(),
253  rows, cols, slices);
254 
255  // update bytes computation
256  bytes += block.rows() * valid_elems * sizeof(double);
257  }
258  }
259 
260  return bytes;
261 }
262 
263 XBot::VariableBuffer * XBot::MatLogger2::find_or_create(const std::string& var_name,
264  int rows, int cols)
265 {
266  // try to find var_name
267  auto it = _vars.find(var_name);
268 
269  // if we found it, return a valid pointer
270  if(it != _vars.end())
271  {
272  return &(it->second);
273  }
274 
275  // if it was not found, and we cannot create it, return nullptr
276  if(!create(var_name, rows, cols))
277  {
278  return nullptr;
279  }
280 
281  // we managed to create the variable, try again to find it
282  return find_or_create(var_name, rows, cols);
283 
284 }
285 
286 
287 bool MatLogger2::flush_to_queue_all()
288 {
289  bool ret = true;
290  for(auto& p : _vars)
291  {
292  ret = p.second.flush_to_queue() && ret;
293  }
294  return ret;
295 }
296 
297 
299 {
300  /* inside this constructor, we have the guarantee that the flusher thread
301  * (if running) is not using this object
302  */
303 
304  // de-register any callback
306 
307  // set producer_consumer mode to be able to call read_block()
309 
310  // flush to queue and then flush to disk till all buffers are empty
311  while(!flush_to_queue_all())
312  {
314  }
315 
316  // flush to disk remaining data from queues
317  while(flush_available_data() > 0);
318 
319  printf("Flushed all data for file '%s'\n", _file_name.c_str());
320 
321  _backend->close();
322 }
323 
324 
325 
326 #define ADD_EXPLICIT_INSTANTIATION(EigenType) \
327 template bool MatLogger2::add(const std::string&, const Eigen::MatrixBase<EigenType>&); \
328 template bool VariableBuffer::add_elem(const Eigen::MatrixBase<EigenType>&); \
329 template bool VariableBuffer::BufferBlock::add(const Eigen::MatrixBase<EigenType>&);
330 
331 #define ADD_EXPLICIT_INSTANTIATION_STD_VECTOR(Scalar) \
332 template bool MatLogger2::add(const std::string&, const std::vector<Scalar>&); \
333 
334 
335 template <typename Scalar>
336 using Vector6 = Eigen::Matrix<Scalar,6,1>;
337 
338 template <typename Scalar>
339 using MapX = Eigen::Map<Eigen::Matrix<Scalar, -1, 1>>;
340 
341 ADD_EXPLICIT_INSTANTIATION(Eigen::MatrixXd)
342 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix2d)
343 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix3d)
344 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix4d)
345 
346 ADD_EXPLICIT_INSTANTIATION(Eigen::MatrixXf)
347 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix2f)
348 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix3f)
349 ADD_EXPLICIT_INSTANTIATION(Eigen::Matrix4f)
350 
351 ADD_EXPLICIT_INSTANTIATION(Eigen::VectorXd)
352 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector2d)
353 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector3d)
354 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector4d)
356 
357 ADD_EXPLICIT_INSTANTIATION(Eigen::VectorXf)
358 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector2f)
359 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector3f)
360 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector4f)
362 
363 ADD_EXPLICIT_INSTANTIATION(Eigen::VectorXi)
364 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector2i)
365 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector3i)
366 ADD_EXPLICIT_INSTANTIATION(Eigen::Vector4i)
368 
~MatLogger2()
Destructor flushes all buffers to disk, then releases any resource connected with the underlying MAT-...
Definition: matlogger2.cpp:298
Mode
Enum for specifying the type of buffer.
Definition: var_buffer.h:40
bool add_elem(const Eigen::MatrixBase< Derived > &data)
Add an element to the buffer.
Definition: var_buffer.h:260
Options get_options() const
Returns the MatLogger2::Options struct associated with this object.
Definition: matlogger2.cpp:124
void set_on_data_available_callback(VariableBuffer::CallbackType callback)
Set the callback that is invoked whenever new data is available for writing to disk.
Definition: matlogger2.cpp:129
Eigen::Map< Eigen::Matrix< Scalar,-1, 1 >> MapX
Definition: matlogger2.cpp:339
#define ADD_EXPLICIT_INSTANTIATION(EigenType)
Definition: matlogger2.cpp:326
const std::string & get_filename() const
Returns the full path associated with this logger object.
Definition: matlogger2.cpp:119
static int NumBlocks()
Definition: var_buffer.cpp:261
#define ADD_EXPLICIT_INSTANTIATION_STD_VECTOR(Scalar)
Definition: matlogger2.cpp:331
The VariableBuffer class implements a memory buffer for a single logged variable. ...
Definition: var_buffer.h:32
bool add(const std::string &var_name, const Eigen::MatrixBase< Derived > &data)
Add an element to an existing variable.
Definition: matlogger2.h:234
Eigen::Matrix< Scalar, 6, 1 > Vector6
Definition: matlogger2.cpp:336
std::function< void(BufferInfo)> CallbackType
Definition: var_buffer.h:63
const std::string & get_name() const
Definition: matlogger2.cpp:42
std::mutex MutexType
Definition: thread.h:19
bool create(const std::string &var_name, int rows, int cols=1, int buffer_size=-1)
Create a logged variable from its name as it will appear inside the MAT-file, dimensions, and buffer size.
Definition: matlogger2.cpp:154
void set_buffer_mode(VariableBuffer::Mode buffer_mode)
Set whether this buffer should be treated as a (possibly dual threaded) producer-consumer queue...
Definition: matlogger2.cpp:141
int flush_available_data()
Flush available data to disk.
Definition: matlogger2.cpp:211