JeVois  1.20
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 template <>
145 jevois::Log<LOG_ALERT>::Log(char const * JEVOIS_UNUSED_PARAM(fullFileName),
146  char const * JEVOIS_UNUSED_PARAM(functionName), std::string * outstr) :
147  itsOutStr(outstr)
148 {
149  // No prefix added here, will just throw the user message
150 }
151 
152 // ##############################################################################################################
153 #ifdef JEVOIS_USE_SYNC_LOG
154 
155 template <int Level>
157 {
158  std::lock_guard<std::mutex> guard(jevois::logOutputMutex);
159  std::string const msg = itsLogStream.str();
160  std::cerr << msg << std::endl;
161  if (itsOutStr) *itsOutStr = msg;
162 }
163 
164 #else // JEVOIS_USE_SYNC_LOG
165 
166 template <int Level>
168 {
169  std::string const msg = itsLogStream.str();
170  LogCore::instance().itsBuffer.push(msg);
171  if (itsOutStr) *itsOutStr = msg;
172 }
173 #endif // JEVOIS_USE_SYNC_LOG
174 
175 // ##############################################################################################################
176 template <int Level>
178 {
179  itsLogStream << static_cast<int>(out_item);
180  return * this;
181 }
182 
183 // ##############################################################################################################
184 template <int Level>
186 {
187  itsLogStream << static_cast<int>(out_item);
188  return * this;
189 }
190 
191 // ##############################################################################################################
192 // Explicit instantiations:
193 namespace jevois
194 {
195  template class Log<LOG_DEBUG>;
196  template class Log<LOG_INFO>;
197  template class Log<LOG_ERR>;
198  template class Log<LOG_CRIT>;
199  template class Log<LOG_ALERT>;
200 }
201 
202 // ##############################################################################################################
203 void jevois::warnAndRethrowException(std::string const & prefix)
204 {
205  std::string pfx;
206  if (prefix.empty() == false) pfx = prefix + ": ";
207 
208  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
209  try { throw; }
210 
211  catch (std::exception const & e)
212  {
213  LERROR(pfx << "Passing through std::exception:");
214  std::vector<std::string> lines = jevois::split(e.what(), "\\n");
215  for (std::string const & li : lines) LERROR(li);
216  throw;
217  }
218 
219  catch (boost::python::error_already_set & e)
220  {
221  LERROR(pfx << "Received exception from the Python interpreter:");
222  std::string str = jevois::getPythonExceptionString(e);
223  std::vector<std::string> lines = jevois::split(str, "\\n");
224  for (std::string const & li : lines) LERROR(li);
225  throw;
226  }
227 
228  catch (...)
229  {
230  LERROR(pfx << "Passing through unknown exception");
231  throw;
232  }
233 }
234 
235 // ##############################################################################################################
236 std::string jevois::warnAndIgnoreException(std::string const & prefix)
237 {
238  std::string pfx;
239  if (prefix.empty() == false) pfx = prefix + ": ";
240 
241  std::vector<std::string> retvec;
242 
243  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
244  try { throw; }
245 
246  catch (std::exception const & e)
247  {
248  retvec.emplace_back(pfx + "Caught std::exception:");
249  std::vector<std::string> lines = jevois::split(e.what(), "\\n");
250  for (std::string const & li : lines) retvec.emplace_back(li);
251  }
252 
253  catch (boost::python::error_already_set & e)
254  {
255  retvec.emplace_back(pfx + "Caught exception from the Python interpreter:");
256  std::string str = jevois::getPythonExceptionString(e);
257  std::vector<std::string> lines = jevois::split(str, "\\n");
258  for (std::string const & li : lines) retvec.emplace_back(li);
259  }
260 
261  catch (...)
262  {
263  retvec.emplace_back(pfx + "Caught unknown exception");
264  }
265 
266  // Write out the message:
267  std::string ret;
268  for (std::string & m : retvec) { LERROR(m); ret += m + "\n"; }
269 
270  return ret;
271 }
272 
273 // ##############################################################################################################
274 void jevois::warnAndRethrowParamCallbackException[[noreturn]](std::string const & descriptor,
275  std::string const & strval)
276 {
277  LERROR("Parameter " << descriptor << ": Provided value [" << strval << "] rejected by callback:");
278 
279  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
280  try { throw; }
281 
282  catch (std::exception const & e)
283  {
284  throw std::runtime_error(e.what());
285  }
286 
287  catch (boost::python::error_already_set & e)
288  {
289  LERROR("Python exception:");
290  throw std::runtime_error(jevois::getPythonExceptionString(e));
291  }
292 
293  catch (...)
294  {
295  throw std::runtime_error("Caught unknown exception");
296  }
297 }
298 
299 // ##############################################################################################################
300 void jevois::drawErrorImage(std::string const & errmsg, jevois::RawImage & videoerrimg)
301 {
302  if (videoerrimg.valid() == false) { LERROR("Cannot draw in empty image -- IGNORED"); return; }
303 
304  int ypos = 40; int fw = 6, fh = 10; jevois::rawimage::Font font = jevois::rawimage::Font6x10;
305  unsigned int white = jevois::whiteColor(videoerrimg.fmt);
306 
307  // Clear image:
308  videoerrimg.clear();
309 
310  // Draw a sad face:
311  jevois::rawimage::drawDisk(videoerrimg, 10, 8, 4, white);
312  jevois::rawimage::drawDisk(videoerrimg, 25, 8, 4, white);
313  jevois::rawimage::drawLine(videoerrimg, 8, 20, 27, 23, 2, white);
314 
315  // Initial message:
316  jevois::rawimage::writeText(videoerrimg, "Oooops...", 45, 3, white, jevois::rawimage::Font14x26);
317 
318  // Prepare font size for error log:
319  if (videoerrimg.width <= 352 || videoerrimg.height <= 240)
320  { font = jevois::rawimage::Font6x10; fw = 6; fh = 10; }
321  else if (videoerrimg.width <= 640 || videoerrimg.height <= 480)
322  { font = jevois::rawimage::Font7x13; fw = 7; fh = 13; }
323  else { font = jevois::rawimage::Font10x20; fw = 10; fh = 20; }
324 
325  // Write out the message:
326  std::vector<std::string> lines = jevois::split(errmsg, "\\n");
327  for (std::string & m : lines)
328  {
329  // Do some simple linewrap:
330  unsigned int nchar = (videoerrimg.width - 6) / fw; // yes this will be a huge number if width < 6
331  while (m.size() > nchar)
332  {
333  jevois::rawimage::writeText(videoerrimg, m.substr(0, nchar), 3, ypos, white, font);
334  ypos += fh + 2;
335  m = m.substr(nchar, m.npos);
336  }
337  // Print out the last chunk (or the whole thing if it was short):
338  jevois::rawimage::writeText(videoerrimg, m, 3, ypos, white, font);
339  ypos += fh + 2;
340  }
341 }
342 
343 // ##############################################################################################################
344 // ##############################################################################################################
345 jevois::timed_lock_guard::timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func) :
346  itsMutex(mtx)
347 {
348  if (itsMutex.try_lock_for(std::chrono::seconds(5)) == false)
349  {
350  jevois::Log<LOG_CRIT>(file, func) << "Timeout trying to acquire lock";
351  throw std::runtime_error("FATAL DEADLOCK ERROR");
352  }
353 }
354 
355 // ##############################################################################################################
357 { 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:336
jevois::whiteColor
unsigned int whiteColor(unsigned int fcc)
Return a value that corresponds to white for the given video format.
Definition: Utils.C:197
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:270
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:203
BoundedBuffer.H
jevois::timed_lock_guard::~timed_lock_guard
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:356
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:300
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:167
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:345
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:211
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::warnAndRethrowParamCallbackException
void warnAndRethrowParamCallbackException(std::string const &descriptor, std::string const &strval)
Convenience function for parameter callback exceptions.
Definition: Log.C:274
jevois::Engine
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:387
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:236
Singleton.H
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
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::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::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:194