Regilo
A simple C++ library for controlling the Neato XV robot and the Hokuyo scanner.
log.hpp
1 /*
2  * Regilo
3  * Copyright (C) 2015-2016 Branislav HolĂ˝ <branoholy@gmail.com>
4  *
5  * This file is part of Regilo.
6  *
7  * Regilo is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Regilo is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Regilo. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef REGILO_LOG_HPP
23 #define REGILO_LOG_HPP
24 
25 #include <chrono>
26 #include <cmath>
27 #include <fstream>
28 #include <iostream>
29 #include <mutex>
30 #include <sstream>
31 #include <thread>
32 
33 #include <boost/algorithm/string/predicate.hpp>
34 
35 #include "regilo/utils.hpp"
36 
37 namespace regilo {
38 
42 class ILog
43 {
44 public:
48  virtual ~ILog() = default;
49 
54  virtual const std::string& getFilePath() const = 0;
55 
60  virtual std::iostream& getStream() = 0;
61 
66  virtual bool isEnd() const = 0;
67 
72  virtual std::string read() = 0;
73 
79  virtual std::string read(std::string& logCommand) = 0;
80 
86  virtual std::string readCommand(const std::string& command) = 0;
87 
94  virtual std::string readCommand(const std::string& command, std::string& logCommand) = 0;
95 
101  virtual void write(const std::string& command, const std::string& response) = 0;
102 };
103 
108 class Log : public virtual ILog
109 {
110 private:
111  std::string filePath;
112  std::fstream *fileStream;
113 
114  std::mutex streamMutex;
115 
116  bool metadataRead = false;
117  bool metadataWritten = false;
118 
119 protected:
120  std::iostream& stream;
121  std::size_t version = 1;
122 
127  virtual void readMetadata(std::istream& metaStream);
128 
133  virtual void writeMetadata(std::ostream& metaStream);
134 
135 public:
136  char MESSAGE_END = '$';
137 
142  Log(const std::string& filePath);
143 
148  Log(std::iostream& stream);
149 
150  virtual ~Log();
151 
152  virtual inline const std::string& getFilePath() const override { return filePath; }
153  virtual inline std::iostream& getStream() override { return stream; }
154  virtual inline bool isEnd() const override { return !stream; }
155 
156  virtual std::string read() override;
157  virtual std::string read(std::string& logCommand) override;
158  virtual std::string readCommand(const std::string& command) override;
159  virtual std::string readCommand(const std::string& command, std::string& logCommand) override;
160 
161  virtual void write(const std::string& command, const std::string& response) override;
162 };
163 
167 class ITimedLog : public virtual ILog
168 {
169 public:
173  virtual ~ITimedLog() = default;
174 
179  virtual std::chrono::nanoseconds getLastCommandNanoseconds() const = 0;
180 
185  template<typename Duration>
186  inline Duration getLastCommandTimeAs() const { return std::chrono::duration_cast<Duration>(this->getLastCommandNanoseconds()); }
187 
192  virtual void syncTime(bool sync = true) = 0;
193 };
194 
198 template<typename DurationT = std::chrono::milliseconds>
199 class TimedLog : public Log, public ITimedLog
200 {
201 private:
202  std::mutex streamMutex;
203 
204  std::intmax_t num, den;
205 
206  DurationT lastCommandTime;
207 
208  DurationT firstReadTime = DurationT::zero();
209  DurationT firstWriteTime = DurationT::min();
210 
211 protected:
212  virtual void readMetadata(std::istream& metaStream) override;
213  virtual void writeMetadata(std::ostream& metaStream) override;
214 
215 public:
216  typedef DurationT Duration;
217 
218  using Log::Log;
219 
223  virtual ~TimedLog() = default;
224 
225  inline virtual std::chrono::nanoseconds getLastCommandNanoseconds() const override
226  {
227  return std::chrono::duration_cast<std::chrono::nanoseconds>(lastCommandTime);
228  }
229 
234  inline DurationT getLastCommandTime() const { return lastCommandTime; }
235 
236  virtual inline void syncTime(bool sync = true) override { firstReadTime = (sync ? DurationT::max() : DurationT::zero()); }
237 
238  virtual std::string read(std::string& logCommand) override;
239  virtual void write(const std::string& command, const std::string& response) override;
240 };
241 
242 extern template class TimedLog<std::chrono::nanoseconds>;
243 extern template class TimedLog<std::chrono::microseconds>;
244 extern template class TimedLog<std::chrono::milliseconds>;
245 extern template class TimedLog<std::chrono::seconds>;
246 
247 template<typename DurationT>
248 void TimedLog<DurationT>::readMetadata(std::istream& metaStream)
249 {
250  Log::readMetadata(metaStream);
251  metaStream >> num >> den;
252 }
253 
254 template<typename DurationT>
255 void TimedLog<DurationT>::writeMetadata(std::ostream& metaStream)
256 {
257  Log::writeMetadata(metaStream);
258  metaStream << ' ' << DurationT::period::num << ' ' << DurationT::period::den;
259 }
260 
261 template<typename DurationT>
262 std::string TimedLog<DurationT>::read(std::string& logCommand)
263 {
264  streamMutex.lock();
265 
266  std::string response = Log::read(logCommand);
267 
268  std::string epochTime;
269  std::getline(stream, epochTime, MESSAGE_END);
270  std::istringstream epochStream(epochTime);
271 
272  std::int64_t commandTimeCount;
273  epochStream >> commandTimeCount;
274 
275  long double numRatio = num / DurationT::period::num;
276  long double denRation = DurationT::period::den / den;
277  lastCommandTime = DurationT(std::int64_t(std::round(commandTimeCount * numRatio * denRation)));
278 
279  if(firstReadTime == DurationT::max()) firstReadTime = epoch<DurationT>();
280  else
281  {
282  DurationT elapsed = epoch<DurationT>() - firstReadTime;
283 
284  while(elapsed < lastCommandTime)
285  {
286  std::this_thread::sleep_for(lastCommandTime - elapsed);
287  elapsed = epoch<DurationT>() - firstReadTime;
288  }
289  }
290 
291  streamMutex.unlock();
292 
293  return response;
294 }
295 
296 template<typename DurationT>
297 void TimedLog<DurationT>::write(const std::string& command, const std::string& response)
298 {
299  streamMutex.lock();
300 
301  Log::write(command, response);
302 
303  if(firstWriteTime == DurationT::min()) firstWriteTime = epoch<DurationT>();
304  stream << (epoch<DurationT>() - firstWriteTime).count() << MESSAGE_END;
305 
306  streamMutex.unlock();
307 }
308 
309 }
310 
311 #endif // REGILO_LOG_HPP
virtual void write(const std::string &command, const std::string &response) override
Write a command and response to the log.
Definition: log.hpp:297
virtual void syncTime(bool sync=true) override
Sync command times with real time.
Definition: log.hpp:236
virtual void readMetadata(std::istream &metaStream)
Read meta data from the log.
The TimedLog class is used to log all commands with their timestamp.
Definition: log.hpp:199
DurationT getLastCommandTime() const
Get the last command time (after reading).
Definition: log.hpp:234
Definition: controller.hpp:35
virtual const std::string & getFilePath() const override
Get the path of file if the log was created with a path otherwise the empty string.
Definition: log.hpp:152
virtual void write(const std::string &command, const std::string &response)=0
Write a command and response to the log.
virtual ~ILog()=default
Default destructor.
Duration getLastCommandTimeAs() const
Get the last command time (after reading).
Definition: log.hpp:186
virtual void writeMetadata(std::ostream &metaStream)
Write meta data to the log.
The Log class is a basic log with a simple read/write functionality.
Definition: log.hpp:108
DurationT Duration
The duration type for this log.
Definition: log.hpp:216
virtual std::string read() override
Read one command from the log.
std::iostream & stream
The underlying stream.
Definition: log.hpp:120
virtual void write(const std::string &command, const std::string &response) override
Write a command and response to the log.
The ILog interface has to be implemented in all Log classes.
Definition: log.hpp:42
virtual bool isEnd() const =0
Test if the stream is EOF.
virtual std::chrono::nanoseconds getLastCommandNanoseconds() const override
Get the last command time (after reading).
Definition: log.hpp:225
virtual bool isEnd() const override
Test if the stream is EOF.
Definition: log.hpp:154
The ITimedLog interface is implemented in TimedLog.
Definition: log.hpp:167
virtual std::iostream & getStream() override
Get the current underlying stream.
Definition: log.hpp:153
virtual std::string read()=0
Read one command from the log.
virtual void readMetadata(std::istream &metaStream) override
Read meta data from the log.
Definition: log.hpp:248
virtual std::string readCommand(const std::string &command)=0
Read specified command from the log (the others are skipped).
virtual void writeMetadata(std::ostream &metaStream) override
Write meta data to the log.
Definition: log.hpp:255
Log(const std::string &filePath)
Log constructor with logging to a file.
virtual std::iostream & getStream()=0
Get the current underlying stream.
virtual const std::string & getFilePath() const =0
Get the path of file if the log was created with a path otherwise the empty string.