JeVois  1.6
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 <mutex>
22 #include <iostream>
23 #include <fstream>
24 #include <stdexcept>
25 
26 namespace jevois
27 {
28  int logLevel = LOG_INFO;
29  int traceLevel = 0;
30 }
31 
32 namespace
33 {
34  template <int Level> char const * levelStr();
35  template <> char const * levelStr<LOG_DEBUG>() { return "DBG"; }
36  template <> char const * levelStr<LOG_INFO>() { return "INF"; }
37  template <> char const * levelStr<LOG_ERR>() { return "ERR"; }
38  template <> char const * levelStr<LOG_CRIT>() { return "FTL"; }
39 }
40 
41 #ifdef JEVOIS_USE_SYNC_LOG
42 namespace jevois
43 {
44  // Mutex used to avoid clashing synchronous outputs from multiple threads
45  std::mutex logOutputMutex;
46 }
47 
49 { LFATAL("Cannot set Engine for logs when JeVois has been compiled with -D JEVOIS_USE_SYNC_LOG"); }
50 
51 #else // JEVOIS_USE_SYNC_LOG
52 #include <future>
54 #include <jevois/Types/Singleton.H>
55 #include <jevois/Core/Engine.H>
56 
57 namespace
58 {
59  class LogCore : public jevois::Singleton<LogCore>
60  {
61  public:
62  LogCore() : itsBuffer(10000), itsRunning(true)
63 #ifdef JEVOIS_LOG_TO_FILE
64  , itsStream("jevois.log")
65 #endif
66  , itsEngine(nullptr)
67  {
68  itsRunFuture = std::async(std::launch::async, &LogCore::run, this);
69  }
70 
71  virtual ~LogCore()
72  {
73  // Tell run() thread to quit:
74  itsRunning = false;
75 
76  // Push a message so we unblock our run() thread in case the buffer was empty:
77  itsBuffer.push("Terminating Log activity");
78 
79  // Wait for the run() thread to complete:
80  if (itsRunFuture.valid()) try { itsRunFuture.get(); } catch (...) { jevois::warnAndIgnoreException(); }
81  }
82 
83  void run()
84  {
85  while (itsRunning)
86  {
87  std::string msg = itsBuffer.pop();
88 #ifdef JEVOIS_LOG_TO_FILE
89  itsStream << msg << std::endl;
90 #else
91 #ifdef JEVOIS_PLATFORM
92  // When using the serial port debug on platform and screen connected to it, screen gets confused if we do not
93  // send a CR here, since some other messages do send CR (and screen might get confused as to which line end to
94  // use). So send a CR too:
95  std::cerr << msg << '\r' << std::endl;
96 #else
97  std::cerr << msg << std::endl;
98 #endif
99 #endif
100  if (itsEngine) itsEngine->sendSerial(msg, true);
101  }
102  }
103 
105  volatile bool itsRunning;
106  std::future<void> itsRunFuture;
107 #ifdef JEVOIS_LOG_TO_FILE
108  std::ofstream itsStream;
109 #endif
110  jevois::Engine * itsEngine;
111  };
112 }
113 
114 void jevois::logSetEngine(Engine * e) { LogCore::instance().itsEngine = e; }
115 
116 #endif // JEVOIS_USE_SYNC_LOG
117 
118 // ##############################################################################################################
119 template <int Level>
120 jevois::Log<Level>::Log(char const * fullFileName, char const * functionName, std::string * outstr) :
121  itsOutStr(outstr)
122 {
123  // Strip out the file path and extension from the full file name
124  std::string const fn(fullFileName);
125  size_t const lastSlashPos = fn.rfind('/');
126  size_t const lastDotPos = fn.rfind('.');
127  std::string const partialFileName = fn.substr(lastSlashPos+1, lastDotPos-lastSlashPos-1);
128 
129  // Print out a pretty prefix to the log message
130  itsLogStream << levelStr<Level>() << ' ' << partialFileName << "::" << functionName << ": ";
131 }
132 
133 // ##############################################################################################################
134 #ifdef JEVOIS_USE_SYNC_LOG
135 
136 template <int Level>
138 {
139  std::lock_guard<std::mutex> guard(jevois::logOutputMutex);
140  std::string const msg = itsLogStream.str();
141  std::cerr << msg << std::endl;
142  if (itsOutStr) *itsOutStr = msg;
143 }
144 
145 #else // JEVOIS_USE_SYNC_LOG
146 
147 template <int Level>
149 {
150  std::string const msg = itsLogStream.str();
151  LogCore::instance().itsBuffer.push(msg);
152  if (itsOutStr) *itsOutStr = msg;
153 }
154 #endif // JEVOIS_USE_SYNC_LOG
155 
156 // ##############################################################################################################
157 template <int Level>
159 {
160  itsLogStream << static_cast<int>(out_item);
161  return * this;
162 }
163 
164 // ##############################################################################################################
165 template <int Level>
167 {
168  itsLogStream << static_cast<int>(out_item);
169  return * this;
170 }
171 
172 // ##############################################################################################################
173 // Explicit instantiations:
174 namespace jevois
175 {
176  template class Log<LOG_DEBUG>;
177  template class Log<LOG_INFO>;
178  template class Log<LOG_ERR>;
179  template class Log<LOG_CRIT>;
180 }
181 
182 // ##############################################################################################################
184 {
185  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
186  try { throw; }
187 
188  catch (std::exception const & e)
189  {
190  LERROR("Passing through std::exception: " << e.what());
191  throw;
192  }
193 
194  catch (boost::python::error_already_set & e)
195  {
196  LERROR("Received exception from the Python interpreter:");
197  std::string str = jevois::getPythonExceptionString(e);
198  std::vector<std::string> lines = jevois::split(str, "\\n");
199  for (std::string const & li : lines) LERROR(" " << li);
200  throw;
201  }
202 
203  catch (...)
204  {
205  LERROR("Passing through unknown exception");
206  throw;
207  }
208 }
209 
210 // ##############################################################################################################
212 {
213  std::vector<std::string> retvec;
214 
215  // great trick to get back the type of an exception caught via a catch(...), just rethrow it and catch again:
216  try { throw; }
217 
218  catch (std::exception const & e)
219  {
220  retvec.push_back("Caught std::exception [" + std::string(e.what()) + ']');
221  }
222 
223  catch (boost::python::error_already_set & e)
224  {
225  retvec.push_back("Caught exception from the Python interpreter:");
226  std::string str = jevois::getPythonExceptionString(e);
227  std::vector<std::string> lines = jevois::split(str, "\\n");
228  for (std::string const & li : lines) retvec.push_back(" " + li);
229  }
230 
231  catch (...)
232  {
233  retvec.push_back("Caught unknown exception");
234  }
235 
236  // Write out the message:
237  std::string ret;
238  for (std::string & m : retvec) { LERROR(m); ret += m + "\n"; }
239 
240  return ret;
241 }
242 
243 // ##############################################################################################################
244 void jevois::drawErrorImage(std::string const & errmsg, jevois::RawImage & videoerrimg)
245 {
246  if (videoerrimg.valid() == false) { LERROR("Cannot draw in empty image -- IGNORED"); return; }
247 
248  int ypos = 40; int fw = 6, fh = 10; jevois::rawimage::Font font = jevois::rawimage::Font6x10;
249  unsigned int white = jevois::whiteColor(videoerrimg.fmt);
250 
251  // Clear image:
252  videoerrimg.clear();
253 
254  // Draw a sad face:
255  jevois::rawimage::drawDisk(videoerrimg, 10, 8, 4, white);
256  jevois::rawimage::drawDisk(videoerrimg, 25, 8, 4, white);
257  jevois::rawimage::drawLine(videoerrimg, 8, 20, 27, 23, 2, white);
258 
259  // Initial message:
260  jevois::rawimage::writeText(videoerrimg, "Oooops...", 45, 3, white, jevois::rawimage::Font14x26);
261 
262  // Prepare font size for error log:
263  if (videoerrimg.width <= 352 || videoerrimg.height <= 240)
264  { font = jevois::rawimage::Font6x10; fw = 6; fh = 10; }
265  else if (videoerrimg.width <= 640 || videoerrimg.height <= 480)
266  { font = jevois::rawimage::Font7x13; fw = 7; fh = 13; }
267  else { font = jevois::rawimage::Font10x20; fw = 10; fh = 20; }
268 
269  // Write out the message:
270  std::vector<std::string> lines = jevois::split(errmsg, "\\n");
271  for (std::string & m : lines)
272  {
273  // Do some simple linewrap:
274  unsigned int nchar = (videoerrimg.width - 6) / fw; // yes this will be a huge number if width < 6
275  while (m.size() > nchar)
276  {
277  jevois::rawimage::writeText(videoerrimg, m.substr(0, nchar), 3, ypos, white, font);
278  ypos += fh + 2;
279  m = m.substr(nchar, m.npos);
280  }
281  // Print out the last chunk (or the whole thing if it was short):
282  jevois::rawimage::writeText(videoerrimg, m, 3, ypos, white, font);
283  ypos += fh + 2;
284  }
285 }
286 
287 // ##############################################################################################################
288 // ##############################################################################################################
289 jevois::timed_lock_guard::timed_lock_guard(std::timed_mutex & mtx, char const * file, char const * func) :
290  itsMutex(mtx)
291 {
292  if (itsMutex.try_lock_for(std::chrono::seconds(1)) == false)
293  {
294  jevois::Log<LOG_CRIT>(file, func) << "Timeout trying to acquire lock";
295  throw std::runtime_error("FATAL DEADLOCK ERROR");
296  }
297 }
298 
299 // ##############################################################################################################
301 { itsMutex.unlock(); }
Thread-safe synchronized producer/consumer queue.
Definition: BoundedBuffer.H:37
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:211
Log< Level > & operator<<(T const &out_item)
Overloaded stream input operator for any type that has operator<< defined for ostream.
Definition: Log.H:67
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:244
unsigned int whiteColor(unsigned int fcc)
Return a value that corresponds to white for the given video format.
Definition: Utils.C:98
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:617
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
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:499
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
bool valid() const
Check whether the image has a valid pixel buffer.
Definition: RawImage.C:46
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:193
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
Font
Available fonts for writeText()
Definition: RawImageOps.H:147
void warnAndRethrowException()
Convenience function to catch an exception, issue some LERROR (depending on type), and rethrow it.
Definition: Log.C:183
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:114
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream...
int logLevel
Current log level.
Definition: Log.C:28
Logger class.
Definition: Log.H:55
~timed_lock_guard()
Destructor, unlocks the mutex.
Definition: Log.C:300
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:212
int traceLevel
Current trace level.
Definition: Log.C:29
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:229
void clear()
Clear the pixels to all black.
Definition: RawImage.C:50
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:289
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:120
A generic singleton class to enforce a single instance of an object.
Definition: Singleton.H:91
void drawDisk(RawImage &img, int x, int y, unsigned int rad, unsigned int col)
Draw a disk in a YUYV image.
Definition: RawImageOps.C:456
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
std::vector< std::string > split(std::string const &input, std::string const &regex="\+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:113
~Log()
Close the Log, outputting the aggregated message.
Definition: Log.C:148