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