JeVois  1.7
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)
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)
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  LINFO("Initalizing Python...");
374  jevois::pythonModuleSetEngine(this);
375 
376  // Instantiate a camera: If device names starts with "/dev/v", assume a hardware camera, otherwise a movie file:
377  std::string const camdev = cameradev::get();
378  if (jevois::stringStartsWith(camdev, "/dev/v"))
379  {
380  LINFO("Starting camera device " << camdev);
381 
382 #ifdef JEVOIS_PLATFORM
383  // Set turbo mode or not:
384  std::ofstream ofs("/sys/module/vfe_v4l2/parameters/turbo");
385  if (ofs.is_open())
386  {
387  if (itsTurbo) ofs << "1" << std::endl; else ofs << "0" << std::endl;
388  ofs.close();
389  }
390  else LERROR("Could not access VFE turbo parameter -- IGNORED");
391 #endif
392 
393  // Now instantiate the camera:
394  itsCamera.reset(new jevois::Camera(camdev, cameranbuf::get()));
395 
396 #ifndef JEVOIS_PLATFORM
397  // No need to confuse people with a non-working camreg param:
398  camreg::set(false);
399  camreg::freeze();
400 #endif
401  }
402  else
403  {
404  LINFO("Using movie input " << camdev << " -- issue a 'streamon' to start processing.");
405  itsCamera.reset(new jevois::MovieInput(camdev, cameranbuf::get()));
406 
407  // No need to confuse people with a non-working camreg param:
408  camreg::set(false);
409  camreg::freeze();
410  }
411 
412  // Instantiate a USB gadget: Note: it will want to access the mappings. If the user-selected video mapping has no usb
413  // out, do not instantiate a gadget:
414  int midx = videomapping::get();
415 
416  // The videomapping parameter is now disabled, users should use the 'setmapping' command once running:
417  videomapping::freeze();
418 
419  if (midx >= int(itsMappings.size()))
420  { LERROR("Mapping index " << midx << " out of range -- USING DEFAULT"); midx = -1; }
421 
422  if (midx < 0) midx = itsDefaultMappingIdx;
423 
424  // Always instantiate a gadget even if not used right now, may be used later:
425  std::string const gd = gadgetdev::get();
426  if (gd == "None")
427  {
428  LINFO("Using no USB video output.");
429  // No USB output and no display, useful for benchmarking only:
430  itsGadget.reset(new jevois::VideoOutputNone());
431  itsManualStreamon = true;
432  }
433  else if (jevois::stringStartsWith(gd, "/dev/"))
434  {
435  LINFO("Loading USB video driver " << gd);
436  // USB gadget driver:
437  itsGadget.reset(new jevois::Gadget(gd, itsCamera.get(), this, gadgetnbuf::get(), multicam::get()));
438  }
439  else if (gd.empty() == false)
440  {
441  LINFO("Saving output video to file " << gd);
442  // Non-empty filename, save to file:
443  itsGadget.reset(new jevois::MovieOutput(gd));
444  itsManualStreamon = true;
445  }
446  else
447  {
448  LINFO("Using display for video output");
449  // Local video display, for use on a host desktop:
450  itsGadget.reset(new jevois::VideoDisplay("jevois", gadgetnbuf::get()));
451  itsManualStreamon = true;
452  }
453 
454  // We are ready to run:
455  itsRunning.store(true);
456 
457  // Set initial format:
458  try { setFormat(midx); } catch (...) { jevois::warnAndIgnoreException(); }
459 
460  // Run init script:
462  runScriptFromFile(JEVOIS_ENGINE_INIT_SCRIPT, nullptr, false);
463 }
464 
465 // ####################################################################################################
467 {
468  JEVOIS_TRACE(1);
469 
470  // Turn off stream if it is on:
471  streamOff();
472 
473  // Tell our run() thread to finish up:
474  itsRunning.store(false);
475 
476 #ifdef JEVOIS_PLATFORM
477  // Tell checkMassStorage() thread to finish up:
478  itsCheckingMassStorage.store(false);
479 #endif
480 
481  // Nuke our module as soon as we can, hopefully soon now that we turned off streaming and running:
482  {
485  itsModule.reset();
486 
487  // Gone, nuke the loader now:
488  itsLoader.reset();
489  }
490 
491  // Because we passed the camera as a raw pointer to the gadget, nuke the gadget first and then the camera:
492  itsGadget.reset();
493  itsCamera.reset();
494 
495 #ifdef JEVOIS_PLATFORM
496  // Will block until the checkMassStorage() thread completes:
497  if (itsCheckMassStorageFut.valid())
498  try { itsCheckMassStorageFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
499 #endif
500 
501  // Things should be quiet now, unhook from the logger (this call is not strictly thread safe):
502  jevois::logSetEngine(nullptr);
503 }
504 
505 // ####################################################################################################
506 #ifdef JEVOIS_PLATFORM
507 void jevois::Engine::checkMassStorage()
508 {
509  itsCheckingMassStorage.store(true);
510 
511  while (itsCheckingMassStorage.load())
512  {
513  // Check from the mass storage gadget (with JeVois extension) whether the virtual USB drive is mounted by the
514  // host. If currently in mass storage mode and the host just ejected the virtual flash drive, resume normal
515  // operation. If not in mass-storage mode and the host mounted it, enter mass-storage mode (may happen if
516  // /boot/usbsdauto was selected):
517  std::ifstream ifs("/sys/devices/platform/sunxi_usb_udc/gadget/lun0/mass_storage_in_use");
518  if (ifs.is_open())
519  {
520  int inuse; ifs >> inuse;
521  if (itsMassStorageMode.load())
522  {
523  if (inuse == 0) stopMassStorageMode();
524  }
525  else
526  {
527  if (inuse) { JEVOIS_TIMED_LOCK(itsMtx); startMassStorageMode(); }
528  }
529  }
530  std::this_thread::sleep_for(std::chrono::milliseconds(500));
531  }
532 }
533 #endif
534 
535 // ####################################################################################################
537 {
538  JEVOIS_TRACE(2);
539 
541  itsCamera->streamOn();
542  itsGadget->streamOn();
543  itsStreaming.store(true);
544 }
545 
546 // ####################################################################################################
548 {
549  JEVOIS_TRACE(2);
550 
551  // First, tell both the camera and gadget to abort streaming, this will make get()/done()/send() throw:
552  itsGadget->abortStream();
553  itsCamera->abortStream();
554 
555  // Stop the main loop, which will flip itsStreaming to false and will make it easier for us to lock itsMtx:
556  LDEBUG("Stopping main loop...");
557  itsStopMainLoop.store(true);
558  while (itsStopMainLoop.load() && itsRunning.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
559  LDEBUG("Main loop stopped.");
560 
561  // Lock up and stream off:
563  itsGadget->streamOff();
564  itsCamera->streamOff();
565 }
566 
567 // ####################################################################################################
569 {
570  JEVOIS_TRACE(2);
571 
572  LDEBUG("Set format number " << idx << " start...");
573 
574  if (idx >= itsMappings.size())
575  LFATAL("Requested mapping index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
576 
578  setFormatInternal(idx);
579  LDEBUG("Set format number " << idx << " done");
580 }
581 
582 // ####################################################################################################
583 void jevois::Engine::setFormatInternal(size_t idx)
584 {
585  // itsMtx should be locked by caller, idx should be valid:
586  JEVOIS_TRACE(2);
587 
588  jevois::VideoMapping const & m = itsMappings[idx];
589  setFormatInternal(m);
590 }
591 
592 // ####################################################################################################
593 void jevois::Engine::setFormatInternal(jevois::VideoMapping const & m)
594 {
595  // itsMtx should be locked by caller, idx should be valid:
596  JEVOIS_TRACE(2);
597 
598  LINFO(m.str());
599 
600 #ifdef JEVOIS_PLATFORM
601  if (itsMassStorageMode.load())
602  LFATAL("Cannot setup video streaming while in mass-storage mode. Eject the USB drive on your host computer first.");
603 #endif
604 
605  // Set the format at the camera and gadget levels:
606  itsCamera->setFormat(m);
607  if (m.ofmt) itsGadget->setFormat(m);
608 
609  // Keep track of our current mapping:
610  itsCurrentMapping = m;
611 
612  // Nuke the processing module, if any, so we can also safely nuke the loader. We always nuke the module instance so we
613  // won't have any issues with latent state even if we re-use the same module but possibly with different input
614  // image resolution, etc:
615  if (itsModule)
616  try { removeComponent(itsModule); itsModule.reset(); } catch (...) { jevois::warnAndIgnoreException(); }
617 
618  // Instantiate the module. If the constructor throws, code is bogus, for example some syntax error in a python module
619  // that is detected at load time. We get the exception's error message for later display into video frames in the main
620  // loop, and mark itsModuleConstructionError:
621  try
622  {
623  // For python modules, we do not need a loader, we just instantiate our special python wrapper module instead:
624  std::string const sopath = m.sopath();
625  if (m.ispython)
626  {
627  // Instantiate the python wrapper:
628  itsLoader.reset();
629  itsModule.reset(new jevois::PythonModule(m));
630  }
631  else
632  {
633  // C++ compiled module. We can re-use the same loader and avoid closing the .so if we will use the same module:
634  if (itsLoader.get() == nullptr || itsLoader->sopath() != sopath)
635  {
636  // Nuke our previous loader and free its resources if needed, then start a new loader:
637  LINFO("Instantiating dynamic loader for " << sopath);
638  itsLoader.reset(new jevois::DynamicLoader(sopath, true));
639  }
640 
641  // Check version match:
642  auto version_major = itsLoader->load<int()>(m.modulename + "_version_major");
643  auto version_minor = itsLoader->load<int()>(m.modulename + "_version_minor");
644  if (version_major() != JEVOIS_VERSION_MAJOR || version_minor() != JEVOIS_VERSION_MINOR)
645  LERROR("Module " << m.modulename << " in file " << sopath << " was build for JeVois v" << version_major() << '.'
646  << version_minor() << ", but running framework is v" << JEVOIS_VERSION_STRING << " -- TRYING ANYWAY");
647 
648  // Instantiate the new module:
649  auto create = itsLoader->load<std::shared_ptr<jevois::Module>(std::string const &)>(m.modulename + "_create");
650  itsModule = create(m.modulename); // Here we just use the class name as instance name
651  }
652 
653  // Add the module as a component to us. Keep this code in sync with Manager::addComponent():
654  {
655  // Lock up so we guarantee the instance name does not get robbed as we add the sub:
656  boost::unique_lock<boost::shared_mutex> ulck(itsSubMtx);
657 
658  // Then add it as a sub-component to us:
659  itsSubComponents.push_back(itsModule);
660  itsModule->itsParent = this;
661  itsModule->setPath(sopath.substr(0, sopath.rfind('/')));
662  }
663 
664  // Bring it to our runstate and load any extra params. NOTE: Keep this in sync with Component::init():
665  if (itsInitialized) itsModule->runPreInit();
666 
667  std::string const paramcfg = itsModule->absolutePath(JEVOIS_MODULE_PARAMS_FILENAME);
668  std::ifstream ifs(paramcfg); if (ifs.is_open()) itsModule->setParamsFromStream(ifs, paramcfg);
669 
670  if (itsInitialized) { itsModule->setInitialized(); itsModule->runPostInit(); }
671 
672  // And finally run any config script:
673  runScriptFromFile(itsModule->absolutePath(JEVOIS_MODULE_SCRIPT_FILENAME), nullptr, false);
674 
675  LINFO("Module [" << m.modulename << "] loaded, initialized, and ready.");
676  itsModuleConstructionError.clear();
677  }
678  catch (...)
679  {
680  itsModuleConstructionError = jevois::warnAndIgnoreException();
681  if (itsModule) try { removeComponent(itsModule); itsModule.reset(); } catch (...) { }
682  LERROR("Module [" << m.modulename << "] startup error and not operational.");
683  }
684 }
685 
686 // ####################################################################################################
688 {
689  JEVOIS_TRACE(2);
690 
691  // Announce that we are ready to the hardware serial port, if any. Do not use sendSerial() here so we always issue
692  // this message irrespectively of the user serial preferences:
693  for (auto & s : itsSerials)
694  if (s->instanceName() == "serial")
695  try { s->writeString("INF READY JEVOIS " JEVOIS_VERSION_STRING); }
696  catch (...) { jevois::warnAndIgnoreException(); }
697 
698  while (itsRunning.load())
699  {
700  bool dosleep = true;
701 
702  if (itsStreaming.load())
703  {
704  // Lock up while we use the module:
706 
707  if (itsModule)
708  {
709  // We have a module ready for action. Call its process function and handle any exceptions:
710  try
711  {
712  if (itsCurrentMapping.ofmt) // Process with USB outputs:
713  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo),
714  jevois::OutputFrame(itsGadget, itsVideoErrors.load() ? &itsVideoErrorImage : nullptr));
715  else // Process with no USB outputs:
716  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo));
717  dosleep = false;
718  }
719  catch (...)
720  {
721  // Report exceptions to video if desired: We have to be extra careful here because the exception might have
722  // been called by the input frame (camera not streaming) or the output frame (gadget not streaming), in
723  // addition to exceptions thrown by the module:
724  if (itsCurrentMapping.ofmt && itsVideoErrors.load())
725  {
726  try
727  {
728  // If the module threw before get() or after send() on the output frame, get a buffer from the gadget:
729  if (itsVideoErrorImage.valid() == false)
730  itsGadget->get(itsVideoErrorImage); // could throw when streamoff
731 
732  // Report module exception to serlog and get it back as a string:
733  std::string errstr = jevois::warnAndIgnoreException();
734 
735  // Draw the error message into our video frame:
736  jevois::drawErrorImage(errstr, itsVideoErrorImage);
737  }
738  catch (...) { jevois::warnAndIgnoreException(); }
739 
740  try
741  {
742  // Send the error image over USB:
743  if (itsVideoErrorImage.valid()) itsGadget->send(itsVideoErrorImage); // could throw if gadget stream off
744  }
745  catch (...) { jevois::warnAndIgnoreException(); }
746 
747  // Invalidate the error image so it is clean for the next frame:
748  itsVideoErrorImage.invalidate();
749  }
750  else
751  {
752  // Report module exception to serlog, and ignore:
754  }
755  }
756  }
757  else
758  {
759  // No module. If we have a module construction error, render it to an image and stream it over USB now (if we
760  // are doing USB and we want to see errors in the video stream):
761  if (itsCurrentMapping.ofmt && itsVideoErrors.load())
762  try
763  {
764  // Get an output image, draw error message, and send to host:
765  itsGadget->get(itsVideoErrorImage);
766  jevois::drawErrorImage(itsModuleConstructionError, itsVideoErrorImage);
767  itsGadget->send(itsVideoErrorImage);
768 
769  // Also get one camera frame to avoid accumulation of stale buffers:
770  (void)jevois::InputFrame(itsCamera, itsTurbo).get();
771  }
772  catch (...) { jevois::warnAndIgnoreException(); }
773  }
774  }
775 
776  if (itsStopMainLoop.load())
777  {
778  itsStreaming.store(false);
779  LDEBUG("-- Main loop stopped --");
780  itsStopMainLoop.store(false);
781  }
782 
783  if (dosleep)
784  {
785  LDEBUG("No processing module loaded or not streaming... Sleeping...");
786  std::this_thread::sleep_for(std::chrono::milliseconds(50));
787  }
788 
789  // Serial input handling. Note that readSome() and writeString() on the serial could throw. The code below is
790  // organized to catch all other exceptions, except for those, which are caught here at the first try level:
791  for (auto & s : itsSerials)
792  {
793  try
794  {
795  std::string str; bool parsed = false; bool success = false;
796 
797  if (s->readSome(str))
798  {
800 
801  // Try to execute this command. If the command is for us (e.g., set a parameter) and is correct,
802  // parseCommand() will return true; if it is for us but buggy, it will throw. If it is not recognized by us,
803  // it will return false and we should try sending it to the Module:
804  try { parsed = parseCommand(str, s); success = parsed; }
805  catch (std::exception const & e) { s->writeString(std::string("ERR ") + e.what()); parsed = true; }
806  catch (...) { s->writeString("ERR Unknown error"); parsed = true; }
807 
808  if (parsed == false)
809  {
810  if (itsModule)
811  {
812  try { itsModule->parseSerial(str, s); success = true; }
813  catch (std::exception const & me) { s->writeString(std::string("ERR ") + me.what()); }
814  catch (...) { s->writeString("ERR Command [" + str + "] not recognized by Engine or Module"); }
815  }
816  else s->writeString("ERR Unsupported command [" + str + "] and no module");
817  }
818 
819  // If success, let user know:
820  if (success) s->writeString("OK");
821  }
822  }
823  catch (...) { jevois::warnAndIgnoreException(); }
824  }
825  }
826 }
827 
828 // ####################################################################################################
829 void jevois::Engine::sendSerial(std::string const & str, bool islog)
830 {
831  // Decide where to send this message based on the value of islog:
832  jevois::engine::SerPort p = islog ? serlog::get() : serout::get();
833 
834  switch (p)
835  {
836  case jevois::engine::SerPort::None:
837  break; // Nothing to send
838 
839  case jevois::engine::SerPort::All:
840  for (auto & s : itsSerials)
841  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
842  break;
843 
844  case jevois::engine::SerPort::Hard:
845  for (auto & s : itsSerials)
846  if (s->type() == jevois::UserInterface::Type::Hard)
847  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
848  break;
849 
850  case jevois::engine::SerPort::USB:
851  for (auto & s : itsSerials)
852  if (s->type() == jevois::UserInterface::Type::USB)
853  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
854  break;
855  }
856 }
857 
858 // ####################################################################################################
860 {
861  return itsCurrentMapping;
862 }
863 
864 // ####################################################################################################
866 {
867  return itsMappings.size();
868 }
869 
870 // ####################################################################################################
872 {
873  if (idx >= itsMappings.size())
874  LFATAL("Index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
875 
876  return itsMappings[idx];
877 }
878 
879 // ####################################################################################################
880 size_t jevois::Engine::getVideoMappingIdx(unsigned int iformat, unsigned int iframe, unsigned int interval) const
881 {
882  // If the iformat or iframe is zero, that's probably a probe for the default mode, so return it:
883  if (iformat == 0 || iframe == 0) return itsDefaultMappingIdx;
884 
885  // If interval is zero, probably a driver trying to probe for our default interval, so return the first available one;
886  // otherwise try to find the desired interval and return the corresponding mapping:
887  if (interval)
888  {
889  float const fps = jevois::VideoMapping::uvcToFps(interval);
890  size_t idx = 0;
891 
892  for (jevois::VideoMapping const & m : itsMappings)
893  if (m.uvcformat == iformat && m.uvcframe == iframe && std::fabs(m.ofps - fps) < 0.1F) return idx;
894  else ++idx;
895 
896  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
897  }
898  else
899  {
900  size_t idx = 0;
901 
902  for (jevois::VideoMapping const & m : itsMappings)
903  if (m.uvcformat == iformat && m.uvcframe == iframe) return idx;
904  else ++idx;
905 
906  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
907  }
908 }
909 
910 // ####################################################################################################
913 
914 // ####################################################################################################
916 { return itsDefaultMappingIdx; }
917 
918 // ####################################################################################################
919 jevois::VideoMapping const &
920 jevois::Engine::findVideoMapping(unsigned int oformat, unsigned int owidth, unsigned int oheight,
921  float oframespersec) const
922 {
923  for (jevois::VideoMapping const & m : itsMappings)
924  if (m.match(oformat, owidth, oheight, oframespersec)) return m;
925 
926  LFATAL("Could not find mapping for output format " << jevois::fccstr(oformat) << ' ' <<
927  owidth << 'x' << oheight << " @ " << oframespersec << " fps");
928 }
929 
930 // ####################################################################################################
931 std::string jevois::Engine::camctrlname(int id, char const * longname) const
932 {
933  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
934  if (camcontrols[i].id == id) return camcontrols[i].shortname;
935 
936  // Darn, this control is not in our list, probably something exotic. Compute a name from the control's long name:
937  return abbreviate(longname);
938 }
939 
940 // ####################################################################################################
941 int jevois::Engine::camctrlid(std::string const & shortname)
942 {
943  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
944  if (shortname.compare(camcontrols[i].shortname) == 0) return camcontrols[i].id;
945 
946  // Not in our list, all right, let's find it then in the camera:
947  struct v4l2_queryctrl qc = { };
948  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
949  {
950  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
951  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
952  // doneids set to keep track of the ones already reported:
953  qc.id = cls | 0x900;
954  while (true)
955  {
956  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; unsigned int old_id = qc.id; bool failed = false;
957  try
958  {
959  itsCamera->queryControl(qc);
960  if (abbreviate(reinterpret_cast<char const *>(qc.name)) == shortname) return qc.id;
961  }
962  catch (...) { failed = true; }
963 
964  // With V4L2_CTRL_FLAG_NEXT_CTRL, the camera kernel driver is supposed to pass down the next valid control if
965  // the requested one is not found, but some drivers do not honor that, so let's move on to the next control
966  // manually if needed:
967  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
968  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
969  else if (failed) break;
970  }
971  }
972 
973  LFATAL("Could not find control [" << shortname << "] in the camera");
974 }
975 
976 // ####################################################################################################
977 std::string jevois::Engine::camCtrlHelp(struct v4l2_queryctrl & qc, std::set<int> & doneids)
978 {
979  // See if we have this control:
980  itsCamera->queryControl(qc);
981  qc.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
982 
983  // If we have already done this control, just return an empty string:
984  if (doneids.find(qc.id) != doneids.end()) return std::string(); else doneids.insert(qc.id);
985 
986  // Control exists, let's also get its current value:
987  struct v4l2_control ctrl = { }; ctrl.id = qc.id;
988  itsCamera->getControl(ctrl);
989 
990  // Print out some description depending on control type:
991  std::ostringstream ss;
992  ss << "- " << camctrlname(qc.id, reinterpret_cast<char const *>(qc.name));
993 
994  switch (qc.type)
995  {
996  case V4L2_CTRL_TYPE_INTEGER:
997  ss << " [int] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
998  << " def=" << qc.default_value << " curr=" << ctrl.value;
999  break;
1000 
1001  //case V4L2_CTRL_TYPE_INTEGER64:
1002  //ss << " [int64] value=" << ctrl.value64;
1003  //break;
1004 
1005  //case V4L2_CTRL_TYPE_STRING:
1006  //ss << " [str] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
1007  // << " curr=" << ctrl.string;
1008  //break;
1009 
1010  case V4L2_CTRL_TYPE_BOOLEAN:
1011  ss << " [bool] default=" << qc.default_value << " curr=" << ctrl.value;
1012  break;
1013 
1014  // This one is not supported by the older kernel on platform:
1015  //case V4L2_CTRL_TYPE_INTEGER_MENU:
1016  //ss << " [intmenu] min=" << qc.minimum << " max=" << qc.maximum
1017  // << " def=" << qc.default_value << " curr=" << ctrl.value;
1018  //break;
1019 
1020  case V4L2_CTRL_TYPE_BUTTON:
1021  ss << " [button]";
1022  break;
1023 
1024  case V4L2_CTRL_TYPE_BITMASK:
1025  ss << " [bitmask] max=" << qc.maximum << " def=" << qc.default_value << " curr=" << ctrl.value;
1026  break;
1027 
1028  case V4L2_CTRL_TYPE_MENU:
1029  {
1030  struct v4l2_querymenu querymenu = { };
1031  querymenu.id = qc.id;
1032  ss << " [menu] values ";
1033  for (querymenu.index = qc.minimum; querymenu.index <= (unsigned int)qc.maximum; ++querymenu.index)
1034  {
1035  try { itsCamera->queryMenu(querymenu); } catch (...) { strcpy((char *)(querymenu.name), "fixme"); }
1036  ss << querymenu.index << ':' << querymenu.name << ' ';
1037  }
1038  ss << "curr=" << ctrl.value;
1039  }
1040  break;
1041 
1042  default:
1043  ss << "[unknown type]";
1044  }
1045 
1046  if (qc.flags & V4L2_CTRL_FLAG_DISABLED) ss << " [DISABLED]";
1047 
1048  return ss.str();
1049 }
1050 
1051 #ifdef JEVOIS_PLATFORM
1052 // ####################################################################################################
1053 void jevois::Engine::startMassStorageMode()
1054 {
1055  // itsMtx must be locked by caller
1056 
1057  if (itsMassStorageMode.load()) { LERROR("Already in mass-storage mode -- IGNORED"); return; }
1058 
1059  // Nuke any module and loader so we have nothing loaded that uses /jevois:
1060  if (itsModule) { removeComponent(itsModule); itsModule.reset(); }
1061  if (itsLoader) itsLoader.reset();
1062 
1063  // Unmount /jevois:
1064  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1065  if (std::system("mount -o remount,ro /jevois")) LERROR("Failed to remount /jevois read-only -- IGNORED");
1066 
1067  // Now set the backing partition in mass-storage gadget:
1068  std::ofstream ofs(JEVOIS_USBSD_SYS);
1069  if (ofs.is_open() == false) LFATAL("Cannot setup mass-storage backing file to " << JEVOIS_USBSD_SYS);
1070  ofs << JEVOIS_USBSD_FILE << std::endl;
1071 
1072  LINFO("Exported JEVOIS partition of microSD to host computer as virtual flash drive.");
1073  itsMassStorageMode.store(true);
1074 }
1075 
1076 // ####################################################################################################
1077 void jevois::Engine::stopMassStorageMode()
1078 {
1079  //itsMassStorageMode.store(false);
1080  LINFO("JeVois virtual USB drive ejected by host -- REBOOTING");
1081  reboot();
1082 }
1083 
1084 // ####################################################################################################
1085 void jevois::Engine::reboot()
1086 {
1087  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1088  itsCheckingMassStorage.store(false);
1089  itsRunning.store(false);
1090 
1091  // Hard reset to avoid possible hanging during module unload, etc:
1092  if ( ! std::ofstream("/proc/sys/kernel/sysrq").put('1')) LERROR("Cannot trigger hard reset -- please unplug me!");
1093  if ( ! std::ofstream("/proc/sysrq-trigger").put('s')) LERROR("Cannot trigger hard reset -- please unplug me!");
1094  if ( ! std::ofstream("/proc/sysrq-trigger").put('b')) LERROR("Cannot trigger hard reset -- please unplug me!");
1095  std::terminate();
1096 }
1097 #endif
1098 
1099 // ####################################################################################################
1100 bool jevois::Engine::parseCommand(std::string const & str, std::shared_ptr<UserInterface> s)
1101 {
1102  // itsMtx should be locked by caller
1103 
1104  std::string errmsg;
1105 
1106  // Note: ModemManager on Ubuntu sends this on startup, kill ModemManager to avoid:
1107  // 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
1108  //
1109  // AT^SQPORT?
1110  // AT
1111  // AT
1112  // AT
1113  // ~
1114  //
1115  // then later on it insists on trying to mess with us, issuing things like AT, AT+CGMI, AT+GMI, AT+CGMM, AT+GMM,
1116  // AT%IPSYS?, ATE0, ATV1, etc etc
1117 
1118  switch (str.length())
1119  {
1120  case 0:
1121  LDEBUG("Ignoring empty string"); return true;
1122  break;
1123 
1124  case 1:
1125  if (str[0] == '~') { LDEBUG("Ignoring modem config command [~]"); return true; }
1126 
1127  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1128  // from the arduino to be printed out to the user:
1129  if (str[0] == '#') { sendSerial(str, true); return true; }
1130  break;
1131 
1132  default: // length is 2 or more:
1133 
1134  // Ignore any command that starts with a '~':
1135  if (str[0] == '~') { LDEBUG("Ignoring modem config command [" << str << ']'); return true; }
1136 
1137  // Ignore any command that starts with "AT":
1138  if (str[0] == 'A' && str[1] == 'T') { LDEBUG("Ignoring AT command [" << str <<']'); return true; }
1139 
1140  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1141  // in the arduino to be printed out to the user:
1142  if (str[0] == '#') { sendSerial(str, true); return true; }
1143 
1144  // Get the first word, i.e., the command:
1145  size_t const idx = str.find(' '); std::string cmd, rem;
1146  if (idx == str.npos) cmd = str; else { cmd = str.substr(0, idx); if (idx < str.length()) rem = str.substr(idx+1); }
1147 
1148  // ----------------------------------------------------------------------------------------------------
1149  if (cmd == "help")
1150  {
1151  // Show all commands, first ours, as supported below:
1152  s->writeString("GENERAL COMMANDS:");
1153  s->writeString("");
1154  s->writeString("help - print this help message");
1155  s->writeString("help2 - print compact help message about current vision module only");
1156  s->writeString("info - show system information including CPU speed, load and temperature");
1157  s->writeString("setpar <name> <value> - set a parameter value");
1158  s->writeString("getpar <name> - get a parameter value(s)");
1159  s->writeString("runscript <filename> - run script commands in specified file");
1160  s->writeString("setcam <ctrl> <val> - set camera control <ctrl> to value <val>");
1161  s->writeString("getcam <ctrl> - get value of camera control <ctrl>");
1162  if (camreg::get())
1163  {
1164  s->writeString("setcamreg <reg> <val> - set raw camera register <reg> to value <val>");
1165  s->writeString("getcamreg <reg> - get value of raw camera register <reg>");
1166  }
1167  s->writeString("listmappings - list all available video mappings");
1168  s->writeString("setmapping <num> - select video mapping <num>, only possible while not streaming");
1169  s->writeString("setmapping2 <CAMmode> <CAMwidth> <CAMheight> <CAMfps> <Vendor> <Module> - set no-USB-out "
1170  "video mapping defined on the fly, while not streaming");
1171  if (itsCurrentMapping.ofmt == 0 || itsManualStreamon)
1172  {
1173  s->writeString("streamon - start camera video streaming");
1174  s->writeString("streamoff - stop camera video streaming");
1175  }
1176  s->writeString("ping - returns 'ALIVE'");
1177  s->writeString("serlog <string> - forward string to the serial port(s) specified by the serlog parameter");
1178  s->writeString("serout <string> - forward string to the serial port(s) specified by the serout parameter");
1179 
1180 #ifdef JEVOIS_PLATFORM
1181  s->writeString("usbsd - export the JEVOIS partition of the microSD card as a virtual USB drive");
1182 #endif
1183  s->writeString("sync - commit any pending data write to microSD");
1184  s->writeString("date [date and time] - get or set the system date and time");
1185 
1186 #ifdef JEVOIS_PLATFORM
1187  s->writeString("restart - restart the JeVois smart camera");
1188 #else
1189  s->writeString("quit - quit this program");
1190 #endif
1191  s->writeString("");
1192 
1193  // Then the module's custom commands, if any:
1194  if (itsModule)
1195  {
1196  std::stringstream css; itsModule->supportedCommands(css);
1197  s->writeString("MODULE-SPECIFIC COMMANDS:");
1198  s->writeString("");
1199  for (std::string line; std::getline(css, line); /* */) s->writeString(line);
1200  s->writeString("");
1201  }
1202 
1203  // Get the help message for our parameters and write it out line by line so the serial fixes the line endings:
1204  std::stringstream pss; constructHelpMessage(pss);
1205  for (std::string line; std::getline(pss, line); /* */) s->writeString(line);
1206 
1207  // Show all camera controls
1208  s->writeString("AVAILABLE CAMERA CONTROLS:");
1209  s->writeString("");
1210 
1211  struct v4l2_queryctrl qc = { }; std::set<int> doneids;
1212  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
1213  {
1214  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
1215  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
1216  // doneids set to keep track of the ones already reported:
1217  qc.id = cls | 0x900; unsigned int old_id;
1218  while (true)
1219  {
1220  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; old_id = qc.id; bool failed = false;
1221  try { std::string hlp = camCtrlHelp(qc, doneids); if (hlp.empty() == false) s->writeString(hlp); }
1222  catch (...) { failed = true; }
1223 
1224  // The camera kernel driver is supposed to pass down the next valid control if the requested one is not
1225  // found, but some drivers do not honor that, so let's move on to the next control manually if needed:
1226  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
1227  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
1228  else if (failed) break;
1229  }
1230  }
1231 
1232  return true;
1233  }
1234 
1235  // ----------------------------------------------------------------------------------------------------
1236  if (cmd == "help2")
1237  {
1238  if (itsModule)
1239  {
1240  // Start with the module's commands:
1241  std::stringstream css; itsModule->supportedCommands(css);
1242  s->writeString("MODULE-SPECIFIC COMMANDS:");
1243  s->writeString("");
1244  for (std::string line; std::getline(css, line); /* */) s->writeString(line);
1245  s->writeString("");
1246 
1247  // Now the parameters for that module (and its subs) only:
1248  s->writeString("MODULE PARAMETERS:");
1249  s->writeString("");
1250 
1251  // Keep this in sync with Manager::constructHelpMessage():
1252  std::unordered_map<std::string, // category:description
1253  std::unordered_map<std::string, // --name (type) default=[def]
1254  std::vector<std::pair<std::string, // component name
1255  std::string // current param value
1256  > > > > helplist;
1257  itsModule->populateHelpMessage("", helplist);
1258 
1259  if (helplist.empty())
1260  s->writeString("None.");
1261  else
1262  {
1263  for (auto const & c : helplist)
1264  {
1265  // Print out the category name and description
1266  s->writeString(c.first);
1267 
1268  // Print out the parameter details
1269  for (auto const & n : c.second)
1270  {
1271  std::vector<std::string> tok = jevois::split(n.first, "[\\r\\n]+");
1272  bool first = true;
1273  for (auto const & t : tok)
1274  {
1275  // Add current value info to the first thing we write (which is name, default, etc)
1276  if (first)
1277  {
1278  auto const & v = n.second;
1279  if (v.size() == 1) // only one component using this param
1280  {
1281  if (v[0].second.empty())
1282  s->writeString(t); // only one comp, and using default val
1283  else
1284  s->writeString(t + " current=[" + v[0].second + ']'); // using non-default val
1285  }
1286  else if (v.size() > 1) // several components using this param with possibly different values
1287  {
1288  std::string sss = t + " current=";
1289  for (auto const & pp : v)
1290  if (pp.second.empty() == false) sss += '[' + pp.first + ':' + pp.second + "] ";
1291  s->writeString(sss);
1292  }
1293  else s->writeString(t); // no non-default value(s) to report
1294 
1295  first = false;
1296  }
1297 
1298  else // just write out the other lines (param description)
1299  s->writeString(t);
1300  }
1301  }
1302  s->writeString("");
1303  }
1304  }
1305  }
1306  else
1307  s->writeString("No module loaded.");
1308 
1309  return true;
1310  }
1311 
1312  // ----------------------------------------------------------------------------------------------------
1313  if (cmd == "info")
1314  {
1315  s->writeString("INFO: JeVois " JEVOIS_VERSION_STRING);
1316  s->writeString("INFO: " + jevois::getSysInfoVersion());
1317  s->writeString("INFO: " + jevois::getSysInfoCPU());
1318  s->writeString("INFO: " + jevois::getSysInfoMem());
1319  if (itsModule) s->writeString("INFO: " + itsCurrentMapping.str());
1320  else s->writeString("INFO: " + jevois::VideoMapping().str());
1321  return true;
1322  }
1323 
1324  // ----------------------------------------------------------------------------------------------------
1325  if (cmd == "setpar")
1326  {
1327  size_t const remidx = rem.find(' ');
1328  if (remidx != rem.npos)
1329  {
1330  std::string const desc = rem.substr(0, remidx);
1331  if (remidx < rem.length())
1332  {
1333  std::string const val = rem.substr(remidx+1);
1334  setParamString(desc, val);
1335  return true;
1336  }
1337  }
1338  errmsg = "Need to provide a parameter name and a parameter value in setpar";
1339  }
1340 
1341  // ----------------------------------------------------------------------------------------------------
1342  if (cmd == "getpar")
1343  {
1344  auto vec = getParamString(rem);
1345  for (auto const & p : vec) s->writeString(p.first + ' ' + p.second);
1346  return true;
1347  }
1348 
1349  // ----------------------------------------------------------------------------------------------------
1350  if (cmd == "setcam")
1351  {
1352  std::istringstream ss(rem); std::string ctrl; int val; ss >> ctrl >> val;
1353  struct v4l2_control c = { }; c.id = camctrlid(ctrl); c.value = val;
1354  itsCamera->setControl(c);
1355  return true;
1356  }
1357 
1358  // ----------------------------------------------------------------------------------------------------
1359  if (cmd == "getcam")
1360  {
1361  struct v4l2_control c = { }; c.id = camctrlid(rem);
1362  itsCamera->getControl(c);
1363  s->writeString(rem + ' ' + std::to_string(c.value));
1364  return true;
1365  }
1366 
1367  // ----------------------------------------------------------------------------------------------------
1368  if (cmd == "setcamreg")
1369  {
1370  if (camreg::get())
1371  {
1372  // Read register and value as strings, then std::stoi to convert to int, supports 0x (and 0 for octal, caution)
1373  std::istringstream ss(rem); std::string reg, val; ss >> reg >> val;
1374  itsCamera->writeRegister(std::stoi(reg, nullptr, 0), std::stoi(val, nullptr, 0));
1375  return true;
1376  }
1377  errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
1378  }
1379 
1380  // ----------------------------------------------------------------------------------------------------
1381  if (cmd == "getcamreg")
1382  {
1383  if (camreg::get())
1384  {
1385  unsigned int val = itsCamera->readRegister(std::stoi(rem, nullptr, 0));
1386  std::ostringstream os; os << std::hex << val;
1387  s->writeString(os.str());
1388  return true;
1389  }
1390  errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
1391  }
1392 
1393  // ----------------------------------------------------------------------------------------------------
1394  if (cmd == "listmappings")
1395  {
1396  s->writeString("AVAILABLE VIDEO MAPPINGS:");
1397  s->writeString("");
1398  for (size_t idx = 0; idx < itsMappings.size(); ++idx)
1399  {
1400  std::string idxstr = std::to_string(idx);
1401  if (idxstr.length() < 5) idxstr = std::string(5 - idxstr.length(), ' ') + idxstr; // pad to 5-char long
1402  s->writeString(idxstr + " - " + itsMappings[idx].str());
1403  }
1404  return true;
1405  }
1406 
1407  // ----------------------------------------------------------------------------------------------------
1408  if (cmd == "setmapping")
1409  {
1410  size_t const idx = std::stoi(rem);
1411  bool was_streaming = itsStreaming.load();
1412 
1413  if (was_streaming)
1414  {
1415  errmsg = "Cannot set mapping while streaming: ";
1416  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1417  else errmsg += "Issue a 'streamoff' command first.";
1418  }
1419  else if (idx >= itsMappings.size())
1420  errmsg = "Requested mapping index " + std::to_string(idx) + " out of range [0 .. " +
1421  std::to_string(itsMappings.size()-1) + ']';
1422  else
1423  {
1424  try
1425  {
1426  setFormatInternal(idx);
1427  return true;
1428  }
1429  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
1430  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
1431  }
1432  }
1433 
1434  // ----------------------------------------------------------------------------------------------------
1435  if (cmd == "setmapping2")
1436  {
1437  bool was_streaming = itsStreaming.load();
1438 
1439  if (was_streaming)
1440  {
1441  errmsg = "Cannot set mapping while streaming: ";
1442  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1443  else errmsg += "Issue a 'streamoff' command first.";
1444  }
1445  else
1446  {
1447  try
1448  {
1449  jevois::VideoMapping m; std::istringstream full("NONE 0 0 0.0 " + rem); full >> m;
1450  setFormatInternal(m);
1451  return true;
1452  }
1453  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
1454  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
1455  }
1456  }
1457 
1458  // ----------------------------------------------------------------------------------------------------
1459  if (itsCurrentMapping.ofmt == 0 || itsManualStreamon)
1460  {
1461  if (cmd == "streamon")
1462  {
1463  // keep this in sync with streamOn(), modulo the fact that here we are already locked:
1464  itsCamera->streamOn();
1465  itsGadget->streamOn();
1466  itsStreaming.store(true);
1467  return true;
1468  }
1469 
1470  if (cmd == "streamoff")
1471  {
1472  // keep this in sync with streamOff(), modulo the fact that here we are already locked:
1473  itsGadget->abortStream();
1474  itsCamera->abortStream();
1475 
1476  itsStreaming.store(false);
1477 
1478  itsGadget->streamOff();
1479  itsCamera->streamOff();
1480  return true;
1481  }
1482  }
1483 
1484  // ----------------------------------------------------------------------------------------------------
1485  if (cmd == "ping")
1486  {
1487  s->writeString("ALIVE");
1488  return true;
1489  }
1490 
1491  // ----------------------------------------------------------------------------------------------------
1492  if (cmd == "serlog")
1493  {
1494  sendSerial(rem, true);
1495  return true;
1496  }
1497 
1498  // ----------------------------------------------------------------------------------------------------
1499  if (cmd == "serout")
1500  {
1501  sendSerial(rem, false);
1502  return true;
1503  }
1504 
1505  // ----------------------------------------------------------------------------------------------------
1506 #ifdef JEVOIS_PLATFORM
1507  if (cmd == "usbsd")
1508  {
1509  if (itsStreaming.load())
1510  {
1511  errmsg = "Cannot export microSD over USB while streaming: ";
1512  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
1513  else errmsg += "Issue a 'streamoff' command first.";
1514  }
1515  else
1516  {
1517  startMassStorageMode();
1518  return true;
1519  }
1520  }
1521 #endif
1522 
1523  // ----------------------------------------------------------------------------------------------------
1524  if (cmd == "sync")
1525  {
1526  if (std::system("sync")) errmsg = "Disk sync failed";
1527  else return true;
1528  }
1529 
1530  // ----------------------------------------------------------------------------------------------------
1531  if (cmd == "date")
1532  {
1533  std::string dat = jevois::system("/bin/date " + rem);
1534  s->writeString("date now " + dat.substr(0, dat.size()-1)); // skip trailing newline
1535  return true;
1536  }
1537 
1538  // ----------------------------------------------------------------------------------------------------
1539  if (cmd == "runscript")
1540  {
1541  std::string fname;
1542  if (itsModule) fname = itsModule->absolutePath(rem); else fname = rem;
1543 
1544  try { runScriptFromFile(fname, s, true); return true; }
1545  catch (...) { errmsg = "Script execution failed"; }
1546  }
1547 
1548 #ifdef JEVOIS_PLATFORM
1549  // ----------------------------------------------------------------------------------------------------
1550  if (cmd == "restart")
1551  {
1552  s->writeString("Restart command received - bye-bye!");
1553 
1554  if (itsStreaming.load())
1555  s->writeString("ERR Video streaming is on - you should quit your video viewer before rebooting");
1556 
1557  // Turn off the SD storage if it is there:
1558  std::ofstream(JEVOIS_USBSD_SYS).put('\n'); // ignore errors
1559  if (std::system("sync")) s->writeString("ERR Disk sync failed -- IGNORED");
1560 
1561  // Hard reboot:
1562  this->reboot();
1563  return true;
1564  }
1565  // ----------------------------------------------------------------------------------------------------
1566 #else
1567  // ----------------------------------------------------------------------------------------------------
1568  if (cmd == "quit")
1569  {
1570  s->writeString("Quit command received - bye-bye!");
1571  itsGadget->abortStream();
1572  itsCamera->abortStream();
1573  itsStreaming.store(false);
1574  itsGadget->streamOff();
1575  itsCamera->streamOff();
1576  itsRunning.store(false);
1577  return true;
1578  }
1579  // ----------------------------------------------------------------------------------------------------
1580 #endif
1581  }
1582 
1583  // If we make it here, we did not parse the command. If we have an error message, that means we had started parsing
1584  // the command but it was buggy, so let's throw. Otherwise, we just return false to indicate that we did not parse
1585  // this command and maybe it is for the Module:
1586  if (errmsg.size()) throw std::runtime_error("Command error [" + str + "]: " + errmsg);
1587  return false;
1588 }
1589 
1590 // ####################################################################################################
1591 void jevois::Engine::runScriptFromFile(std::string const & filename, std::shared_ptr<jevois::UserInterface> ser,
1592  bool throw_no_file)
1593 {
1594  // itsMtx should be locked by caller
1595 
1596  // Try to find the file:
1597  std::ifstream ifs(filename);
1598  if (!ifs) { if (throw_no_file) LFATAL("Could not open file " << filename); else return; }
1599 
1600  // 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
1601  // none is specified there, the first available serial:
1602  if (!ser)
1603  {
1604  if (itsSerials.empty()) LFATAL("Need at least one active serial to run script");
1605  switch (serlog::get())
1606  {
1607  case jevois::engine::SerPort::Hard:
1608  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::Hard) { ser = s; break; }
1609  break;
1610 
1611  case jevois::engine::SerPort::USB:
1612  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::USB) { ser = s; break; }
1613  break;
1614 
1615  default: break;
1616  }
1617  if (!ser) ser = itsSerials.front();
1618  }
1619 
1620  // Ok, run the script, plowing through any errors:
1621  size_t linenum = 1;
1622  for (std::string line; std::getline(ifs, line); /* */)
1623  {
1624  // Skip comments and empty lines:
1625  if (line.length() == 0 || line[0] == '#') continue;
1626 
1627  // Go and parse that line:
1628  try
1629  {
1630  bool parsed = false;
1631  try { parsed = parseCommand(line, ser); }
1632  catch (std::exception const & e)
1633  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + e.what()); }
1634  catch (...)
1635  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": Bogus command ["+line+"] ignored"); }
1636 
1637  if (parsed == false)
1638  {
1639  if (itsModule)
1640  {
1641  try { itsModule->parseSerial(line, ser); }
1642  catch (std::exception const & me)
1643  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + me.what()); }
1644  catch (...)
1645  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum)+": Bogus command ["+line+"] ignored"); }
1646  }
1647  else ser->writeString("ERR Unsupported command [" + line + "] and no module");
1648  }
1649  }
1650  catch (...) { jevois::warnAndIgnoreException(); }
1651 
1652  ++linenum;
1653  }
1654 }
#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:880
size_t numVideoMappings() const
Return the number of video mappings.
Definition: Engine.C:865
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:325
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:329
#define V4L2_CTRL_CLASS_DETECT
Definition: Engine.C:48
void streamOff()
Stop streaming on video from camera, processing, and USB.
Definition: Engine.C:547
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:1100
std::unique_ptr< DynamicLoader > itsLoader
Our module loader.
Definition: Engine.H:332
std::atomic< bool > itsStreaming
True when we are streaming video.
Definition: Engine.H:336
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:536
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:920
#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:687
#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:829
std::string str() const
Convenience function to print out the whole mapping in a human-friendly way.
Definition: VideoMapping.C:87
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:330
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:327
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:871
~Engine()
Destructor.
Definition: Engine.C:466
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:859
std::atomic< bool > itsStopMainLoop
Flag used to stop the main loop.
Definition: Engine.H:337
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:326
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:915
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:1591
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:339
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:911
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:568
#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:335
std::shared_ptr< Module > itsModule
Our current module.
Definition: Engine.H:333
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