JeVois  1.8
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Engine.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/Core/Engine.H>
19 
20 #include <jevois/Core/Camera.H>
21 #include <jevois/Core/MovieInput.H>
22 
23 #include <jevois/Core/Gadget.H>
27 
28 #include <jevois/Core/Serial.H>
30 
31 #include <jevois/Core/Module.H>
35 
36 #include <jevois/Debug/Log.H>
37 #include <jevois/Util/Utils.H>
38 #include <jevois/Debug/SysInfo.H>
39 
40 #include <cmath> // for fabs
41 #include <fstream>
42 #include <algorithm>
43 #include <cstdlib> // for std::system()
44 #include <cstdio> // for std::remove()
45 
46 // On the older platform kernel, detect class is not defined:
47 #ifndef V4L2_CTRL_CLASS_DETECT
48 #define V4L2_CTRL_CLASS_DETECT 0x00a30000
49 #endif
50 
51 namespace
52 {
53  // Assign a short name to every V4L2 control, for use by getcam and setcam commands
54  struct shortcontrol { int id; char const * const shortname; };
55 
56  // All V4L2 controls
57  // From this: grep V4L2_CID v4l2-controls.h | awk '{ print " { " $2 ", \"\" }," }'
58  // then fill-in the short names.
59  static shortcontrol camcontrols[] = {
60  // In V4L2_CID_BASE class:
61  { V4L2_CID_BRIGHTNESS, "brightness" },
62  { V4L2_CID_CONTRAST, "contrast" },
63  { V4L2_CID_SATURATION, "saturation" },
64  { V4L2_CID_HUE, "hue" },
65  { V4L2_CID_AUDIO_VOLUME, "audiovol" },
66  { V4L2_CID_AUDIO_BALANCE, "audiobal" },
67  { V4L2_CID_AUDIO_BASS, "audiobass" },
68  { V4L2_CID_AUDIO_TREBLE, "audiotreble" },
69  { V4L2_CID_AUDIO_MUTE, "audiomute" },
70  { V4L2_CID_AUDIO_LOUDNESS, "audioloudness" },
71  { V4L2_CID_BLACK_LEVEL, "blacklevel" },
72  { V4L2_CID_AUTO_WHITE_BALANCE, "autowb" },
73  { V4L2_CID_DO_WHITE_BALANCE, "dowb" },
74  { V4L2_CID_RED_BALANCE, "redbal" },
75  { V4L2_CID_BLUE_BALANCE, "bluebal" },
76  { V4L2_CID_GAMMA, "gamma" },
77  { V4L2_CID_WHITENESS, "whiteness" },
78  { V4L2_CID_EXPOSURE, "exposure" },
79  { V4L2_CID_AUTOGAIN, "autogain" },
80  { V4L2_CID_GAIN, "gain" },
81  { V4L2_CID_HFLIP, "hflip" },
82  { V4L2_CID_VFLIP, "vflip" },
83  { V4L2_CID_POWER_LINE_FREQUENCY, "powerfreq" },
84  { V4L2_CID_HUE_AUTO, "autohue" },
85  { V4L2_CID_WHITE_BALANCE_TEMPERATURE, "wbtemp" },
86  { V4L2_CID_SHARPNESS, "sharpness" },
87  { V4L2_CID_BACKLIGHT_COMPENSATION, "backlight" },
88  { V4L2_CID_CHROMA_AGC, "chromaagc" },
89  { V4L2_CID_COLOR_KILLER, "colorkiller" },
90  { V4L2_CID_COLORFX, "colorfx" },
91  { V4L2_CID_AUTOBRIGHTNESS, "autobrightness" },
92  { V4L2_CID_BAND_STOP_FILTER, "bandfilter" },
93  { V4L2_CID_ROTATE, "rotate" },
94  { V4L2_CID_BG_COLOR, "bgcolor" },
95  { V4L2_CID_CHROMA_GAIN, "chromagain" },
96  { V4L2_CID_ILLUMINATORS_1, "illum1" },
97  { V4L2_CID_ILLUMINATORS_2, "illum2" },
98  { V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, "mincapbuf" },
99  { V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, "minoutbuf" },
100  { V4L2_CID_ALPHA_COMPONENT, "alphacompo" },
101  // This one is not defined in our older platform kernel:
102  //{ V4L2_CID_COLORFX_CBCR, "colorfxcbcr" },
103 
104  // In V4L2_CID_CAMERA_CLASS_BASE class
105  { V4L2_CID_EXPOSURE_AUTO, "autoexp" },
106  { V4L2_CID_EXPOSURE_ABSOLUTE, "absexp" },
107  { V4L2_CID_EXPOSURE_AUTO_PRIORITY, "exppri" },
108  { V4L2_CID_PAN_RELATIVE, "panrel" },
109  { V4L2_CID_TILT_RELATIVE, "tiltrel" },
110  { V4L2_CID_PAN_RESET, "panreset" },
111  { V4L2_CID_TILT_RESET, "tiltreset" },
112  { V4L2_CID_PAN_ABSOLUTE, "panabs" },
113  { V4L2_CID_TILT_ABSOLUTE, "tiltabs" },
114  { V4L2_CID_FOCUS_ABSOLUTE, "focusabs" },
115  { V4L2_CID_FOCUS_RELATIVE, "focusrel" },
116  { V4L2_CID_FOCUS_AUTO, "focusauto" },
117  { V4L2_CID_ZOOM_ABSOLUTE, "zoomabs" },
118  { V4L2_CID_ZOOM_RELATIVE, "zoomrel" },
119  { V4L2_CID_ZOOM_CONTINUOUS, "zoomcontinuous" },
120  { V4L2_CID_PRIVACY, "privacy" },
121  { V4L2_CID_IRIS_ABSOLUTE, "irisabs" },
122  { V4L2_CID_IRIS_RELATIVE, "irisrel" },
123 
124  // definition for this one seems to be in the kernel but missing somehow here:
125 #ifndef V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE
126 #define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20)
127 #endif
128  { V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, "presetwb" },
129 
130  // Those are not defined in our older platform kernel:
131  //{ V4L2_CID_AUTO_EXPOSURE_BIAS, "expbias" },
132  //{ V4L2_CID_WIDE_DYNAMIC_RANGE, "wdr" },
133  //{ V4L2_CID_IMAGE_STABILIZATION, "stabilization" },
134  //{ V4L2_CID_ISO_SENSITIVITY, "isosens" },
135  //{ V4L2_CID_ISO_SENSITIVITY_AUTO, "isosensauto" },
136  //{ V4L2_CID_EXPOSURE_METERING, "expmetering" },
137  //{ V4L2_CID_SCENE_MODE, "scene" },
138  //{ V4L2_CID_3A_LOCK, "3alock" },
139  //{ V4L2_CID_AUTO_FOCUS_START, "autofocusstart" },
140  //{ V4L2_CID_AUTO_FOCUS_STOP, "autofocusstop" },
141  //{ V4L2_CID_AUTO_FOCUS_STATUS, "autofocusstatus" },
142  //{ V4L2_CID_AUTO_FOCUS_RANGE, "autofocusrange" },
143  //{ V4L2_CID_PAN_SPEED, "panspeed" },
144  //{ V4L2_CID_TILT_SPEED, "tiltspeed" },
145 
146  // In V4L2_CID_FLASH_CLASS_BASE:
147  { V4L2_CID_FLASH_LED_MODE, "flashled" },
148  { V4L2_CID_FLASH_STROBE_SOURCE, "flashstrobesrc" },
149  { V4L2_CID_FLASH_STROBE, "flashstrobe" },
150  { V4L2_CID_FLASH_STROBE_STOP, "flashstrobestop" },
151  { V4L2_CID_FLASH_STROBE_STATUS, "flashstrovestat" },
152  { V4L2_CID_FLASH_TIMEOUT, "flashtimeout" },
153  { V4L2_CID_FLASH_INTENSITY, "flashintens" },
154  { V4L2_CID_FLASH_TORCH_INTENSITY, "flashtorch" },
155  { V4L2_CID_FLASH_INDICATOR_INTENSITY, "flashindintens" },
156  { V4L2_CID_FLASH_FAULT, "flashfault" },
157  { V4L2_CID_FLASH_CHARGE, "flashcharge" },
158  { V4L2_CID_FLASH_READY, "flashready" },
159 
160  // In V4L2_CID_JPEG_CLASS_BASE:
161  { V4L2_CID_JPEG_CHROMA_SUBSAMPLING, "jpegchroma" },
162  { V4L2_CID_JPEG_RESTART_INTERVAL, "jpegrestartint" },
163  { V4L2_CID_JPEG_COMPRESSION_QUALITY, "jpegcompression" },
164  { V4L2_CID_JPEG_ACTIVE_MARKER, "jpegmarker" },
165 
166  // In V4L2_CID_IMAGE_SOURCE_CLASS_BASE:
167  // Those are not defined in our older platform kernel:
168  //{ V4L2_CID_VBLANK, "vblank" },
169  //{ V4L2_CID_HBLANK, "hblank" },
170  //{ V4L2_CID_ANALOGUE_GAIN, "again" },
171  //{ V4L2_CID_TEST_PATTERN_RED, "testpatred" },
172  //{ V4L2_CID_TEST_PATTERN_GREENR, "testpatgreenr" },
173  //{ V4L2_CID_TEST_PATTERN_BLUE, "testpatblue" },
174  //{ V4L2_CID_TEST_PATTERN_GREENB, "testpatbreenb" },
175 
176  // In V4L2_CID_IMAGE_PROC_CLASS_BASE:
177  //{ V4L2_CID_LINK_FREQ, "linkfreq" },
178  //{ V4L2_CID_PIXEL_RATE, "pixrate" },
179  //{ V4L2_CID_TEST_PATTERN, "testpat" },
180 
181  // In V4L2_CID_DETECT_CLASS_BASE:
182  //{ V4L2_CID_DETECT_MD_MODE, "detectmode" },
183  //{ V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, "detectthresh" },
184  //{ V4L2_CID_DETECT_MD_THRESHOLD_GRID, "detectthreshgrid" },
185  //{ V4L2_CID_DETECT_MD_REGION_GRID, "detectregiongrid" },
186  };
187 
188  // Convert a long name to a short name:
189  std::string abbreviate(std::string const & longname)
190  {
191  std::string name(longname);
192  std::transform(name.begin(), name.end(), name.begin(), ::tolower);
193  name.erase(std::remove_if(name.begin(), name.end(), [](int c) { return !std::isalnum(c); }), name.end());
194  return name;
195  }
196 } // anonymous namespace
197 
198 
199 // ####################################################################################################
200 jevois::Engine::Engine(std::string const & instance) :
201  jevois::Manager(instance), itsMappings(jevois::loadVideoMappings(itsDefaultMappingIdx)),
202  itsRunning(false), itsStreaming(false), itsStopMainLoop(false), itsTurbo(false),
203  itsManualStreamon(false), itsVideoErrors(false), itsFrame(0), itsNumSerialSent(0)
204 {
205  JEVOIS_TRACE(1);
206 
207  LINFO("Loaded " << itsMappings.size() << " vision processing modes.");
208 
209 #ifdef JEVOIS_PLATFORM
210  // Start mass storage thread:
211  itsCheckingMassStorage.store(false); itsMassStorageMode.store(false);
212  itsCheckMassStorageFut = std::async(std::launch::async, &jevois::Engine::checkMassStorage, this);
213  while (itsCheckingMassStorage.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(5));
214 #endif
215 }
216 
217 // ####################################################################################################
218 jevois::Engine::Engine(int argc, char const* argv[], std::string const & instance) :
219  jevois::Manager(argc, argv, instance), itsMappings(jevois::loadVideoMappings(itsDefaultMappingIdx)),
220  itsRunning(false), itsStreaming(false), itsStopMainLoop(false), itsTurbo(false),
221  itsManualStreamon(false), itsVideoErrors(false), itsFrame(0), itsNumSerialSent(0)
222 {
223  JEVOIS_TRACE(1);
224 
225  LINFO("Loaded " << itsMappings.size() << " vision processing modes.");
226 
227 #ifdef JEVOIS_PLATFORM
228  // Start mass storage thread:
229  itsCheckingMassStorage.store(false); itsMassStorageMode.store(false);
230  itsCheckMassStorageFut = std::async(std::launch::async, &jevois::Engine::checkMassStorage, this);
231  while (itsCheckingMassStorage.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(5));
232 #endif
233 }
234 
235 // ####################################################################################################
236 void jevois::Engine::onParamChange(jevois::engine::serialdev const & JEVOIS_UNUSED_PARAM(param),
237  std::string const & newval)
238 {
240 
241  // If we have a serial already, nuke it:
242  for (std::list<std::shared_ptr<UserInterface> >::iterator itr = itsSerials.begin(); itr != itsSerials.end(); ++itr)
243  if ((*itr)->instanceName() == "serial") itr = itsSerials.erase(itr);
244  removeComponent("serial", false);
245 
246  // Open the usb hardware (4-pin connector) serial port, if any:
247  if (newval.empty() == false)
248  try
249  {
250  std::shared_ptr<jevois::UserInterface> s;
251  if (newval == "stdio")
252  s = addComponent<jevois::StdioInterface>("serial");
253  else
254  {
255  s = addComponent<jevois::Serial>("serial", jevois::UserInterface::Type::Hard);
256  s->setParamVal("devname", newval);
257  }
258 
259  itsSerials.push_back(s);
260  LINFO("Using [" << newval << "] hardware (4-pin connector) serial port");
261  }
262  catch (...) { jevois::warnAndIgnoreException(); LERROR("Could not start hardware (4-pin connector) serial port"); }
263  else LINFO("No hardware (4-pin connector) serial port used");
264 }
265 
266 // ####################################################################################################
267 void jevois::Engine::onParamChange(jevois::engine::usbserialdev const & JEVOIS_UNUSED_PARAM(param),
268  std::string const & newval)
269 {
271 
272  // If we have a usbserial already, nuke it:
273  for (std::list<std::shared_ptr<UserInterface> >::iterator itr = itsSerials.begin(); itr != itsSerials.end(); ++itr)
274  if ((*itr)->instanceName() == "usbserial") itr = itsSerials.erase(itr);
275  removeComponent("usbserial", false);
276 
277  // Open the USB serial port, if any:
278  if (newval.empty() == false)
279  try
280  {
281  std::shared_ptr<jevois::UserInterface> s =
282  addComponent<jevois::Serial>("usbserial", jevois::UserInterface::Type::USB);
283  s->setParamVal("devname", newval);
284  itsSerials.push_back(s);
285  LINFO("Using [" << newval << "] USB serial port");
286  }
287  catch (...) { jevois::warnAndIgnoreException(); LERROR("Could not start USB serial port"); }
288  else LINFO("No USB serial port used");
289 }
290 
291 // ####################################################################################################
292 void jevois::Engine::onParamChange(jevois::engine::cpumode const & JEVOIS_UNUSED_PARAM(param),
293  jevois::engine::CPUmode const & newval)
294 {
295  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
296  if (ofs.is_open() == false)
297  {
298 #ifdef JEVOIS_PLATFORM
299  LERROR("Cannot set cpu frequency governor mode -- IGNORED");
300 #endif
301  return;
302  }
303 
304  switch (newval)
305  {
306  case engine::CPUmode::PowerSave: ofs << "powersave" << std::endl; break;
307  case engine::CPUmode::Conservative: ofs << "conservative" << std::endl; break;
308  case engine::CPUmode::OnDemand: ofs << "ondemand" << std::endl; break;
309  case engine::CPUmode::Interactive: ofs << "interactive" << std::endl; break;
310  case engine::CPUmode::Performance: ofs << "performance" << std::endl; break;
311  }
312 }
313 
314 // ####################################################################################################
315 void jevois::Engine::onParamChange(jevois::engine::cpumax const & JEVOIS_UNUSED_PARAM(param),
316  unsigned int const & newval)
317 {
318  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
319  if (ofs.is_open() == false)
320  {
321 #ifdef JEVOIS_PLATFORM
322  LERROR("Cannot set cpu max frequency -- IGNORED");
323 #endif
324  return;
325  }
326 
327  ofs << newval * 1000U << std::endl;
328 }
329 
330 // ####################################################################################################
331 void jevois::Engine::onParamChange(jevois::engine::videoerrors const & JEVOIS_UNUSED_PARAM(param),
332  bool const & newval)
333 {
334  itsVideoErrors.store(newval);
335 }
336 
337 // ####################################################################################################
339 {
340  // Set any initial parameters from global config file:
341  std::string const paramcfg = std::string(JEVOIS_CONFIG_PATH) + '/' + JEVOIS_MODULE_PARAMS_FILENAME;
342  std::ifstream ifs(paramcfg); if (ifs.is_open()) setParamsFromStream(ifs, paramcfg);
343 
344  // Run the Manager version. This parses the command line:
346 }
347 
348 // ####################################################################################################
350 {
351  // First make sure the manager gets to run this:
353 
354  // Freeze the serial port device names, their params, and camera and gadget too:
355  serialdev::freeze();
356  usbserialdev::freeze();
357  for (auto & s : itsSerials) s->freezeAllParams();
358  cameradev::freeze();
359  cameranbuf::freeze();
360  camturbo::freeze();
361  gadgetdev::freeze();
362  gadgetnbuf::freeze();
363  itsTurbo = camturbo::get();
364  multicam::freeze();
365 
366  // Grab the log messages, itsSerials is not going to change anymore now that the serial params are frozen:
367  jevois::logSetEngine(this);
368 
369  // Get python going, we need to do this here to avoid segfaults on platform when instantiating our first python
370  // module. This likely has to do with the fact that the python core is not very thread-safe, and setFormatInternal()
371  // in Engine, which instantiates python modules, will indeed be invoked from a different thread (the one that receives
372  // USB UVC events). Have a look at Python Thread State, Python Gobal Interpreter Lock, etc if interested:
373  if (python::get())
374  {
375  LINFO("Initalizing Python...");
376  jevois::pythonModuleSetEngine(this);
377  }
378 
379  // Instantiate a camera: If device names starts with "/dev/v", assume a hardware camera, otherwise a movie file:
380  std::string const camdev = cameradev::get();
381  if (jevois::stringStartsWith(camdev, "/dev/v"))
382  {
383  LINFO("Starting camera device " << camdev);
384 
385 #ifdef JEVOIS_PLATFORM
386  // Set turbo mode or not:
387  std::ofstream ofs("/sys/module/vfe_v4l2/parameters/turbo");
388  if (ofs.is_open())
389  {
390  if (itsTurbo) ofs << "1" << std::endl; else ofs << "0" << std::endl;
391  ofs.close();
392  }
393  else LERROR("Could not access VFE turbo parameter -- IGNORED");
394 #endif
395 
396  // Now instantiate the camera:
397  itsCamera.reset(new jevois::Camera(camdev, cameranbuf::get()));
398 
399 #ifndef JEVOIS_PLATFORM
400  // No need to confuse people with a non-working camreg param:
401  camreg::set(false);
402  camreg::freeze();
403 #endif
404  }
405  else
406  {
407  LINFO("Using movie input " << camdev << " -- issue a 'streamon' to start processing.");
408  itsCamera.reset(new jevois::MovieInput(camdev, cameranbuf::get()));
409 
410  // No need to confuse people with a non-working camreg param:
411  camreg::set(false);
412  camreg::freeze();
413  }
414 
415  // Instantiate a USB gadget: Note: it will want to access the mappings. If the user-selected video mapping has no usb
416  // out, do not instantiate a gadget:
417  int midx = videomapping::get();
418 
419  // The videomapping parameter is now disabled, users should use the 'setmapping' command once running:
420  videomapping::freeze();
421 
422  if (midx >= int(itsMappings.size()))
423  { LERROR("Mapping index " << midx << " out of range -- USING DEFAULT"); midx = -1; }
424 
425  if (midx < 0) midx = itsDefaultMappingIdx;
426 
427  // Always instantiate a gadget even if not used right now, may be used later:
428  std::string const gd = gadgetdev::get();
429  if (gd == "None")
430  {
431  LINFO("Using no USB video output.");
432  // No USB output and no display, useful for benchmarking only:
433  itsGadget.reset(new jevois::VideoOutputNone());
434  itsManualStreamon = true;
435  }
436  else if (jevois::stringStartsWith(gd, "/dev/"))
437  {
438  LINFO("Loading USB video driver " << gd);
439  // USB gadget driver:
440  itsGadget.reset(new jevois::Gadget(gd, itsCamera.get(), this, gadgetnbuf::get(), multicam::get()));
441  }
442  else if (gd.empty() == false)
443  {
444  LINFO("Saving output video to file " << gd);
445  // Non-empty filename, save to file:
446  itsGadget.reset(new jevois::MovieOutput(gd));
447  itsManualStreamon = true;
448  }
449  else
450  {
451  LINFO("Using display for video output");
452  // Local video display, for use on a host desktop:
453  itsGadget.reset(new jevois::VideoDisplay("jevois", gadgetnbuf::get()));
454  itsManualStreamon = true;
455  }
456 
457  // We are ready to run:
458  itsRunning.store(true);
459 
460  // Set initial format:
461  try { setFormat(midx); } catch (...) { jevois::warnAndIgnoreException(); }
462 
463  // Run init script:
465  runScriptFromFile(JEVOIS_ENGINE_INIT_SCRIPT, nullptr, false);
466 }
467 
468 // ####################################################################################################
470 {
471  JEVOIS_TRACE(1);
472 
473  // Turn off stream if it is on:
474  streamOff();
475 
476  // Tell our run() thread to finish up:
477  itsRunning.store(false);
478 
479 #ifdef JEVOIS_PLATFORM
480  // Tell checkMassStorage() thread to finish up:
481  itsCheckingMassStorage.store(false);
482 #endif
483 
484  // Nuke our module as soon as we can, hopefully soon now that we turned off streaming and running:
485  {
488  itsModule.reset();
489 
490  // Gone, nuke the loader now:
491  itsLoader.reset();
492  }
493 
494  // Because we passed the camera as a raw pointer to the gadget, nuke the gadget first and then the camera:
495  itsGadget.reset();
496  itsCamera.reset();
497 
498 #ifdef JEVOIS_PLATFORM
499  // Will block until the checkMassStorage() thread completes:
500  if (itsCheckMassStorageFut.valid())
501  try { itsCheckMassStorageFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
502 #endif
503 
504  // Things should be quiet now, unhook from the logger (this call is not strictly thread safe):
505  jevois::logSetEngine(nullptr);
506 }
507 
508 // ####################################################################################################
509 #ifdef JEVOIS_PLATFORM
510 void jevois::Engine::checkMassStorage()
511 {
512  itsCheckingMassStorage.store(true);
513 
514  while (itsCheckingMassStorage.load())
515  {
516  // Check from the mass storage gadget (with JeVois extension) whether the virtual USB drive is mounted by the
517  // host. If currently in mass storage mode and the host just ejected the virtual flash drive, resume normal
518  // operation. If not in mass-storage mode and the host mounted it, enter mass-storage mode (may happen if
519  // /boot/usbsdauto was selected):
520  std::ifstream ifs("/sys/devices/platform/sunxi_usb_udc/gadget/lun0/mass_storage_in_use");
521  if (ifs.is_open())
522  {
523  int inuse; ifs >> inuse;
524  if (itsMassStorageMode.load())
525  {
526  if (inuse == 0) stopMassStorageMode();
527  }
528  else
529  {
530  if (inuse) { JEVOIS_TIMED_LOCK(itsMtx); startMassStorageMode(); }
531  }
532  }
533  std::this_thread::sleep_for(std::chrono::milliseconds(500));
534  }
535 }
536 #endif
537 
538 // ####################################################################################################
540 {
541  JEVOIS_TRACE(2);
542 
544  itsCamera->streamOn();
545  itsGadget->streamOn();
546  itsStreaming.store(true);
547 }
548 
549 // ####################################################################################################
551 {
552  JEVOIS_TRACE(2);
553 
554  // First, tell both the camera and gadget to abort streaming, this will make get()/done()/send() throw:
555  itsGadget->abortStream();
556  itsCamera->abortStream();
557 
558  // Stop the main loop, which will flip itsStreaming to false and will make it easier for us to lock itsMtx:
559  LDEBUG("Stopping main loop...");
560  itsStopMainLoop.store(true);
561  while (itsStopMainLoop.load() && itsRunning.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
562  LDEBUG("Main loop stopped.");
563 
564  // Lock up and stream off:
566  itsGadget->streamOff();
567  itsCamera->streamOff();
568 }
569 
570 // ####################################################################################################
572 {
573  JEVOIS_TRACE(2);
574 
575  LDEBUG("Set format number " << idx << " start...");
576 
577  if (idx >= itsMappings.size())
578  LFATAL("Requested mapping index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
579 
581  setFormatInternal(idx);
582  LDEBUG("Set format number " << idx << " done");
583 }
584 
585 // ####################################################################################################
586 void jevois::Engine::setFormatInternal(size_t idx)
587 {
588  // itsMtx should be locked by caller, idx should be valid:
589  JEVOIS_TRACE(2);
590 
591  jevois::VideoMapping const & m = itsMappings[idx];
592  setFormatInternal(m);
593 }
594 
595 // ####################################################################################################
596 void jevois::Engine::setFormatInternal(jevois::VideoMapping const & m)
597 {
598  // itsMtx should be locked by caller, idx should be valid:
599  JEVOIS_TRACE(2);
600 
601  LINFO(m.str());
602 
603 #ifdef JEVOIS_PLATFORM
604  if (itsMassStorageMode.load())
605  LFATAL("Cannot setup video streaming while in mass-storage mode. Eject the USB drive on your host computer first.");
606 #endif
607 
608  // Set the format at the camera and gadget levels:
609  itsCamera->setFormat(m);
610  if (m.ofmt) itsGadget->setFormat(m);
611 
612  // Keep track of our current mapping:
613  itsCurrentMapping = m;
614 
615  // Nuke the processing module, if any, so we can also safely nuke the loader. We always nuke the module instance so we
616  // won't have any issues with latent state even if we re-use the same module but possibly with different input
617  // image resolution, etc:
618  if (itsModule)
619  try { removeComponent(itsModule); itsModule.reset(); } catch (...) { jevois::warnAndIgnoreException(); }
620 
621  // Reset our master frame counter on each module load:
622  itsFrame.store(0);
623 
624  // Instantiate the module. If the constructor throws, code is bogus, for example some syntax error in a python module
625  // that is detected at load time. We get the exception's error message for later display into video frames in the main
626  // loop, and mark itsModuleConstructionError:
627  try
628  {
629  // For python modules, we do not need a loader, we just instantiate our special python wrapper module instead:
630  std::string const sopath = m.sopath();
631  if (m.ispython)
632  {
633  if (python::get() == false) LFATAL("Python disabled, delete BOOT:nopython and restart to enable python");
634 
635  // Instantiate the python wrapper:
636  itsLoader.reset();
637  itsModule.reset(new jevois::PythonModule(m));
638  }
639  else
640  {
641  // C++ compiled module. We can re-use the same loader and avoid closing the .so if we will use the same module:
642  if (itsLoader.get() == nullptr || itsLoader->sopath() != sopath)
643  {
644  // Nuke our previous loader and free its resources if needed, then start a new loader:
645  LINFO("Instantiating dynamic loader for " << sopath);
646  itsLoader.reset(new jevois::DynamicLoader(sopath, true));
647  }
648 
649  // Check version match:
650  auto version_major = itsLoader->load<int()>(m.modulename + "_version_major");
651  auto version_minor = itsLoader->load<int()>(m.modulename + "_version_minor");
652  if (version_major() != JEVOIS_VERSION_MAJOR || version_minor() != JEVOIS_VERSION_MINOR)
653  LERROR("Module " << m.modulename << " in file " << sopath << " was build for JeVois v" << version_major() << '.'
654  << version_minor() << ", but running framework is v" << JEVOIS_VERSION_STRING << " -- TRYING ANYWAY");
655 
656  // Instantiate the new module:
657  auto create = itsLoader->load<std::shared_ptr<jevois::Module>(std::string const &)>(m.modulename + "_create");
658  itsModule = create(m.modulename); // Here we just use the class name as instance name
659  }
660 
661  // Add the module as a component to us. Keep this code in sync with Manager::addComponent():
662  {
663  // Lock up so we guarantee the instance name does not get robbed as we add the sub:
664  boost::unique_lock<boost::shared_mutex> ulck(itsSubMtx);
665 
666  // Then add it as a sub-component to us:
667  itsSubComponents.push_back(itsModule);
668  itsModule->itsParent = this;
669  itsModule->setPath(sopath.substr(0, sopath.rfind('/')));
670  }
671 
672  // Bring it to our runstate and load any extra params. NOTE: Keep this in sync with Component::init():
673  if (itsInitialized) itsModule->runPreInit();
674 
675  std::string const paramcfg = itsModule->absolutePath(JEVOIS_MODULE_PARAMS_FILENAME);
676  std::ifstream ifs(paramcfg); if (ifs.is_open()) itsModule->setParamsFromStream(ifs, paramcfg);
677 
678  if (itsInitialized) { itsModule->setInitialized(); itsModule->runPostInit(); }
679 
680  // And finally run any config script:
681  runScriptFromFile(itsModule->absolutePath(JEVOIS_MODULE_SCRIPT_FILENAME), nullptr, false);
682 
683  LINFO("Module [" << m.modulename << "] loaded, initialized, and ready.");
684  itsModuleConstructionError.clear();
685  }
686  catch (...)
687  {
688  itsModuleConstructionError = jevois::warnAndIgnoreException();
689  if (itsModule) try { removeComponent(itsModule); itsModule.reset(); } catch (...) { }
690  LERROR("Module [" << m.modulename << "] startup error and not operational.");
691  }
692 }
693 
694 // ####################################################################################################
696 {
697  JEVOIS_TRACE(2);
698 
699  // Announce that we are ready to the hardware serial port, if any. Do not use sendSerial() here so we always issue
700  // this message irrespectively of the user serial preferences:
701  for (auto & s : itsSerials)
702  if (s->instanceName() == "serial")
703  try { s->writeString("INF READY JEVOIS " JEVOIS_VERSION_STRING); }
704  catch (...) { jevois::warnAndIgnoreException(); }
705 
706  while (itsRunning.load())
707  {
708  bool dosleep = true;
709 
710  if (itsStreaming.load())
711  {
712  // Lock up while we use the module:
714 
715  if (itsModule)
716  {
717  // We have a module ready for action. Call its process function and handle any exceptions:
718  try
719  {
720  if (itsCurrentMapping.ofmt) // Process with USB outputs:
721  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo),
722  jevois::OutputFrame(itsGadget, itsVideoErrors.load() ? &itsVideoErrorImage : nullptr));
723  else // Process with no USB outputs:
724  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo));
725  dosleep = false;
726  }
727  catch (...)
728  {
729  // Report exceptions to video if desired: We have to be extra careful here because the exception might have
730  // been called by the input frame (camera not streaming) or the output frame (gadget not streaming), in
731  // addition to exceptions thrown by the module:
732  if (itsCurrentMapping.ofmt && itsVideoErrors.load())
733  {
734  try
735  {
736  // If the module threw before get() or after send() on the output frame, get a buffer from the gadget:
737  if (itsVideoErrorImage.valid() == false)
738  itsGadget->get(itsVideoErrorImage); // could throw when streamoff
739 
740  // Report module exception to serlog and get it back as a string:
741  std::string errstr = jevois::warnAndIgnoreException();
742 
743  // Draw the error message into our video frame:
744  jevois::drawErrorImage(errstr, itsVideoErrorImage);
745  }
746  catch (...) { jevois::warnAndIgnoreException(); }
747 
748  try
749  {
750  // Send the error image over USB:
751  if (itsVideoErrorImage.valid()) itsGadget->send(itsVideoErrorImage); // could throw if gadget stream off
752  }
753  catch (...) { jevois::warnAndIgnoreException(); }
754 
755  // Invalidate the error image so it is clean for the next frame:
756  itsVideoErrorImage.invalidate();
757  }
758  else
759  {
760  // Report module exception to serlog, and ignore:
762  }
763  }
764  // Increment our master frame counter
765  ++ itsFrame;
766  itsNumSerialSent.store(0);
767  }
768  else
769  {
770  // No module. If we have a module construction error, render it to an image and stream it over USB now (if we
771  // are doing USB and we want to see errors in the video stream):
772  if (itsCurrentMapping.ofmt && itsVideoErrors.load())
773  try
774  {
775  // Get an output image, draw error message, and send to host:
776  itsGadget->get(itsVideoErrorImage);
777  jevois::drawErrorImage(itsModuleConstructionError, itsVideoErrorImage);
778  itsGadget->send(itsVideoErrorImage);
779 
780  // Also get one camera frame to avoid accumulation of stale buffers:
781  (void)jevois::InputFrame(itsCamera, itsTurbo).get();
782  }
783  catch (...) { jevois::warnAndIgnoreException(); }
784  }
785  }
786 
787  if (itsStopMainLoop.load())
788  {
789  itsStreaming.store(false);
790  LDEBUG("-- Main loop stopped --");
791  itsStopMainLoop.store(false);
792  }
793 
794  if (dosleep)
795  {
796  LDEBUG("No processing module loaded or not streaming... Sleeping...");
797  std::this_thread::sleep_for(std::chrono::milliseconds(50));
798  }
799 
800  // Serial input handling. Note that readSome() and writeString() on the serial could throw. The code below is
801  // organized to catch all other exceptions, except for those, which are caught here at the first try level:
802  for (auto & s : itsSerials)
803  {
804  try
805  {
806  std::string str; bool parsed = false; bool success = false;
807 
808  if (s->readSome(str))
809  {
811 
812  // Try to execute this command. If the command is for us (e.g., set a parameter) and is correct,
813  // parseCommand() will return true; if it is for us but buggy, it will throw. If it is not recognized by us,
814  // it will return false and we should try sending it to the Module:
815  try { parsed = parseCommand(str, s); success = parsed; }
816  catch (std::exception const & e) { s->writeString(std::string("ERR ") + e.what()); parsed = true; }
817  catch (...) { s->writeString("ERR Unknown error"); parsed = true; }
818 
819  if (parsed == false)
820  {
821  if (itsModule)
822  {
823  try { itsModule->parseSerial(str, s); success = true; }
824  catch (std::exception const & me) { s->writeString(std::string("ERR ") + me.what()); }
825  catch (...) { s->writeString("ERR Command [" + str + "] not recognized by Engine or Module"); }
826  }
827  else s->writeString("ERR Unsupported command [" + str + "] and no module");
828  }
829 
830  // If success, let user know:
831  if (success && quietcmd::get() == false) s->writeString("OK");
832  }
833  }
834  catch (...) { jevois::warnAndIgnoreException(); }
835  }
836  }
837 }
838 
839 // ####################################################################################################
840 void jevois::Engine::sendSerial(std::string const & str, bool islog)
841 {
842  // If not a log message, we may want to limit the number of serout messages that a module sends on each frame:
843  size_t slim = serlimit::get();
844  if (islog == false && slim)
845  {
846  if (itsNumSerialSent.load() >= slim) return; // limit reached, message dropped
847  ++itsNumSerialSent; // increment number of messages sent. It is reset in the main loop on each new frame.
848  }
849 
850  // Decide where to send this message based on the value of islog:
851  jevois::engine::SerPort p = islog ? serlog::get() : serout::get();
852 
853  switch (p)
854  {
855  case jevois::engine::SerPort::None:
856  break; // Nothing to send
857 
858  case jevois::engine::SerPort::All:
859  for (auto & s : itsSerials)
860  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
861  break;
862 
863  case jevois::engine::SerPort::Hard:
864  for (auto & s : itsSerials)
865  if (s->type() == jevois::UserInterface::Type::Hard)
866  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
867  break;
868 
869  case jevois::engine::SerPort::USB:
870  for (auto & s : itsSerials)
871  if (s->type() == jevois::UserInterface::Type::USB)
872  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
873  break;
874  }
875 }
876 
877 // ####################################################################################################
879 { return itsFrame; }
880 
881 // ####################################################################################################
883 {
884  return itsCurrentMapping;
885 }
886 
887 // ####################################################################################################
889 {
890  return itsMappings.size();
891 }
892 
893 // ####################################################################################################
895 {
896  if (idx >= itsMappings.size())
897  LFATAL("Index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
898 
899  return itsMappings[idx];
900 }
901 
902 // ####################################################################################################
903 size_t jevois::Engine::getVideoMappingIdx(unsigned int iformat, unsigned int iframe, unsigned int interval) const
904 {
905  // If the iformat or iframe is zero, that's probably a probe for the default mode, so return it:
906  if (iformat == 0 || iframe == 0) return itsDefaultMappingIdx;
907 
908  // If interval is zero, probably a driver trying to probe for our default interval, so return the first available one;
909  // otherwise try to find the desired interval and return the corresponding mapping:
910  if (interval)
911  {
912  float const fps = jevois::VideoMapping::uvcToFps(interval);
913  size_t idx = 0;
914 
915  for (jevois::VideoMapping const & m : itsMappings)
916  if (m.uvcformat == iformat && m.uvcframe == iframe && std::fabs(m.ofps - fps) < 0.1F) return idx;
917  else ++idx;
918 
919  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
920  }
921  else
922  {
923  size_t idx = 0;
924 
925  for (jevois::VideoMapping const & m : itsMappings)
926  if (m.uvcformat == iformat && m.uvcframe == iframe) return idx;
927  else ++idx;
928 
929  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
930  }
931 }
932 
933 // ####################################################################################################
936 
937 // ####################################################################################################
939 { return itsDefaultMappingIdx; }
940 
941 // ####################################################################################################
942 jevois::VideoMapping const &
943 jevois::Engine::findVideoMapping(unsigned int oformat, unsigned int owidth, unsigned int oheight,
944  float oframespersec) const
945 {
946  for (jevois::VideoMapping const & m : itsMappings)
947  if (m.match(oformat, owidth, oheight, oframespersec)) return m;
948 
949  LFATAL("Could not find mapping for output format " << jevois::fccstr(oformat) << ' ' <<
950  owidth << 'x' << oheight << " @ " << oframespersec << " fps");
951 }
952 
953 // ####################################################################################################
954 std::string jevois::Engine::camctrlname(int id, char const * longname) const
955 {
956  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
957  if (camcontrols[i].id == id) return camcontrols[i].shortname;
958 
959  // Darn, this control is not in our list, probably something exotic. Compute a name from the control's long name:
960  return abbreviate(longname);
961 }
962 
963 // ####################################################################################################
964 int jevois::Engine::camctrlid(std::string const & shortname)
965 {
966  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
967  if (shortname.compare(camcontrols[i].shortname) == 0) return camcontrols[i].id;
968 
969  // Not in our list, all right, let's find it then in the camera:
970  struct v4l2_queryctrl qc = { };
971  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
972  {
973  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
974  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
975  // doneids set to keep track of the ones already reported:
976  qc.id = cls | 0x900;
977  while (true)
978  {
979  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; unsigned int old_id = qc.id; bool failed = false;
980  try
981  {
982  itsCamera->queryControl(qc);
983  if (abbreviate(reinterpret_cast<char const *>(qc.name)) == shortname) return qc.id;
984  }
985  catch (...) { failed = true; }
986 
987  // With V4L2_CTRL_FLAG_NEXT_CTRL, the camera kernel driver is supposed to pass down the next valid control if
988  // the requested one is not found, but some drivers do not honor that, so let's move on to the next control
989  // manually if needed:
990  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
991  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
992  else if (failed) break;
993  }
994  }
995 
996  LFATAL("Could not find control [" << shortname << "] in the camera");
997 }
998 
999 // ####################################################################################################
1000 std::string jevois::Engine::camCtrlHelp(struct v4l2_queryctrl & qc, std::set<int> & doneids)
1001 {
1002  // See if we have this control:
1003  itsCamera->queryControl(qc);
1004  qc.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
1005 
1006  // If we have already done this control, just return an empty string:
1007  if (doneids.find(qc.id) != doneids.end()) return std::string(); else doneids.insert(qc.id);
1008 
1009  // Control exists, let's also get its current value:
1010  struct v4l2_control ctrl = { }; ctrl.id = qc.id;
1011  itsCamera->getControl(ctrl);
1012 
1013  // Print out some description depending on control type:
1014  std::ostringstream ss;
1015  ss << "- " << camctrlname(qc.id, reinterpret_cast<char const *>(qc.name));
1016 
1017  switch (qc.type)
1018  {
1019  case V4L2_CTRL_TYPE_INTEGER:
1020  ss << " [int] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
1021  << " def=" << qc.default_value << " curr=" << ctrl.value;
1022  break;
1023 
1024  //case V4L2_CTRL_TYPE_INTEGER64:
1025  //ss << " [int64] value=" << ctrl.value64;
1026  //break;
1027 
1028  //case V4L2_CTRL_TYPE_STRING:
1029  //ss << " [str] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
1030  // << " curr=" << ctrl.string;
1031  //break;
1032 
1033  case V4L2_CTRL_TYPE_BOOLEAN:
1034  ss << " [bool] default=" << qc.default_value << " curr=" << ctrl.value;
1035  break;
1036 
1037  // This one is not supported by the older kernel on platform:
1038  //case V4L2_CTRL_TYPE_INTEGER_MENU:
1039  //ss << " [intmenu] min=" << qc.minimum << " max=" << qc.maximum
1040  // << " def=" << qc.default_value << " curr=" << ctrl.value;
1041  //break;
1042 
1043  case V4L2_CTRL_TYPE_BUTTON:
1044  ss << " [button]";
1045  break;
1046 
1047  case V4L2_CTRL_TYPE_BITMASK:
1048  ss << " [bitmask] max=" << qc.maximum << " def=" << qc.default_value << " curr=" << ctrl.value;
1049  break;
1050 
1051  case V4L2_CTRL_TYPE_MENU:
1052  {
1053  struct v4l2_querymenu querymenu = { };
1054  querymenu.id = qc.id;
1055  ss << " [menu] values ";
1056  for (querymenu.index = qc.minimum; querymenu.index <= (unsigned int)qc.maximum; ++querymenu.index)
1057  {
1058  try { itsCamera->queryMenu(querymenu); } catch (...) { strcpy((char *)(querymenu.name), "fixme"); }
1059  ss << querymenu.index << ':' << querymenu.name << ' ';
1060  }
1061  ss << "curr=" << ctrl.value;
1062  }
1063  break;
1064 
1065  default:
1066  ss << "[unknown type]";
1067  }
1068 
1069  if (qc.flags & V4L2_CTRL_FLAG_DISABLED) ss << " [DISABLED]";
1070 
1071  return ss.str();
1072 }
1073 
1074 #ifdef JEVOIS_PLATFORM
1075 // ####################################################################################################
1076 void jevois::Engine::startMassStorageMode()
1077 {
1078  // itsMtx must be locked by caller
1079 
1080  if (itsMassStorageMode.load()) { LERROR("Already in mass-storage mode -- IGNORED"); return; }
1081 
1082  // Nuke any module and loader so we have nothing loaded that uses /jevois:
1083  if (itsModule) { removeComponent(itsModule); itsModule.reset(); }
1084  if (itsLoader) itsLoader.reset();
1085 
1086  // Unmount /jevois:
1087  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1088  if (std::system("mount -o remount,ro /jevois")) LERROR("Failed to remount /jevois read-only -- IGNORED");
1089 
1090  // Now set the backing partition in mass-storage gadget:
1091  std::ofstream ofs(JEVOIS_USBSD_SYS);
1092  if (ofs.is_open() == false) LFATAL("Cannot setup mass-storage backing file to " << JEVOIS_USBSD_SYS);
1093  ofs << JEVOIS_USBSD_FILE << std::endl;
1094 
1095  LINFO("Exported JEVOIS partition of microSD to host computer as virtual flash drive.");
1096  itsMassStorageMode.store(true);
1097 }
1098 
1099 // ####################################################################################################
1100 void jevois::Engine::stopMassStorageMode()
1101 {
1102  //itsMassStorageMode.store(false);
1103  LINFO("JeVois virtual USB drive ejected by host -- REBOOTING");
1104  reboot();
1105 }
1106 
1107 // ####################################################################################################
1108 void jevois::Engine::reboot()
1109 {
1110  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1111  itsCheckingMassStorage.store(false);
1112  itsRunning.store(false);
1113 
1114  // Hard reset to avoid possible hanging during module unload, etc:
1115  if ( ! std::ofstream("/proc/sys/kernel/sysrq").put('1')) LERROR("Cannot trigger hard reset -- please unplug me!");
1116  if ( ! std::ofstream("/proc/sysrq-trigger").put('s')) LERROR("Cannot trigger hard reset -- please unplug me!");
1117  if ( ! std::ofstream("/proc/sysrq-trigger").put('b')) LERROR("Cannot trigger hard reset -- please unplug me!");
1118  std::terminate();
1119 }
1120 #endif
1121 
1122 // ####################################################################################################
1123 bool jevois::Engine::parseCommand(std::string const & str, std::shared_ptr<UserInterface> s)
1124 {
1125  // itsMtx should be locked by caller
1126 
1127  std::string errmsg;
1128 
1129  // Note: ModemManager on Ubuntu sends this on startup, kill ModemManager to avoid:
1130  // 41 54 5e 53 51 50 4f 52 54 3f 0d 41 54 0d 41 54 0d 41 54 0d 7e 00 78 f0 7e 7e 00 78 f0 7e
1131  //
1132  // AT^SQPORT?
1133  // AT
1134  // AT
1135  // AT
1136  // ~
1137  //
1138  // then later on it insists on trying to mess with us, issuing things like AT, AT+CGMI, AT+GMI, AT+CGMM, AT+GMM,
1139  // AT%IPSYS?, ATE0, ATV1, etc etc
1140 
1141  switch (str.length())
1142  {
1143  case 0:
1144  LDEBUG("Ignoring empty string"); return true;
1145  break;
1146 
1147  case 1:
1148  if (str[0] == '~') { LDEBUG("Ignoring modem config command [~]"); return true; }
1149 
1150  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1151  // from the arduino to be printed out to the user:
1152  if (str[0] == '#') { sendSerial(str, true); return true; }
1153  break;
1154 
1155  default: // length is 2 or more:
1156 
1157  // Ignore any command that starts with a '~':
1158  if (str[0] == '~') { LDEBUG("Ignoring modem config command [" << str << ']'); return true; }
1159 
1160  // Ignore any command that starts with "AT":
1161  if (str[0] == 'A' && str[1] == 'T') { LDEBUG("Ignoring AT command [" << str <<']'); return true; }
1162 
1163  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1164  // in the arduino to be printed out to the user:
1165  if (str[0] == '#') { sendSerial(str, true); return true; }
1166 
1167  // Get the first word, i.e., the command:
1168  size_t const idx = str.find(' '); std::string cmd, rem;
1169  if (idx == str.npos) cmd = str; else { cmd = str.substr(0, idx); if (idx < str.length()) rem = str.substr(idx+1); }
1170 
1171  // ----------------------------------------------------------------------------------------------------
1172  if (cmd == "help")
1173  {
1174  // Show all commands, first ours, as supported below:
1175  s->writeString("GENERAL COMMANDS:");
1176  s->writeString("");
1177  s->writeString("help - print this help message");
1178  s->writeString("help2 - print compact help message about current vision module only");
1179  s->writeString("info - show system information including CPU speed, load and temperature");
1180  s->writeString("setpar <name> <value> - set a parameter value");
1181  s->writeString("getpar <name> - get a parameter value(s)");
1182  s->writeString("runscript <filename> - run script commands in specified file");
1183  s->writeString("setcam <ctrl> <val> - set camera control <ctrl> to value <val>");
1184  s->writeString("getcam <ctrl> - get value of camera control <ctrl>");
1185  if (camreg::get())
1186  {
1187  s->writeString("setcamreg <reg> <val> - set raw camera register <reg> to value <val>");
1188  s->writeString("getcamreg <reg> - get value of raw camera register <reg>");
1189  }
1190  s->writeString("listmappings - list all available video mappings");
1191  s->writeString("setmapping <num> - select video mapping <num>, only possible while not streaming");
1192  s->writeString("setmapping2 <CAMmode> <CAMwidth> <CAMheight> <CAMfps> <Vendor> <Module> - set no-USB-out "
1193  "video mapping defined on the fly, while not streaming");
1194  if (itsCurrentMapping.ofmt == 0 || itsManualStreamon)
1195  {
1196  s->writeString("streamon - start camera video streaming");
1197  s->writeString("streamoff - stop camera video streaming");
1198  }
1199  s->writeString("ping - returns 'ALIVE'");
1200  s->writeString("serlog <string> - forward string to the serial port(s) specified by the serlog parameter");
1201  s->writeString("serout <string> - forward string to the serial port(s) specified by the serout parameter");
1202 
1203 #ifdef JEVOIS_PLATFORM
1204  s->writeString("usbsd - export the JEVOIS partition of the microSD card as a virtual USB drive");
1205 #endif
1206  s->writeString("sync - commit any pending data write to microSD");
1207  s->writeString("date [date and time] - get or set the system date and time");
1208 
1209 #ifdef JEVOIS_PLATFORM
1210  s->writeString("restart - restart the JeVois smart camera");
1211 #else
1212  s->writeString("quit - quit this program");
1213 #endif
1214  s->writeString("");
1215 
1216  // Then the module's custom commands, if any:
1217  if (itsModule)
1218  {
1219  std::stringstream css; itsModule->supportedCommands(css);
1220  s->writeString("MODULE-SPECIFIC COMMANDS:");
1221  s->writeString("");
1222  for (std::string line; std::getline(css, line); /* */) s->writeString(line);
1223  s->writeString("");
1224  }
1225 
1226  // Get the help message for our parameters and write it out line by line so the serial fixes the line endings:
1227  std::stringstream pss; constructHelpMessage(pss);
1228  for (std::string line; std::getline(pss, line); /* */) s->writeString(line);
1229 
1230  // Show all camera controls
1231  s->writeString("AVAILABLE CAMERA CONTROLS:");
1232  s->writeString("");
1233 
1234  struct v4l2_queryctrl qc = { }; std::set<int> doneids;
1235  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
1236  {
1237  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
1238  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
1239  // doneids set to keep track of the ones already reported:
1240  qc.id = cls | 0x900; unsigned int old_id;
1241  while (true)
1242  {
1243  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; old_id = qc.id; bool failed = false;
1244  try { std::string hlp = camCtrlHelp(qc, doneids); if (hlp.empty() == false) s->writeString(hlp); }
1245  catch (...) { failed = true; }
1246 
1247  // The camera kernel driver is supposed to pass down the next valid control if the requested one is not
1248  // found, but some drivers do not honor that, so let's move on to the next control manually if needed:
1249  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
1250  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
1251  else if (failed) break;
1252  }
1253  }
1254 
1255  return true;
1256  }
1257 
1258  // ----------------------------------------------------------------------------------------------------
1259  if (cmd == "help2")
1260  {
1261  if (itsModule)
1262  {
1263  // Start with the module's commands:
1264  std::stringstream css; itsModule->supportedCommands(css);
1265  s->writeString("MODULE-SPECIFIC COMMANDS:");
1266  s->writeString("");
1267  for (std::string line; std::getline(css, line); /* */) s->writeString(line);
1268  s->writeString("");
1269 
1270  // Now the parameters for that module (and its subs) only:
1271  s->writeString("MODULE PARAMETERS:");
1272  s->writeString("");
1273 
1274  // Keep this in sync with Manager::constructHelpMessage():
1275  std::unordered_map<std::string, // category:description
1276  std::unordered_map<std::string, // --name (type) default=[def]
1277  std::vector<std::pair<std::string, // component name
1278  std::string // current param value
1279  > > > > helplist;
1280  itsModule->populateHelpMessage("", helplist);
1281 
1282  if (helplist.empty())
1283  s->writeString("None.");
1284  else
1285  {
1286  for (auto const & c : helplist)
1287  {
1288  // Print out the category name and description
1289  s->writeString(c.first);
1290 
1291  // Print out the parameter details
1292  for (auto const & n : c.second)
1293  {
1294  std::vector<std::string> tok = jevois::split(n.first, "[\\r\\n]+");
1295  bool first = true;
1296  for (auto const & t : tok)
1297  {
1298  // Add current value info to the first thing we write (which is name, default, etc)
1299  if (first)
1300  {
1301  auto const & v = n.second;
1302  if (v.size() == 1) // only one component using this param
1303  {
1304  if (v[0].second.empty())
1305  s->writeString(t); // only one comp, and using default val
1306  else
1307  s->writeString(t + " current=[" + v[0].second + ']'); // using non-default val
1308  }
1309  else if (v.size() > 1) // several components using this param with possibly different values
1310  {
1311  std::string sss = t + " current=";
1312  for (auto const & pp : v)
1313  if (pp.second.empty() == false) sss += '[' + pp.first + ':' + pp.second + "] ";
1314  s->writeString(sss);
1315  }
1316  else s->writeString(t); // no non-default value(s) to report
1317 
1318  first = false;
1319  }
1320 
1321  else // just write out the other lines (param description)
1322  s->writeString(t);
1323  }
1324  }
1325  s->writeString("");
1326  }
1327  }
1328  }
1329  else
1330  s->writeString("No module loaded.");
1331 
1332  return true;
1333  }
1334 
1335  // ----------------------------------------------------------------------------------------------------
1336  if (cmd == "info")
1337  {
1338  s->writeString("INFO: JeVois " JEVOIS_VERSION_STRING);
1339  s->writeString("INFO: " + jevois::getSysInfoVersion());
1340  s->writeString("INFO: " + jevois::getSysInfoCPU());
1341  s->writeString("INFO: " + jevois::getSysInfoMem());
1342  if (itsModule) s->writeString("INFO: " + itsCurrentMapping.str());
1343  else s->writeString("INFO: " + jevois::VideoMapping().str());
1344  return true;
1345  }
1346 
1347  // ----------------------------------------------------------------------------------------------------
1348  if (cmd == "setpar")
1349  {
1350  size_t const remidx = rem.find(' ');
1351  if (remidx != rem.npos)
1352  {
1353  std::string const desc = rem.substr(0, remidx);
1354  if (remidx < rem.length())
1355  {
1356  std::string const val = rem.substr(remidx+1);
1357  setParamString(desc, val);
1358  return true;
1359  }
1360  }
1361  errmsg = "Need to provide a parameter name and a parameter value in setpar";
1362  }
1363 
1364  // ----------------------------------------------------------------------------------------------------
1365  if (cmd == "getpar")
1366  {
1367  auto vec = getParamString(rem);
1368  for (auto const & p : vec) s->writeString(p.first + ' ' + p.second);
1369  return true;
1370  }
1371 
1372  // ----------------------------------------------------------------------------------------------------
1373  if (cmd == "setcam")
1374  {
1375  std::istringstream ss(rem); std::string ctrl; int val; ss >> ctrl >> val;
1376  struct v4l2_control c = { }; c.id = camctrlid(ctrl); c.value = val;
1377  itsCamera->setControl(c);
1378  return true;
1379  }
1380 
1381  // ----------------------------------------------------------------------------------------------------
1382  if (cmd == "getcam")
1383  {
1384  struct v4l2_control c = { }; c.id = camctrlid(rem);
1385  itsCamera->getControl(c);
1386  s->writeString(rem + ' ' + std::to_string(c.value));
1387  return true;
1388  }
1389 
1390  // ----------------------------------------------------------------------------------------------------
1391  if (cmd == "setcamreg")
1392  {
1393  if (camreg::get())
1394  {
1395  // Read register and value as strings, then std::stoi to convert to int, supports 0x (and 0 for octal, caution)
1396  std::istringstream ss(rem); std::string reg, val; ss >> reg >> val;
1397  itsCamera->writeRegister(std::stoi(reg, nullptr, 0), std::stoi(val, nullptr, 0));
1398  return true;
1399  }
1400  errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
1401  }
1402 
1403  // ----------------------------------------------------------------------------------------------------
1404  if (cmd == "getcamreg")
1405  {
1406  if (camreg::get())
1407  {
1408  unsigned int val = itsCamera->readRegister(std::stoi(rem, nullptr, 0));
1409  std::ostringstream os; os << std::hex << val;
1410  s->writeString(os.str());
1411  return true;
1412  }
1413  errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
1414  }
1415 
1416  // ----------------------------------------------------------------------------------------------------
1417  if (cmd == "listmappings")
1418  {
1419  s->writeString("AVAILABLE VIDEO MAPPINGS:");
1420  s->writeString("");
1421  for (size_t idx = 0; idx < itsMappings.size(); ++idx)
1422  {
1423  std::string idxstr = std::to_string(idx);
1424  if (idxstr.length() < 5) idxstr = std::string(5 - idxstr.length(), ' ') + idxstr; // pad to 5-char long
1425  s->writeString(idxstr + " - " + itsMappings[idx].str());
1426  }
1427  return true;
1428  }
1429 
1430  // ----------------------------------------------------------------------------------------------------
1431  if (cmd == "setmapping")
1432  {
1433  size_t const idx = std::stoi(rem);
1434  bool was_streaming = itsStreaming.load();
1435 
1436  if (was_streaming)
1437  {
1438  errmsg = "Cannot set mapping while streaming: ";
1439  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1440  else errmsg += "Issue a 'streamoff' command first.";
1441  }
1442  else if (idx >= itsMappings.size())
1443  errmsg = "Requested mapping index " + std::to_string(idx) + " out of range [0 .. " +
1444  std::to_string(itsMappings.size()-1) + ']';
1445  else
1446  {
1447  try
1448  {
1449  setFormatInternal(idx);
1450  return true;
1451  }
1452  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
1453  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
1454  }
1455  }
1456 
1457  // ----------------------------------------------------------------------------------------------------
1458  if (cmd == "setmapping2")
1459  {
1460  bool was_streaming = itsStreaming.load();
1461 
1462  if (was_streaming)
1463  {
1464  errmsg = "Cannot set mapping while streaming: ";
1465  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1466  else errmsg += "Issue a 'streamoff' command first.";
1467  }
1468  else
1469  {
1470  try
1471  {
1472  jevois::VideoMapping m; std::istringstream full("NONE 0 0 0.0 " + rem); full >> m;
1473  setFormatInternal(m);
1474  return true;
1475  }
1476  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
1477  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
1478  }
1479  }
1480 
1481  // ----------------------------------------------------------------------------------------------------
1482  if (itsCurrentMapping.ofmt == 0 || itsManualStreamon)
1483  {
1484  if (cmd == "streamon")
1485  {
1486  // keep this in sync with streamOn(), modulo the fact that here we are already locked:
1487  itsCamera->streamOn();
1488  itsGadget->streamOn();
1489  itsStreaming.store(true);
1490  return true;
1491  }
1492 
1493  if (cmd == "streamoff")
1494  {
1495  // keep this in sync with streamOff(), modulo the fact that here we are already locked:
1496  itsGadget->abortStream();
1497  itsCamera->abortStream();
1498 
1499  itsStreaming.store(false);
1500 
1501  itsGadget->streamOff();
1502  itsCamera->streamOff();
1503  return true;
1504  }
1505  }
1506 
1507  // ----------------------------------------------------------------------------------------------------
1508  if (cmd == "ping")
1509  {
1510  s->writeString("ALIVE");
1511  return true;
1512  }
1513 
1514  // ----------------------------------------------------------------------------------------------------
1515  if (cmd == "serlog")
1516  {
1517  sendSerial(rem, true);
1518  return true;
1519  }
1520 
1521  // ----------------------------------------------------------------------------------------------------
1522  if (cmd == "serout")
1523  {
1524  sendSerial(rem, false);
1525  return true;
1526  }
1527 
1528  // ----------------------------------------------------------------------------------------------------
1529 #ifdef JEVOIS_PLATFORM
1530  if (cmd == "usbsd")
1531  {
1532  if (itsStreaming.load())
1533  {
1534  errmsg = "Cannot export microSD over USB while streaming: ";
1535  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1536  else errmsg += "Issue a 'streamoff' command first.";
1537  }
1538  else
1539  {
1540  startMassStorageMode();
1541  return true;
1542  }
1543  }
1544 #endif
1545 
1546  // ----------------------------------------------------------------------------------------------------
1547  if (cmd == "sync")
1548  {
1549  if (std::system("sync")) errmsg = "Disk sync failed";
1550  else return true;
1551  }
1552 
1553  // ----------------------------------------------------------------------------------------------------
1554  if (cmd == "date")
1555  {
1556  std::string dat = jevois::system("/bin/date " + rem);
1557  s->writeString("date now " + dat.substr(0, dat.size()-1)); // skip trailing newline
1558  return true;
1559  }
1560 
1561  // ----------------------------------------------------------------------------------------------------
1562  if (cmd == "runscript")
1563  {
1564  std::string fname;
1565  if (itsModule) fname = itsModule->absolutePath(rem); else fname = rem;
1566 
1567  try { runScriptFromFile(fname, s, true); return true; }
1568  catch (...) { errmsg = "Script execution failed"; }
1569  }
1570 
1571 #ifdef JEVOIS_PLATFORM
1572  // ----------------------------------------------------------------------------------------------------
1573  if (cmd == "restart")
1574  {
1575  s->writeString("Restart command received - bye-bye!");
1576 
1577  if (itsStreaming.load())
1578  s->writeString("ERR Video streaming is on - you should quit your video viewer before rebooting");
1579 
1580  // Turn off the SD storage if it is there:
1581  std::ofstream(JEVOIS_USBSD_SYS).put('\n'); // ignore errors
1582  if (std::system("sync")) s->writeString("ERR Disk sync failed -- IGNORED");
1583 
1584  // Hard reboot:
1585  this->reboot();
1586  return true;
1587  }
1588  // ----------------------------------------------------------------------------------------------------
1589 #else
1590  // ----------------------------------------------------------------------------------------------------
1591  if (cmd == "quit")
1592  {
1593  s->writeString("Quit command received - bye-bye!");
1594  itsGadget->abortStream();
1595  itsCamera->abortStream();
1596  itsStreaming.store(false);
1597  itsGadget->streamOff();
1598  itsCamera->streamOff();
1599  itsRunning.store(false);
1600  return true;
1601  }
1602  // ----------------------------------------------------------------------------------------------------
1603 #endif
1604  }
1605 
1606  // If we make it here, we did not parse the command. If we have an error message, that means we had started parsing
1607  // the command but it was buggy, so let's throw. Otherwise, we just return false to indicate that we did not parse
1608  // this command and maybe it is for the Module:
1609  if (errmsg.size()) throw std::runtime_error("Command error [" + str + "]: " + errmsg);
1610  return false;
1611 }
1612 
1613 // ####################################################################################################
1614 void jevois::Engine::runScriptFromFile(std::string const & filename, std::shared_ptr<jevois::UserInterface> ser,
1615  bool throw_no_file)
1616 {
1617  // itsMtx should be locked by caller
1618 
1619  // Try to find the file:
1620  std::ifstream ifs(filename);
1621  if (!ifs) { if (throw_no_file) LFATAL("Could not open file " << filename); else return; }
1622 
1623  // We need to identify a serial to send any errors to, if none was given to us. Let's use the one in serlog, or, if
1624  // none is specified there, the first available serial:
1625  if (!ser)
1626  {
1627  if (itsSerials.empty()) LFATAL("Need at least one active serial to run script");
1628  switch (serlog::get())
1629  {
1630  case jevois::engine::SerPort::Hard:
1631  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::Hard) { ser = s; break; }
1632  break;
1633 
1634  case jevois::engine::SerPort::USB:
1635  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::USB) { ser = s; break; }
1636  break;
1637 
1638  default: break;
1639  }
1640  if (!ser) ser = itsSerials.front();
1641  }
1642 
1643  // Ok, run the script, plowing through any errors:
1644  size_t linenum = 1;
1645  for (std::string line; std::getline(ifs, line); /* */)
1646  {
1647  // Skip comments and empty lines:
1648  if (line.length() == 0 || line[0] == '#') continue;
1649 
1650  // Go and parse that line:
1651  try
1652  {
1653  bool parsed = false;
1654  try { parsed = parseCommand(line, ser); }
1655  catch (std::exception const & e)
1656  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + e.what()); }
1657  catch (...)
1658  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": Bogus command ["+line+"] ignored"); }
1659 
1660  if (parsed == false)
1661  {
1662  if (itsModule)
1663  {
1664  try { itsModule->parseSerial(line, ser); }
1665  catch (std::exception const & me)
1666  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + me.what()); }
1667  catch (...)
1668  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum)+": Bogus command ["+line+"] ignored"); }
1669  }
1670  else ser->writeString("ERR Unsupported command [" + line + "] and no module");
1671  }
1672  }
1673  catch (...) { jevois::warnAndIgnoreException(); }
1674 
1675  ++linenum;
1676  }
1677 }
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level. ...
Definition: Log.H:155
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:211
float ofps
output frame rate in frames/sec
Definition: VideoMapping.H:46
size_t getVideoMappingIdx(unsigned int iformat, unsigned int iframe, unsigned int interval) const
Get the video mapping index for a given UVC iformat, iframe and interval.
Definition: Engine.C:903
size_t numVideoMappings() const
Return the number of video mappings.
Definition: Engine.C:888
unsigned int uvcformat
USB-UVC format number (1-based)
Definition: VideoMapping.H:53
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:244
Class to open shared object (.so) files and load functions contained in them.
Definition: DynamicLoader.H:69
Exception-safe wrapper around a raw camera input frame.
Definition: Module.H:56
size_t itsDefaultMappingIdx
Index of default mapping.
Definition: Engine.H:356
std::vector< std::string > setParamString(std::string const &paramdescriptor, std::string const &val)
Set a parameter value, by string.
Definition: Component.C:346
void postInit() override
Override of Manager::postInit()
Definition: Engine.C:349
std::shared_ptr< VideoInput > itsCamera
Our camera.
Definition: Engine.H:360
#define V4L2_CTRL_CLASS_DETECT
Definition: Engine.C:48
void streamOff()
Stop streaming on video from camera, processing, and USB.
Definition: Engine.C:550
void preInit() override
Calls parseCommandLine()
Definition: Manager.C:49
void preInit() override
Override of Manager::preInit()
Definition: Engine.C:338
bool parseCommand(std::string const &str, std::shared_ptr< UserInterface > s)
Parse a user command received over serial port.
Definition: Engine.C:1123
std::unique_ptr< DynamicLoader > itsLoader
Our module loader.
Definition: Engine.H:363
std::atomic< bool > itsStreaming
True when we are streaming video.
Definition: Engine.H:367
Manager of a hierarchy of Component objects.
Definition: Manager.H:73
void streamOn()
Start streaming on video from camera, processing, and USB.
Definition: Engine.C:539
unsigned int ofmt
output pixel format, or 0 for no output over USB
Definition: VideoMapping.H:43
VideoMapping const & findVideoMapping(unsigned int oformat, unsigned int owidth, unsigned int oheight, float oframespersec) const
Find the VideoMapping that has the given output specs, or throw if not found.
Definition: Engine.C:943
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition: Log.H:299
#define success()
void invalidate()
Invalidate the image by zero&#39;ing out the pointer to pixel buffer and the dims and format...
Definition: RawImage.C:42
RawImage const & get(bool casync=false) const
Get the next captured camera image.
Definition: Module.C:51
void mainLoop()
Main loop: grab, process, send over USB. Should be called by main application thread.
Definition: Engine.C:695
#define JEVOIS_TRACE(level)
Trace object.
Definition: Log.H:267
std::string modulename
Name of the Module that will process this mapping.
Definition: VideoMapping.H:58
Video output to a movie file, using OpenCV video encoding.
Definition: MovieOutput.H:33
std::string getSysInfoVersion()
Get O.S. version info.
Definition: SysInfo.C:72
void sendSerial(std::string const &str, bool islog=false)
Send a string to all serial ports.
Definition: Engine.C:840
std::string str() const
Convenience function to print out the whole mapping in a human-friendly way.
Definition: VideoMapping.C:87
size_t frameNum() const
Get frame number.
Definition: Engine.C:878
bool valid() const
Check whether the image has a valid pixel buffer.
Definition: RawImage.C:46
void removeComponent(std::shared_ptr< Comp > &component)
Remove a top-level Component from the Manager, by shared_ptr.
Definition: ManagerImpl.H:100
std::shared_ptr< VideoOutput > itsGadget
Our gadget.
Definition: Engine.H:361
void constructHelpMessage(std::ostream &out) const
Constructs a help message from all parameters in the model, and outputs it to &#39;out&#39;.
Definition: Manager.C:82
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:193
std::string sopath() const
Return the full absolute path and file name of the module&#39;s .so or .py file.
Definition: VideoMapping.C:30
VideoMapping itsCurrentMapping
Current video mapping, may not match any in itsMappings if setmapping2 used.
Definition: Engine.H:358
static float uvcToFps(unsigned int interval)
Convert from USB/UVC interval to fps.
Definition: VideoMapping.C:45
VideoMapping const & getVideoMapping(size_t idx) const
Allow access to our video mappings which are parsed from file at construction.
Definition: Engine.C:894
~Engine()
Destructor.
Definition: Engine.C:469
std::string getSysInfoCPU()
Get CPU info: frequency, thermal, load.
Definition: SysInfo.C:38
JeVois camera driver class - grabs frames from a Video4Linux camera sensor.
Definition: Camera.H:64
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:114
Simple struct to hold video mapping definitions for the processing Engine.
Definition: VideoMapping.H:41
VideoMapping const & getCurrentVideoMapping() const
Get the current video mapping.
Definition: Engine.C:882
std::atomic< bool > itsStopMainLoop
Flag used to stop the main loop.
Definition: Engine.H:368
void onParamChange(engine::cameradev const &param, std::string const &newval)
Parameter callback.
bool stringStartsWith(std::string const &str, std::string const &prefix)
Return true if str starts with prefix (including if both strings are equal)
Definition: Utils.C:137
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:37
unsigned int uvcframe
USB UVC frame number (1-based)
Definition: VideoMapping.H:54
std::vector< VideoMapping > const itsMappings
All our mappings from videomappings.cfg.
Definition: Engine.H:357
JeVois gadget driver - exposes a uvcvideo interface to host computer connected over USB...
Definition: Gadget.H:65
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:212
Movie input, can be used as a replacement for Camera to debug algorithms using a fixed video sequence...
Definition: MovieInput.H:35
size_t getDefaultVideoMappingIdx() const
Allow access to the default video mapping index.
Definition: Engine.C:938
std::istream & setParamsFromStream(std::istream &is, std::string const &absfile)
Set some parameters from an open stream.
Definition: Component.C:449
Video output to local screen.
Definition: VideoDisplay.H:34
Wrapper module to allow users to develop new modules written in Python.
Definition: PythonModule.H:143
std::vector< std::pair< std::string, std::string > > getParamString(std::string const &paramdescriptor) const
Get a parameter value, by string.
Definition: Component.C:373
void runScriptFromFile(std::string const &filename, std::shared_ptr< UserInterface > ser, bool throw_no_file)
Run a script from file.
Definition: Engine.C:1614
std::string to_string(T const &val)
Convert from type to string.
Definition: UtilsImpl.H:51
bool match(unsigned int oformat, unsigned int owidth, unsigned int oheight, float oframespersec) const
Return true if this VideoMapping&#39;s output format is a match to the given output parameters.
Definition: VideoMapping.C:302
std::timed_mutex itsMtx
Mutex to protect our internals.
Definition: Engine.H:370
Exception-safe wrapper around a raw image to be sent over USB.
Definition: Module.H:144
VideoMapping const & getDefaultVideoMapping() const
Allow access to the default video mapping.
Definition: Engine.C:934
bool ispython
True if the module is written in Python; affects behavior of sopath() only.
Definition: VideoMapping.H:60
std::string system(std::string const &cmd)
Execute a command and grab stdout output to a string.
Definition: Utils.C:211
void postInit() override
Checks for the –help flag.
Definition: Manager.C:58
std::string getSysInfoMem()
Get memory info.
Definition: SysInfo.C:64
void setFormat(size_t idx)
Callback for when the user selects a new output video format.
Definition: Engine.C:571
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:176
std::atomic< bool > itsRunning
True when we are running.
Definition: Engine.H:366
std::shared_ptr< Module > itsModule
Our current module.
Definition: Engine.H:364
No-op VideoOutput derivative for when there is no video output.
Engine(std::string const &instance)
Constructor.
Definition: Engine.C:200
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
#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE