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