JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
Component.H
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// This code is inspired by the Neuromorphic Robotics Toolkit (http://nrtkit.org)
19
20#pragma once
21
23#include <boost/thread.hpp>
24#include <string>
25#include <vector>
26#include <map>
27#include <unordered_map>
28#include <future>
29#include <filesystem>
30
31#include <jevois/Util/Async.H> // actually not needed here, but many derived components and modules will want to use it
32
33namespace jevois
34{
35 class Manager;
36 class Engine;
37 class UserInterface;
38
39 /*! \defgroup component Model components, parameters, manager, and associated classes
40
41 These classes define how a complex model or vision processing pipeline with user-tunable parameters can be built
42 from components.
43
44 How to explore this documentation module:
45
46 - Start with looking at Component, which highlights the broad conceptual framework of how one may assemble complex
47 vision processing pipelines in JeVois, with substantial re-use of shared algorithmic elements.
48
49 - Then have a look a \ref parameter which provides details on how to implement and use parameters in your
50 components. Parameters can be created for any valid C++ type, and serve the goal of allowing end-users of your
51 algorithm to tune some of the behavior of your algorithms.
52
53 - Finally have a look at Manager, which is a special component that is the root of a component hierarchy.
54
55 - You will then be ready to move to the next level by looking at the other elements in the documentation
56 module.
57
58 \ingroup core */
59
60 // ######################################################################
61 //! A component of a model hierarchy.
62 /*! Model Components form the heart of every JeVois application, as their main purpose is to allow 1) building complex
63 vision processing pipelines from shared, re-usable processing elements; 2) a uniform interface for tunable
64 parameters that these processing elements may have. In fact, every class which needs to expose parameters to the
65 user should inherit directly or indirectly from Component. Parameters are simple wrappers around any valid C++
66 type, along with some description, default values, possibly specification of valid values, etc. Parameters are
67 used to adjust the behavior of processing elements by allowing end-users to change the parameter values (think,
68 e.g., of a threshold in some image processing algorithm).
69
70 Components typically form a component hierarchy with a Manager (including Engine) at the root, and a tree of
71 sub-Components below. To form this tree, just use the Manager::addComponent() and Component::addSubComponent()
72 methods to add some children, and then add some children to those children, etc...
73
74 One intent behind Component is to enable substantial code re-use when implementing complex vision processing
75 frameworks, and to alleviate the burden of passing many parameters to function calls when invoking the processing
76 elements. This is a strong departure from many other frameworks such as OpenCV, which by and large rely on
77 functions with many parameters (though later versions do use classes as well). For example, the way one invokes a
78 Canny edge detector in OpenCV is to call the function:
79
80 \code
81 void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2,
82 int apertureSize = 3, bool L2gradient = false)
83 \endcode
84
85 Beyond possible confusion about which value goes to which argument in the long list (which languages such as
86 Python solve by allowing access to arguments by name), one major issue with this approach is that either every
87 function using Canny must provide a mechanism for the user to set the parameters (threshold1, threshold2, etc),
88 or, in most cases, those will just end up being hardwired, limiting the applicability of the end application to
89 different image sizes, environment types, etc.
90
91 In contrast, in the JeVois framework, one would create a Canny Component, with Parameter settings for the
92 thresholds, etc and a member function
93
94 \code
95 OutputArray process(InputArray in)
96 \endcode
97
98 One would typically first set the parameter values, then call the function. Setting the parameters can be done by
99 the code that will use the function, but, more often, it is left to the user. In a particular vision pipeline,
100 resonable default values may be provided for the parameters at the beginning, then leaving those parameters
101 accessible to end users who may want to modify them. Modification of parameters in JeVois is handled either at the
102 start of the application by parsing command-line arguments, when a new processing Module is instantiated, or while
103 it is running, by interacting with the Engine that manages the system via its Serial ports.
104
105 Any complex vision processing pipeline that includes the Canny component in its hierarchy will expose its
106 parameters so that they can be set, either by users (via command-line arguments, commands issued over serial
107 ports, etc), or by other components in the hierarchy. If multiple instances of a Component are present in a
108 hierarchy, their parameters can either all be set at once to a single (shared) value, or they can be accessed and
109 set individually to different values (each Component has a unique instance name, and a so-called descriptor is
110 created by concatenating instance names of a component and all its parents; parameters of a specific Component
111 instance can be accessed by prefixing that instance's descriptor to the parameter name). Parameters provide a
112 uniform interface for setting/getting the parameter values, so that programmers do not have to program accessor
113 functions in each of their components for each of their parameters(e.g., adding a Parameter<int> for threshold1 to
114 your component will automatically add set(), get(), etc functions for it into your component).
115
116
117 In addition, Component provides some level of introspection or reflection: one can access a component instance
118 name, class name, descriptor that includes names of all its parents in the hierarchy, and iterate over its
119 parameters, over its sub-components, etc.
120
121 Component is inherently thread-safe and the component interface is written so as to encourage thread-safe use; for
122 example, no function is provided that returns the parent of a component, as another thread could change that
123 between the time that parent is returned and the time it is used. Instead, functions are provided that can
124 traverse the hierarchy up or down in a thread-safe manner and execute some action on the components that are
125 encountered during that traversal.
126
127 Components are brought into action in several phases:
128
129 - Construction: In constructors, Parameter values have not been set yet so nothing that depends on them should be
130 initialized yet. Typically, constructors should only initialize fixed component resources which are independent
131 of Parameter values, or they can also set default values for parameters, which may later be overriden by
132 command-line arguments. In many situations, the constructor would do nothing, and most of the initialization
133 work would be delayed until the Parameter values have been set, during the component's init() phase.
134
135 - initialization: When init() is called on the manager (including Engine), it propagates down the entire Component
136 hierarchy, as follows:
137
138 - recursively call preInit() on all sub-components.
139 - run preInit() on the current component (the preInit() function can be overriden by derived classes to
140 implement some custom initialization; note that Parameter values are not yet finalized at this point and
141 should not be used yet).
142 - Manager then parses the command-line and possibly sets parameters. Parameters are considered ready to use from
143 this point on.
144 - for parameters of this component that have a callback but it has not yet been invoked because that parameter
145 has not been explicitly set at the command line, call the callback one first time with the default parameter
146 value. This is so that callbacks that create resources (e.g., callback for a parameter for the device name of
147 a device, which opens the device when called) are called at least once before init() is complete.
148 - recursively flip the state of all sub-components to initialized, then flip the component state to initialized.
149 - recursively run postInit() on all sub-components (derived classes may override postInit(), and would typically
150 allocate resources that depend on parameter values in their override of postInit()), then on the current
151 component.
152
153 The initialization flow is fixed. Classes that derive from Component can override preInit() for intializations
154 that do not rely on Parameter values and postInit() for those that do. Once init() is complete on the Manager,
155 all components in the hierarchy are considered ready for operation.
156
157 If a component is added to an already-initialized hierarchy, it will be brought up to the same init state as its
158 parent at the time of addition. This is typically the case when one selects a new video format from a USB host
159 on a JeVois system that is already running and streaming video. The Module handling the old format is furst
160 un-initialized by Engine, then it is destroyed, the new Module is instantiated for the new format, and it is
161 initialized before the stream of video frames is directed to it.
162
163 - uninit: This is the dual of init(), with preUninit() and postUninit() following the same logic as preInit() and
164 postInit(). Specific order is:
165
166 - preUninit() is recursively called on all sub-component then on the component (note that initialized() is still
167 true at this point);
168 - component flips to un-initalized, then recursivey all its sub-components
169 - postUninit() is called on component, then recursively on all its sub-components.
170
171 Note how both init() and uninit() are private in Component, and thus cannot be called directly. Manager (and hence
172 Engine which derives from Manager) makes them public so that you can call them on the manager. That is, your
173 entire hierarchy is either initialized or not.
174
175 Because powering up the JeVois hardware platform automatically constructs and initializes the default processing
176 pipeline, and because of Engine instantiates and then initializes processing modules when users change video
177 format, most components just assume that their vision processing functions will never be called when the component
178 is not initialized, and thus they just skip testing for initialized() altogether (to save CPU cycles).
179
180 \ingroup component */
181 class Component : public virtual ParameterRegistry
182 {
183 public:
184 //protected: // FIXME inherited constructor does not compile if protected!
185 //! Constructor
186 /*! The standard way to create a component is via Component::addSubComponent() or Manager::addComponent(), rather
187 than constructing them by hand. Components constructed via the constructor (e.g., calling operator new) will
188 not be attached to a Component hierarchy. It is recommended that derived components also have an instance
189 constructor argument and pass it down to the base Component class, to allow complex systems with several
190 instances of a same Component. In fact, for most components, the inherited constructor is appropriate:
191
192 \code
193 class MyComp : public jevois::Component
194 {
195 public:
196 // Inherited constructor
197 using jevois::Component::Component;
198
199 // Virtual destructor for safe inheritance
200 virtual ~MyComp();
201 };
202 \endcode */
203 Component(std::string const & instance);
204
205 //! Virtual destructor for safe inheritance
206 /*! Calls uninit() if component is initialized. */
207 virtual ~Component();
208
209 //! @name Component hierarchies
210 //! @{
211
212 //! Pseudo-constructor: construct and add another component as a subcomponent of this one
213 /*! A child component of type Comp (which must derive from jevois::Component) will be created and added as a
214 sub-component of this. The child logically "belongs" to this component, and will automatically be initialized,
215 un-initialized, and deleted when the parent component is. In addition to construction, adding a subcomponent
216 will bring it up to the same init() state as the owner component.
217
218 When setting parameters, the sub-Component can be referenced as a child of this component. For instance, if
219 we have a ComponentParent which has ComponentChild as a sub-Component, and ComponentChild has parameter named
220 coolChildParam, then that parameter could be specified on the command line by
221
222 \code
223 --ComponentParentInstanceName:ComponentChildInstanceName:coolChildParamName="whatever"
224 \endcode
225
226 or over a Serial port by
227
228 \code
229 setpar ComponentParentInstanceName:ComponentChildInstanceName:coolChildParamName whatever
230 \endcode
231
232 \param instance A string identifying a particular instance of a Component, no two sub-components of a
233 component may have the same instance name. The instance name should be passed in through your derived
234 Component's constructor, allowing users to disambiguate between multiple instances of your Component. If the
235 instance name is empty, the actual instance will be named ComponentClassname# with # replaced (if necessary)
236 by a unique number. If the instance name is not empty but contains a #, only that # is replaced (if necessary)
237 by a number that makes the instance name unique. The final name is accessible via instanceName() once your
238 component is constructed. There is no default value for instance in the base class to catch derived classes
239 that forgot to pass it down to the base, but it may be a good idea to set an empty string default to instance
240 in derived classes.
241
242 \param args any constructor parameters used to instantiate the sub-component
243
244 \note Sub-components always inherit the path of their top-level parent (a top-level component is one that was
245 directly added to a Manager via Manager::addComponent()). See absolutePath(). This is so that different
246 modules can provide different data for their components; for example, a FaceDetector component may require
247 some face template data files to operate; if those are loaded using a relative path name, different modules
248 that use the FaceDetector may use different face template data files. */
249 template <class Comp, typename... Args>
250 std::shared_ptr<Comp> addSubComponent(std::string const & instance, Args && ...args);
251
252 //! Remove a sub-Component from this Component, by shared_ptr
253 /*! \note Beware that the passed shared_ptr is invalidated in the process. A warning is issued if the use_count is
254 not down to zero after that (i.e., there are additional shared_ptr pointers to this Component floating around,
255 which prevent it from actually being deleted. */
256 template <class Comp>
257 void removeSubComponent(std::shared_ptr<Comp> & component);
258
259 //! Remove a sub-Component from this Component, by instance name
260 void removeSubComponent(std::string const & instance, bool warnIfNotFound = true);
261
262 //! Get a sub-component by instance name
263 /*! This method does a dynamic_pointer_cast to Comp if it is not the default (jevois::Component). Throws if
264 component is not found by instance name, or it is found but not of type Comp (if Comp is specified). Note that
265 once you hold a shared_ptr to a Component, it is guaranteed that the component will not be destroyed until
266 that shared_ptr is released. If the JeVois system tries to destroy the component (e.g., someone calls
267 removeSubComponent()), the component will be un-initialized and its parent will be unset, so it will not be
268 fully operational and will be actually deleted when the last shared_ptr to it runs out of scope. */
269 template <class Comp = jevois::Component>
270 std::shared_ptr<Comp> getSubComponent(std::string const & instance) const;
271
272 //! Returns true if this component is top-level, i.e., its parent is jevois::Manager
273 /*! The Module of Engine is top-level. */
274 bool isTopLevel() const;
275
276 //! Get a handle to our Engine, or throw if we do not have an Engine as root ancestor
277 /*! Use with caution as this could break runtime loading/unloading of component hierarchies. */
278 Engine * engine() const;
279
280 //! @}
281
282 //! @name Component runtime
283 //! @{
284
285 //! Has this component been initialized yet?
286 bool initialized() const;
287
288 //! @}
289
290 //! @name Component metainfo-related functions
291 //! @{
292
293 //! The class name of this component
294 std::string const & className() const;
295
296 //! The instance name of this component
297 std::string const & instanceName() const;
298
299 //! @}
300
301 /*! @name Component Parameter-related functions
302
303 Each Component can hold Parameter objects (through inheritance) that can be set externally by users to modify
304 the operation of the Component, and that can be accessed or set by the Component itself.
305
306 Note how the JeVois framework uses inheritance for parameters as opposed to making them class data members of
307 the owning Component. See \ref parameter for detailed explanations. */
308 //! @{
309
310 //! Set a parameter value
311 /*! Throws if we don't have a parameter by that name or the value does not work out. Here is how the descriptor
312 works:
313
314 The basic format is
315
316 \code
317 [ComponentInstanceName]:[...]:[paramname]
318 \endcode
319
320 Note that any <code>[ComponentInstanceName]</code> token can also be replaced by a *, which is equivalent to
321 any number of any ComponentInstanceName specifications. So use the * to reach parameters when you don't know
322 how deep they are. If there is some other ComponentInstanceName between a * and the final paramname, then
323 recursion is turned off and anything between the * and paramname must match intervening components/instances.
324
325 For example,
326
327 @code
328 MyInst:*:CoolComp:MyParam
329 @endcode
330
331 would match parameters where MyInst matches the top-level component (the one on which you call setParamVal()),
332 then would recurse through any number of subcomponents, until one of them matches CoolComp, and then we would
333 look for MyParam parameter in that subcomponent, and we would not look any deeper.
334
335 Finally note that there is an implicit first *: that is automatically prepended to your description, so if you
336 just specify a paramname and nothing else before it, we will set all params by that name in all subcomponents
337 no matter how deep they are (as if you had specified *:paramname).
338
339 @throws std::exception if no Parameter matches the given descriptor.
340 @return list of fully-unrolled (no '*') descriptors of the parameters that were matched and set. The list is
341 guaranteed to have at least one element since we throw if no matching parameter is found. */
342 template <typename T>
343 std::vector<std::string> setParamVal(std::string const & paramdescriptor, T const & val);
344
345 //! Set a parameter value, simple version assuming only one parameter match
346 /*! This calls setParamVal(), and checks that exactly one match was found.
347 @throws std::exception if not exactly one Parameter matches the given descriptor. */
348 template <typename T>
349 void setParamValUnique(std::string const & paramdescriptor, T const & val);
350
351 //! Get parameter(s) value(s) by descriptor
352 /*! Use this method to get the current value of a Component's parameter from the string descriptor. Values for all
353 parameters that match the descriptor are returned.
354
355 For example, if the class MyComponent has an integer parameter named "myparam" you could get the
356 value like so:
357
358 @code
359 std::shared_ptr<MyComponent> comp = addSubComponent<MyComponent>("mycomp");
360 auto paramValues = comp->getParamVal<int>("myparam");
361 for (auto const & pp : paramValues) LINFO("Parameter " << pp.first << " = " << pp.second);
362 @endcode
363
364 @throws jevois::exception::ParameterException if no Parameter matches the given descriptor.
365 @return list of <paramdescriptor, value> for all parameters that matched the given descriptor. The list is
366 guaranteed to have at least one element since we throw if no matching parameter is found.
367
368 \see setParamVal for a detailed explanation of the paramdescriptor */
369 template <typename T>
370 std::vector<std::pair<std::string, T> > getParamVal(std::string const & paramdescriptor) const;
371
372 //! Get a parameter value, simple version assuming only one parameter match
373 /*! This calls getParamVal(), checks that exactly one match was found, and returns its value.
374 For example, if the class MyComponent has an integer parameter named "myparam" you could get the
375 value like so:
376
377 @code
378 std::shared_ptr<MyComponent> comp(new MyComponent);
379 int paramValue = comp->getParamValUnique<int>("myparam");
380 @endcode
381
382 @throws std::range_error if not exactly one Parameter matches the given descriptor. */
383 template <typename T>
384 T getParamValUnique(std::string const & paramdescriptor) const;
385
386 //! Set a parameter value, by string
387 /*! \see setParamVal for a detailed explanation of the paramdescriptor
388 @throws jevois::exception::ParameterException if no Parameter matches the given descriptor or if the
389 given string cannot be converted to the Parameter's native type.
390 @return list of fully-unrolled (no '*') descriptors of the parameters that were matched and set. The list is
391 guaranteed to have at least one element since we throw if no matching parameter is found. */
392 std::vector<std::string> setParamString(std::string const & paramdescriptor, std::string const & val);
393
394 //! Set a parameter value by string, simple version assuming only one parameter match
395 /*! This calls setParamVal(), and checks that exactly one match was found.
396 @throws std::range_error if not exactly one Parameter matches the given descriptor. */
397 void setParamStringUnique(std::string const & paramdescriptor, std::string const & val);
398
399 //! Get a parameter value, by string
400 /*! \see setParamVal for a detailed explanation of the paramdescriptor
401 Use this method to get the current value of a Component's parameter from the string descriptor. Values for all
402 parameters that match the descriptor are returned, as string.
403 @throws jevois::exception::ParameterException if no Parameter matches the given descriptor.
404 @return list of <paramdescriptor, valuestring> for all parameters that matched the given descriptor. The list
405 is guaranteed to have at least one element since we throw if no matching parameter is found. */
406 std::vector<std::pair<std::string, std::string> > getParamString(std::string const & paramdescriptor) const;
407
408 //! Get a parameter value by string, simple version assuming only one parameter match
409 /*! This calls getParamVal(), checks that exactly one match was found, and returns its value as a string.
410 @throws jevois::exception::ParameterException if not exactly one Parameter matches the given descriptor. */
411 std::string getParamStringUnique(std::string const & paramdescriptor) const;
412
413 //! Freeze/unfreeze a parameter, by name, see ParameterBase::freeze()
414 void freezeParam(std::string const & paramdescriptor, bool doit);
415
416 //! Freeze all parameters
417 void freezeAllParams(bool doit);
418
419 //! Get our full descriptor (including all parents) as [Instancename]:[...]:[...]
420 std::string descriptor() const;
421
422 //! Set some parameters from a file
423 /*! The file should have entries "descriptor=value", one per line, where the parameter descriptors should be
424 relative to this component. If the file name is relative, our component path will be prefixed to it using
425 absolutePath(). */
426 void setParamsFromFile(std::string const & filename);
427
428 //! Set some parameters from an open stream
429 /*! The stream should have entries "descriptor=value", one per line, where the parameter descriptors should be
430 relative to this component. absfile is only used for error messages and should be the absolute path of the
431 file opened in 'is', or some other text for error messages. */
432 std::istream & setParamsFromStream(std::istream & is, std::string const & absfile);
433
434 //! Get machine-oriented descriptions of all parameters
435 virtual void paramInfo(std::shared_ptr<UserInterface> s, std::map<std::string, std::string> & categs,
436 bool skipFrozen, std::string const & cname = "", std::string const & pfx = "");
437
438 //! Run a function on every param we hold
439 void foreachParam(std::function<void(std::string const & compname, ParameterBase * p)> func,
440 std::string const & cname = "");
441
442 //! Add a new parameter after the Component has already been constructed
443 /*! Dynamic parameters can only be accessed by descriptor using Component::setParamVal(),
444 Component::getParamVal(), etc., since the owning component does not inherit from them like standard
445 parameters. Callbacks can be added manually after creation using
446 Component::setDynamicParameterCallback(). This version creates a Parameter with no given valid values, valid
447 values are whatever T can take. */
448 template <typename T>
449 std::shared_ptr<DynamicParameter<T>>
450 addDynamicParameter(std::string const & name, std::string const & description, T const & defaultValue,
451 ParameterCategory const & category);
452
453 //! Add a new parameter after the Component has already been constructed
454 /*! Dynamic parameters can only be accessed by descriptor, since the owning component does not inherit from them
455 like standard parameters. Callbacks can be added manually after creation using
456 Component::setDynamicParameterCallback(). This version creates a Parameter with specified valid values from a
457 ValidValueSpec */
458 template <typename T, template <typename> class ValidValuesSpec>
459 std::shared_ptr<DynamicParameter<T>>
460 addDynamicParameter(std::string const & name, std::string const & description, T const & defaultValue,
461 ValidValuesSpec<T> const & validValuesSpec, ParameterCategory const & category);
462
463 //! Register a callback with a previously created dynamic parameter
464 /*! If callnow is true, the callback will be called right after it is registered, using the parameter's current
465 value. This is to mimic standard parameters where the callback is called at least once during init. */
466 template <typename T>
467 void setDynamicParameterCallback(std::string const & name, std::function<void(T const &)> cb,
468 bool callnow = true);
469
470 //! Remove a previously added dynamic parameter
471 void removeDynamicParameter(std::string const & name, bool throw_if_not_found = true);
472
473 //! @}
474
475 /*! @name Component path-related functions
476
477 Each Component can keep track of a preferred filesystem path. Typically, users should not tamper with this,
478 but Module objects (which derive from Component) that are dynamically loaded will have that path set to the
479 path where the Module's shared object file (.so) was found. This allows those modules to access some of their
480 local configuration data, which may, for example, be stored in an \c etc/ directory under the module path. */
481 //! @{
482
483 //! Assign a filesystem path to this component
484 void setPath(std::string const & path);
485
486 //! If given path is relative (not starting with /), prepend the Component path to it
487 /*! If path is absolute, no-op. If path is empty, return the Component's path as set with setPath(). */
488 std::filesystem::path absolutePath(std::filesystem::path const & path = "");
489
490 //! @}
491
492 protected:
493 //! @name Component setup functions overloadable by derived classes
494
495 //! @{
496
497 //! Called before all sub-Components are init()ed
498 virtual void preInit() { }
499
500 //! Called after all sub-Components are init()ed
501 virtual void postInit() { }
502
503 //! Called before all sub-Components are uninit()ed
504 virtual void preUninit() { }
505
506 //! Called after all sub-Components are uninit()ed
507 virtual void postUninit() { }
508
509 //! @}
510
511 private:
512 template <typename T> friend class ParameterCore;
513 friend class Manager; // Allow Manager to access our subs directly (in addComponent, etc)
514 friend class Engine; // Allow Engine to add already-created components (the module)
515 friend class Module; // Allow Module to access itsParent
516
517 mutable boost::shared_mutex itsMtx; // Mutex used to protect our internals other than subcomps and parameters
518
519 // Storage for sub-Components
520 std::vector<std::shared_ptr<Component> > itsSubComponents;
521
522 // Mutex to protect our list of subcomponents
523 mutable boost::shared_mutex itsSubMtx;
524
525 // Recursively populate a list of parameters, for help message
526 void populateHelpMessage(std::string const & cname,
527 std::unordered_map<std::string /* categ+categdesc */,
528 std::unordered_map<std::string /* name+defaultval+validvals+descrip */,
529 std::vector<std::pair<std::string /* component */, std::string /* value */ > > > > &
530 helplist, bool recurse = true) const;
531
532 // Initialization: Invoke: runPreInit(), then setInitialized(), finally runPostInit()
533 virtual void init();
534
535 // Recursively run preInit()
536 void runPreInit();
537
538 // Recursively set initialized flag
539 void setInitialized();
540
541 // Recursively run postInit()
542 void runPostInit();
543
544 // Un-initialize: Invoke: runPreUninit(), then setUninitialized(), finally runPostUninit()
545 virtual void uninit();
546
547 // Recursively run preUninit()
548 void runPreUninit();
549
550 // Recursively set uninitialized flag
551 void setUninitialized();
552
553 // Recursively run postUninit()
554 void runPostUninit();
555
556 // Our meta information. Only access this through meta(), even from within Component!
557 mutable boost::shared_mutex itsMetaMtx;
558 std::string const itsClassName;
559 std::string itsInstanceName;
560
561 // Has this Component been initialized?
562 volatile bool itsInitialized;
563
564 // The Component which contains this one as a sub-Component - this may be NULL if the component is not part of any
565 // hierarchy, or is the manager.
566 Component * itsParent;
567
568 void findParamAndActOnIt(std::string const & descrip,
569 std::function<void(jevois::ParameterBase * param, std::string const & unrolled)> doit,
570 std::function<bool()> empty) const;
571 void findParamAndActOnIt(std::vector<std::string> const & descrip, bool recur, size_t idx,
572 std::string const & unrolled,
573 std::function<void(jevois::ParameterBase *, std::string const &)> doit) const;
574
575 std::string itsPath; // filesystem path assigned to this Component, empty by default
576
577 // Helper function to compute an automatic instance name, if needed
578 /* Parent modules call this before adding a sub-module, to make sure its name will not clash with existing
579 sub-modules. Parameter classname can be empty if instance is not. Throws if a unique name cannot be created
580 (e.g., no # was provided and the instance name clashes with other sub-modules). itsSubMtx on the parent must
581 be locked by caller to make sure the computed name remains available as we return it */
582 std::string computeInstanceName(std::string const & instance, std::string const & classname = "") const;
583
584 // Actually remove a sub, no type checking here, for internal use only
585 /* Uplck is assumed to already be locked for read, will be upgraded to write lock. Itr is an iterator on
586 itsSubComponents pointing at the component to remove. It will get reset(). displayname is just Component,
587 SubModule, etc for messages purposes. */
588 void doRemoveSubComponent(std::vector<std::shared_ptr<Component> >::iterator & itr,
589 boost::upgrade_lock<boost::shared_mutex> & uplck,
590 std::string const & displayname);
591
592 // Bank of defs for dynamic parameters, someone needs to hold them
593 std::map<std::string /* name */, std::shared_ptr<ParameterBase>> itsDynParams;
594 mutable std::mutex itsDynParMtx;
595 };
596
597
598 //! Get the current video processing frame number
599 /*! The Engine maintains a master frame counter that is incremented on each call to a Module's process(), whether
600 or not the call succeeds. The counter is not incremented when a module has not been loaded (e.g., failed to
601 load). The counter is reset to zero each time a new module is loaded.
602
603 This is implemented as a free function so that anyone can access it:
604
605 \code
606 size_t current_frame_number = jevois::frameNum();
607 \endcode
608
609 It is thread-safe. Programmer note: the implementation is in Engine.C since Engine is the one that manages the
610 counter. It is declared here so that users don't have to include Engine.H \relates Engine */
611 size_t frameNum();
612
613} //jevois
614
615// Include inlined implementation details that are of no interest to the end user
616#include <jevois/Component/details/ComponentImpl.H>
617
A component of a model hierarchy.
Definition Component.H:182
std::vector< std::string > setParamString(std::string const &paramdescriptor, std::string const &val)
Set a parameter value, by string.
Definition Component.C:358
std::filesystem::path absolutePath(std::filesystem::path const &path="")
If given path is relative (not starting with /), prepend the Component path to it.
Definition Component.C:514
std::string descriptor() const
Get our full descriptor (including all parents) as [Instancename]:[...]:[...].
Definition Component.C:276
void setParamValUnique(std::string const &paramdescriptor, T const &val)
Set a parameter value, simple version assuming only one parameter match.
std::vector< std::pair< std::string, std::string > > getParamString(std::string const &paramdescriptor) const
Get a parameter value, by string.
Definition Component.C:389
std::vector< std::string > setParamVal(std::string const &paramdescriptor, T const &val)
Set a parameter value.
void freezeParam(std::string const &paramdescriptor, bool doit)
Freeze/unfreeze a parameter, by name, see ParameterBase::freeze()
Definition Component.C:417
bool initialized() const
Has this component been initialized yet?
Definition Component.C:206
std::shared_ptr< Comp > getSubComponent(std::string const &instance) const
Get a sub-component by instance name.
std::string const & instanceName() const
The instance name of this component.
Definition Component.C:50
std::shared_ptr< DynamicParameter< T > > addDynamicParameter(std::string const &name, std::string const &description, T const &defaultValue, ParameterCategory const &category)
Add a new parameter after the Component has already been constructed.
void foreachParam(std::function< void(std::string const &compname, ParameterBase *p)> func, std::string const &cname="")
Run a function on every param we hold.
Definition Component.C:568
std::istream & setParamsFromStream(std::istream &is, std::string const &absfile)
Set some parameters from an open stream.
Definition Component.C:446
void freezeAllParams(bool doit)
Freeze all parameters.
Definition Component.C:429
void removeSubComponent(std::shared_ptr< Comp > &component)
Remove a sub-Component from this Component, by shared_ptr.
bool isTopLevel() const
Returns true if this component is top-level, i.e., its parent is jevois::Manager.
Definition Component.C:119
std::shared_ptr< Comp > addSubComponent(std::string const &instance, Args &&...args)
Pseudo-constructor: construct and add another component as a subcomponent of this one.
std::vector< std::pair< std::string, T > > getParamVal(std::string const &paramdescriptor) const
Get parameter(s) value(s) by descriptor.
virtual void preUninit()
Called before all sub-Components are uninit()ed.
Definition Component.H:504
void setPath(std::string const &path)
Assign a filesystem path to this component.
Definition Component.C:484
std::shared_ptr< DynamicParameter< T > > addDynamicParameter(std::string const &name, std::string const &description, T const &defaultValue, ValidValuesSpec< T > const &validValuesSpec, ParameterCategory const &category)
Add a new parameter after the Component has already been constructed.
virtual ~Component()
Virtual destructor for safe inheritance.
Definition Component.C:54
void setDynamicParameterCallback(std::string const &name, std::function< void(T const &)> cb, bool callnow=true)
Register a callback with a previously created dynamic parameter.
virtual void postUninit()
Called after all sub-Components are uninit()ed.
Definition Component.H:507
virtual void preInit()
Called before all sub-Components are init()ed.
Definition Component.H:498
virtual void paramInfo(std::shared_ptr< UserInterface > s, std::map< std::string, std::string > &categs, bool skipFrozen, std::string const &cname="", std::string const &pfx="")
Get machine-oriented descriptions of all parameters.
Definition Component.C:521
std::string const & className() const
The class name of this component.
Definition Component.C:39
std::string getParamStringUnique(std::string const &paramdescriptor) const
Get a parameter value by string, simple version assuming only one parameter match.
Definition Component.C:405
virtual void postInit()
Called after all sub-Components are init()ed.
Definition Component.H:501
void setParamsFromFile(std::string const &filename)
Set some parameters from a file.
Definition Component.C:437
void removeDynamicParameter(std::string const &name, bool throw_if_not_found=true)
Remove a previously added dynamic parameter.
Definition Component.C:498
void setParamStringUnique(std::string const &paramdescriptor, std::string const &val)
Set a parameter value by string, simple version assuming only one parameter match.
Definition Component.C:374
T getParamValUnique(std::string const &paramdescriptor) const
Get a parameter value, simple version assuming only one parameter match.
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition Engine.H:416
size_t frameNum()
Get the current video processing frame number.
Manager of a hierarchy of Component objects.
Definition Manager.H:74
Virtual base class for a vision processing module.
Definition Module.H:105
Base class for Parameter.
Definition Parameter.H:123
A changeable parameter for a Component, core class.
Definition Parameter.H:194
A simple registry of all parameters associated with a Component.
friend class Component
Allow Component and DynamicParameter to access our registry data, everyone else is locked out.
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2
A category to which multiple ParameterDef definitions can belong.