JeVois  1.17
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Log.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #include <jevois/Debug/Log.H>
21 #include <jevois/Util/Async.H>
22 #include <mutex>
23 #include <iostream>
24 #include <fstream>
25 #include <stdexcept>
26 
27 namespace jevois
28 {
29  int logLevel = LOG_INFO;
30  int traceLevel = 0;
31 }
32 
33 namespace
34 {
35  template <int Level> char const * levelStr();
36  template <> char const * levelStr<LOG_DEBUG>() { return "DBG"; }
37  template <> char const * levelStr<LOG_INFO>() { return "INF"; }
38  template <> char const * levelStr<LOG_ERR>() { return "ERR"; }
39  template <> char const * levelStr<LOG_CRIT>() { return "FTL"; }
40 }
41 
42 #ifdef JEVOIS_USE_SYNC_LOG
43 namespace jevois
44 {
45  // Mutex used to avoid clashing synchronous outputs from multiple threads
46  std::mutex logOutputMutex;
47 }
48 
49 void jevois::logSetEngine(Engine * e)
50 { LFATAL("Cannot set Engine for logs when JeVois has been compiled with -D JEVOIS_USE_SYNC_LOG"); }
51 void jevois::logEnd()
52 { LINFO("Terminating Log service"); }
53 
54 #else // JEVOIS_USE_SYNC_LOG
55 #include <future>
57 #include <jevois/Types/Singleton.H>
58 #include <jevois/Core/Engine.H>
59 
60 namespace
61 {
62  class LogCore : public jevois::Singleton<LogCore>
63  {
64  public:
65  LogCore() : itsBuffer(10000), itsRunning(true)
66 #ifdef JEVOIS_LOG_TO_FILE
67  , itsStream("jevois.log")
68 #endif
69  , itsEngine(nullptr)
70  {
71  itsRunFuture = jevois::async_little(std::bind(&LogCore::run, this));
72  }
73 
74  virtual ~LogCore()
75  {
76  // Tell run() thread to quit:
77  itsRunning = false;
78 
79  // Push a message so we unblock our run() thread in case the buffer was empty:
80  itsBuffer.push("Terminating Log service");
81 
82  // Wait for the run() thread to complete:
83  JEVOIS_WAIT_GET_FUTURE(itsRunFuture);
84  }
85 
86  void run()
87  {
88  while (itsRunning)
89  {
90  std::string msg = itsBuffer.pop();
91 #ifdef JEVOIS_LOG_TO_FILE
92  itsStream << msg << std::endl;
93 #else
94 #ifdef JEVOIS_PLATFORM
95  // When using the serial port debug on platform and screen connected to it, screen gets confused if we do not
96  // send a CR here, since some other messages do send CR (and screen might get confused as to which line end to
97  // use). So send a CR too:
98  std::cerr << msg << '\r' << std::endl;
99 #else
100  std::cerr << msg << std::endl;
101 #endif
102 #endif
103  if (itsEngine) itsEngine->sendSerial(msg, true);
104  }
105  }
106 
107  void abort()
108  {
109  itsRunning = false;
110  // One more message to make sure our run() thread will not be stuck on pop():
111  LINFO("Terminating log facility.");
112  }
113 
115  volatile bool itsRunning;
116  std::future<void> itsRunFuture;
117 #ifdef JEVOIS_LOG_TO_FILE
118  std::ofstream itsStream;
119 #endif
120  jevois::Engine * itsEngine;
121  };
122 }
123 
124 void jevois::logSetEngine(Engine * e) { LogCore::instance().itsEngine = e; }
125 void jevois::logEnd() { LogCore::instance().abort(); jevois::logSetEngine(nullptr); }
126 #endif // JEVOIS_USE_SYNC_LOG
127 
128 // ##############################################################################################################
129 template <int Level>
130 jevois::Log<Level>::Log(char const * fullFileName, char const * functionName, std::string * outstr) :
131  itsOutStr(outstr)
132 {
133  // Strip out the file path and extension from the full file name
134  std::string const fn(fullFileName);
135  size_t const lastSlashPos = fn.rfind('/');
136  size_t const lastDotPos = fn.rfind('.');
137  std::string const partialFileName = fn.substr(lastSlashPos+1, lastDotPos-lastSlashPos-1);
138 
139  // Print out a pretty prefix to the log message
140  itsLogStream << levelStr<Level>() << ' ' << partialFileName << "::" << functionName << ": ";
141 }
142 
143 // ##############################################################################################################
144 #ifdef JEVOIS_USE_SYNC_LOG
145 
146 template <int Level>
148 {
149  std::lock_guard<std::mutex> guard(jevois::logOutputMutex);
150  std::string const msg = itsLogStream.str();
151  std::cerr << msg << std::endl;
152  if (itsOutStr) *itsOutStr = msg;
153 }
154 
155 #else // JEVOIS_USE_SYNC_LOG
156 
157 template <int Level>
159 {
160  std::string const msg = itsLogStream.str();
161  LogCore::instance().itsBuffer.push(msg);
162  if (itsOutStr) *itsOutStr = msg;
163 }
164 #endif // JEVOIS_USE_SYNC_LOG
165 
166 // ##############################################################################################################
167 template <int Level>
169 {
170  itsLogStream << static_cast<int>(out_item);
171  return * this;
172 }
173 
174 // ##############################################################################################################
175 template <int Level>
177 {
178  itsLogStream << static_cast<int>(out_item);
179  return * this;
180 }
181 
182 // ##############################################################################################################
183 // Explicit instantiations:
184 namespace jevois
185 {
186  template class Log<LOG_DEBUG>;
187  template class Log<LOG_INFO>;
188  template class Log<LOG_ERR>;
189  template class Log<LOG_CRIT>;
190 }
191 
192 // ##############################################################################################################
193 void jevois::warnAndRethrowException(std::string const & prefix)
194 {
195  std::string pfx;
196  if (prefix.empty() == false) pfx = prefix + ": ";
197 
198  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
199  try { throw; }
200 
201  catch (std::exception const & e)
202  {
203  LERROR(pfx << "Passing through std::exception: " << e.what());
204  throw;
205  }
206 
207  catch (boost::python::error_already_set & e)
208  {
209  LERROR(pfx << "Received exception from the Python interpreter:");
210  std::string str = jevois::getPythonExceptionString(e);
211  std::vector<std::string> lines = jevois::split(str, "\\n");
212  for (std::string const & li : lines) LERROR(" " << li);
213  throw;
214  }
215 
216  catch (...)
217  {
218  LERROR(pfx << "Passing through unknown exception");
219  throw;
220  }
221 }
222 
223 // ##############################################################################################################
224 std::string jevois::warnAndIgnoreException(std::string const & prefix)
225 {
226  std::string pfx;
227  if (prefix.empty() == false) pfx = prefix + ": ";
228 
229  std::vector<std::string> retvec;
230 
231  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
232  try { throw; }
233 
234  catch (std::exception const & e)
235  {
236  retvec.emplace_back(pfx + "Caught std::exception [" + std::string(e.what()) + ']');
237  }
238 
239  catch (boost::python::error_already_set & e)
240  {
241  retvec.emplace_back(pfx + "Caught exception from the Python interpreter:");
242  std::string str = jevois::getPythonExceptionString(e);
243  std::vector<std::string> lines = jevois::split(str, "\\n");
244  for (std::string const & li : lines) retvec.emplace_back(" " + li);
245  }
246 
247  catch (...)
248  {
249  retvec.emplace_back(pfx + "Caught unknown exception");
250  }
251 
252  // Write out the message:
253  std::string ret;
254  for (std::string & m : retvec) { LERROR(m); ret += m + "\n"; }
255 
256  return ret;
257 }
258 
259 // ##############################################################################################################
260 void jevois::drawErrorImage(std::string const & errmsg, jevois::RawImage & videoerrimg)
261 {
262  if (videoerrimg.valid() == false) { LERROR("Cannot draw in empty image -- IGNORED"); return; }
263 
264  int ypos = 40; int fw = 6, fh = 10; jevois::rawimage::Font font = jevois::rawimage::Font6x10;
265  unsigned int white = jevois::whiteColor(videoerrimg.fmt);
266 
267  // Clear image:
268  videoerrimg.clear();
269 
270  // Draw a sad face:
271  jevois::rawimage::drawDisk(videoerrimg, 10, 8, 4, white);
272  jevois::rawimage::drawDisk(videoerrimg, 25, 8, 4, white);
273  jevois::rawimage::drawLine(videoerrimg, 8, 20, 27, 23, 2, white);
274 
275  // Initial message:
276  jevois::rawimage::writeText(videoerrimg, "Oooops...", 45, 3, white, jevois::rawimage::Font14x26);
277 
278  // Prepare font size for error log:
279  if (videoerrimg.width <= 352 || videoerrimg.height <= 240)
280  { font = jevois::rawimage::Font6x10; fw = 6; fh = 10; }
281  else if (videoerrimg.width <= 640 || videoerrimg.height <= 480)
282  { font = jevois::rawimage::Font7x13; fw = 7; fh = 13; }
283  else { font = jevois::rawimage::Font10x20; fw = 10; fh = 20; }
284 
285  // Write out the message:
286  std::vector<std::string> lines = jevois::split(errmsg, "\\n");
287  for (std::string & m : lines)
288  {
289  // Do some simple linewrap:
290  unsigned int nchar = (videoerrimg.width - 6) / fw; // yes this will be a huge number if width < 6
291  while (m.size() > nchar)
292  {
293  jevois::rawimage::writeText(videoerrimg, m.substr(0, nchar), 3, ypos, white, font);
294  ypos += fh + 2;
295  m = m.substr(nchar, m.npos);
296  }
297  // Print out the last chunk (or the whole thing if it was short):
298  jevois::rawimage::writeText(videoerrimg, m, 3, ypos, white, font);
299  ypos += fh + 2;
300  }
301 }
302 
303 // ##############################################################################################################
304 // ##############################################################################################################
305 jevois::timed_lock_guard::timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func) :
306  itsMutex(mtx)
307 {
308  if (itsMutex.try_lock_for(std::chrono::seconds(5)) == false)
309  {
310  jevois::Log<LOG_CRIT>(file, func) << "Timeout trying to acquire lock";
311  throw std::runtime_error("FATAL DEADLOCK ERROR");
312  }
313 }
314 
315 // ##############################################################################################################
317 { itsMutex.unlock(); }
JEVOIS_WAIT_GET_FUTURE
#define JEVOIS_WAIT_GET_FUTURE(f)
Wait for a future to become ready for 5 seconds, get(), warn and ignore exception,...
Definition: Log.H:312
jevois::whiteColor
unsigned int whiteColor(unsigned int fcc)
Return a value that corresponds to white for the given video format.
Definition: Utils.C:187
jevois::rawimage::drawDisk
void drawDisk(RawImage &img, int x, int y, unsigned int rad, unsigned int col)
Draw a disk in a YUYV image.
Definition: RawImageOps.C:465
jevois::RawImage::clear
void clear()
Clear the pixels to all black.
Definition: RawImage.C:50
Async.H
RawImageOps.H
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:257
jevois::rawimage::Font14x26
@ Font14x26
Definition: RawImageOps.H:162
jevois::warnAndRethrowException
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:193
BoundedBuffer.H
jevois::timed_lock_guard::~timed_lock_guard
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:316
jevois::BoundedBuffer
Thread-safe synchronized producer/consumer queue.
Definition: BoundedBuffer.H:37
jevois::rawimage::Font
Font
Available fonts for writeText()
Definition: RawImageOps.H:152
jevois::drawErrorImage
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:260
jevois::RawImage
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
jevois::Log::~Log
~Log()
Close the Log, outputting the aggregated message.
Definition: Log.C:158
jevois::rawimage::Font6x10
@ Font6x10
Definition: RawImageOps.H:155
jevois::RawImage::valid
bool valid() const
Check whether the image has a valid pixel buffer.
Definition: RawImage.C:46
jevois::timed_lock_guard::timed_lock_guard
timed_lock_guard(std::timed_mutex &mtx, char const *file, char const *func)
Constructor, locks the mutex or throw if it cannot be locked before timeout.
Definition: Log.C:305
jevois::traceLevel
int traceLevel
Current trace level.
Definition: Log.C:30
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:198
jevois::async_little
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async_little(Function &&f, Args &&... args)
Async execution using a thread pool.
jevois::imu::file
Data collection mode RAW means that the latest available raw data is returned each time hence timing may not be very accurate depending on how regularly grate into a FIFO and and accumulates resulting output data into the IMU s internal FIFO buffer at a fixed rate This parameter can only be set in a module s params cfg file
Definition: ICM20948.H:67
jevois::RawImage::width
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
jevois::rawimage::writeText
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
Write some text in an image.
Definition: RawImageOps.C:689
jevois
Definition: Concepts.dox:1
Engine.H
Log.H
jevois::logSetEngine
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:124
jevois::Engine
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:381
jevois::Log
Logger class.
Definition: Log.H:55
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:224
Singleton.H
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
jevois::RawImage::height
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
jevois::Log::operator<<
Log< Level > & operator<<(T const &out_item)
Overloaded stream input operator for any type that has operator<< defined for ostream.
Definition: Log.H:67
jevois::logLevel
int logLevel
Current log level.
Definition: Log.C:29
jevois::Log::Log
Log(char const *fullFileName, char const *functionName, std::string *outstr=nullptr)
Construct a new Log, adding a prefix to the log stream.
Definition: Log.C:130
jevois::Singleton
A generic singleton class to enforce a single instance of an object.
Definition: Singleton.H:91
jevois::RawImage::fmt
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
jevois::getPythonExceptionString
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream.
Definition: PythonException.C:146
PythonException.H
jevois::logEnd
void logEnd()
Terminate log service.
Definition: Log.C:125
jevois::rawimage::Font7x13
@ Font7x13
Definition: RawImageOps.H:156
jevois::rawimage::drawLine
void drawLine(RawImage &img, int x1, int y1, int x2, int y2, unsigned int thick, unsigned int col)
Draw a line in a YUYV image.
Definition: RawImageOps.C:564
jevois::rawimage::Font10x20
@ Font10x20
Definition: RawImageOps.H:159
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:181