JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
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 
33 namespace 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 the aforementioned was in which Engine changes 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  \note Sub-components always inherit the path of their top-level parent (a top-level component is one that was
243  directly added to a Manager via Manager::addComponent()). See absolutePath(). This is so that different
244  modules can provide different data for their components; for example, a FaceDetector component may require
245  some face template data files to operate; if those are loaded using a relative path name, different modules
246  that use the FaceDetector may use different face template data files. */
247  template <class Comp, typename... Args>
248  std::shared_ptr<Comp> addSubComponent(std::string const & instance, Args && ...args);
249 
250  //! Remove a sub-Component from this Component, by shared_ptr
251  /*! \note Beware that the passed shared_ptr is invalidated in the process. A warning is issued if the use_count is
252  not down to zero after that (i.e., there are additional shared_ptr pointers to this Component floating around,
253  which prevent it from actually being deleted. */
254  template <class Comp>
255  void removeSubComponent(std::shared_ptr<Comp> & component);
256 
257  //! Remove a sub-Component from this Component, by instance name
258  void removeSubComponent(std::string const & instance, bool warnIfNotFound = true);
259 
260  //! Get a sub-component by instance name
261  /*! This method does a dynamic_pointer_cast to Comp if it is not the default (jevois::Component). Throws if
262  component is not found by instance name, or it is found but not of type Comp (if Comp is specified). Note that
263  once you hold a shared_ptr to a Component, it is guaranteed that the component will not be destroyed until
264  that shared_ptr is released. If the JeVois system tries to destroy the component (e.g., someone calls
265  removeSubComponent()), the component will be un-initialized and its parent will be unset, so it will not be
266  fully operational and will be actually deleted when the last shared_ptr to it runs out of scope. */
267  template <class Comp = jevois::Component>
268  std::shared_ptr<Comp> getSubComponent(std::string const & instance) const;
269 
270  //! Returns true if this component is top-level, i.e., its parent is jevois::Manager
271  /*! The Module of Engine is top-level. */
272  bool isTopLevel() const;
273 
274  //! Get a handle to our Engine, or throw if we do not have an Engine as root ancestor
275  /*! Use with caution as this could break runtime loading/unloading of component hierarchies. */
276  Engine * engine() const;
277 
278  //! @}
279 
280  //! @name Component runtime
281  //! @{
282 
283  //! Has this component been initialized yet?
284  bool initialized() const;
285 
286  //! @}
287 
288  //! @name Component metainfo-related functions
289  //! @{
290 
291  //! The class name of this component
292  std::string const & className() const;
293 
294  //! The instance name of this component
295  std::string const & instanceName() const;
296 
297  //! @}
298 
299  /*! @name Component Parameter-related functions
300 
301  Each Component can hold Parameter objects (through inheritance) that can be set externally by users to modify
302  the operation of the Component, and that can be accessed or set by the Component itself.
303 
304  Note how the JeVois framework uses inheritance for parameters as opposed to making them class data members of
305  the owning Component. See \ref parameter for detailed explanations. */
306  //! @{
307 
308  //! Set a parameter value
309  /*! Throws if we don't have a parameter by that name or the value does not work out. Here is how the descriptor
310  works:
311 
312  The basic format is
313 
314  \code
315  [ComponentInstanceName]:[...]:[paramname]
316  \endcode
317 
318  Note that any <code>[ComponentInstanceName]</code> token can also be replaced by a *, which is equivalent to
319  any number of any ComponentInstanceName specifications. So use the * to reach parameters when you don't know
320  how deep they are. If there is some other ComponentInstanceName between a * and the final paramname, then
321  recursion is turned off and anything between the * and paramname must match intervening components/instances.
322 
323  For example,
324 
325  @code
326  MyInst:*:CoolComp:MyParam
327  @endcode
328 
329  would match parameters where MyInst matches the top-level component (the one on which you call setParamVal()),
330  then would recurse through any number of subcomponents, until one of them matches CoolComp, and then we would
331  look for MyParam parameter in that subcomponent, and we would not look any deeper.
332 
333  Finally note that there is an implicit first *: that is automatically prepended to your description, so if you
334  just specify a paramname and nothing else before it, we will set all params by that name in all subcomponents
335  no matter how deep they are (as if you had specified *:paramname).
336 
337  @throws std::exception if no Parameter matches the given descriptor.
338  @return list of fully-unrolled (no '*') descriptors of the parameters that were matched and set. The list is
339  guaranteed to have at least one element since we throw if no matching parameter is found. */
340  template <typename T>
341  std::vector<std::string> setParamVal(std::string const & paramdescriptor, T const & val);
342 
343  //! Set a parameter value, simple version assuming only one parameter match
344  /*! This calls setParamVal(), and checks that exactly one match was found.
345  @throws std::exception if not exactly one Parameter matches the given descriptor. */
346  template <typename T>
347  void setParamValUnique(std::string const & paramdescriptor, T const & val);
348 
349  //! Get parameter(s) value(s) by descriptor
350  /*! Use this method to get the current value of a Component's parameter from the string descriptor. Values for all
351  parameters that match the descriptor are returned.
352 
353  For example, if the class MyComponent has an integer parameter named "myparam" you could get the
354  value like so:
355 
356  @code
357  std::shared_ptr<MyComponent> comp = addSubComponent<MyComponent>("mycomp");
358  auto paramValues = comp->getParamVal<int>("myparam");
359  for (auto const & pp : paramValues) LINFO("Parameter " << pp.first << " = " << pp.second);
360  @endcode
361 
362  @throws jevois::exception::ParameterException if no Parameter matches the given descriptor.
363  @return list of <paramdescriptor, value> for all parameters that matched the given descriptor. The list is
364  guaranteed to have at least one element since we throw if no matching parameter is found.
365 
366  \see setParamVal for a detailed explanation of the paramdescriptor */
367  template <typename T>
368  std::vector<std::pair<std::string, T> > getParamVal(std::string const & paramdescriptor) const;
369 
370  //! Get a parameter value, simple version assuming only one parameter match
371  /*! This calls getParamVal(), checks that exactly one match was found, and returns its value.
372  For example, if the class MyComponent has an integer parameter named "myparam" you could get the
373  value like so:
374 
375  @code
376  std::shared_ptr<MyComponent> comp(new MyComponent);
377  int paramValue = comp->getParamValUnique<int>("myparam");
378  @endcode
379 
380  @throws std::range_error if not exactly one Parameter matches the given descriptor. */
381  template <typename T>
382  T getParamValUnique(std::string const & paramdescriptor) const;
383 
384  //! Set a parameter value, by string
385  /*! \see setParamVal for a detailed explanation of the paramdescriptor
386  @throws jevois::exception::ParameterException if no Parameter matches the given descriptor or if the
387  given string cannot be converted to the Parameter's native type.
388  @return list of fully-unrolled (no '*') descriptors of the parameters that were matched and set. The list is
389  guaranteed to have at least one element since we throw if no matching parameter is found. */
390  std::vector<std::string> setParamString(std::string const & paramdescriptor, std::string const & val);
391 
392  //! Set a parameter value by string, simple version assuming only one parameter match
393  /*! This calls setParamVal(), and checks that exactly one match was found.
394  @throws std::range_error if not exactly one Parameter matches the given descriptor. */
395  void setParamStringUnique(std::string const & paramdescriptor, std::string const & val);
396 
397  //! Get a parameter value, by string
398  /*! \see setParamVal for a detailed explanation of the paramdescriptor
399  Use this method to get the current value of a Component's parameter from the string descriptor. Values for all
400  parameters that match the descriptor are returned, as string.
401  @throws jevois::exception::ParameterException if no Parameter matches the given descriptor.
402  @return list of <paramdescriptor, valuestring> for all parameters that matched the given descriptor. The list
403  is guaranteed to have at least one element since we throw if no matching parameter is found. */
404  std::vector<std::pair<std::string, std::string> > getParamString(std::string const & paramdescriptor) const;
405 
406  //! Get a parameter value by string, simple version assuming only one parameter match
407  /*! This calls getParamVal(), checks that exactly one match was found, and returns its value as a string.
408  @throws jevois::exception::ParameterException if not exactly one Parameter matches the given descriptor. */
409  std::string getParamStringUnique(std::string const & paramdescriptor) const;
410 
411  //! Freeze a parameter, by name, see ParameterBase::freeze()
412  void freezeParam(std::string const & paramdescriptor);
413 
414  //! Unfreeze a parameter, by name, see ParameterBase::unFreeze()
415  void unFreezeParam(std::string const & paramdescriptor);
416 
417  //! Freeze all parameters
418  void freezeAllParams();
419 
420  //! Unfreeze all parameters
421  void unFreezeAllParams();
422 
423  //! Get our full descriptor (including all parents) as [Instancename]:[...]:[...]
424  std::string descriptor() const;
425 
426  //! Set some parameters from a file
427  /*! The file should have entries "descriptor=value", one per line, where the parameter descriptors should be
428  relative to this component. If the file name is relative, our component path will be prefixed to it using
429  absolutePath(). */
430  void setParamsFromFile(std::string const & filename);
431 
432  //! Set some parameters from an open stream
433  /*! The stream should have entries "descriptor=value", one per line, where the parameter descriptors should be
434  relative to this component. absfile is only used for error messages and should be the absolute path of the
435  file opened in 'is', or some other text for error messages. */
436  std::istream & setParamsFromStream(std::istream & is, std::string const & absfile);
437 
438  //! Get machine-oriented descriptions of all parameters
439  virtual void paramInfo(std::shared_ptr<UserInterface> s, std::map<std::string, std::string> & categs,
440  bool skipFrozen, std::string const & cname = "", std::string const & pfx = "");
441 
442  //! Run a function on every param we hold
443  void foreachParam(std::function<void(std::string const & compname, ParameterBase * p)> func,
444  std::string const & cname = "");
445 
446  //! Add a new parameter after the Component has already been constructed
447  /*! Dynamic parameters can only be accessed by descriptor using Component::setParamVal(),
448  Component::getParamVal(), etc., since the owning component does not inherit from them like standard
449  parameters. Callbacks can be added manually after creation using
450  Component::setDynamicParameterCallback(). This version creates a Parameter with no given valid values, valid
451  values are whatever T can take. */
452  template <typename T>
453  std::shared_ptr<DynamicParameter<T>>
454  addDynamicParameter(std::string const & name, std::string const & description, T const & defaultValue,
455  ParameterCategory const & category);
456 
457  //! Add a new parameter after the Component has already been constructed
458  /*! Dynamic parameters can only be accessed by descriptor, since the owning component does not inherit from them
459  like standard parameters. Callbacks can be added manually after creation using
460  Component::setDynamicParameterCallback(). This version creates a Parameter with specified valid values from a
461  ValidValueSpec */
462  template <typename T, template <typename> class ValidValuesSpec>
463  std::shared_ptr<DynamicParameter<T>>
464  addDynamicParameter(std::string const & name, std::string const & description, T const & defaultValue,
465  ValidValuesSpec<T> const & validValuesSpec, ParameterCategory const & category);
466 
467  //! Register a callback with a previously created dynamic parameter
468  /*! If callnow is true, the callback will be called right after it is registered, using the parameter's current
469  value. This is to mimic standard parameters where the callback is called at least once during init. */
470  template <typename T>
471  void setDynamicParameterCallback(std::string const & name, std::function<void(T const &)> cb,
472  bool callnow = true);
473 
474  //! Remove a previously added dynamic parameter
475  void removeDynamicParameter(std::string const & name);
476 
477  //! @}
478 
479  /*! @name Component path-related functions
480 
481  Each Component can keep track of a preferred filesystem path. Typically, users should not tamper with this,
482  but Module objects (which derive from Component) that are dynamically loaded will have that path set to the
483  path where the Module's shared object file (.so) was found. This allows those modules to access some of their
484  local configuration data, which may, for example, be stored in an \c etc/ directory under the module path. */
485  //! @{
486 
487  //! Assign a filesystem path to this component
488  void setPath(std::string const & path);
489 
490  //! If given path is relative (not starting with /), prepend the Component path to it
491  /*! If path is absolute, no-op. If path is empty, return the Component's path as set with setPath(). */
492  std::filesystem::path absolutePath(std::filesystem::path const & path = "");
493 
494  //! @}
495 
496  protected:
497  //! @name Component setup functions overloadable by derived classes
498 
499  //! @{
500 
501  //! Called before all sub-Components are init()ed
502  virtual void preInit() { }
503 
504  //! Called after all sub-Components are init()ed
505  virtual void postInit() { }
506 
507  //! Called before all sub-Components are uninit()ed
508  virtual void preUninit() { }
509 
510  //! Called after all sub-Components are uninit()ed
511  virtual void postUninit() { }
512 
513  //! @}
514 
515  private:
516  template <typename T> friend class ParameterCore;
517  friend class Manager; // Allow Manager to access our subs directly (in addComponent, etc)
518  friend class Engine; // Allow Engine to add already-created components (the module)
519  friend class Module; // Allow Module to access itsParent
520 
521  mutable boost::shared_mutex itsMtx; // Mutex used to protect our internals other than subcomps and parameters
522 
523  // Storage for sub-Components
524  std::vector<std::shared_ptr<Component> > itsSubComponents;
525 
526  // Mutex to protect our list of subcomponents
527  mutable boost::shared_mutex itsSubMtx;
528 
529  // Recursively populate a list of parameters, for help message
530  void populateHelpMessage(std::string const & cname,
531  std::unordered_map<std::string /* categ+categdesc */,
532  std::unordered_map<std::string /* name+defaultval+validvals+descrip */,
533  std::vector<std::pair<std::string /* component */, std::string /* value */ > > > > &
534  helplist, bool recurse = true) const;
535 
536  //! Initialization
537  /*! Invoke: runPreInit(), then setInitialized(), finally runPostInit() */
538  virtual void init();
539 
540  //! Recursively run preInit()
541  void runPreInit();
542 
543  //! Recursively set initialized flag
544  void setInitialized();
545 
546  //! Recursively run postInit()
547  void runPostInit();
548 
549  //! Un-initialize
550  /* Invoke: runPreUninit(), then setUninitialized(), finally runPostUninit() */
551  virtual void uninit();
552 
553  //! Recursively run preUninit()
554  void runPreUninit();
555 
556  //! Recursively set uninitialized flag
557  void setUninitialized();
558 
559  //! Recursively run postUninit()
560  void runPostUninit();
561 
562  // Our meta information. Only access this through meta(), even from within Component!
563  mutable boost::shared_mutex itsMetaMtx;
564  std::string const itsClassName;
565  std::string itsInstanceName;
566 
567  // Has this Component been initialized?
568  volatile bool itsInitialized;
569 
570  // The Component which contains this one as a sub-Component - this may be NULL if the component is not part of any
571  // hierarchy, or is the manager.
572  Component * itsParent;
573 
574  void findParamAndActOnIt(std::string const & descriptor,
575  std::function<void(jevois::ParameterBase * param, std::string const & unrolled)> doit,
576  std::function<bool()> empty) const;
577  void findParamAndActOnIt(std::vector<std::string> const & descrip, bool recur, size_t idx,
578  std::string const & unrolled,
579  std::function<void(jevois::ParameterBase *, std::string const &)> doit) const;
580 
581  std::string itsPath; // filesystem path assigned to this Component, empty by default
582 
583  //! Helper function to compute an automatic instance name, if needed
584  /*! Parent modules call this before adding a sub-module, to make sure its name will not clash with existing
585  sub-modules. Parameter classname can be empty if instance is not. Throws if a unique name cannot be created
586  (e.g., no # was provided and the instance name clashes with other sub-modules). itsSubMtx on the parent must
587  be locked by caller to make sure the computed name remains available as we return it */
588  std::string computeInstanceName(std::string const & instance, std::string const & classname = "") const;
589 
590  //! Actually remove a sub, no type checking here, for internal use only
591  /*! Uplck is assumed to already be locked for read, will be upgraded to write lock. Itr is an iterator on
592  itsSubComponents pointing at the component to remove. It will get reset(). displayname is just Component,
593  SubModule, etc for messages purposes. */
594  void doRemoveSubComponent(std::vector<std::shared_ptr<Component> >::iterator & itr,
595  boost::upgrade_lock<boost::shared_mutex> & uplck,
596  std::string const & displayname);
597 
598  // Bank of defs for dynamic parameters, someone needs to hold them
599  std::map<std::string /* name */, std::shared_ptr<ParameterBase>> itsDynParams;
600  mutable std::mutex itsDynParMtx;
601  };
602 
603 
604  //! Get the current video processing frame number
605  /*! The Engine maintains a master frame counter that is incremented on each call to a Module's process(), whether
606  or not the call succeeds. The counter is not incremented when a module has not been loaded (e.g., failed to
607  load). The counter is reset to zero each time a new module is loaded.
608 
609  This is implemented as a free function so that anyone can access it:
610 
611  \code
612  size_t current_frame_number = jevois::frameNum();
613  \endcode
614 
615  It is thread-safe. Programmer note: the implementation is in Engine.C since Engine is the one that manages the
616  counter. It is declared here so that users don't have to include Engine.H \relates Engine */
617  size_t frameNum();
618 
619 } //jevois
620 
621 // Include inlined implementation details that are of no interest to the end user
622 #include <jevois/Component/details/ComponentImpl.H>
623 
jevois::Component::setParamsFromStream
std::istream & setParamsFromStream(std::istream &is, std::string const &absfile)
Set some parameters from an open stream.
Definition: Component.C:466
jevois::Component::descriptor
std::string descriptor() const
Get our full descriptor (including all parents) as [Instancename]:[...]:[...].
Definition: Component.C:276
jevois::Component::instanceName
const std::string & instanceName() const
The instance name of this component.
Definition: Component.C:50
jevois::imu::get
Data collection mode RAW means that the latest available raw data is returned each time get() is called
jevois::Component::freezeParam
void freezeParam(std::string const &paramdescriptor)
Freeze a parameter, by name, see ParameterBase::freeze()
Definition: Component.C:417
jevois::ParameterRegistry::Component
friend class Component
Allow Component and DynamicParameter to access our registry data, everyone else is locked out.
Definition: ParameterRegistry.H:51
NEW_NORM_TENSOR
#define NEW_NORM_TENSOR(_id, _attr, _dtype)
Definition: NetworkNPU.C:59
Async.H
jevois::Manager
Manager of a hierarchy of Component objects.
Definition: Manager.H:73
jevois::Component::isTopLevel
bool isTopLevel() const
Returns true if this component is top-level, i.e., its parent is jevois::Manager.
Definition: Component.C:119
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:270
jevois::module::end
Send serial message to mark the the end(MARK STOP)
jevois::Component
A component of a model hierarchy.
Definition: Component.H:181
jevois::Component::unFreezeParam
void unFreezeParam(std::string const &paramdescriptor)
Unfreeze a parameter, by name, see ParameterBase::unFreeze()
Definition: Component.C:429
jevois::Component::addDynamicParameter
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.
jevois::Component::getSubComponent
std::shared_ptr< Comp > getSubComponent(std::string const &instance) const
Get a sub-component by instance name.
jevois::Component::setDynamicParameterCallback
void setDynamicParameterCallback(std::string const &name, std::function< void(T const &)> cb, bool callnow=true)
Register a callback with a previously created dynamic parameter.
jevois::Component::foreachParam
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:584
jevois::ParameterBase
Base class for Parameter.
Definition: Parameter.H:121
jevois::Component::getParamStringUnique
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
jevois::Component::removeSubComponent
void removeSubComponent(std::shared_ptr< Comp > &component)
Remove a sub-Component from this Component, by shared_ptr.
jevois::Component::removeDynamicParameter
void removeDynamicParameter(std::string const &name)
Remove a previously added dynamic parameter.
Definition: Component.C:518
jevois::ParameterCategory
A category to which multiple ParameterDef definitions can belong.
Definition: ParameterDef.H:33
jevois::Component::addSubComponent
std::shared_ptr< Comp > addSubComponent(std::string const &instance, Args &&...args)
Pseudo-constructor: construct and add another component as a subcomponent of this one.
jevois::Component::setPath
void setPath(std::string const &path)
Assign a filesystem path to this component.
Definition: Component.C:504
jevois::imu::file
Data collection mode RAW means that the latest available raw data is returned each time hence timing may not be very accurate depending on how regularly grate into a FIFO and and accumulates resulting output data into the IMU s internal FIFO buffer at a fixed rate This parameter can only be set in a module s params cfg file
Definition: ICM20948.H:67
jevois::Component::getParamVal
std::vector< std::pair< std::string, T > > getParamVal(std::string const &paramdescriptor) const
Get parameter(s) value(s) by descriptor.
jevois::Component::setParamsFromFile
void setParamsFromFile(std::string const &filename)
Set some parameters from a file.
Definition: Component.C:457
jevois::Component::className
const std::string & className() const
The class name of this component.
Definition: Component.C:39
jevois
Definition: Concepts.dox:1
jevois::Component::getParamValUnique
T getParamValUnique(std::string const &paramdescriptor) const
Get a parameter value, simple version assuming only one parameter match.
jevois::Component::preUninit
virtual void preUninit()
Called before all sub-Components are uninit()ed.
Definition: Component.H:508
jevois::Component::setParamVal
std::vector< std::string > setParamVal(std::string const &paramdescriptor, T const &val)
Set a parameter value.
Parameter.H
jevois::Component::paramInfo
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:537
jevois::ParameterRegistry
A simple registry of all parameters associated with a Component.
Definition: ParameterRegistry.H:33
jevois::Component::setParamValUnique
void setParamValUnique(std::string const &paramdescriptor, T const &val)
Set a parameter value, simple version assuming only one parameter match.
jevois::Engine
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:387
jevois::Component::preInit
virtual void preInit()
Called before all sub-Components are init()ed.
Definition: Component.H:502
jevois::Component::unFreezeAllParams
void unFreezeAllParams()
Unfreeze all parameters.
Definition: Component.C:449
jevois::Module
Virtual base class for a vision processing module.
Definition: Module.H:104
jevois::Component::freezeAllParams
void freezeAllParams()
Freeze all parameters.
Definition: Component.C:441
jevois::ParameterCore
A changeable parameter for a Component, core class.
Definition: Parameter.H:189
jevois::Component::~Component
virtual ~Component()
Virtual destructor for safe inheritance.
Definition: Component.C:54
jevois::Component::postUninit
virtual void postUninit()
Called after all sub-Components are uninit()ed.
Definition: Component.H:511
jevois::Component::engine
Engine * engine() const
Get a handle to our Engine, or throw if we do not have an Engine as root ancestor.
Definition: Component.C:129
jevois::Component::absolutePath
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:530
h
int h
Definition: GUIhelper.C:2373
jevois::Component::postInit
virtual void postInit()
Called after all sub-Components are init()ed.
Definition: Component.H:505
jevois::Component::initialized
bool initialized() const
Has this component been initialized yet?
Definition: Component.C:206
jevois::Component::setParamString
std::vector< std::string > setParamString(std::string const &paramdescriptor, std::string const &val)
Set a parameter value, by string.
Definition: Component.C:358
jevois::Component::setParamStringUnique
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
jevois::Component::getParamString
std::vector< std::pair< std::string, std::string > > getParamString(std::string const &paramdescriptor) const
Get a parameter value, by string.
Definition: Component.C:389