JeVois  1.18
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/IMU.H>
24 #include <jevois/Core/IMUspi.H>
25 #include <jevois/Core/IMUi2c.H>
26 
27 #include <jevois/Core/Gadget.H>
33 #include <jevois/GPU/GUIhelper.H>
34 #include <jevois/GPU/GUIconsole.H>
35 
36 #include <jevois/Core/Serial.H>
38 
39 #include <jevois/Core/Module.H>
43 
44 #include <jevois/Debug/Log.H>
45 #include <jevois/Util/Utils.H>
46 #include <jevois/Util/Async.H>
47 #include <jevois/Debug/SysInfo.H>
48 
49 #include <cmath> // for fabs
50 #include <fstream>
51 #include <algorithm>
52 #include <cstdlib> // for std::system()
53 #include <cstdio> // for std::remove()
54 #include <regex>
55 
56 #ifdef JEVOIS_PRO
57 #include <imgui_internal.h>
58 #endif
59 
60 // On the older JeVois-A33 platform kernel, detect class is not defined:
61 #ifndef V4L2_CTRL_CLASS_DETECT
62 #define V4L2_CTRL_CLASS_DETECT 0x00a30000
63 #endif
64 
65 namespace
66 {
67  // Assign a short name to every V4L2 control, for use by getcam and setcam commands
68  struct shortcontrol { int id; char const * const shortname; };
69 
70  // All V4L2 controls
71  // From this: grep V4L2_CID v4l2-controls.h | awk '{ print " { " $2 ", \"\" }," }'
72  // then fill-in the short names.
73  static shortcontrol camcontrols[] = {
74  // In V4L2_CID_BASE class:
75  { V4L2_CID_BRIGHTNESS, "brightness" },
76  { V4L2_CID_CONTRAST, "contrast" },
77  { V4L2_CID_SATURATION, "saturation" },
78  { V4L2_CID_HUE, "hue" },
79  { V4L2_CID_AUDIO_VOLUME, "audiovol" },
80  { V4L2_CID_AUDIO_BALANCE, "audiobal" },
81  { V4L2_CID_AUDIO_BASS, "audiobass" },
82  { V4L2_CID_AUDIO_TREBLE, "audiotreble" },
83  { V4L2_CID_AUDIO_MUTE, "audiomute" },
84  { V4L2_CID_AUDIO_LOUDNESS, "audioloudness" },
85  { V4L2_CID_BLACK_LEVEL, "blacklevel" },
86  { V4L2_CID_AUTO_WHITE_BALANCE, "autowb" },
87  { V4L2_CID_DO_WHITE_BALANCE, "dowb" },
88  { V4L2_CID_RED_BALANCE, "redbal" },
89  { V4L2_CID_BLUE_BALANCE, "bluebal" },
90  { V4L2_CID_GAMMA, "gamma" },
91  { V4L2_CID_WHITENESS, "whiteness" },
92  { V4L2_CID_EXPOSURE, "exposure" },
93  { V4L2_CID_AUTOGAIN, "autogain" },
94  { V4L2_CID_GAIN, "gain" },
95  { V4L2_CID_HFLIP, "hflip" },
96  { V4L2_CID_VFLIP, "vflip" },
97  { V4L2_CID_POWER_LINE_FREQUENCY, "powerfreq" },
98  { V4L2_CID_HUE_AUTO, "autohue" },
99  { V4L2_CID_WHITE_BALANCE_TEMPERATURE, "wbtemp" },
100  { V4L2_CID_SHARPNESS, "sharpness" },
101  { V4L2_CID_BACKLIGHT_COMPENSATION, "backlight" },
102  { V4L2_CID_CHROMA_AGC, "chromaagc" },
103  { V4L2_CID_COLOR_KILLER, "colorkiller" },
104  { V4L2_CID_COLORFX, "colorfx" },
105  { V4L2_CID_AUTOBRIGHTNESS, "autobrightness" },
106  { V4L2_CID_BAND_STOP_FILTER, "bandfilter" },
107  { V4L2_CID_ROTATE, "rotate" },
108  { V4L2_CID_BG_COLOR, "bgcolor" },
109  { V4L2_CID_CHROMA_GAIN, "chromagain" },
110  { V4L2_CID_ILLUMINATORS_1, "illum1" },
111  { V4L2_CID_ILLUMINATORS_2, "illum2" },
112  { V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, "mincapbuf" },
113  { V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, "minoutbuf" },
114  { V4L2_CID_ALPHA_COMPONENT, "alphacompo" },
115  // This one is not defined in our older platform kernel:
116  //{ V4L2_CID_COLORFX_CBCR, "colorfxcbcr" },
117 
118  // In V4L2_CID_CAMERA_CLASS_BASE class
119  { V4L2_CID_EXPOSURE_AUTO, "autoexp" },
120  { V4L2_CID_EXPOSURE_ABSOLUTE, "absexp" },
121  { V4L2_CID_EXPOSURE_AUTO_PRIORITY, "exppri" },
122  { V4L2_CID_PAN_RELATIVE, "panrel" },
123  { V4L2_CID_TILT_RELATIVE, "tiltrel" },
124  { V4L2_CID_PAN_RESET, "panreset" },
125  { V4L2_CID_TILT_RESET, "tiltreset" },
126  { V4L2_CID_PAN_ABSOLUTE, "panabs" },
127  { V4L2_CID_TILT_ABSOLUTE, "tiltabs" },
128  { V4L2_CID_FOCUS_ABSOLUTE, "focusabs" },
129  { V4L2_CID_FOCUS_RELATIVE, "focusrel" },
130  { V4L2_CID_FOCUS_AUTO, "focusauto" },
131  { V4L2_CID_ZOOM_ABSOLUTE, "zoomabs" },
132  { V4L2_CID_ZOOM_RELATIVE, "zoomrel" },
133  { V4L2_CID_ZOOM_CONTINUOUS, "zoomcontinuous" },
134  { V4L2_CID_PRIVACY, "privacy" },
135  { V4L2_CID_IRIS_ABSOLUTE, "irisabs" },
136  { V4L2_CID_IRIS_RELATIVE, "irisrel" },
137 
138  // definition for this one seems to be in the kernel but missing somehow here:
139 #ifndef V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE
140 #define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20)
141 #endif
142  { V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, "presetwb" },
143 
144  // Those are not defined in our older platform kernel:
145  //{ V4L2_CID_AUTO_EXPOSURE_BIAS, "expbias" },
146  //{ V4L2_CID_WIDE_DYNAMIC_RANGE, "wdr" },
147  //{ V4L2_CID_IMAGE_STABILIZATION, "stabilization" },
148  //{ V4L2_CID_ISO_SENSITIVITY, "isosens" },
149  //{ V4L2_CID_ISO_SENSITIVITY_AUTO, "isosensauto" },
150  //{ V4L2_CID_EXPOSURE_METERING, "expmetering" },
151  //{ V4L2_CID_SCENE_MODE, "scene" },
152  //{ V4L2_CID_3A_LOCK, "3alock" },
153  //{ V4L2_CID_AUTO_FOCUS_START, "autofocusstart" },
154  //{ V4L2_CID_AUTO_FOCUS_STOP, "autofocusstop" },
155  //{ V4L2_CID_AUTO_FOCUS_STATUS, "autofocusstatus" },
156  //{ V4L2_CID_AUTO_FOCUS_RANGE, "autofocusrange" },
157  //{ V4L2_CID_PAN_SPEED, "panspeed" },
158  //{ V4L2_CID_TILT_SPEED, "tiltspeed" },
159 
160  // In V4L2_CID_FLASH_CLASS_BASE:
161  { V4L2_CID_FLASH_LED_MODE, "flashled" },
162  { V4L2_CID_FLASH_STROBE_SOURCE, "flashstrobesrc" },
163  { V4L2_CID_FLASH_STROBE, "flashstrobe" },
164  { V4L2_CID_FLASH_STROBE_STOP, "flashstrobestop" },
165  { V4L2_CID_FLASH_STROBE_STATUS, "flashstrovestat" },
166  { V4L2_CID_FLASH_TIMEOUT, "flashtimeout" },
167  { V4L2_CID_FLASH_INTENSITY, "flashintens" },
168  { V4L2_CID_FLASH_TORCH_INTENSITY, "flashtorch" },
169  { V4L2_CID_FLASH_INDICATOR_INTENSITY, "flashindintens" },
170  { V4L2_CID_FLASH_FAULT, "flashfault" },
171  { V4L2_CID_FLASH_CHARGE, "flashcharge" },
172  { V4L2_CID_FLASH_READY, "flashready" },
173 
174  // In V4L2_CID_JPEG_CLASS_BASE:
175  { V4L2_CID_JPEG_CHROMA_SUBSAMPLING, "jpegchroma" },
176  { V4L2_CID_JPEG_RESTART_INTERVAL, "jpegrestartint" },
177  { V4L2_CID_JPEG_COMPRESSION_QUALITY, "jpegcompression" },
178  { V4L2_CID_JPEG_ACTIVE_MARKER, "jpegmarker" },
179 
180  // In V4L2_CID_IMAGE_SOURCE_CLASS_BASE:
181  // Those are not defined in our older platform kernel:
182  //{ V4L2_CID_VBLANK, "vblank" },
183  //{ V4L2_CID_HBLANK, "hblank" },
184  //{ V4L2_CID_ANALOGUE_GAIN, "again" },
185  //{ V4L2_CID_TEST_PATTERN_RED, "testpatred" },
186  //{ V4L2_CID_TEST_PATTERN_GREENR, "testpatgreenr" },
187  //{ V4L2_CID_TEST_PATTERN_BLUE, "testpatblue" },
188  //{ V4L2_CID_TEST_PATTERN_GREENB, "testpatbreenb" },
189 
190  // In V4L2_CID_IMAGE_PROC_CLASS_BASE:
191  //{ V4L2_CID_LINK_FREQ, "linkfreq" },
192  //{ V4L2_CID_PIXEL_RATE, "pixrate" },
193  //{ V4L2_CID_TEST_PATTERN, "testpat" },
194 
195  // In V4L2_CID_DETECT_CLASS_BASE:
196  //{ V4L2_CID_DETECT_MD_MODE, "detectmode" },
197  //{ V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, "detectthresh" },
198  //{ V4L2_CID_DETECT_MD_THRESHOLD_GRID, "detectthreshgrid" },
199  //{ V4L2_CID_DETECT_MD_REGION_GRID, "detectregiongrid" },
200  };
201 
202  // Convert a long name to a short name:
203  std::string abbreviate(std::string const & longname)
204  {
205  std::string name(longname);
206  std::transform(name.begin(), name.end(), name.begin(), ::tolower);
207  name.erase(std::remove_if(name.begin(), name.end(), [](int c) { return !std::isalnum(c); }), name.end());
208  return name;
209  }
210 } // anonymous namespace
211 
212 
213 // ####################################################################################################
214 namespace jevois { namespace engine { static std::atomic<size_t> frameNumber(0); } }
215 
216 size_t jevois::frameNum()
217 { return jevois::engine::frameNumber.load(); }
218 
219 // ####################################################################################################
220 jevois::Engine::Engine(std::string const & instance) :
221  jevois::Manager(instance), itsMappings(), itsRunning(false), itsStreaming(false), itsStopMainLoop(false),
222  itsShellMode(false), itsTurbo(false), itsManualStreamon(false), itsVideoErrors(false),
223  itsNumSerialSent(0), itsRequestedFormat(-2)
224 {
225  JEVOIS_TRACE(1);
226 
227 #ifdef JEVOIS_PLATFORM_A33
228  // Start mass storage thread:
229  itsCheckingMassStorage.store(false); itsMassStorageMode.store(false);
230  itsCheckMassStorageFut = jevois::async_little(&jevois::Engine::checkMassStorage, this);
231  while (itsCheckingMassStorage.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(5));
232 #endif
233 
234  jevois::engine::frameNumber.store(0);
235 }
236 
237 // ####################################################################################################
238 jevois::Engine::Engine(int argc, char const* argv[], std::string const & instance) :
239  jevois::Manager(argc, argv, instance), itsMappings(), itsRunning(false), itsStreaming(false),
240  itsStopMainLoop(false), itsShellMode(false), itsTurbo(false), itsManualStreamon(false), itsVideoErrors(false),
241  itsNumSerialSent(0), itsRequestedFormat(-2)
242 {
243  JEVOIS_TRACE(1);
244 
245 #ifdef JEVOIS_PLATFORM_A33
246  // Start mass storage thread:
247  itsCheckingMassStorage.store(false); itsMassStorageMode.store(false);
248  itsCheckMassStorageFut = jevois::async_little(&jevois::Engine::checkMassStorage, this);
249  while (itsCheckingMassStorage.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(5));
250 #endif
251 
252  jevois::engine::frameNumber.store(0);
253 }
254 
255 // ####################################################################################################
256 void jevois::Engine::onParamChange(jevois::engine::serialdev const & JEVOIS_UNUSED_PARAM(param),
257  std::string const & newval)
258 {
259  JEVOIS_TIMED_LOCK(itsMtx);
260 
261  // If we have a serial already, nuke it:
262  for (std::list<std::shared_ptr<UserInterface> >::iterator itr = itsSerials.begin(); itr != itsSerials.end(); ++itr)
263  if ((*itr)->instanceName() == "serial") itr = itsSerials.erase(itr);
264  removeComponent("serial", false);
265 
266  // Open the usb hardware (4-pin connector) serial port, if any:
267  if (newval.empty() == false)
268  try
269  {
270  std::shared_ptr<jevois::UserInterface> s;
271  if (newval == "stdio")
272  s = addComponent<jevois::StdioInterface>("serial");
273  else
274  {
275  s = addComponent<jevois::Serial>("serial", jevois::UserInterface::Type::Hard);
276  s->setParamVal("devname", newval);
277  }
278 
279  itsSerials.push_back(s);
280  LINFO("Using [" << newval << "] hardware (4-pin connector) serial port");
281  }
282  catch (...) { jevois::warnAndIgnoreException(); LERROR("Could not start hardware (4-pin connector) serial port"); }
283  else LINFO("No hardware (4-pin connector) serial port used");
284 }
285 
286 // ####################################################################################################
287 void jevois::Engine::onParamChange(jevois::engine::usbserialdev const & JEVOIS_UNUSED_PARAM(param),
288  std::string const & newval)
289 {
290  JEVOIS_TIMED_LOCK(itsMtx);
291 
292  // If we have a usbserial already, nuke it:
293  for (std::list<std::shared_ptr<UserInterface> >::iterator itr = itsSerials.begin(); itr != itsSerials.end(); ++itr)
294  if ((*itr)->instanceName() == "usbserial") itr = itsSerials.erase(itr);
295  removeComponent("usbserial", false);
296 
297  // Open the USB serial port, if any:
298  if (newval.empty() == false)
299  try
300  {
301  std::shared_ptr<jevois::UserInterface> s =
302  addComponent<jevois::Serial>("usbserial", jevois::UserInterface::Type::USB);
303  s->setParamVal("devname", newval);
304  itsSerials.push_back(s);
305  LINFO("Using [" << newval << "] USB serial port");
306  }
307  catch (...) { jevois::warnAndIgnoreException(); LERROR("Could not start USB serial port"); }
308  else LINFO("No USB serial port used");
309 }
310 
311 // ####################################################################################################
312 void jevois::Engine::onParamChange(jevois::engine::cpumode const & JEVOIS_UNUSED_PARAM(param),
313  jevois::engine::CPUmode const & newval)
314 {
315 #ifdef JEVOIS_PRO
316  std::ofstream ofs("/sys/devices/system/cpu/cpu2/cpufreq/scaling_governor");
317 #else
318  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
319 #endif
320  if (ofs.is_open() == false)
321  {
322 #ifdef JEVOIS_PLATFORM
323  LERROR("Cannot set cpu frequency governor mode -- IGNORED");
324 #endif
325  return;
326  }
327 
328  switch (newval)
329  {
330  case engine::CPUmode::PowerSave: ofs << "powersave" << std::endl; break;
331  case engine::CPUmode::Conservative: ofs << "conservative" << std::endl; break;
332  case engine::CPUmode::OnDemand: ofs << "ondemand" << std::endl; break;
333  case engine::CPUmode::Interactive: ofs << "interactive" << std::endl; break;
334  case engine::CPUmode::Performance: ofs << "performance" << std::endl; break;
335  }
336 }
337 
338 #ifdef JEVOIS_PRO
339 // ####################################################################################################
340 void jevois::Engine::onParamChange(jevois::engine::cpumodel const & JEVOIS_UNUSED_PARAM(param),
341  jevois::engine::CPUmode const & newval)
342 {
343  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
344  if (ofs.is_open() == false)
345  {
346 #ifdef JEVOIS_PLATFORM
347  LERROR("Cannot set cpu frequency governor mode -- IGNORED");
348 #endif
349  return;
350  }
351 
352  switch (newval)
353  {
354  case engine::CPUmode::PowerSave: ofs << "powersave" << std::endl; break;
355  case engine::CPUmode::Conservative: ofs << "conservative" << std::endl; break;
356  case engine::CPUmode::OnDemand: ofs << "ondemand" << std::endl; break;
357  case engine::CPUmode::Interactive: ofs << "interactive" << std::endl; break;
358  case engine::CPUmode::Performance: ofs << "performance" << std::endl; break;
359  }
360 }
361 #endif
362 
363 // ####################################################################################################
364 void jevois::Engine::onParamChange(jevois::engine::cpumax const & JEVOIS_UNUSED_PARAM(param),
365  unsigned int const & newval)
366 {
367 #ifdef JEVOIS_PRO
368  std::ofstream ofs("/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq");
369 #else
370  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
371 #endif
372 
373  if (ofs.is_open() == false)
374  {
375 #ifdef JEVOIS_PLATFORM
376  LERROR("Cannot set cpu max frequency -- IGNORED");
377 #endif
378  return;
379  }
380 
381  ofs << newval * 1000U << std::endl;
382 }
383 
384 // ####################################################################################################
385 void jevois::Engine::onParamChange(jevois::engine::videoerrors const & JEVOIS_UNUSED_PARAM(param),
386  bool const & newval)
387 {
388  itsVideoErrors.store(newval);
389 }
390 
391 #ifdef JEVOIS_PRO
392 // ####################################################################################################
393 void jevois::Engine::onParamChange(jevois::engine::gui const & JEVOIS_UNUSED_PARAM(param),
394  bool const & newval)
395 {
396  JEVOIS_TIMED_LOCK(itsMtx);
397  if (newval)
398  {
399  if (!itsGUIhelper)
400  {
401  itsGUIhelper = addComponent<jevois::GUIhelper>("gui", conslock::get());
402  auto s = addComponent<jevois::GUIconsole>("guiconsole");
403  itsSerials.push_back(s);
404  LINFO("GUI enabled.");
405  }
406  }
407  else if (itsGUIhelper)
408  {
409  for (auto itr = itsSerials.begin(); itr != itsSerials.end(); ++itr)
410  if ((*itr)->instanceName() == "guiconsole") { itsSerials.erase(itr); break; }
411  removeComponent("guiconsole", false);
412  removeComponent(itsGUIhelper);
413  itsGUIhelper.reset();
414  LINFO("GUI disabled.");
415  }
416 }
417 
418 // ####################################################################################################
419 void jevois::Engine::onParamChange(jevois::engine::cpumaxl const & JEVOIS_UNUSED_PARAM(param),
420  unsigned int const & newval)
421 {
422  std::ofstream ofs("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
423 
424  if (ofs.is_open() == false)
425  {
426 #ifdef JEVOIS_PLATFORM
427  LERROR("Cannot set cpu max frequency -- IGNORED");
428 #endif
429  return;
430  }
431 
432  ofs << newval * 1000U << std::endl;
433 }
434 #endif
435 
436 // ####################################################################################################
438 {
439  // Set any initial parameters from global config file:
440  std::string const paramcfg = std::string(JEVOIS_CONFIG_PATH) + '/' + JEVOIS_MODULE_PARAMS_FILENAME;
441  std::ifstream ifs(paramcfg); if (ifs.is_open()) setParamsFromStream(ifs, paramcfg);
442 
443  // Run the Manager version. This parses the command line:
445 }
446 
447 // ####################################################################################################
449 {
450  // Check iw we want to use GUI mode:
451  bool usegui = false;
452 #ifdef JEVOIS_PRO
453  usegui = gui::get();
454 #endif
455 
456  itsMappings = jevois::loadVideoMappings(camerasens::get(), itsDefaultMappingIdx, true, usegui);
457  LINFO("Loaded " << itsMappings.size() << " vision processing modes.");
458 }
459 
460 // ####################################################################################################
462 {
463  // First make sure the manager gets to run this:
465 
466  // Prevent any setFormat() that may be requested, e.g., by the Inventor, as soon as it detects our gadget, until after
467  // we have completed running the initscript:
468  JEVOIS_TIMED_LOCK(itsMtx);
469 
470  // Freeze the serial port device names, their params, and camera and gadget too:
471  serialdev::freeze();
472  usbserialdev::freeze();
473  for (auto & s : itsSerials) s->freezeAllParams();
474  cameradev::freeze();
475  imudev::freeze();
476  camerasens::freeze();
477  jevois::CameraSensor const camsens = camerasens::get();
478  cameranbuf::freeze();
479  camturbo::freeze();
480  gadgetdev::freeze();
481  gadgetnbuf::freeze();
482  itsTurbo = camturbo::get();
483  multicam::freeze();
484  quietcmd::freeze();
485  python::freeze();
486 
487  // Check iw we want to use GUI mode:
488  bool usegui = false;
489 #ifdef JEVOIS_PRO
490  gui::freeze();
491  usegui = gui::get();
492  conslock::freeze();
493  watchdog::freeze();
494 #endif
495 
496  // Grab the log messages, itsSerials is not going to change anymore now that the serial params are frozen:
497  jevois::logSetEngine(this);
498 
499  // Load our video mappings:
500  reloadVideoMappings();
501 
502  // Get python going, we need to do this here to avoid segfaults on platform when instantiating our first python
503  // module. This likely has to do with the fact that the python core is not very thread-safe, and setFormatInternal()
504  // in Engine, which instantiates python modules, will indeed be invoked from a different thread (the one that receives
505  // USB UVC events). Have a look at Python Thread State, Python Gobal Interpreter Lock, etc if interested:
506  if (python::get())
507  {
508  LINFO("Initalizing Python...");
510  }
511 
512  // Instantiate a camera: If device names starts with "/dev/v", assume a hardware camera, otherwise a movie file:
513  std::string const camdev = cameradev::get();
514  if (jevois::stringStartsWith(camdev, "/dev/v"))
515  {
516  LINFO("Starting camera device " << camdev);
517 
518 #ifdef JEVOIS_PLATFORM_A33
519  // Set turbo mode or not:
520  std::ofstream ofs("/sys/module/vfe_v4l2/parameters/turbo");
521  if (ofs.is_open())
522  {
523  if (itsTurbo) ofs << "1" << std::endl; else ofs << "0" << std::endl;
524  ofs.close();
525  }
526  else LERROR("Could not access VFE turbo parameter -- IGNORED");
527 #endif
528 
529  // Now instantiate the camera:
530  itsCamera.reset(new jevois::Camera(camdev, camsens, cameranbuf::get()));
531 
532 #ifndef JEVOIS_PLATFORM
533  // No need to confuse people with a non-working camreg and imureg params:
534  camreg::set(false); camreg::freeze();
535  imureg::set(false); imureg::freeze();
536 #endif
537 
538  try
539  {
540  // On JeVois-A33 platform, hook up an I2C-based IMU to the camera sensor, if supported:
541 #ifdef JEVOIS_PLATFORM_A33
542  if (jevois::sensorHasIMU(camsens))
543  itsIMU.reset(new jevois::IMUi2c(std::dynamic_pointer_cast<jevois::Camera>(itsCamera)));
544 #endif
545 
546  // On JeVois-Pro platform, instantiate an SPI-based IMU, if supported:
547 #ifdef JEVOIS_PLATFORM_PRO
548  if (jevois::sensorHasIMU(camsens))
549  itsIMU.reset(new jevois::IMUspi(imudev::get()));
550 #endif
551  } catch (...) { LERROR("Sensor should have an IMU but we failed to initialize it."); }
552  }
553  else
554  {
555  LINFO("Using movie input " << camdev << " -- issue a 'streamon' to start processing.");
556  itsCamera.reset(new jevois::MovieInput(camdev, cameranbuf::get()));
557 
558  // No need to confuse people with a non-working camreg param:
559  camreg::set(false);
560  camreg::freeze();
561  }
562 
563  // Instantiate a USB gadget: Note: it will want to access the mappings. If the user-selected video mapping has no usb
564  // out, do not instantiate a gadget:
565  int midx = videomapping::get();
566 
567  // The videomapping parameter is now disabled, users should use the 'setmapping' command once running:
568  videomapping::freeze();
569 
570  if (midx >= int(itsMappings.size()))
571  { LERROR("Mapping index " << midx << " out of range -- USING DEFAULT"); midx = -1; }
572 
573  if (midx < 0) midx = itsDefaultMappingIdx;
574 
575  // Always instantiate a gadget even if not used right now, may be used later:
576  std::string const gd = gadgetdev::get();
577  if (gd == "None")
578  {
579  LINFO("Using no USB video output.");
580  // No USB output and no display, useful for benchmarking only:
581  itsGadget.reset(new jevois::VideoOutputNone());
582  itsManualStreamon = true;
583  }
584  else if (jevois::stringStartsWith(gd, "/dev/"))
585  {
586  LINFO("Loading USB video driver " << gd);
587  // USB gadget driver:
588  itsGadget.reset(new jevois::Gadget(gd, itsCamera.get(), this, gadgetnbuf::get(), multicam::get()));
589  }
590  else if (gd.empty() == false)
591  {
592  LINFO("Saving output video to file " << gd);
593  // Non-empty filename, save to file:
594  itsGadget.reset(new jevois::MovieOutput(gd));
595  itsManualStreamon = true;
596  }
597  else
598  {
599  // Local video display, for use on a host desktop or on JeVois-Pro HDMI output:
600 #ifdef JEVOIS_PRO
601  // On JevoisPro, use OpenGL or ImGui display:
602  if (usegui)
603  {
604  LINFO("Using OpenGL + ImGui display for video output");
605  itsGadget.reset(new jevois::VideoDisplayGUI(itsGUIhelper, gadgetnbuf::get()));
606  }
607  else
608  {
609  LINFO("Using OpenGL display for video output");
610  itsGadget.reset(new jevois::VideoDisplayGL(gadgetnbuf::get()));
611  }
612 #else
613  // On JeVois-A33, use an OpenCV display:
614  LINFO("Using OpenCV display for video output");
615  itsGadget.reset(new jevois::VideoDisplay("JeVois", gadgetnbuf::get()));
616  (void)usegui; // keep compiler happy
617 #endif
618  itsManualStreamon = true;
619  }
620 
621  // We are ready to run:
622  itsRunning.store(true);
623 
624  // Set initial format:
625  try { setFormatInternal(midx); } catch (...) { jevois::warnAndIgnoreException(); }
626 
627  // Run init script:
628  runScriptFromFile(JEVOIS_ENGINE_INIT_SCRIPT, nullptr, false);
629 }
630 
631 // ####################################################################################################
633 {
634  JEVOIS_TRACE(1);
635 
636  // Turn off stream if it is on:
637  streamOff();
638 
639  // Tell our run() thread to finish up:
640  itsRunning.store(false);
641 
642 #ifdef JEVOIS_PLATFORM_A33
643  // Tell checkMassStorage() thread to finish up:
644  itsCheckingMassStorage.store(false);
645 #endif
646 
647  // Nuke our module as soon as we can, hopefully soon now that we turned off streaming and running:
648  {
649  JEVOIS_TIMED_LOCK(itsMtx);
650  if (itsModule) removeComponent(itsModule);
651  itsModule.reset();
652 
653  // Gone, nuke the loader now:
654  itsLoader.reset();
655  }
656 
657  // Because we passed the camera as a raw pointer to the gadget, nuke the gadget first and then the camera:
658  itsGadget.reset();
659  itsCamera.reset();
660 
661 #ifdef JEVOIS_PLATFORM_A33
662  // Will block until the checkMassStorage() thread completes:
663  if (itsCheckMassStorageFut.valid())
664  try { itsCheckMassStorageFut.get(); } catch (...) { jevois::warnAndIgnoreException(); }
665 #endif
666 
667  // Things should be quiet now, unhook from the logger (this call is not strictly thread safe):
668  jevois::logSetEngine(nullptr);
669 }
670 
671 // ####################################################################################################
672 #ifdef JEVOIS_PLATFORM_A33
673 void jevois::Engine::checkMassStorage()
674 {
675  itsCheckingMassStorage.store(true);
676 
677  while (itsCheckingMassStorage.load())
678  {
679  // Check from the mass storage gadget (with JeVois extension) whether the virtual USB drive is mounted by the
680  // host. If currently in mass storage mode and the host just ejected the virtual flash drive, resume normal
681  // operation. If not in mass-storage mode and the host mounted it, enter mass-storage mode (may happen if
682  // /boot/usbsdauto was selected):
683  std::ifstream ifs("/sys/devices/platform/sunxi_usb_udc/gadget/lun0/mass_storage_in_use");
684  if (ifs.is_open())
685  {
686  int inuse; ifs >> inuse;
687  if (itsMassStorageMode.load())
688  {
689  if (inuse == 0) stopMassStorageMode();
690  }
691  else
692  {
693  if (inuse) { JEVOIS_TIMED_LOCK(itsMtx); startMassStorageMode(); }
694  }
695  }
696  std::this_thread::sleep_for(std::chrono::milliseconds(500));
697  }
698 }
699 #endif
700 
701 // ####################################################################################################
703 {
704  JEVOIS_TRACE(2);
705 
706  JEVOIS_TIMED_LOCK(itsMtx);
707  if (itsCamera) itsCamera->streamOn();
708  if (itsGadget) itsGadget->streamOn();
709  itsStreaming.store(true);
710 }
711 
712 // ####################################################################################################
714 {
715  JEVOIS_TRACE(2);
716 
717  // First, tell both the camera and gadget to abort streaming, this will make get()/done()/send() throw:
718  if (itsGadget) itsGadget->abortStream();
719  if (itsCamera) itsCamera->abortStream();
720 
721  // Stop the main loop, which will flip itsStreaming to false and will make it easier for us to lock itsMtx:
722  LDEBUG("Stopping main loop...");
723  itsStopMainLoop.store(true);
724  while (itsStopMainLoop.load() && itsRunning.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
725  LDEBUG("Main loop stopped.");
726 
727  // Lock up and stream off:
728  JEVOIS_TIMED_LOCK(itsMtx);
729  if (itsGadget) itsGadget->streamOff();
730  if (itsCamera) itsCamera->streamOff();
731 }
732 
733 // ####################################################################################################
735 {
736  JEVOIS_TRACE(2);
737  itsRequestedFormat.store(idx);
738 }
739 
740 // ####################################################################################################
742 {
743  JEVOIS_TRACE(2);
744 
745  LDEBUG("Set format number " << idx << " start...");
746 
747  if (idx >= itsMappings.size())
748  LFATAL("Requested mapping index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
749 
750  JEVOIS_TIMED_LOCK(itsMtx);
751  setFormatInternal(idx);
752  LDEBUG("Set format number " << idx << " done");
753 }
754 
755 // ####################################################################################################
756 void jevois::Engine::setFormatInternal(size_t idx)
757 {
758  // itsMtx should be locked by caller, idx should be valid:
759  JEVOIS_TRACE(2);
760 
761  jevois::VideoMapping const & m = itsMappings[idx];
762  setFormatInternal(m);
763 }
764 
765 // ####################################################################################################
766 void jevois::Engine::setFormatInternal(jevois::VideoMapping const & m, bool reload)
767 {
768  // itsMtx should be locked by caller, idx should be valid:
769  JEVOIS_TRACE(2);
770 
771  LINFO(m.str());
772  itsModuleConstructionError = "Unknown error while starting module " + m.modulename + " ...";
773 
774 #ifdef JEVOIS_PLATFORM_A33
775  if (itsMassStorageMode.load())
776  LFATAL("Cannot setup video streaming while in mass-storage mode. Eject the USB drive on your host computer first.");
777 #endif
778 
779  // Nuke the processing module, if any, so we can also safely nuke the loader. We always nuke the module instance so we
780  // won't have any issues with latent state even if we re-use the same module but possibly with different input
781  // image resolution, etc:
782  if (itsModule)
783  {
784  LDEBUG("Removing current module " << itsModule->className() << ": " << itsModule->descriptor());
785  try { removeComponent(itsModule); itsModule.reset(); LDEBUG("Current module removed."); }
786  catch (...) { jevois::warnAndIgnoreException(); }
787  }
788 
789  // Set the format at the camera and gadget levels, unless we are just reloading:
790  if (reload == false)
791  {
792  LDEBUG("Setting camera format: " << m.cstrall());
793  try { itsCamera->setFormat(m); }
794  catch (...)
795  {
797  itsModuleConstructionError = "Camera did not accept format:\n\n" + m.cstrall() +
798  "\n\nCheck videomappings.cfg and camera sensor specifications.";
799  return;
800  }
801 
802  LDEBUG("Setting gadget format: " << m.ostr());
803  try { itsGadget->setFormat(m); }
804  catch (...)
805  {
807  itsModuleConstructionError = "Gadget did not accept format:\n\n" + m.ostr() +
808  "\n\nCheck videomappings.cfg for any unsupported output formats.";
809  return;
810  }
811  }
812 
813  // Keep track of our current mapping:
814  itsCurrentMapping = m;
815 
816  // Reset our master frame counter on each module load:
817  jevois::engine::frameNumber.store(0);
818 
819  // Instantiate the module. If the constructor throws, code is bogus, for example some syntax error in a python module
820  // that is detected at load time. We get the exception's error message for later display into video frames in the main
821  // loop, and mark itsModuleConstructionError:
822  try
823  {
824  // For python modules, we do not need a loader, we just instantiate our special python wrapper module instead:
825  std::string const sopath = m.sopath();
826  if (m.ispython)
827  {
828  if (python::get() == false) LFATAL("Python disabled, delete BOOT:nopython and restart to enable python");
829 
830  // Instantiate the python wrapper:
831  itsLoader.reset();
832  itsModule.reset(new jevois::PythonModule(m));
833  }
834  else
835  {
836  // C++ compiled module. We can re-use the same loader and avoid closing the .so if we will use the same module:
837  if (itsLoader.get() == nullptr || itsLoader->sopath() != sopath)
838  {
839  // Nuke our previous loader and free its resources if needed, then start a new loader:
840  LINFO("Instantiating dynamic loader for " << sopath);
841  itsLoader.reset(new jevois::DynamicLoader(sopath, true));
842  }
843 
844  // Check version match:
845  auto version_major = itsLoader->load<int()>(m.modulename + "_version_major");
846  auto version_minor = itsLoader->load<int()>(m.modulename + "_version_minor");
847  if (version_major() != JEVOIS_VERSION_MAJOR || version_minor() != JEVOIS_VERSION_MINOR)
848  LERROR("Module " << m.modulename << " in file " << sopath << " was build for JeVois v" << version_major() << '.'
849  << version_minor() << ", but running framework is v" << JEVOIS_VERSION_STRING << " -- TRYING ANYWAY");
850 
851  // Instantiate the new module:
852  auto create = itsLoader->load<std::shared_ptr<jevois::Module>(std::string const &)>(m.modulename + "_create");
853  itsModule = create(m.modulename); // Here we just use the class name as instance name
854  }
855 
856  // Add the module as a component to us. Keep this code in sync with Manager::addComponent():
857  {
858  // Lock up so we guarantee the instance name does not get robbed as we add the sub:
859  boost::unique_lock<boost::shared_mutex> ulck(itsSubMtx);
860 
861  // Then add it as a sub-component to us:
862  itsSubComponents.push_back(itsModule);
863  itsModule->itsParent = this;
864  itsModule->setPath(sopath.substr(0, sopath.rfind('/')));
865  }
866 
867  // Bring it to our runstate and load any extra params. NOTE: Keep this in sync with Component::init():
868  if (itsInitialized) itsModule->runPreInit();
869 
870  std::string const paramcfg = itsModule->absolutePath(JEVOIS_MODULE_PARAMS_FILENAME);
871  std::ifstream ifs(paramcfg); if (ifs.is_open()) itsModule->setParamsFromStream(ifs, paramcfg);
872 
873  if (itsInitialized) { itsModule->setInitialized(); itsModule->runPostInit(); }
874 
875  // And finally run any config script, sending any errors to USB (likely JeVois Inventor) and GUI:
876  std::shared_ptr<jevois::UserInterface> ser;
877  for (auto & s : itsSerials)
879  { ser = s; break; }
880 
881  runScriptFromFile(itsModule->absolutePath(JEVOIS_MODULE_SCRIPT_FILENAME), ser, false);
882 
883  LINFO("Module [" << m.modulename << "] loaded, initialized, and ready.");
884  itsModuleConstructionError.clear();
885  }
886  catch (...)
887  {
888  // Note: we do not nuke the module here, as the Inventor may need its path to fix some config files.
889  itsModuleConstructionError = jevois::warnAndIgnoreException();
890  LERROR("Module [" << m.modulename << "] startup error and not operational.");
891  }
892 }
893 
894 // ####################################################################################################
896 {
897  JEVOIS_TRACE(2);
898 
899 #ifdef JEVOIS_PRO
900  // Start watchdog:
901  itsWatchdog.reset(new jevois::Watchdog(watchdog::get()));
902 #endif
903 
904  std::string pfx; // optional command prefix
905  int ret = 0; // our return value
906 
907  // Announce that we are ready to the hardware serial port, if any. Do not use sendSerial() here so we always issue
908  // this message irrespectively of the user serial preferences:
909  for (auto & s : itsSerials)
911  try { s->writeString("INF READY JEVOIS " JEVOIS_VERSION_STRING); }
912  catch (...) { jevois::warnAndIgnoreException(); }
913 
914  while (itsRunning.load())
915  {
916  bool dosleep = true;
917 
918 #ifdef JEVOIS_PRO
919  // Reset the watchdog:
920  itsWatchdog->reset();
921 #endif
922 
923  // If we got a format change request through requestSetFormat(), honor it now while we are unlocked:
924  // -2 means no change requested; -1 means reload requested (we do not need to change camera or gadget)
925  int rf = itsRequestedFormat.load();
926  if (rf != -2)
927  {
928  // This format change request is now marked as handled:
929  itsRequestedFormat.store(-2);
930 
931  try
932  {
933  // Stop camera and gadget unless we are just reloading:
934  if (rf != -1 && itsStreaming.load())
935  {
936  // Keep this code in sync with streamOff():
937  if (itsGadget) itsGadget->abortStream();
938  if (itsCamera) itsCamera->abortStream();
939  JEVOIS_TIMED_LOCK(itsMtx);
940  if (itsGadget) itsGadget->streamOff();
941  if (itsCamera) itsCamera->streamOff();
942  itsStreaming.store(false);
943  }
944 
945  // Set new format or reload current module:
946  if (rf == -1)
947  {
948  // Reload the current format, eg, after editing code:
949  JEVOIS_TIMED_LOCK(itsMtx);
950  setFormatInternal(itsCurrentMapping, true);
951  }
952  else setFormat(rf);
953 
954 #ifdef JEVOIS_PRO
955  // Reset the GUI to clear various texture caches and such:
956  if (itsGUIhelper) itsGUIhelper->reset( (rf != -1) );
957 #endif
958 
959  // Restart camera and gadget if we stopped them:
960  if (rf != -1 && itsCurrentMapping.ofmt != 0)
961  {
962  // Keep this code in sync with streamOn();
963  JEVOIS_TIMED_LOCK(itsMtx);
964  if (itsCamera) itsCamera->streamOn();
965  if (itsGadget) itsGadget->streamOn();
966  itsStreaming.store(true);
967  }
968 
969  // On JeVois Pro running the GUI we need to get the camera and gadget streaming at all times for the GUI to
970  // refresh, so restart them. When not using the GUI, users will have to issue a "streamon" to get going:
971 #ifdef JEVOIS_PRO
972  if (itsGUIhelper && itsStreaming.load() == false)
973  {
974  // Keep this code in sync with streamOn();
975  JEVOIS_TIMED_LOCK(itsMtx);
976  if (itsCamera) itsCamera->streamOn();
977  if (itsGadget) itsGadget->streamOn();
978  itsStreaming.store(true);
979  }
980 #endif
981  }
982  catch (...)
983  {
984  reportErrorInternal();
985 
986  // Stream off:
987  try
988  {
989  if (itsGadget) itsGadget->abortStream();
990  if (itsCamera) itsCamera->abortStream();
991  JEVOIS_TIMED_LOCK(itsMtx);
992  if (itsGadget) itsGadget->streamOff();
993  if (itsCamera) itsCamera->streamOff();
994  itsStreaming.store(false);
995  }
996  catch (...) { }
997  }
998  }
999 
1000  if (itsStreaming.load())
1001  {
1002  // Lock up while we use the module:
1003  JEVOIS_TIMED_LOCK(itsMtx);
1004 
1005  if (itsModuleConstructionError.empty() == false)
1006  {
1007  // If we have a module construction error, report it now to GUI/USB/console:
1008  reportErrorInternal(itsModuleConstructionError);
1009 
1010  // Also get one camera frame to avoid accumulation of stale buffers:
1011  //try { (void)jevois::InputFrame(itsCamera, itsTurbo).get(); }
1012  //catch (...) { jevois::warnAndIgnoreException(); }
1013  }
1014  else if (itsModule)
1015  {
1016  // For standard modules, indicate frame start mark if user wants it:
1017  jevois::StdModule * stdmod = dynamic_cast<jevois::StdModule *>(itsModule.get());
1018  if (stdmod) stdmod->sendSerialMarkStart();
1019 
1020  // We have a module ready for action. Call its process function and handle any exceptions:
1021  try
1022  {
1023  switch (itsCurrentMapping.ofmt)
1024  {
1025  case 0:
1026  {
1027  // Process with no USB outputs:
1028  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo));
1029 
1030 #ifdef JEVOIS_PRO
1031  // We always need startFrame()/endFrame() when using the GUI:
1032  if (itsGUIhelper) itsGUIhelper->headlessDisplay();
1033 #endif
1034  break;
1035  }
1036 
1037 #ifdef JEVOIS_PRO
1038  case JEVOISPRO_FMT_GUI:
1039  {
1040  // Process with GUI display on JeVois-Pro:
1041  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo), *itsGUIhelper);
1042  break;
1043  }
1044 #endif
1045  default:
1046  {
1047  // Process with USB outputs:
1048  itsModule->process(jevois::InputFrame(itsCamera, itsTurbo),
1049  jevois::OutputFrame(itsGadget, itsVideoErrors.load() ? &itsVideoErrorImage : nullptr));
1050  }
1051  }
1052 
1053  // If process() did not throw, no need to sleep:
1054  dosleep = false;
1055  }
1056  catch (...) { reportErrorInternal(); }
1057 
1058  // For standard modules, indicate frame start stop if user wants it:
1059  if (stdmod) stdmod->sendSerialMarkStop();
1060 
1061  // Increment our master frame counter
1062  ++ jevois::engine::frameNumber;
1063  itsNumSerialSent.store(0);
1064  }
1065  }
1066 
1067  if (itsStopMainLoop.load())
1068  {
1069  itsStreaming.store(false);
1070  LDEBUG("-- Main loop stopped --");
1071  itsStopMainLoop.store(false);
1072  }
1073 
1074  if (dosleep)
1075  {
1076  LDEBUG("No processing module loaded or not streaming... Sleeping...");
1077  std::this_thread::sleep_for(std::chrono::milliseconds(25));
1078  }
1079 
1080  // Serial input handling. Note that readSome() and writeString() on the serial could throw. The code below is
1081  // organized to catch all other exceptions, except for those, which are caught here at the first try level:
1082  for (auto & s : itsSerials)
1083  {
1084  try
1085  {
1086  std::string str; bool parsed = false; bool success = false;
1087 
1088  if (s->readSome(str))
1089  {
1090  JEVOIS_TIMED_LOCK(itsMtx);
1091 
1092  // If the command starts with our hidden command prefix, set the prefix, otherwise clear it:
1093  if (jevois::stringStartsWith(str, JEVOIS_JVINV_PREFIX))
1094  {
1095  pfx = JEVOIS_JVINV_PREFIX;
1096  str = str.substr(pfx.length());
1097  }
1098  else pfx.clear();
1099 
1100  // Try to execute this command. If the command is for us (e.g., set a parameter) and is correct,
1101  // parseCommand() will return true; if it is for us but buggy, it will throw. If it is not recognized by us,
1102  // it will return false and we should try sending it to the Module:
1103  try { parsed = parseCommand(str, s, pfx); success = parsed; }
1104  catch (std::exception const & e)
1105  { s->writeString(pfx, std::string("ERR ") + e.what()); parsed = true; }
1106  catch (...)
1107  { s->writeString(pfx, "ERR Unknown error"); parsed = true; }
1108 
1109  if (parsed == false)
1110  {
1111  if (itsModule)
1112  {
1113  // Note: prefixing is currently not supported for modules, it is for the Engine only
1114  try { itsModule->parseSerial(str, s); success = true; }
1115  catch (std::exception const & me) { s->writeString(pfx, std::string("ERR ") + me.what()); }
1116  catch (...) { s->writeString(pfx, "ERR Command [" + str + "] not recognized by Engine or Module"); }
1117  }
1118  else s->writeString(pfx, "ERR Unsupported command [" + str + "] and no module");
1119  }
1120 
1121  // If success, let user know:
1122  if (success && quietcmd::get() == false && itsShellMode == false) s->writeString(pfx, "OK");
1123  }
1124  }
1125  catch (...) { jevois::warnAndIgnoreException(); }
1126  }
1127  }
1128  return ret;
1129 }
1130 
1131 // ####################################################################################################
1132 void jevois::Engine::sendSerial(std::string const & str, bool islog)
1133 {
1134  // If not a log message, we may want to limit the number of serout messages that a module sends on each frame:
1135  size_t slim = serlimit::get();
1136  if (islog == false && slim)
1137  {
1138  if (itsNumSerialSent.load() >= slim) return; // limit reached, message dropped
1139  ++itsNumSerialSent; // increment number of messages sent. It is reset in the main loop on each new frame.
1140  }
1141 
1142  // Decide where to send this message based on the value of islog:
1143  jevois::engine::SerPort p = islog ? serlog::get() : serout::get();
1144  switch (p)
1145  {
1146  case jevois::engine::SerPort::None:
1147  break; // Nothing to send
1148 
1149  case jevois::engine::SerPort::All:
1150  for (auto & s : itsSerials)
1151  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
1152  break;
1153 
1154  case jevois::engine::SerPort::Hard:
1155  for (auto & s : itsSerials)
1157  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
1158  break;
1159 
1160  case jevois::engine::SerPort::USB:
1161  for (auto & s : itsSerials)
1163  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
1164  break;
1165  }
1166 
1167 #ifdef JEVOIS_PRO
1168  // If we did not send to All (which includes the GUI), check whether the GUI wants it too:
1169  if (itsGUIhelper && ((islog && itsGUIhelper->serlogEnabled()) || (!islog && itsGUIhelper->seroutEnabled())))
1170  for (auto & s : itsSerials)
1172  try { s->writeString(str); } catch (...) { jevois::warnAndIgnoreException(); }
1173 #endif
1174 }
1175 
1176 // ####################################################################################################
1177 void jevois::Engine::reportError(std::string const & err)
1178 {
1179 #ifdef JEVOIS_PRO
1180  if (itsGUIhelper) itsGUIhelper->reportError(err);
1181 #endif
1182  LERROR(err);
1183 }
1184 
1185 // ####################################################################################################
1187 {
1188 #ifdef JEVOIS_PRO
1189  // If using a GUI, clear errors in the GUI:
1190  if (itsGUIhelper) itsGUIhelper->clearErrors();
1191 #endif
1192  // Otherwise, no need to clear anything, other errors are not persistently displayed.
1193 }
1194 
1195 // ####################################################################################################
1196 void jevois::Engine::reportErrorInternal(std::string const & err)
1197 {
1198 #ifdef JEVOIS_PRO
1199  // If using a GUI, report error to GUI:
1200  if (itsGUIhelper && itsCurrentMapping.ofmt == JEVOISPRO_FMT_GUI)
1201  {
1202  if (itsGUIhelper->frameStarted() == false) { unsigned short w, h; itsGUIhelper->startFrame(w, h); }
1203  if (err.empty()) itsGUIhelper->reportError(jevois::warnAndIgnoreException());
1204  else itsGUIhelper->reportError(err);
1205  itsGUIhelper->endFrame();
1206  }
1207  else
1208 #endif
1209  // Report exceptions to video if desired: We have to be extra careful here because the exception might have
1210  // been called by the input frame (camera not streaming) or the output frame (gadget not streaming), in
1211  // addition to exceptions thrown by the module:
1212  if (itsCurrentMapping.ofmt != 0 && itsCurrentMapping.ofmt != JEVOISPRO_FMT_GUI && itsVideoErrors.load())
1213  {
1214  try
1215  {
1216  // If the module threw before get() or after send() on the output frame, get a buffer from the gadget:
1217  if (itsVideoErrorImage.valid() == false) itsGadget->get(itsVideoErrorImage); // could throw if streamoff
1218 
1219  // Draw the error message into our video frame:
1220  if (err.empty()) jevois::drawErrorImage(jevois::warnAndIgnoreException(), itsVideoErrorImage);
1221  else jevois::drawErrorImage(err, itsVideoErrorImage);
1222  }
1223  catch (...) { jevois::warnAndIgnoreException(); }
1224 
1225  try
1226  {
1227  // Send the error image over USB:
1228  if (itsVideoErrorImage.valid()) itsGadget->send(itsVideoErrorImage); // could throw if gadget stream off
1229  }
1230  catch (...) { jevois::warnAndIgnoreException(); }
1231 
1232  // Invalidate the error image so it is clean for the next frame:
1233  itsVideoErrorImage.invalidate();
1234  }
1235  else
1236  {
1237  // Report module exception to serlog, and ignore:
1238  if (err.empty()) jevois::warnAndIgnoreException();
1239  else LERROR(err);
1240  }
1241 }
1242 
1243 // ####################################################################################################
1244 std::shared_ptr<jevois::Module> jevois::Engine::module() const
1245 { return itsModule; }
1246 
1247 // ####################################################################################################
1248 std::shared_ptr<jevois::IMU> jevois::Engine::imu() const
1249 { return itsIMU; }
1250 
1251 // ####################################################################################################
1252 std::shared_ptr<jevois::Camera> jevois::Engine::camera() const
1253 { return std::dynamic_pointer_cast<jevois::Camera>(itsCamera); }
1254 
1255 // ####################################################################################################
1256 jevois::VideoMapping const & jevois::Engine::getCurrentVideoMapping() const
1257 { return itsCurrentMapping; }
1258 
1259 // ####################################################################################################
1261 { return itsMappings.size(); }
1262 
1263 // ####################################################################################################
1264 jevois::VideoMapping const & jevois::Engine::getVideoMapping(size_t idx) const
1265 {
1266  if (idx >= itsMappings.size())
1267  LFATAL("Index " << idx << " out of range [0 .. " << itsMappings.size()-1 << ']');
1268 
1269  return itsMappings[idx];
1270 }
1271 
1272 // ####################################################################################################
1273 size_t jevois::Engine::getVideoMappingIdx(unsigned int iformat, unsigned int iframe, unsigned int interval) const
1274 {
1275  // If the iformat or iframe is zero, that's probably a probe for the default mode, so return it:
1276  if (iformat == 0 || iframe == 0) return itsDefaultMappingIdx;
1277 
1278  // If interval is zero, probably a driver trying to probe for our default interval, so return the first available one;
1279  // otherwise try to find the desired interval and return the corresponding mapping:
1280  if (interval)
1281  {
1282  float const fps = jevois::VideoMapping::uvcToFps(interval);
1283  size_t idx = 0;
1284 
1285  for (jevois::VideoMapping const & m : itsMappings)
1286  if (m.uvcformat == iformat && m.uvcframe == iframe && std::fabs(m.ofps - fps) < 0.1F) return idx;
1287  else ++idx;
1288 
1289  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
1290  }
1291  else
1292  {
1293  size_t idx = 0;
1294 
1295  for (jevois::VideoMapping const & m : itsMappings)
1296  if (m.uvcformat == iformat && m.uvcframe == iframe) return idx;
1297  else ++idx;
1298 
1299  LFATAL("No video mapping for iformat=" << iformat <<", iframe=" << iframe << ", interval=" << interval);
1300  }
1301 }
1302 
1303 // ####################################################################################################
1304 jevois::VideoMapping const & jevois::Engine::getDefaultVideoMapping() const
1305 { return itsMappings[itsDefaultMappingIdx]; }
1306 
1307 // ####################################################################################################
1309 { return itsDefaultMappingIdx; }
1310 
1311 // ####################################################################################################
1312 void jevois::Engine::foreachVideoMapping(std::function<void(jevois::VideoMapping const & m)> && func)
1313 {
1314  for (jevois::VideoMapping const & m : itsMappings)
1315  try { func(m); } catch (...) { jevois::warnAndIgnoreException(); }
1316 }
1317 
1318 // ####################################################################################################
1319 jevois::VideoMapping const &
1320 jevois::Engine::findVideoMapping(unsigned int oformat, unsigned int owidth, unsigned int oheight,
1321  float oframespersec) const
1322 {
1323  for (jevois::VideoMapping const & m : itsMappings)
1324  if (m.match(oformat, owidth, oheight, oframespersec)) return m;
1325 
1326  LFATAL("Could not find mapping for output format " << jevois::fccstr(oformat) << ' ' <<
1327  owidth << 'x' << oheight << " @ " << oframespersec << " fps");
1328 }
1329 
1330 // ####################################################################################################
1331 void jevois::Engine::foreachCamCtrl(std::function<void(struct v4l2_queryctrl & qc, std::set<int> & doneids)> && func)
1332 {
1333  struct v4l2_queryctrl qc = { }; std::set<int> doneids;
1334  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
1335  {
1336  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
1337  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
1338  // doneids set to keep track of the ones already reported:
1339  qc.id = cls | 0x900; unsigned int old_id;
1340  while (true)
1341  {
1342  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; old_id = qc.id; bool failed = false;
1343  try { func(qc, doneids); } catch (...) { failed = true; }
1344 
1345  // The camera kernel driver is supposed to pass down the next valid control if the requested one is not
1346  // found, but some drivers do not honor that, so let's move on to the next control manually if needed:
1347  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
1348  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
1349  else if (failed) break;
1350  }
1351  }
1352 }
1353 
1354 // ####################################################################################################
1355 std::string jevois::Engine::camctrlname(int id, char const * longname) const
1356 {
1357  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
1358  if (camcontrols[i].id == id) return camcontrols[i].shortname;
1359 
1360  // Darn, this control is not in our list, probably something exotic. Compute a name from the control's long name:
1361  return abbreviate(longname);
1362 }
1363 
1364 // ####################################################################################################
1365 int jevois::Engine::camctrlid(std::string const & shortname)
1366 {
1367  for (size_t i = 0; i < sizeof camcontrols / sizeof camcontrols[0]; ++i)
1368  if (shortname.compare(camcontrols[i].shortname) == 0) return camcontrols[i].id;
1369 
1370  // Not in our list, all right, let's find it then in the camera:
1371  struct v4l2_queryctrl qc = { };
1372  for (int cls = V4L2_CTRL_CLASS_USER; cls <= V4L2_CTRL_CLASS_DETECT; cls += 0x10000)
1373  {
1374  // Enumerate all controls in this class. Looks like there is some spillover between V4L2 classes in the V4L2
1375  // enumeration process, we end up with duplicate controls if we try to enumerate all the classes. Hence the
1376  // doneids set to keep track of the ones already reported:
1377  qc.id = cls | 0x900;
1378  while (true)
1379  {
1380  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL; unsigned int old_id = qc.id; bool failed = false;
1381  try
1382  {
1383  itsCamera->queryControl(qc);
1384  if (abbreviate(reinterpret_cast<char const *>(qc.name)) == shortname) return qc.id;
1385  }
1386  catch (...) { failed = true; }
1387 
1388  // With V4L2_CTRL_FLAG_NEXT_CTRL, the camera kernel driver is supposed to pass down the next valid control if
1389  // the requested one is not found, but some drivers do not honor that, so let's move on to the next control
1390  // manually if needed:
1391  qc.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
1392  if (qc.id == old_id) { ++qc.id; if (qc.id > 100 + (cls | 0x900 | V4L2_CTRL_FLAG_NEXT_CTRL)) break; }
1393  else if (failed) break;
1394  }
1395  }
1396 
1397  LFATAL("Could not find control [" << shortname << "] in the camera");
1398 }
1399 
1400 // ####################################################################################################
1401 std::string jevois::Engine::camCtrlHelp(struct v4l2_queryctrl & qc, std::set<int> & doneids)
1402 {
1403  // See if we have this control:
1404  itsCamera->queryControl(qc);
1405  qc.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
1406 
1407  // If we have already done this control, just return an empty string:
1408  if (doneids.find(qc.id) != doneids.end()) return std::string(); else doneids.insert(qc.id);
1409 
1410  // Control exists, let's also get its current value:
1411  struct v4l2_control ctrl = { }; ctrl.id = qc.id;
1412  itsCamera->getControl(ctrl);
1413 
1414  // Print out some description depending on control type:
1415  std::ostringstream ss;
1416  ss << "- " << camctrlname(qc.id, reinterpret_cast<char const *>(qc.name));
1417 
1418  switch (qc.type)
1419  {
1420  case V4L2_CTRL_TYPE_INTEGER:
1421  ss << " [int] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
1422  << " def=" << qc.default_value << " curr=" << ctrl.value;
1423  break;
1424 
1425  //case V4L2_CTRL_TYPE_INTEGER64:
1426  //ss << " [int64] value=" << ctrl.value64;
1427  //break;
1428 
1429  //case V4L2_CTRL_TYPE_STRING:
1430  //ss << " [str] min=" << qc.minimum << " max=" << qc.maximum << " step=" << qc.step
1431  // << " curr=" << ctrl.string;
1432  //break;
1433 
1434  case V4L2_CTRL_TYPE_BOOLEAN:
1435  ss << " [bool] default=" << qc.default_value << " curr=" << ctrl.value;
1436  break;
1437 
1438  // This one is not supported by the older kernel on platform:
1439  //case V4L2_CTRL_TYPE_INTEGER_MENU:
1440  //ss << " [intmenu] min=" << qc.minimum << " max=" << qc.maximum
1441  // << " def=" << qc.default_value << " curr=" << ctrl.value;
1442  //break;
1443 
1444  case V4L2_CTRL_TYPE_BUTTON:
1445  ss << " [button]";
1446  break;
1447 
1448  case V4L2_CTRL_TYPE_BITMASK:
1449  ss << " [bitmask] max=" << qc.maximum << " def=" << qc.default_value << " curr=" << ctrl.value;
1450  break;
1451 
1452  case V4L2_CTRL_TYPE_MENU:
1453  {
1454  struct v4l2_querymenu querymenu = { };
1455  querymenu.id = qc.id;
1456  ss << " [menu] values ";
1457  for (querymenu.index = qc.minimum; querymenu.index <= (unsigned int)qc.maximum; ++querymenu.index)
1458  {
1459  try { itsCamera->queryMenu(querymenu); } catch (...) { strcpy((char *)(querymenu.name), "fixme"); }
1460  ss << querymenu.index << ':' << querymenu.name << ' ';
1461  }
1462  ss << "curr=" << ctrl.value;
1463  }
1464  break;
1465 
1466  default:
1467  ss << "[unknown type]";
1468  }
1469 
1470  if (qc.flags & V4L2_CTRL_FLAG_DISABLED) ss << " [DISABLED]";
1471 
1472  return ss.str();
1473 }
1474 
1475 // ####################################################################################################
1476 std::string jevois::Engine::camCtrlInfo(struct v4l2_queryctrl & qc, std::set<int> & doneids)
1477 {
1478  // See if we have this control:
1479  itsCamera->queryControl(qc);
1480  qc.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
1481 
1482  // If we have already done this control, just return an empty string:
1483  if (doneids.find(qc.id) != doneids.end()) return std::string(); else doneids.insert(qc.id);
1484 
1485  // Control exists, let's also get its current value:
1486  struct v4l2_control ctrl = { }; ctrl.id = qc.id;
1487  itsCamera->getControl(ctrl);
1488 
1489  // Print out some description depending on control type:
1490  std::ostringstream ss;
1491  ss << camctrlname(qc.id, reinterpret_cast<char const *>(qc.name));
1492 
1493  if (qc.flags & V4L2_CTRL_FLAG_DISABLED) ss << " D ";
1494 
1495  switch (qc.type)
1496  {
1497  case V4L2_CTRL_TYPE_INTEGER:
1498  ss << " I " << qc.minimum << ' ' << qc.maximum << ' ' << qc.step
1499  << ' ' << qc.default_value << ' ' << ctrl.value;
1500  break;
1501 
1502  //case V4L2_CTRL_TYPE_INTEGER64:
1503  //ss << " J " << ctrl.value64;
1504  //break;
1505 
1506  //case V4L2_CTRL_TYPE_STRING:
1507  //ss << " S " << qc.minimum << ' ' << qc.maximum << ' ' << qc.step << ' ' << ctrl.string;
1508  //break;
1509 
1510  case V4L2_CTRL_TYPE_BOOLEAN:
1511  ss << " B " << qc.default_value << ' ' << ctrl.value;
1512  break;
1513 
1514  // This one is not supported by the older kernel on platform:
1515  //case V4L2_CTRL_TYPE_INTEGER_MENU:
1516  //ss << " N " << qc.minimum << ' ' << qc.maximum << ' ' << qc.default_value << ' ' << ctrl.value;
1517  //break;
1518 
1519  case V4L2_CTRL_TYPE_BUTTON:
1520  ss << " U";
1521  break;
1522 
1523  case V4L2_CTRL_TYPE_BITMASK:
1524  ss << " K " << qc.maximum << ' ' << qc.default_value << ' ' << ctrl.value;
1525  break;
1526 
1527  case V4L2_CTRL_TYPE_MENU:
1528  {
1529  struct v4l2_querymenu querymenu = { };
1530  querymenu.id = qc.id;
1531  ss << " M " << qc.default_value << ' ' << ctrl.value;
1532  for (querymenu.index = qc.minimum; querymenu.index <= (unsigned int)qc.maximum; ++querymenu.index)
1533  {
1534  try { itsCamera->queryMenu(querymenu); } catch (...) { strcpy((char *)(querymenu.name), "fixme"); }
1535  ss << ' ' << querymenu.index << ':' << querymenu.name << ' ';
1536  }
1537  }
1538  break;
1539 
1540  default:
1541  ss << 'X';
1542  }
1543 
1544  return ss.str();
1545 }
1546 
1547 #ifdef JEVOIS_PLATFORM_A33
1548 // ####################################################################################################
1549 void jevois::Engine::startMassStorageMode()
1550 {
1551  // itsMtx must be locked by caller
1552 
1553  if (itsMassStorageMode.load()) { LERROR("Already in mass-storage mode -- IGNORED"); return; }
1554 
1555  // Nuke any module and loader so we have nothing loaded that uses /jevois:
1556  if (itsModule) { removeComponent(itsModule); itsModule.reset(); }
1557  if (itsLoader) itsLoader.reset();
1558 
1559  // Unmount /jevois:
1560  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1561  if (std::system("mount -o remount,ro /jevois")) LERROR("Failed to remount /jevois read-only -- IGNORED");
1562 
1563  // Now set the backing partition in mass-storage gadget:
1564  std::ofstream ofs(JEVOIS_USBSD_SYS);
1565  if (ofs.is_open() == false) LFATAL("Cannot setup mass-storage backing file to " << JEVOIS_USBSD_SYS);
1566  ofs << JEVOIS_USBSD_FILE << std::endl;
1567 
1568  LINFO("Exported JEVOIS partition of microSD to host computer as virtual flash drive.");
1569  itsMassStorageMode.store(true);
1570 }
1571 
1572 // ####################################################################################################
1573 void jevois::Engine::stopMassStorageMode()
1574 {
1575  //itsMassStorageMode.store(false);
1576  LINFO("JeVois virtual USB drive ejected by host -- REBOOTING");
1577  reboot();
1578 }
1579 #endif
1580 
1581 // ####################################################################################################
1583 {
1584  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1585  if (std::system("sync")) LERROR("Disk sync failed -- IGNORED");
1586 #ifdef JEVOIS_PLATFORM_A33
1587  itsCheckingMassStorage.store(false);
1588 #endif
1589  itsRunning.store(false);
1590 
1591 #ifdef JEVOIS_PLATFORM_A33
1592  // Hard reset to avoid possible hanging during module unload, etc:
1593  if ( ! std::ofstream("/proc/sys/kernel/sysrq").put('1')) LERROR("Cannot trigger hard reset -- please unplug me!");
1594  if ( ! std::ofstream("/proc/sysrq-trigger").put('s')) LERROR("Cannot trigger hard reset -- please unplug me!");
1595  if ( ! std::ofstream("/proc/sysrq-trigger").put('b')) LERROR("Cannot trigger hard reset -- please unplug me!");
1596 #endif
1597 
1598  this->quit();
1599  //std::terminate();
1600 }
1601 
1602 // ####################################################################################################
1604 {
1605  // must be locked, camera and gadget must exist:
1606  itsGadget->abortStream();
1607  itsCamera->abortStream();
1608  itsStreaming.store(false);
1609  itsGadget->streamOff();
1610  itsCamera->streamOff();
1611  itsRunning.store(false);
1612 
1613  //std::terminate();
1614 }
1615 
1616 // ####################################################################################################
1617 void jevois::Engine::cmdInfo(std::shared_ptr<UserInterface> s, bool showAll, std::string const & pfx)
1618 {
1619  s->writeString(pfx, "help - print this help message");
1620  s->writeString(pfx, "help2 - print compact help message about current vision module only");
1621  s->writeString(pfx, "info - show system information including CPU speed, load and temperature");
1622  s->writeString(pfx, "setpar <name> <value> - set a parameter value");
1623  s->writeString(pfx, "getpar <name> - get a parameter value(s)");
1624  s->writeString(pfx, "runscript <filename> - run script commands in specified file");
1625  s->writeString(pfx, "setcam <ctrl> <val> - set camera control <ctrl> to value <val>");
1626  s->writeString(pfx, "getcam <ctrl> - get value of camera control <ctrl>");
1627 
1628  if (showAll || camreg::get())
1629  {
1630  s->writeString(pfx, "setcamreg <reg> <val> - set raw camera register <reg> to value <val>");
1631  s->writeString(pfx, "getcamreg <reg> - get value of raw camera register <reg>");
1632  s->writeString(pfx, "setimureg <reg> <val> - set raw IMU register <reg> to value <val>");
1633  s->writeString(pfx, "getimureg <reg> - get value of raw IMU register <reg>");
1634  s->writeString(pfx, "setimuregs <reg> <num> <val1> ... <valn> - set array of raw IMU register values");
1635  s->writeString(pfx, "getimuregs <reg> <num> - get array of raw IMU register values");
1636  s->writeString(pfx, "setdmpreg <reg> <val> - set raw DMP register <reg> to value <val>");
1637  s->writeString(pfx, "getdmpreg <reg> - get value of raw DMP register <reg>");
1638  s->writeString(pfx, "setdmpregs <reg> <num> <val1> ... <valn> - set array of raw DMP register values");
1639  s->writeString(pfx, "getdmpregs <reg> <num> - get array of raw DMP register values");
1640  }
1641 
1642  s->writeString(pfx, "listmappings - list all available video mappings");
1643  s->writeString(pfx, "setmapping <num> - select video mapping <num>, only possible while not streaming");
1644  s->writeString(pfx, "setmapping2 <CAMmode> <CAMwidth> <CAMheight> <CAMfps> <Vendor> <Module> - set no-USB-out "
1645  "video mapping defined on the fly, while not streaming");
1646  s->writeString(pfx, "reload - reload and reset the current module");
1647 
1648  if (showAll || itsCurrentMapping.ofmt == 0 || itsManualStreamon)
1649  {
1650  s->writeString(pfx, "streamon - start camera video streaming");
1651  s->writeString(pfx, "streamoff - stop camera video streaming");
1652  }
1653 
1654  s->writeString(pfx, "ping - returns 'ALIVE'");
1655  s->writeString(pfx, "serlog <string> - forward string to the serial port(s) specified by the serlog parameter");
1656  s->writeString(pfx, "serout <string> - forward string to the serial port(s) specified by the serout parameter");
1657 
1658  if (showAll)
1659  {
1660  // Hide machine-oriented commands by default
1661  s->writeString(pfx, "caminfo - returns machine-readable info about camera parameters");
1662  s->writeString(pfx, "cmdinfo [all] - returns machine-readable info about Engine commands");
1663  s->writeString(pfx, "modcmdinfo - returns machine-readable info about Module commands");
1664  s->writeString(pfx, "paraminfo [hot|mod|modhot] - returns machine-readable info about parameters");
1665  s->writeString(pfx, "serinfo - returns machine-readable info about serial settings (serout serlog serstyle serprec serstamp)");
1666  s->writeString(pfx, "fileget <filepath> - get a file from JeVois to the host. Use with caution!");
1667  s->writeString(pfx, "fileput <filepath> - put a file from the host to JeVois. Use with caution!");
1668  }
1669 
1670 #ifdef JEVOIS_PLATFORM_A33
1671  s->writeString(pfx, "usbsd - export the JEVOIS partition of the microSD card as a virtual USB drive");
1672 #endif
1673  s->writeString(pfx, "sync - commit any pending data write to microSD");
1674  s->writeString(pfx, "date [date and time] - get or set the system date and time");
1675 
1676  s->writeString(pfx, "!<string> - execute <string> as a Linux shell command. Use with caution!");
1677  s->writeString(pfx, "shell <string> - execute <string> as a Linux shell command. Use with caution!");
1678  s->writeString(pfx, "shellstart - execute all subsequent commands as Linux shell commands. Use with caution!");
1679  s->writeString(pfx, "shellstop - stop executing all subsequent commands as Linux shell commands.");
1680 
1681 #ifdef JEVOIS_PRO
1682  s->writeString(pfx, "dnnget <key> - download and install a DNN from JeVois Model Converter");
1683 #endif
1684 
1685 #ifdef JEVOIS_PLATFORM
1686  s->writeString(pfx, "restart - restart the JeVois smart camera");
1687 #endif
1688 
1689 #ifndef JEVOIS_PLATFORM_A33
1690  s->writeString(pfx, "quit - quit this program");
1691 #endif
1692 }
1693 
1694 // ####################################################################################################
1695 void jevois::Engine::modCmdInfo(std::shared_ptr<UserInterface> s, std::string const & pfx)
1696 {
1697  if (itsModule)
1698  {
1699  std::stringstream css; itsModule->supportedCommands(css);
1700  for (std::string line; std::getline(css, line); /* */) s->writeString(pfx, line);
1701  }
1702 }
1703 
1704 // ####################################################################################################
1705 bool jevois::Engine::parseCommand(std::string const & str, std::shared_ptr<UserInterface> s, std::string const & pfx)
1706 {
1707  // itsMtx should be locked by caller
1708 
1709  std::string errmsg;
1710 
1711  // If we are in shell mode, pass any command to the shell except for 'shellstop':
1712  if (itsShellMode)
1713  {
1714  if (str == "shellstop") { itsShellMode = false; return true; }
1715 
1716  std::string ret = jevois::system(str, true);
1717  std::vector<std::string> rvec = jevois::split(ret, "\n");
1718  for (std::string const & r : rvec) s->writeString(pfx, r);
1719  return true;
1720  }
1721 
1722  // Note: ModemManager on Ubuntu sends this on startup, kill ModemManager to avoid:
1723  // 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
1724  //
1725  // AT^SQPORT?
1726  // AT
1727  // AT
1728  // AT
1729  // ~
1730  //
1731  // then later on it insists on trying to mess with us, issuing things like AT, AT+CGMI, AT+GMI, AT+CGMM, AT+GMM,
1732  // AT%IPSYS?, ATE0, ATV1, etc etc
1733 
1734  switch (str.length())
1735  {
1736  case 0:
1737  LDEBUG("Ignoring empty string"); return true;
1738  break;
1739 
1740  case 1:
1741  if (str[0] == '~') { LDEBUG("Ignoring modem config command [~]"); return true; }
1742 
1743  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1744  // from the arduino to be printed out to the user:
1745  if (str[0] == '#') { sendSerial(str, true); return true; }
1746  break;
1747 
1748  default: // length is 2 or more:
1749 
1750  // Ignore any command that starts with a '~':
1751  if (str[0] == '~') { LDEBUG("Ignoring modem config command [" << str << ']'); return true; }
1752 
1753  // Ignore any command that starts with "AT":
1754  if (str[0] == 'A' && str[1] == 'T') { LDEBUG("Ignoring AT command [" << str <<']'); return true; }
1755 
1756  // If the string starts with "#", then just print it out on the serlog port(s). We use this to allow debug messages
1757  // in the arduino to be printed out to the user:
1758  if (str[0] == '#') { sendSerial(str, true); return true; }
1759 
1760  // If the string starts with "!", this is like the "shell" command, but parsed differently:
1761  std::string cmd, rem;
1762  if (str[0] == '!')
1763  {
1764  cmd = "shell"; rem = str.substr(1);
1765  }
1766  else
1767  {
1768  // Get the first word, i.e., the command:
1769  size_t const idx = str.find(' ');
1770  if (idx == str.npos) cmd = str;
1771  else { cmd = str.substr(0, idx); if (idx < str.length()) rem = str.substr(idx+1); }
1772  }
1773 
1774  // ----------------------------------------------------------------------------------------------------
1775  if (cmd == "help")
1776  {
1777  // Show all commands, first ours, as supported below:
1778  s->writeString(pfx, "GENERAL COMMANDS:");
1779  s->writeString(pfx, "");
1780  cmdInfo(s, false, pfx);
1781  s->writeString(pfx, "");
1782 
1783  // Then the module's custom commands, if any:
1784  if (itsModule)
1785  {
1786  s->writeString(pfx, "MODULE-SPECIFIC COMMANDS:");
1787  s->writeString(pfx, "");
1788  modCmdInfo(s, pfx);
1789  s->writeString(pfx, "");
1790  }
1791 
1792  // Get the help message for our parameters and write it out line by line so the serial fixes the line endings:
1793  std::stringstream pss; constructHelpMessage(pss);
1794  for (std::string line; std::getline(pss, line); /* */) s->writeString(pfx, line);
1795 
1796  // Show all camera controls
1797  s->writeString(pfx, "AVAILABLE CAMERA CONTROLS:");
1798  s->writeString(pfx, "");
1799 
1800  foreachCamCtrl([this,&pfx,&s](struct v4l2_queryctrl & qc, std::set<int> & doneids)
1801  {
1802  try
1803  {
1804  std::string hlp = camCtrlHelp(qc, doneids);
1805  if (hlp.empty() == false) s->writeString(pfx, hlp);
1806  } catch (...) { } // silently ignore errors, e.g., some write-only controls
1807  });
1808  return true;
1809  }
1810 
1811  // ----------------------------------------------------------------------------------------------------
1812  if (cmd == "caminfo")
1813  {
1814  // Machine-readable list of camera parameters:
1815  foreachCamCtrl([this,&pfx,&s](struct v4l2_queryctrl & qc, std::set<int> & doneids)
1816  {
1817  try
1818  {
1819  std::string hlp = camCtrlInfo(qc, doneids);
1820  if (hlp.empty() == false) s->writeString(pfx, hlp);
1821  } catch (...) { } // silently ignore errors, e.g., some write-only controls
1822  });
1823  return true;
1824  }
1825 
1826  // ----------------------------------------------------------------------------------------------------
1827  if (cmd == "cmdinfo")
1828  {
1829  bool showAll = (rem == "all") ? true : false;
1830  cmdInfo(s, showAll, pfx);
1831  return true;
1832  }
1833 
1834  // ----------------------------------------------------------------------------------------------------
1835  if (cmd == "modcmdinfo")
1836  {
1837  modCmdInfo(s, pfx);
1838  return true;
1839  }
1840 
1841  // ----------------------------------------------------------------------------------------------------
1842  if (cmd == "paraminfo")
1843  {
1844  std::map<std::string, std::string> categs;
1845  bool skipFrozen = (rem == "hot" || rem == "modhot") ? true : false;
1846 
1847  if (rem == "mod" || rem == "modhot")
1848  {
1849  // Report only on our module's parameter, if any:
1850  if (itsModule) itsModule->paramInfo(s, categs, skipFrozen, instanceName(), pfx);
1851  }
1852  else
1853  {
1854  // Report on all parameters:
1855  paramInfo(s, categs, skipFrozen, "", pfx);
1856  }
1857 
1858  return true;
1859  }
1860 
1861  // ----------------------------------------------------------------------------------------------------
1862  if (cmd == "serinfo")
1863  {
1864  std::string info = getParamStringUnique("serout") + ' ' + getParamStringUnique("serlog");
1865  if (auto mod = dynamic_cast<jevois::StdModule *>(itsModule.get()))
1866  info += ' ' + mod->getParamStringUnique("serstyle") + ' ' + mod->getParamStringUnique("serprec") +
1867  ' ' + mod->getParamStringUnique("serstamp");
1868  else info += " - - -";
1869 
1870  s->writeString(pfx, info);
1871 
1872  return true;
1873  }
1874 
1875  // ----------------------------------------------------------------------------------------------------
1876  if (cmd == "help2")
1877  {
1878  if (itsModule)
1879  {
1880  // Start with the module's commands:
1881  std::stringstream css; itsModule->supportedCommands(css);
1882  s->writeString(pfx, "MODULE-SPECIFIC COMMANDS:");
1883  s->writeString(pfx, "");
1884  for (std::string line; std::getline(css, line); /* */) s->writeString(pfx, line);
1885  s->writeString(pfx, "");
1886 
1887  // Now the parameters for that module (and its subs) only:
1888  s->writeString(pfx, "MODULE PARAMETERS:");
1889  s->writeString(pfx, "");
1890 
1891  // Keep this in sync with Manager::constructHelpMessage():
1892  std::unordered_map<std::string, // category:description
1893  std::unordered_map<std::string, // --name (type) default=[def]
1894  std::vector<std::pair<std::string, // component name
1895  std::string // current param value
1896  > > > > helplist;
1897  itsModule->populateHelpMessage("", helplist);
1898 
1899  if (helplist.empty())
1900  s->writeString(pfx, "None.");
1901  else
1902  {
1903  for (auto const & c : helplist)
1904  {
1905  // Print out the category name and description
1906  s->writeString(pfx, c.first);
1907 
1908  // Print out the parameter details
1909  for (auto const & n : c.second)
1910  {
1911  std::vector<std::string> tok = jevois::split(n.first, "[\\r\\n]+");
1912  bool first = true;
1913  for (auto const & t : tok)
1914  {
1915  // Add current value info to the first thing we write (which is name, default, etc)
1916  if (first)
1917  {
1918  auto const & v = n.second;
1919  if (v.size() == 1) // only one component using this param
1920  {
1921  if (v[0].second.empty())
1922  s->writeString(pfx, t); // only one comp, and using default val
1923  else
1924  s->writeString(pfx, t + " current=[" + v[0].second + ']'); // using non-default val
1925  }
1926  else if (v.size() > 1) // several components using this param with possibly different values
1927  {
1928  std::string sss = t + " current=";
1929  for (auto const & pp : v)
1930  if (pp.second.empty() == false) sss += '[' + pp.first + ':' + pp.second + "] ";
1931  s->writeString(pfx, sss);
1932  }
1933  else s->writeString(pfx, t); // no non-default value(s) to report
1934 
1935  first = false;
1936  }
1937 
1938  else // just write out the other lines (param description)
1939  s->writeString(pfx, t);
1940  }
1941  }
1942  s->writeString(pfx, "");
1943  }
1944  }
1945  }
1946  else
1947  s->writeString(pfx, "No module loaded.");
1948 
1949  return true;
1950  }
1951 
1952  // ----------------------------------------------------------------------------------------------------
1953  if (cmd == "info")
1954  {
1955  s->writeString(pfx, "INFO: JeVois " JEVOIS_VERSION_STRING);
1956  s->writeString(pfx, "INFO: " + jevois::getSysInfoVersion());
1957  s->writeString(pfx, "INFO: " + jevois::getSysInfoCPU());
1958  s->writeString(pfx, "INFO: " + jevois::getSysInfoMem());
1959  if (itsModule) s->writeString(pfx, "INFO: " + itsCurrentMapping.str());
1960  else s->writeString(pfx, "INFO: " + jevois::VideoMapping().str());
1961  return true;
1962  }
1963 
1964  // ----------------------------------------------------------------------------------------------------
1965  if (cmd == "setpar")
1966  {
1967  size_t const remidx = rem.find(' ');
1968  if (remidx != rem.npos)
1969  {
1970  std::string const desc = rem.substr(0, remidx);
1971  if (remidx < rem.length())
1972  {
1973  std::string const val = rem.substr(remidx+1);
1974  setParamString(desc, val);
1975  return true;
1976  }
1977  }
1978  errmsg = "Need to provide a parameter name and a parameter value in setpar";
1979  }
1980 
1981  // ----------------------------------------------------------------------------------------------------
1982  if (cmd == "getpar")
1983  {
1984  auto vec = getParamString(rem);
1985  for (auto const & p : vec) s->writeString(pfx, p.first + ' ' + p.second);
1986  return true;
1987  }
1988 
1989  // ----------------------------------------------------------------------------------------------------
1990  if (cmd == "setcam")
1991  {
1992  std::istringstream ss(rem); std::string ctrl; int val; ss >> ctrl >> val;
1993  struct v4l2_control c = { }; c.id = camctrlid(ctrl); c.value = val;
1994  itsCamera->setControl(c);
1995  return true;
1996  }
1997 
1998  // ----------------------------------------------------------------------------------------------------
1999  if (cmd == "getcam")
2000  {
2001  struct v4l2_control c = { }; c.id = camctrlid(rem);
2002  itsCamera->getControl(c);
2003  s->writeString(pfx, rem + ' ' + std::to_string(c.value));
2004  return true;
2005  }
2006 
2007  // ----------------------------------------------------------------------------------------------------
2008  if (cmd == "setcamreg")
2009  {
2010  if (camreg::get())
2011  {
2012  auto cam = std::dynamic_pointer_cast<jevois::Camera>(itsCamera);
2013  if (cam)
2014  {
2015  // Read register and value as strings, then std::stoi to int, supports 0x (and 0 for octal, caution)
2016  std::istringstream ss(rem); std::string reg, val; ss >> reg >> val;
2017  cam->writeRegister(std::stoi(reg, nullptr, 0), std::stoi(val, nullptr, 0));
2018  return true;
2019  }
2020  else errmsg = "Not using a camera for video input";
2021  }
2022  else errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
2023  }
2024 
2025  // ----------------------------------------------------------------------------------------------------
2026  if (cmd == "getcamreg")
2027  {
2028  if (camreg::get())
2029  {
2030  auto cam = std::dynamic_pointer_cast<jevois::Camera>(itsCamera);
2031  if (cam)
2032  {
2033  unsigned int val = cam->readRegister(std::stoi(rem, nullptr, 0));
2034  std::ostringstream os; os << std::hex << val;
2035  s->writeString(pfx, os.str());
2036  return true;
2037  }
2038  else errmsg = "Not using a camera for video input";
2039  }
2040  else errmsg = "Access to camera registers is disabled, enable with: setpar camreg true";
2041  }
2042 
2043  // ----------------------------------------------------------------------------------------------------
2044  if (cmd == "setimureg")
2045  {
2046  if (imureg::get())
2047  {
2048  if (itsIMU)
2049  {
2050  // Read register and value as strings, then std::stoi to int, supports 0x (and 0 for octal, caution)
2051  std::istringstream ss(rem); std::string reg, val; ss >> reg >> val;
2052  itsIMU->writeRegister(std::stoi(reg, nullptr, 0), std::stoi(val, nullptr, 0));
2053  return true;
2054  }
2055  else errmsg = "No IMU driver loaded";
2056  }
2057  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2058  }
2059 
2060  // ----------------------------------------------------------------------------------------------------
2061  if (cmd == "getimureg")
2062  {
2063  if (imureg::get())
2064  {
2065  if (itsIMU)
2066  {
2067  unsigned int val = itsIMU->readRegister(std::stoi(rem, nullptr, 0));
2068  std::ostringstream os; os << std::hex << val;
2069  s->writeString(pfx, os.str());
2070  return true;
2071  }
2072  else errmsg = "No IMU driver loaded";
2073  }
2074  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2075  }
2076 
2077  // ----------------------------------------------------------------------------------------------------
2078  if (cmd == "setimuregs")
2079  {
2080  if (imureg::get())
2081  {
2082  if (itsIMU)
2083  {
2084  // Read register and value as strings, then std::stoi to int, supports 0x (and 0 for octal, caution)
2085  std::vector<std::string> v = jevois::split(rem);
2086  if (v.size() < 3) errmsg = "Malformed arguments, need at least 3";
2087  else
2088  {
2089  unsigned short reg = std::stoi(v[0], nullptr, 0);
2090  size_t num = std::stoi(v[1], nullptr, 0);
2091  if (num > 32) errmsg = "Maximum transfer size is 32 bytes";
2092  else if (num != v.size() - 2) errmsg = "Incorrect number of data bytes, should pass " + v[1] + " values.";
2093  else
2094  {
2095  unsigned char data[32];
2096  for (size_t i = 2; i < v.size(); ++i) data[i-2] = std::stoi(v[i], nullptr, 0) & 0xff;
2097 
2098  itsIMU->writeRegisterArray(reg, data, num);
2099  return true;
2100  }
2101  }
2102  }
2103  else errmsg = "No IMU driver loaded";
2104  }
2105  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2106  }
2107 
2108  // ----------------------------------------------------------------------------------------------------
2109  if (cmd == "getimuregs")
2110  {
2111  if (imureg::get())
2112  {
2113  if (itsIMU)
2114  {
2115  std::istringstream ss(rem); std::string reg, num; ss >> reg >> num;
2116  int n = std::stoi(num, nullptr, 0);
2117 
2118  if (n > 32) errmsg = "Maximum transfer size is 32 bytes";
2119  else
2120  {
2121  unsigned char data[32];
2122  itsIMU->readRegisterArray(std::stoi(reg, nullptr, 0), data, n);
2123 
2124  std::ostringstream os; os << std::hex;
2125  for (int i = 0; i < n; ++i) os << (unsigned int)(data[i]) << ' ';
2126  s->writeString(pfx, os.str());
2127  return true;
2128  }
2129  }
2130  else errmsg = "No IMU driver loaded";
2131  }
2132  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2133  }
2134 
2135  // ----------------------------------------------------------------------------------------------------
2136  if (cmd == "setdmpreg")
2137  {
2138  if (imureg::get())
2139  {
2140  if (itsIMU)
2141  {
2142  // Read register and value as strings, then std::stoi to int, supports 0x (and 0 for octal, caution)
2143  std::istringstream ss(rem); std::string reg, val; ss >> reg >> val;
2144  itsIMU->writeDMPregister(std::stoi(reg, nullptr, 0), std::stoi(val, nullptr, 0));
2145  return true;
2146  }
2147  else errmsg = "No IMU driver loaded";
2148  }
2149  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2150  }
2151 
2152  // ----------------------------------------------------------------------------------------------------
2153  if (cmd == "getdmpreg")
2154  {
2155  if (camreg::get())
2156  {
2157  if (itsIMU)
2158  {
2159  unsigned int val = itsIMU->readDMPregister(std::stoi(rem, nullptr, 0));
2160  std::ostringstream os; os << std::hex << val;
2161  s->writeString(pfx, os.str());
2162  return true;
2163  }
2164  else errmsg = "No IMU driver loaded";
2165  }
2166  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2167  }
2168 
2169  // ----------------------------------------------------------------------------------------------------
2170  if (cmd == "setdmpregs")
2171  {
2172  if (camreg::get())
2173  {
2174  if (itsIMU)
2175  {
2176  // Read register and value as strings, then std::stoi to int, supports 0x (and 0 for octal, caution)
2177  std::vector<std::string> v = jevois::split(rem);
2178  if (v.size() < 3) errmsg = "Malformed arguments, need at least 3";
2179  else
2180  {
2181  unsigned short reg = std::stoi(v[0], nullptr, 0);
2182  size_t num = std::stoi(v[1], nullptr, 0);
2183  if (num > 32) errmsg = "Maximum transfer size is 32 bytes";
2184  else if (num != v.size() - 2) errmsg = "Incorrect number of data bytes, should pass " + v[1] + " values.";
2185  else
2186  {
2187  unsigned char data[32];
2188  for (size_t i = 2; i < v.size(); ++i) data[i-2] = std::stoi(v[i], nullptr, 0) & 0xff;
2189 
2190  itsIMU->writeDMPregisterArray(reg, data, num);
2191  return true;
2192  }
2193  }
2194  }
2195  else errmsg = "No IMU driver loaded";
2196  }
2197  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2198  }
2199 
2200  // ----------------------------------------------------------------------------------------------------
2201  if (cmd == "getdmpregs")
2202  {
2203  if (imureg::get())
2204  {
2205  if (itsIMU)
2206  {
2207  std::istringstream ss(rem); std::string reg, num; ss >> reg >> num;
2208  int n = std::stoi(num, nullptr, 0);
2209 
2210  if (n > 32) errmsg = "Maximum transfer size is 32 bytes";
2211  else
2212  {
2213  unsigned char data[32];
2214  itsIMU->readDMPregisterArray(std::stoi(reg, nullptr, 0), data, n);
2215 
2216  std::ostringstream os; os << std::hex;
2217  for (int i = 0; i < n; ++i) os << (unsigned int)(data[i]) << ' ';
2218  s->writeString(pfx, os.str());
2219  return true;
2220  }
2221  }
2222  else errmsg = "No IMU driver loaded";
2223  }
2224  else errmsg = "Access to IMU registers is disabled, enable with: setpar imureg true";
2225  }
2226 
2227  // ----------------------------------------------------------------------------------------------------
2228  if (cmd == "listmappings")
2229  {
2230  s->writeString(pfx, "AVAILABLE VIDEO MAPPINGS:");
2231  s->writeString(pfx, "");
2232  for (size_t idx = 0; idx < itsMappings.size(); ++idx)
2233  {
2234  std::string idxstr = std::to_string(idx);
2235  if (idxstr.length() < 5) idxstr = std::string(5 - idxstr.length(), ' ') + idxstr; // pad to 5-char long
2236  s->writeString(pfx, idxstr + " - " + itsMappings[idx].str());
2237  }
2238  return true;
2239  }
2240 
2241  // ----------------------------------------------------------------------------------------------------
2242  if (cmd == "setmapping")
2243  {
2244  size_t const idx = std::stoi(rem);
2245 
2246  if (itsStreaming.load() && itsCurrentMapping.ofmt)
2247  errmsg = "Cannot set mapping while streaming: Stop your webcam program on the host computer first.";
2248  else if (idx >= itsMappings.size())
2249  errmsg = "Requested mapping index " + std::to_string(idx) + " out of range [0 .. " +
2250  std::to_string(itsMappings.size()-1) + ']';
2251  else
2252  {
2253  try
2254  {
2255  setFormatInternal(idx);
2256  return true;
2257  }
2258  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
2259  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
2260  }
2261  }
2262 
2263  // ----------------------------------------------------------------------------------------------------
2264  if (cmd == "setmapping2")
2265  {
2266  if (itsStreaming.load() && itsCurrentMapping.ofmt)
2267  errmsg = "Cannot set mapping while streaming: Stop your webcam program on the host computer first.";
2268  else
2269  {
2270  try
2271  {
2272  jevois::VideoMapping m; std::istringstream full("NONE 0 0 0.0 " + rem); full >> m;
2273  setFormatInternal(m);
2274  return true;
2275  }
2276  catch (std::exception const & e) { errmsg = "Error parsing or setting mapping [" + rem + "]: " + e.what(); }
2277  catch (...) { errmsg = "Error parsing or setting mapping [" + rem + ']'; }
2278  }
2279  }
2280 
2281  // ----------------------------------------------------------------------------------------------------
2282  if (cmd == "reload")
2283  {
2284  setFormatInternal(itsCurrentMapping, true);
2285  return true;
2286  }
2287 
2288  // ----------------------------------------------------------------------------------------------------
2289  if (itsCurrentMapping.ofmt == 0 || itsCurrentMapping.ofmt == JEVOISPRO_FMT_GUI || itsManualStreamon)
2290  {
2291  if (cmd == "streamon")
2292  {
2293  // keep this in sync with streamOn(), modulo the fact that here we are already locked:
2294  itsCamera->streamOn();
2295  itsGadget->streamOn();
2296  itsStreaming.store(true);
2297  return true;
2298  }
2299 
2300  if (cmd == "streamoff")
2301  {
2302  // keep this in sync with streamOff(), modulo the fact that here we are already locked:
2303  itsGadget->abortStream();
2304  itsCamera->abortStream();
2305 
2306  itsStreaming.store(false);
2307 
2308  itsGadget->streamOff();
2309  itsCamera->streamOff();
2310  return true;
2311  }
2312  }
2313 
2314  // ----------------------------------------------------------------------------------------------------
2315  if (cmd == "ping")
2316  {
2317  s->writeString(pfx, "ALIVE");
2318  return true;
2319  }
2320 
2321  // ----------------------------------------------------------------------------------------------------
2322  if (cmd == "serlog")
2323  {
2324  sendSerial(rem, true);
2325  return true;
2326  }
2327 
2328  // ----------------------------------------------------------------------------------------------------
2329  if (cmd == "serout")
2330  {
2331  sendSerial(rem, false);
2332  return true;
2333  }
2334 
2335  // ----------------------------------------------------------------------------------------------------
2336 #ifdef JEVOIS_PLATFORM_A33
2337  if (cmd == "usbsd")
2338  {
2339  if (itsStreaming.load())
2340  {
2341  errmsg = "Cannot export microSD over USB while streaming: ";
2342  if (itsCurrentMapping.ofmt) errmsg += "Stop your webcam program on the host computer first.";
2343  else errmsg += "Issue a 'streamoff' command first.";
2344  }
2345  else
2346  {
2347  startMassStorageMode();
2348  return true;
2349  }
2350  }
2351 #endif
2352 
2353  // ----------------------------------------------------------------------------------------------------
2354  if (cmd == "sync")
2355  {
2356  if (std::system("sync")) errmsg = "Disk sync failed";
2357  else return true;
2358  }
2359 
2360  // ----------------------------------------------------------------------------------------------------
2361  if (cmd == "date")
2362  {
2363  std::string dat = jevois::system("/bin/date " + rem);
2364  s->writeString(pfx, "date now " + dat.substr(0, dat.size()-1)); // skip trailing newline
2365  return true;
2366  }
2367 
2368  // ----------------------------------------------------------------------------------------------------
2369  if (cmd == "runscript")
2370  {
2371  std::string const fname = itsModule ? itsModule->absolutePath(rem).string() : rem;
2372 
2373  try { runScriptFromFile(fname, s, true); return true; }
2374  catch (...) { errmsg = "Script " + fname + " execution failed"; }
2375  }
2376 
2377  // ----------------------------------------------------------------------------------------------------
2378  if (cmd == "shell")
2379  {
2380  std::string ret = jevois::system(rem, true);
2381  std::vector<std::string> rvec = jevois::split(ret, "\n");
2382  for (std::string const & r : rvec) s->writeString(pfx, r);
2383  return true;
2384  }
2385 
2386  // ----------------------------------------------------------------------------------------------------
2387  if (cmd == "shellstart")
2388  {
2389  itsShellMode = true;
2390  return true;
2391  // note: shellstop is handled above
2392  }
2393 
2394 #ifdef JEVOIS_PRO
2395  // ----------------------------------------------------------------------------------------------------
2396  if (cmd == "dnnget")
2397  {
2398  if (rem.length() != 4 || std::regex_match(rem, std::regex("^[a-zA-Z0-9]+$")) == false)
2399  errmsg = "Key must be a 4-character alphanumeric string, as emailed to you by the model converter.";
2400  else
2401  {
2402  // Download the zip using curl:
2403  s->writeString(pfx, "Downloading custom DNN model " + rem + " ...");
2404  std::string const zip = rem + ".zip";
2405  std::string ret = jevois::system("/usr/bin/curl " JEVOIS_CUSTOM_DNN_URL "/" + zip + " -o "
2406  JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
2407  std::vector<std::string> rvec = jevois::split(ret, "\n");
2408  for (std::string const & r : rvec) s->writeString(pfx, r);
2409 
2410  // Check that the file exists:
2411  std::ifstream ifs(JEVOIS_CUSTOM_DNN_PATH "/" + zip);
2412  if (ifs.is_open() == false)
2413  errmsg = "Failed to download. Check network connectivity and available disk space.";
2414  else
2415  {
2416  // Unzip it:
2417  s->writeString(pfx, "Unpacking custom DNN model " + rem + " ...");
2418  ret = jevois::system("/usr/bin/unzip -o " JEVOIS_CUSTOM_DNN_PATH "/" + zip +
2419  " -d " JEVOIS_CUSTOM_DNN_PATH, true);
2420  rvec = jevois::split(ret, "\n"); for (std::string const & r : rvec) s->writeString(pfx, r);
2421 
2422  ret = jevois::system("/bin/rm " JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
2423  rvec = jevois::split(ret, "\n"); for (std::string const & r : rvec) s->writeString(pfx, r);
2424 
2425  s->writeString(pfx, "Reload your model zoo for changes to take effect.");
2426 
2427  return true;
2428  }
2429  }
2430  }
2431 #endif
2432 
2433  // ----------------------------------------------------------------------------------------------------
2434  if (cmd == "fileget")
2435  {
2436  std::shared_ptr<jevois::Serial> ser = std::dynamic_pointer_cast<jevois::Serial>(s);
2437  if (!ser)
2438  errmsg = "File transfer only supported over USB or Hard serial ports";
2439  else
2440  {
2441  std::string const abspath = itsModule ? itsModule->absolutePath(rem).string() : rem;
2442  ser->fileGet(abspath);
2443  return true;
2444  }
2445  }
2446 
2447  // ----------------------------------------------------------------------------------------------------
2448  if (cmd == "fileput")
2449  {
2450  std::shared_ptr<jevois::Serial> ser = std::dynamic_pointer_cast<jevois::Serial>(s);
2451  if (!ser)
2452  errmsg = "File transfer only supported over USB or Hard serial ports";
2453  else
2454  {
2455  std::string const abspath = itsModule ? itsModule->absolutePath(rem).string() : rem;
2456  ser->filePut(abspath);
2457  if (std::system("sync")) { } // quietly ignore any errors on sync
2458  return true;
2459  }
2460  }
2461 
2462 #ifdef JEVOIS_PLATFORM
2463  // ----------------------------------------------------------------------------------------------------
2464  if (cmd == "restart")
2465  {
2466  s->writeString(pfx, "Restart command received - bye-bye!");
2467 
2468  if (itsStreaming.load())
2469  s->writeString(pfx, "ERR Video streaming is on - you should quit your video viewer before rebooting");
2470 
2471  if (std::system("sync")) s->writeString(pfx, "ERR Disk sync failed -- IGNORED");
2472 
2473 #ifdef JEVOIS_PLATFORM_A33
2474  // Turn off the SD storage if it is there:
2475  std::ofstream(JEVOIS_USBSD_SYS).put('\n'); // ignore errors
2476 
2477  if (std::system("sync")) s->writeString(pfx, "ERR Disk sync failed -- IGNORED");
2478 #endif
2479 
2480  // Hard reboot:
2481  this->reboot();
2482  return true;
2483  }
2484  // ----------------------------------------------------------------------------------------------------
2485 #endif
2486 
2487 #ifndef JEVOIS_PLATFORM_A33
2488  // ----------------------------------------------------------------------------------------------------
2489  if (cmd == "quit")
2490  {
2491  s->writeString(pfx, "Quit command received - bye-bye!");
2492  this->quit();
2493  return true;
2494  }
2495  // ----------------------------------------------------------------------------------------------------
2496 #endif
2497  }
2498 
2499  // If we make it here, we did not parse the command. If we have an error message, that means we had started parsing
2500  // the command but it was buggy, so let's throw. Otherwise, we just return false to indicate that we did not parse
2501  // this command and maybe it is for the Module:
2502  if (errmsg.size()) throw std::runtime_error("Command error [" + str + "]: " + errmsg);
2503  return false;
2504 }
2505 
2506 // ####################################################################################################
2507 void jevois::Engine::runScriptFromFile(std::string const & filename, std::shared_ptr<jevois::UserInterface> ser,
2508  bool throw_no_file)
2509 {
2510  // itsMtx should be locked by caller
2511 
2512  // Try to find the file:
2513  std::ifstream ifs(filename);
2514  if (!ifs) { if (throw_no_file) LFATAL("Could not open file " << filename); else return; }
2515 
2516  // We need to identify a serial to send any errors to, if none was given to us. Let's use the GUI console, or the
2517  // serial in serlog, or, if none is specified there, the first available serial:
2518  if (!ser)
2519  {
2520  if (itsSerials.empty()) LFATAL("Need at least one active serial to run script");
2521 
2522  switch (serlog::get())
2523  {
2524  case jevois::engine::SerPort::Hard:
2525  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::Hard) { ser = s; break; }
2526  break;
2527 
2528  case jevois::engine::SerPort::USB:
2529  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::USB) { ser = s; break; }
2530  break;
2531 
2532  default: break;
2533  }
2534 
2535 #ifdef JEVOIS_PRO
2536  if (itsGUIhelper)
2537  for (auto & s : itsSerials) if (s->type() == jevois::UserInterface::Type::GUI) { ser = s; break; }
2538 #endif
2539 
2540  if (!ser) ser = itsSerials.front();
2541  }
2542 
2543  // Ok, run the script, plowing through any errors:
2544  size_t linenum = 0;
2545  for (std::string line; std::getline(ifs, line); /* */)
2546  {
2547  ++linenum;
2548 
2549  // Strip any extra whitespace at end, which could be a CR if the file was edited in Windows:
2550  line = jevois::strip(line);
2551 
2552  // Skip comments and empty lines:
2553  if (line.length() == 0 || line[0] == '#') continue;
2554 
2555  // Go and parse that line:
2556  try
2557  {
2558  bool parsed = false;
2559  try { parsed = parseCommand(line, ser); }
2560  catch (std::exception const & e)
2561  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + e.what()); }
2562  catch (...)
2563  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": Bogus command ["+line+"] ignored"); }
2564 
2565  if (parsed == false)
2566  {
2567  if (itsModule)
2568  {
2569  try { itsModule->parseSerial(line, ser); }
2570  catch (std::exception const & me)
2571  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum) + ": " + me.what()); }
2572  catch (...)
2573  { ser->writeString("ERR " + filename + ':' + std::to_string(linenum)+": Bogus command ["+line+"] ignored"); }
2574  }
2575  else ser->writeString("ERR Unsupported command [" + line + "] and no module");
2576  }
2577  }
2578  catch (...) { jevois::warnAndIgnoreException(); }
2579  }
2580 }
2581 
2582 // ####################################################################################################
2583 #ifdef JEVOIS_PRO
2584 // ####################################################################################################
2586 {
2587  ImGui::Columns(2, "camctrl");
2588 
2589  foreachCamCtrl([this](struct v4l2_queryctrl & qc, std::set<int> & doneids)
2590  {
2591  try { camCtrlGUI(qc, doneids); } catch (...) { }
2592  });
2593 
2594  ImGui::Columns(1);
2595 }
2596 
2597 // ####################################################################################################
2598 void jevois::Engine::camCtrlGUI(struct v4l2_queryctrl & qc, std::set<int> & doneids)
2599 {
2600  // See if we have this control:
2601  itsCamera->queryControl(qc);
2602  qc.id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
2603 
2604  // If we have already done this control, just return:
2605  if (doneids.find(qc.id) != doneids.end()) return; else doneids.insert(qc.id);
2606 
2607  // Control exists, let's also get its current value:
2608  struct v4l2_control ctrl = { }; ctrl.id = qc.id;
2609  itsCamera->getControl(ctrl);
2610 
2611  // Instantiate widgets depending on control type:
2612  ImGui::AlignTextToFramePadding();
2613  ImGui::TextUnformatted(reinterpret_cast<char const *>(qc.name));
2614  ImGui::NextColumn();
2615 
2616  // Grey out the item if it is disabled:
2617  if (qc.flags & V4L2_CTRL_FLAG_DISABLED)
2618  {
2619  ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
2620  ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
2621  }
2622 
2623  // We need a unique ID for each ImGui widget, and we will use no visible widget name:
2624  static char wname[16]; snprintf(wname, 16, "##c%d", ctrl.id);
2625  bool reset = false; // will set to true if we want a reset button
2626 
2627  switch (qc.type)
2628  {
2629  case V4L2_CTRL_TYPE_INTEGER:
2630  case V4L2_CTRL_TYPE_INTEGER_MENU:
2631  {
2632  // Do a slider if range is reasonable, otherwise typein:
2633  long range = long(qc.maximum) - long(qc.minimum);
2634  if (range > 1 && range < 5000)
2635  {
2636  if (ImGui::SliderInt(wname, &ctrl.value, qc.minimum, qc.maximum)) itsCamera->setControl(ctrl);
2637  reset = true;
2638  }
2639  else
2640  {
2641  if (ImGui::InputInt(wname, &ctrl.value, qc.step, qc.step * 2)) itsCamera->setControl(ctrl);
2642  reset = true;
2643  }
2644  }
2645  break;
2646 
2647  //case V4L2_CTRL_TYPE_INTEGER64:
2648  //{
2649  // double val = ctrl.value64;
2650  // if (ImGui::InputDouble(wname, &val)) { ctrl.value64 = long(val + 0.4999); itsCamera->setControl(ctrl); }
2651  //}
2652  //break;
2653 
2654  //case V4L2_CTRL_TYPE_STRING:
2655  // if (ImGui::InputText(wname, ctrl.string, sizeof(ctrl.string))) itsCamera->setControl(ctrl);
2656  // break;
2657 
2658  case V4L2_CTRL_TYPE_BOOLEAN:
2659  {
2660  bool checked = (ctrl.value != 0);
2661  if (ImGui::Checkbox(wname, &checked)) { ctrl.value = checked ? 1 : 0; itsCamera->setControl(ctrl); }
2662  }
2663  break;
2664 
2665 
2666  case V4L2_CTRL_TYPE_BUTTON:
2667  static char bname[16]; snprintf(bname, 16, "Go##%d", ctrl.id);
2668  if (ImGui::Button(bname)) { ctrl.value = 1; itsCamera->setControl(ctrl); }
2669  break;
2670 
2671  case V4L2_CTRL_TYPE_BITMASK:
2672  ///ss << " K " << qc.maximum << ' ' << qc.default_value << ' ' << ctrl.value;
2673  break;
2674 
2675  case V4L2_CTRL_TYPE_MENU:
2676  {
2677  struct v4l2_querymenu querymenu = { };
2678  querymenu.id = qc.id;
2679  char * items[qc.maximum - qc.minimum + 1];
2680 
2681  for (querymenu.index = qc.minimum; querymenu.index <= (unsigned int)qc.maximum; ++querymenu.index)
2682  {
2683  try { itsCamera->queryMenu(querymenu); } catch (...) { strncpy((char *)querymenu.name, "fixme", 32); }
2684  items[querymenu.index] = new char[32];
2685  strncpy(items[querymenu.index], (char const *)querymenu.name, 32);
2686  }
2687 
2688  int idx = ctrl.value - qc.minimum;
2689  if (ImGui::Combo(wname, &idx, items, qc.maximum - qc.minimum + 1))
2690  { ctrl.value = qc.minimum + idx; itsCamera->setControl(ctrl); }
2691 
2692  for (int i = qc.minimum; i <= qc.maximum; ++i) delete [] items[i];
2693  }
2694  break;
2695 
2696  default: break;
2697  }
2698 
2699  // Add a reset button if desired:
2700  if (reset)
2701  {
2702  static char rname[16]; snprintf(rname, 16, "Reset##%d", ctrl.id);
2703  ImGui::SameLine();
2704  if (ImGui::Button(rname)) { ctrl.value = qc.default_value; itsCamera->setControl(ctrl); }
2705  }
2706 
2707  // Restore any grey out:
2708  if (qc.flags & V4L2_CTRL_FLAG_DISABLED)
2709  {
2710  ImGui::PopItemFlag();
2711  ImGui::PopStyleVar();
2712  }
2713 
2714  // Ready for next row:
2715  ImGui::NextColumn();
2716 }
2717 
2718 // ####################################################################################################
2719 #endif // JEVOIS_PRO
2720 
2721 // ####################################################################################################
2723 {
2724  LDEBUG(comp->instanceName() << " -> " << std::hex << pyinst);
2725  std::lock_guard<std::mutex> _(itsPyRegMtx);
2726  auto itr = itsPythonRegistry.find(pyinst);
2727  if (itr != itsPythonRegistry.end()) LFATAL("Trying to register twice -- ABORT");
2728  itsPythonRegistry.insert(std::make_pair(pyinst, comp));
2729 }
2730 
2731 // ####################################################################################################
2733 {
2734  LDEBUG(comp->instanceName());
2735  std::lock_guard<std::mutex> _(itsPyRegMtx);
2736  auto itr = itsPythonRegistry.begin(), stop = itsPythonRegistry.end();
2737  while (itr != stop) if (itr->second == comp) itr = itsPythonRegistry.erase(itr); else ++itr;
2738 }
2739 
2740 // ####################################################################################################
2742 {
2743  LDEBUG(std::hex << pyinst);
2744  std::lock_guard<std::mutex> _(itsPyRegMtx);
2745  auto itr = itsPythonRegistry.find(pyinst);
2746  if (itr == itsPythonRegistry.end()) LFATAL("Python instance not registered -- ABORT");
2747  return itr->second;
2748 }
jevois::Engine::getDefaultVideoMappingIdx
size_t getDefaultVideoMappingIdx() const
Allow access to the default video mapping index.
Definition: Engine.C:1308
JEVOISPRO_FMT_GUI
#define JEVOISPRO_FMT_GUI
JeVois-Pro zero-copy display of camera input frame (to be used as output mode in VideoMapping)
Definition: Utils.H:31
jevois::MovieOutput
Video output to a movie file, using OpenCV video encoding.
Definition: MovieOutput.H:33
jevois::Component::instanceName
const std::string & instanceName() const
The instance name of this component.
Definition: Component.C:50
jevois::imu::get
Data collection mode RAW means that the latest available raw data is returned each time get() is called
jevois::UserInterface::readSome
virtual bool readSome(std::string &str)=0
Read some bytes if available, and return true and a string when one is complete (RETURN pressed)
jevois::OutputFrame
Exception-safe wrapper around a raw image to be sent over USB.
Definition: OutputFrame.H:52
jevois::Engine::foreachVideoMapping
void foreachVideoMapping(std::function< void(VideoMapping const &m)> &&func)
Run a function on every video mapping.
Definition: Engine.C:1312
jevois::Engine::quit
void quit()
Terminate the program.
Definition: Engine.C:1603
jevois::VideoDisplayGUI
Video output to local screen with basic GUI.
Definition: VideoDisplayGUI.H:36
Module.H
Async.H
PythonSupport.H
LDEBUG
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level.
Definition: Log.H:173
jevois::Manager
Manager of a hierarchy of Component objects.
Definition: Manager.H:73
JEVOIS_TIMED_LOCK
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition: Log.H:328
jevois::Gadget
JeVois gadget driver - exposes a uvcvideo interface to host computer connected over USB.
Definition: Gadget.H:65
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:258
jevois::UserInterface::Type::Hard
@ Hard
jevois::VideoDisplayGL
Video output to local screen.
Definition: VideoDisplayGL.H:50
jevois::PythonModule
Wrapper module to allow users to develop new modules written in Python.
Definition: PythonModule.H:333
jevois::getSysInfoMem
std::string getSysInfoMem()
Get memory info.
Definition: SysInfo.C:66
MovieInput.H
jevois::Component
A component of a model hierarchy.
Definition: Component.H:181
jevois::UserInterface::writeString
virtual void writeString(std::string const &str)=0
Write a string.
jevois::Watchdog
Simple watchdog class.
Definition: Watchdog.H:29
V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE
#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE
jevois::Engine::camera
std::shared_ptr< Camera > camera() const
Get a pointer to our Camera (may be null, especially if not using a camera but, eg,...
Definition: Engine.C:1252
StdioInterface.H
jevois::MovieInput
Movie input, can be used as a replacement for Camera to debug algorithms using a fixed video sequence...
Definition: MovieInput.H:35
jevois::drawErrorImage
void drawErrorImage(std::string const &errmsg, RawImage &videoerrimg)
Display an error message into a RawImage.
Definition: Log.C:300
jevois::Manager::preInit
void preInit() override
Calls parseCommandLine()
Definition: Manager.C:49
jevois::Engine::numVideoMappings
size_t numVideoMappings() const
Return the number of video mappings.
Definition: Engine.C:1260
jevois::Engine::unRegisterPythonComponent
void unRegisterPythonComponent(Component *comp)
Unregister a component as linked to some python code, used by dynamic params created in python.
Definition: Engine.C:2732
jevois::VideoOutputNone
No-op VideoOutput derivative for when there is no video output.
Definition: VideoOutputNone.H:28
jevois::Engine::setFormat
void setFormat(size_t idx)
Callback for when the user selects a new output video format.
Definition: Engine.C:741
JEVOIS_TRACE
#define JEVOIS_TRACE(level)
Trace object.
Definition: Log.H:296
jevois::Engine::reboot
void reboot()
Request a reboot.
Definition: Engine.C:1582
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:211
jevois::Engine::getDefaultVideoMapping
const VideoMapping & getDefaultVideoMapping() const
Allow access to the default video mapping.
Definition: Engine.C:1304
jevois::DynamicLoader
Class to open shared object (.so) files and load functions contained in them.
Definition: DynamicLoader.H:69
jevois::Serial::filePut
void filePut(std::string const &abspath)
Receive a file from the host and write it to the local microSD.
Definition: Serial.C:497
jevois::getSysInfoVersion
std::string getSysInfoVersion()
Get O.S. version info.
Definition: SysInfo.C:74
jevois
Definition: Concepts.dox:1
jevois::Manager::postInit
void postInit() override
Checks for the –help flag.
Definition: Manager.C:58
MovieOutput.H
jevois::sensorHasIMU
bool sensorHasIMU(CameraSensor s)
Check whether sensor has an IMU (inertial measurement unit)
Definition: CameraSensor.C:219
jevois::Engine::streamOn
void streamOn()
Start streaming on video from camera, processing, and USB.
Definition: Engine.C:702
jevois::getSysInfoCPU
std::string getSysInfoCPU()
Get CPU info: frequency, thermal, load.
Definition: SysInfo.C:24
Engine.H
Log.H
jevois::system
std::string system(std::string const &cmd, bool errtoo=true)
Execute a command and grab stdout output to a string.
Definition: Utils.C:441
jevois::logSetEngine
void logSetEngine(Engine *e)
Set an Engine so that all log messages will be forwarded to its serial ports.
Definition: Log.C:124
jevois::Engine::clearErrors
void clearErrors()
Clear all errors currently displayed in the JeVois-Pro GUI.
Definition: Engine.C:1186
GUIconsole.H
SysInfo.H
jevois::Component::setParamVal
std::vector< std::string > setParamVal(std::string const &paramdescriptor, T const &val)
Set a parameter value.
jevois::Engine::findVideoMapping
const VideoMapping & 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:1320
success
#define success()
VideoOutputNone.H
jevois::Component::Engine
friend class Engine
Definition: Component.H:518
jevois::Engine::streamOff
void streamOff()
Stop streaming on video from camera, processing, and USB.
Definition: Engine.C:713
jevois::StdModule::sendSerialMarkStart
void sendSerialMarkStart()
Send a message MARK START to indicate the beginning of processing.
Definition: Module.C:520
jevois::IMUspi
IMU with SPI interface, such as the ICM20948 IMU on the JeVois-Pro IMX290 camera sensor board.
Definition: IMUspi.H:28
jevois::Engine::requestSetFormat
void requestSetFormat(int idx)
Use this to request a format change from within process()
Definition: Engine.C:734
jevois::fccstr
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:45
Serial.H
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:236
jevois::Engine::getVideoMapping
const VideoMapping & getVideoMapping(size_t idx) const
Allow access to our video mappings which are parsed from file at construction.
Definition: Engine.C:1264
jevois::VideoDisplay
Video output to local screen.
Definition: VideoDisplay.H:34
jevois::Engine::runScriptFromFile
void runScriptFromFile(std::string const &filename, std::shared_ptr< UserInterface > ser, bool throw_no_file)
Run a script from file.
Definition: Engine.C:2507
jevois::Engine::reportError
void reportError(std::string const &err)
Definition: Engine.C:1177
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
IMUi2c.H
jevois::python::setEngine
void setEngine(jevois::Engine *e)
Initialize Python, numpy, and allow python modules to send serial outputs through the JeVois Engine.
Definition: PythonSupport.C:93
jevois::Engine::mainLoop
int mainLoop()
Main loop: grab, process, send over USB. Should be called by main application thread.
Definition: Engine.C:895
jevois::Engine::onParamChange
void onParamChange(engine::serialdev const &param, std::string const &newval) override
Parameter callback.
jevois::stringStartsWith
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:282
jevois::Engine::imu
std::shared_ptr< IMU > imu() const
Get a pointer to our IMU (may be null)
Definition: Engine.C:1248
Camera.H
PythonModule.H
jevois::UserInterface::Type::USB
@ USB
jevois::Engine::getVideoMappingIdx
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:1273
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
jevois::InputFrame
Exception-safe wrapper around a raw camera input frame.
Definition: InputFrame.H:50
jevois::Component::freezeAllParams
void freezeAllParams()
Freeze all parameters.
Definition: Component.C:441
jevois::IMUi2c
IMU with I2C interface shared with camera sensor, such as ICM20948 on JeVois-A33 AR0135 camera sensor...
Definition: IMUi2c.H:28
jevois::StdModule::sendSerialMarkStop
void sendSerialMarkStop()
Send a message MARK STOP to indicate the end of processing.
Definition: Module.C:528
jevois::Engine::preInit
void preInit() override
Override of Manager::preInit()
Definition: Engine.C:437
jevois::Engine::registerPythonComponent
void registerPythonComponent(Component *comp, void *pyinst)
Register a component as linked to some python code, used by dynamic params created in python.
Definition: Engine.C:2722
jevois::Engine::getPythonComponent
Component * getPythonComponent(void *pyinst) const
Get the component registered with a given python instance.
Definition: Engine.C:2741
Utils.H
jevois::Engine::postInit
void postInit() override
Override of Manager::postInit()
Definition: Engine.C:461
IMUspi.H
DynamicLoader.H
jevois::Engine::parseCommand
bool parseCommand(std::string const &str, std::shared_ptr< UserInterface > s, std::string const &pfx="")
Parse a user command received over serial port.
Definition: Engine.C:1705
h
int h
Definition: GUIhelper.C:1968
jevois::Engine::~Engine
~Engine()
Destructor.
Definition: Engine.C:632
jevois::Engine::sendSerial
void sendSerial(std::string const &str, bool islog=false)
Send a string to all serial ports.
Definition: Engine.C:1132
jevois::Engine::module
std::shared_ptr< Module > module() const
Get a pointer to our current module (may be null)
Definition: Engine.C:1244
jevois::Engine::reloadVideoMappings
void reloadVideoMappings()
Re-load video mappings from videomappings.cfg.
Definition: Engine.C:448
jevois::async_little
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async_little(Function &&f, Args &&... args)
Async execution using a thread pool.
jevois::StdModule
Base class for a module that supports standardized serial messages.
Definition: Module.H:232
VideoDisplayGUI.H
jevois::Camera
JeVois camera driver class - grabs frames from a Video4Linux camera sensor.
Definition: Camera.H:62
jevois::Engine::getCurrentVideoMapping
const VideoMapping & getCurrentVideoMapping() const
Get the current video mapping.
Definition: Engine.C:1256
VideoDisplayGL.H
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:194
jevois::strip
std::string strip(std::string const &str)
Strip white space (including CR, LF, tabs, etc) from the end of a string.
Definition: Utils.C:296
jevois::Serial::fileGet
void fileGet(std::string const &abspath)
Send a file from the local microSD to the host computer.
Definition: Serial.C:473
jevois::UserInterface::Type::GUI
@ GUI
IMU.H
jevois::UserInterface::type
virtual Type type() const =0
Derived classes must implement this and return their interface type.
GUIhelper.H
jevois::Engine::drawCameraGUI
void drawCameraGUI()
Draw all camera controls into our GUI.
Definition: Engine.C:2585
Gadget.H
VideoDisplay.H
V4L2_CTRL_CLASS_DETECT
#define V4L2_CTRL_CLASS_DETECT
Definition: Engine.C:62