iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Blackboard.H
Go to the documentation of this file.
1 /*! @file
2  @author Laurent Itti
3  @copyright GNU Public License (GPL v3)
4  @section License
5  @verbatim
6  // ////////////////////////////////////////////////////////////////////////
7  // The iLab Neuromorphic Robotics Toolkit (NRT) //
8  // Copyright 2010-2012 by the University of Southern California (USC) //
9  // and the iLab at USC. //
10  // //
11  // iLab - University of Southern California //
12  // Hedco Neurociences Building, Room HNB-10 //
13  // Los Angeles, Ca 90089-2520 - USA //
14  // //
15  // See http://ilab.usc.edu for information about this project. //
16  // ////////////////////////////////////////////////////////////////////////
17  // This file is part of The iLab Neuromorphic Robotics Toolkit. //
18  // //
19  // The iLab Neuromorphic Robotics Toolkit is free software: you can //
20  // redistribute it and/or modify it under the terms of the GNU General //
21  // Public License as published by the Free Software Foundation, either //
22  // version 3 of the License, or (at your option) any later version. //
23  // //
24  // The iLab Neuromorphic Robotics Toolkit is distributed in the hope //
25  // that it will be useful, but WITHOUT ANY WARRANTY; without even the //
26  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
27  // PURPOSE. See the GNU General Public License for more details. //
28  // //
29  // You should have received a copy of the GNU General Public License //
30  // along with The iLab Neuromorphic Robotics Toolkit. If not, see //
31  // <http://www.gnu.org/licenses/>. //
32  // ////////////////////////////////////////////////////////////////////////
33  @endverbatim */
34 
35 
36 #ifndef INCLUDE_NRT_CORE_BLACKBOARD_BLACKBOARD_H
37 #define INCLUDE_NRT_CORE_BLACKBOARD_BLACKBOARD_H
38 
50 
51 #include <boost/thread/shared_mutex.hpp> // for shared_mutex, shared_lock and friends
52 #include <atomic>
53 
54 namespace nrt
55 {
56  template <class Subscription> class MessageSubscriberCore;
57  template <class Posting> class MessagePosterCore;
58  template <class Checking> class MessageCheckerCore;
59  class Module;
61  namespace NetIce { class BlackboardClientI; }
65  class BlackboardMaster;
66 
67  /*! \defgroup blackboard Blackboard and message passing classes and functions
68 
69  The general design and usage philosophy of the NRT Blackboard is as follows:
70 
71  - A distributed system consists of a Blackboard Federation whereby a number of processes on one or more physical
72  machines each instantiate a Blackboard. The Blackboard holds a collection of messages posted by modules, which
73  can be asynchronously checked for and retrieved. In addition, posting a message can trigger callbacks of
74  subscriber modules.
75 
76  - Thus, the three main facets which modules can implement are: support post() of a message, which will be stored
77  on the Blackboard and will trigger matching subscriber callbacks; subscribe to some message, which means
78  implementing an onMessage() callback function that the Blackboard will trigger when a matching message is
79  posted; support check() for messages, if any, that have been previously posted to the Blackboard.
80 
81  - Message storage on the Blackboard is such that only the most recent message from each poster is kept on the
82  Blackboard. Thus, when a given poster posts(), the posted message replaces any previous posted message from that
83  exact same poster. Messages from different posters do not interact, even if their message types or other aspects
84  match.
85 
86  - MessagePoster, MessageSubscriber and MessageChecker use three mechanisms to target their messages: (1) Posted
87  Message type (and callback return Message type) must match exactly between a poster and a subscriber for the
88  subscriber's callback to be triggered, and posted Message type must match exactly between a poster and a checker
89  for the checker to retrieve the Message; (2) Modules and their associated MessagePoster, MessageChecker, and
90  MessageSubscriber facets exist in a given "namespace" (a string), and only posters, checkers and subscribers
91  which belong to the exact same namespace will interact with each other; (3) Posters post on a topic (a string),
92  while checkers and subscribers use a topic filter (a string regex, following the std::regex syntax), such that
93  only subscribers whose topic filter matches a poster's topic (in addition to conditions (1) and (2) being also
94  satisfied) will be triggered by a given post (and, likewise, only messages posted on a topic that matches a
95  checker's topic filter will be returned to the checker).
96 
97  - Strong typing: Module objects must explicitly declare at compile type what they intend to post, check, and
98  subscribe to. This allows us to encapsulate modules as having ports which are known at compile time (see GUI
99  design), and it allows also for highly efficient matching of posters to subscribers and checkers by
100  pre-computing in advance who will match with whom.
101 
102  - High efficiency: Message objects are handled via std::shared_ptr and thus are not physically copied as they are
103  stored on the Blackboard or handed out to subscriber callbacks. Note that some serialization and copying is
104  necessary when messages must be transported over the network between distant machines; see the documentation of
105  Module.H for more on this.
106 
107  - Loose coherency across a distributed Blackboard Federation: Blackboards on different machines do not maintain
108  full coherency to avoid high network overhead. Rather, all blackboards are informed of what all blackboards may
109  post and subscribe to. As long as a federation is stable (modules and/or blackboards are not being created or
110  deleted), this guarantees that all matching subscribers will be triggered for a given post, etc. However,
111  exceptions may occur during transition periods (e.g., a new Blackboard connecting to the federation may miss a
112  few messages while its existence is communicated to other Blackboards). In addition, blackboards only
113  systematically forward messages over the network if matching subscribers are known to exist, but this is not
114  true for checkers. The reason for this is to make check() an infrequent, truly asynchronous action, which incurs
115  no overhead to the system until it is actually used. Thus, when a check() is issued and it is known that a
116  remote Blackboard has matching posts, a request for possible remote messages will be issued to that Blackboard,
117  which may take some time to complete. Thus, a general rule is as follows: if a consumer wants to know about all
118  the messages produced by a producer, then the consumer should be a subscriber; if the consumer only wants to
119  occasionally get the latest message from a producer, then the consumer should be a checker. The canonical
120  example is a camera producer, posting every image captured (e.g., 30 images/s); a remote module which wishes to
121  process every image should be a subscriber, and every image will then be sent to it; a module which only once in
122  a while (e.g., when the user clicks a button) wants the latest image should be a checker (thus, no network
123  bandwidth will be consumed transferring lots of unwanted images). Note that if producer and consumer are both
124  local to the same Blackboard (i.e., they both live within the same process on one machine), check() is
125  instantaneous.
126 
127  - Distributed processing: The system is aimed at distributed, parallel processing, both within one machine (using
128  multiple threads dispatched to multiple CPU cores) and across a network of machines. By design, whether a post()
129  triggers a local or remote subscriber callback is indistinguishable from the poster's viewpoint. To facilitate
130  distributed processing, post() and check() are non-blocking calls, and they return objects (MessagePosterResults
131  and MessageCheckerResults) which essentially contain a number of std::future objects, one per possible result
132  from the post() or check() call. Immediately following post() or check(), users are returned these objects and
133  it is up to them to then do something until the futures are ready (i.e., results from callbacks or checks are
134  available), or just wait on the futures, etc. Note that as results from MessagePosterResults and
135  MessageCheckerResults are consumed, they are in unspecified but optimized order, i.e., the earliest completed
136  result will be handed out first. This system implements "perfect forwarding" of subscriber callback execution,
137  i.e., there is no difference to a poster whether the callback triggered by a post() was executed on the local vs
138  a rmote machine (except that remote callbacks may be slower to return their results).
139 
140  - Exception handling: when a post() triggers some callbacks, or when a check() triggers requests to remote
141  Blackboards for possible matching messages, these triggered actions may fail, typically for two possible
142  reasons: communication failure or other inability to honor the request (this triggers an
143  nrt::exception::BlackboardException), or failure during callback execution (this triggers an
144  nrt::exception::ModuleException, no matter what the module's callback actually threw). Care is taken to
145  serialize and transport exceptions across the network, so that, from a poster's or checker's viewpoint, there is
146  no difference in processing local vs remote exceptions. Exceptions ars stuffed into the futures of
147  MessagePosterResults and MessageCheckerResults, so that there is a single point where one may encounter them
148  (while issuing a get() for the next result on either MessagePosterResults and MessageCheckerResults). See the
149  MessagePosterResults and MessageCheckerResults classes for more on exceptions. Because only BlackboardException
150  and ModuleException are thrown, and any exception caught during execution of a callback is converted to a
151  ModuleException, we do not implement "perfect fowarding" of exceptions, the main reason for this being support
152  for modules that may have been compiled at different times with different knowledge of various exception
153  types.
154 
155  See \link test-Module.C \endlink for examples.
156 
157  \ingroup core */
158 
159  /*! @{ */ // **********************************************************************
160 
161  //! The Blackboard handles communication of Message objects between Module objects
162  /*! For the most part, users should never need to directly reference a Blackboard. Each executable has a global
163  Singleton Blackboard that can be accessed as \c nrt::Blackboard::instance(). All further interaction with a
164  Blackboard is usually handled automatically via a Module's post(), onMessage(), check(), etc.
165 
166  The NRT Blackboard system exposes three interfaces:
167 
168  - A strongly-typed and highly-efficient interface via Module ports. This is the preferred interface when
169  programming NRT modules. A call to a Module's poster port post() function will, under the hood, communicate with
170  the localBlackboard, which may further communicate with remote Blackboards in a federation. Likewise,
171  instantiating a Module with ports will automatically register these ports with the Blackboard federation.
172 
173  - A weakly-typed, strings-based, less efficient interface available on the Blackboard directly. This interface
174  should typically never be used from within Module objects. Instead, it allows transparent access to modules,
175  ports, connectors, parameters, etc on local or remote Blackboards via a unified interface. This interface is
176  useful, for example, for GUI designers or people who write applications that interact with the Blackboard
177  federation or present a view of it to users, as opposed to standard NRT modules that just post and receive
178  messages and process them. For example, if you created a module, then call Module::setModuleNamespace() on that
179  module rather than Blackboard::setModuleNamespace() on the Blackboard. The nrtDesigner is an example of an
180  application that makes extensive use of the strings-based interface to instantiate new modules on local or
181  remote Blackboards, split/unsplit ports, set parameters, create/delete connectors, etc.
182 
183  - The BlackboardManager interface, which allows configuration of network communications, who/where the master
184  Blackboard is, etc. See BlackboardManager for details. Blackboard derives from BlackboardManager, which itself
185  derives from Manager, so this interface also includes the management of command-line arguments and of
186  collections of Component objects (Module derives from Component). See Manager and Component for details. Because
187  BlackboardManager and Manager are both folder into Blackboard, they do not need to be instantiated eplicitly in
188  executables that will use a Blackboard. Just access the Blackboard and the managers are part of it. */
189  class Blackboard : public nrt::Singleton<Blackboard>,
191  {
192  public:
193  //! \name Blackboard basics and debugging
194  /*! @{ */ // **********************************************************************
195 
196  //! Constructor
197  /*! Because the Blackboard is a singleton, you should never try to contruct one by hand. Instead, just call
198  \code
199  nrt::Blackboard & bb = nrt::Blackboard::instance();
200  \endcode
201  to get a reference to the Blackboard. The Blackboard will be constructed the first time it is accessed. */
202  Blackboard();
203 
204  //! Destructor
205  virtual ~Blackboard();
206 
207  //! Our UID
208  BlackboardUID const & uid() const;
209 
210  //! Our UID as a string
211  std::string const & uidstr() const;
212 
213  //! Our Nickname
214  std::string const & nickname() const;
215 
216  //! The UID (as a string) of the Blackboard federation master (could be ourselves)
217  std::string const & masteruid() const;
218 
219  //! Print some debugging info about registered posters, callbacks, etc
220  void printInfo(std::ostream & os) const;
221 
222  //! Print current Message objects on the Blackboard
223  void printMessages(std::ostream & os) const;
224 
225  //! Print out the data in a BlackboardFederationSummary
226  void printSummary(std::ostream & os) const;
227 
228  //! Get the number of currently active onMessage() subscriber callbacks
229  int getCallbackCount() const;
230 
231  /*! @} */ // **********************************************************************
232 
233  //! \name Distributed Blackboard Federation updates
234  /*! @{ */ // **********************************************************************
235 
236  //! Wait until all pending Blackboard federation transactions (if any) are complete
237  /*! Note that this just waits until a point in time where all in-progress transactions have completed
238  (transactions are things like loading modules, creating ports, setting topics, etc). However, there is no
239  guarantee that things will stay that way, i.e., as soon as the wait is over, a new transaction may start even
240  before this function returns. Thus you should not assume that no transaction is in process when this function
241  returns. This function is just to help ensure that some previous transaction that you may have initiated is
242  complete before you initiate another transaction that is dependent on the first one. That is, you cannot
243  guarantee that transactions from others (remote Blackboards, or even other threads acting on this local
244  Blackboard) are cleared before initiating yours, but you can guarantee that your previous transactions are
245  complete befofe initiating additional ones. */
246  void waitUntilUpdated() const;
247 
248  //! Get the latest available BlackboardFederationSummary
249  /*! If you need to receive updates to that summary each time they become available, consider using a
250  BlackboardFederationSummaryListener instead. */
251  std::shared_ptr<nrt::blackboard::BlackboardFederationSummary const>
252  getLatestBFS(bool waituntilupdated = false) const;
253 
254  //! Get an automatically-generated, federation-unique-within-a-namespace topic name
255  std::string getAutoTopic(std::string const & namespc);
256 
257  /*! @} */ // **********************************************************************
258 
259 
260  //! \name Strings-based (less efficient) access to local and remote modules, ports, connectors, etc
261  /*! @{ */ // **********************************************************************
262 
263  //! Get the help message from local or remote Blackboard
264  /*! This is the message that is returned by commadn-line option --help, here accessible on remote Blackboards as
265  well. This message will list all available parameters of all modules and components on that Blackboard, along
266  with the current parameter values and how to access these parameters (parameter descriptor) */
267  std::string getHelpMessage(std::string const & bbuid);
268 
269  //! Create a namespace on the master blackboard
270  void createNamespace(std::string const & namespc);
271 
272  //! Rename a namespace and all its contents
273  void renameNamespace(std::string const & oldnamespc, std::string const & newnamespc);
274 
275  //! Delete a namespace
276  void deleteNamespace(std::string const & namespc);
277 
278  //! Set the namespace of a module
279  void setModuleNamespace(std::string const & module, std::string const & namespc);
280 
281  //! Get the namespace of a module
282  /*! \note Avoid using this function as it is not synchronization-safe, i.e., someone may change that namespace
283  between the time you get it and you try to do anything with it. Mostly this is used internally to mark a
284  macro-module as modified when it is moved in the GUI, in which case such synchronization errors may be
285  tolerable. */
286  std::string getModuleNamespace(std::string const & module);
287 
288  //! Mark a namespace (macro-module) as modified, so that GUI will request user to save it
289  /*! Operations that modify the contents of a namespace call this automatically, so no need to call it in general,
290  unless you need to explicitly mark a namespace for saving even though no module, parameter, connection, etc
291  has been changed yet. */
292  void markNamespaceModified(std::string const & namespc);
293 
294  //! Create a connector on the master Blackboard
295  /*! @return connector UID */
296  std::string
297  createConnector(nrt::ConnectorFlavor const flavor, std::string const & msgtype, std::string const & rettype,
298  std::string const & portname, std::string const & namespc, nrt::ConnectorType const type,
299  std::string const & topic, std::string const & topicfilt);
300 
301  //! Lookup a Connector UID from a namespace and instance name (portname) within that namespace
302  std::string getConnector(std::string const & namespc, std::string const & portname);
303 
304  //! Delete a connector on the master blackboard
305  void deleteConnector(std::string const & connuid);
306 
307  //! Set the topic on a local or remote connector
308  void setConnectorTopic(std::string const & connuid, std::string const & topic);
309 
310  //! Set the topic filter on a local or remote connector
311  void setConnectorTopicFilter(std::string const & connuid, std::string const & topicfilter);
312 
313  //! Set the topic of a poster port of a module
314  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
315  void setMessagePosterTopic(std::string const & module, std::string const & portname, std::string const & topic);
316 
317  //! Split/unsplit a poster port of a module
318  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
319  void splitMessagePoster(std::string const & module, std::string const & portname, bool const splitit = true);
320 
321  //! Set the topic filter of a checker
322  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
323  void setMessageCheckerTopicFilter(std::string const & module, std::string const & portname,
324  std::string const & topicfilter);
325 
326  //! Set the topic filter of a subscriber port of a module
327  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
328  void setMessageSubscriberTopicFilter(std::string const & module, std::string const & portname,
329  std::string const & topicfilter);
330 
331  //! Split/unsplit a subscriber port of a module
332  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
333  void splitMessageSubscriber(std::string const & module, std::string const & portname, bool const splitit = true);
334 
335  //! Instantiate a ModuleLibrary on the local Blackboard and parse all the manifests, load all the icons, etc
336  /*! @param paths Colon-separated list of paths. If the list is empty, we will use the NRTMODULEPATH environment
337  variable instead. */
338  void useModuleLibrary(std::string const & paths = "");
339 
340  //! Get a copy of the ModuleLibrary contents on a (local or remote) blackboard
341  /*! Simply returns an empty library if none was loaded by a call to useModuleLibrary */
342  std::vector<ModuleDescription> getModuleLibraryContents(std::string const & bbuid);
343 
344  //! Create a Module and register it with the local Blackboard
345  /*! The created module becomes a so-called top-level module, and it is owned directly by the Blackboard. One can
346  later change the namespace of such top-level modules (whereas sub-modules inherit their namespace from their
347  top-level parent). */
348  template <class Mod>
349  std::shared_ptr<Mod> addModule(std::string const & instanceName = "", std::string const & namespc = "");
350 
351  //! Use addModule() to add a top-level module to the Blackboard, as opposed to nrt::Module::addSubModule()
352  template <class Mod>
353  std::shared_ptr<Mod> addSubModule(std::string const & instanceName) = delete;
354 
355  //! Load a module from a shared library file, on the local Blackboard or on a remote one
356  /*! The module will be loaded, instantiated, added as a sub-component to the blackboard, placed in the desired
357  namespace, and brought to the same initialized/started/running state as the creating Blackboard.
358  @param bbuid the Blackboard to load the module on
359  @param sopath full absolute file path (including filename) of the .so file to load
360  @param logicalpath the logical path of the module, as categ/subcat/.../classname. If this module is not
361  categorized, provide at least classname in there. This classname is the name of the class in the .so file
362  to instantiate to create the module
363  @param instance name to give to the instance at construction. If empty, a unique name of the form NRT_AUTO_#
364  (with # replaced by a number) will be computed and returned.
365  @param namespc the namespace to place the module in.
366  @param versioncheck if true, check that the module was compiled with the same NRT version as the loading
367  Blackboard.
368  @returns moduleuid (as a string) of the loaded module, and instance name. */
369  std::pair<std::string /* module uid */, std::string /* instance */>
370  loadModule(std::string const & bbuid, std::string const & sopath, std::string const & logicalpath,
371  std::string const & instance = "", std::string const & namespc = "", bool versioncheck = true);
372 
373  //! Load a module from a shared library file, on the local Blackboard or on a remote one
374  /*! The module will be loaded, instantiated, added as a sub-component to the blackboard, placed in the desired
375  namespace, and brought to the same initialized/started/running state as the creating Blackboard.
376  @param bbnick the nick of the Blackboard that should load the module
377  @param logicalpath logical file path of the module, format categ/subcat/.../classname
378  @param instance name to give to the instance at construction. If empty, a unique name of the form NRT_AUTO_#
379  (with # replaced by a number) will be computed and returned.
380  @param namespc namespace to place the module in.
381  @param versioncheck if true, check that the module was compiled with the same NRT version as the loading
382  Blackboard.
383  @returns moduleuid (as a string) of the loaded module, and instance name.
384 
385  @note This function is slightly slower than loadModule() as it requires a lookup from bbnick to bbuid, and
386  from logicalpath to sopath. */
387  std::pair<std::string /* module uid */, std::string /* instance */>
388  loadModuleLogical(std::string const & bbnick, std::string const & logicalpath, std::string const & instance = "",
389  std::string const & namespc = "", bool versioncheck = true);
390 
391  //! Load a macro-module on the Blackboard master, by logical path
392  /*! The Blackboard master will load the macro-module (but this may involve instantiating modules on other
393  Blackboards, which will be done automatically). A namespace is created for namespc/instancename and all the
394  contents of the macro-module file are instantiated in that namespace.
395 
396  @param logicalpath logical file path of the macro-module, format categ/subcat/.../macromodname
397  @param instancename name to give to this macro-module. If empty, a unique name of the form NRT_AUTO_#
398  (with # replaced by a number) will be computed and returned.
399  @param namespc namespace to place all of the macro-module's contents into
400  @param bbnickremap remapping of bbnicks between those in the file and those that will be used for
401  instantiation of the macro-module's actual modules */
402  std::string /* instance */
403  loadMacroModule(std::string const & logicalpath, std::string const & instancename, std::string const & namespc,
404  std::map<std::string /* file bbnick */, std::string /* used bbnick */> const & bbnickremap);
405 
406  //! Save a macro-module
407  /*! The Blackboard master will do the saving, on its local disk. */
408  void saveMacroModule(std::string const & namespc, nrt::ModuleManifest const & manifest);
409 
410  //! Remove a top-level Module from the local Blackboard, by shared_ptr
411  /*! \note Beware that the passed shared_ptr is invalidated in the process. A warning is issued if the use_count is
412  not down to zero after that (i.e., there are additional shared_ptr pointers to this Module floating around,
413  which prevent it from actually being deleted. */
414  template <class Mod>
415  void removeModule(std::shared_ptr<Mod> & module);
416 
417  //! Use removeModule() to remove a top-level module from Blackboard, as opposed to nrt::Module::removeSubModule()
418  template <class Mod>
419  void removeSubModule(std::shared_ptr<Mod> & module) = delete;
420 
421  //! Remove a top-level Module from the local Blackboard, by instance name
422  void removeModule(std::string const & instanceName);
423 
424  //! Use removeModule() to remove a top-level module from Blackboard, as opposed to nrt::Module::removeSubModule()
425  void removeSubModule(std::string const & instanceName) = delete;
426 
427  //! Get a top-level Module by instance name
428  /*! This method does a dynamic_pointer_cast to Mod if it is not the default (nrt::Module). Throws if module
429  is not found by instance name, or it is found but not of type Mod (if Mod is specified). Note that once you
430  hold a shared_ptr to a Module, it is guaranteed that the module will not be destroyed until that
431  shared_ptr is released. If the NRT system tries to destroy the module (e.g., someone calls
432  removeModule()), the module will be un-initialized and its parent will be unset, so it will not be
433  fully operational and will be actually deleted when the last shared_ptr to it runs out of scope. */
434  template <class Mod = nrt::Module>
435  std::shared_ptr<Mod> getModule(std::string const & instanceName) const;
436 
437  //! Use getModule() on the Blackboard as opposed to nrt::Module::getSubModule() on modules
438  template <class Mod>
439  std::shared_ptr<Mod> getSubModule(std::string const & instanceName) const = delete;
440 
441  //! Unload a module previously loaded with loadModule() or added with addModule(), local or remote
442  /*! The module will be stopped, un-initialized, removed as sub-component, and destroyed. Only works for modules
443  that have been loaded with loadModule(). */
444  void unloadModule(std::string const & module);
445 
446  //! Load a Module or Macro-Module manifest
447  /*! If not found, a default manifest is returned, populated with default values from NRTAUTHOR and from the
448  running NRT software and operating system. */
449  nrt::ModuleManifest loadManifest(std::string const & bbuid, std::string const & unixroot,
450  std::string const & logicalpath);
451 
452  //! Save a Module or Macro-Module manifest
453  void saveManifest(std::string const & bbuid, std::string const & unixroot, std::string const & logicalpath,
454  nrt::ModuleManifest const & manifest);
455 
456  /*! @} */ // **********************************************************************
457 
458  //! Abort all ongoing split receive
459  void abortAllSplitReceive() const;
460 
461  //! Create a port for a Module parameter
462  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
463  ////std::vector<std::string>
464  ////createModuleParamPort(ModuleParamPort const ptype, std::string const & module, std::string const & descriptor);
465 
466  //! Delete a port for a Module parameter
467  /*! If the module is not local, request will be dispatched to the appropriate remote Blackboard */
468  ////std::vector<std::string>
469  ////deleteModuleParamPort(ModuleParamPort const ptype, std::string const & module, std::string const & descriptor);
470  //! \name Interface to listener objects that get activated when various Blackboard contents are updated
471  /*! @{ */ // **********************************************************************
472 
473  //! Register a listener that should be called each time a parameter of a module on bbuid changes
474  /*! Note: make sure your listener is fully constructed and ready to process callbacks by the time you register it,
475  as callbacks will start immediately. */
476  std::vector<nrt::ParameterSummary> registerParamChangedListener(nrt::ParamChangedListener * lis);
477 
478  //! Un-Register a listener that should be called each time a parameter of a module on bbuid changes
480 
481  //! Notify that a Parameter of a local Module/Component changed, will forward the notify to matching listeners
482  void notifyParamChanged(nrt::ModuleBase const * mod, nrt::ParameterState const state,
483  nrt::ParameterBase const * const param);
484 
485  //! Set the value of a Parameter on a (possibly remote) Blackboard
486  /*! Throws just like Parameter::set() if the value is rejected by the parameter. This function is not const
487  because Blackboard has parameters too, which could be modified using this function. */
488  void setParam(std::string const & bbuid, std::string const & descriptor, std::string const & value);
489 
490  //! Set the value of a Parameter on a Module on a (possibly remote) Blackboard
491  /*! Throws just like Parameter::set() if the value is rejected by the parameter. This function is not const
492  because Blackboard has parameters too, which could be modified using this function.
493  @param module Module UID, the param should be in that module or one of its sub-components/sub-modules
494  @param relativedescriptor Descriptor relative to the module. If the param is in the module itself, that would
495  just be the param name; if it is in a sub then strandard descriptor syntax applies, starting from the
496  specified module.
497  @param value Value to set the param to. */
498  void setModuleParam(std::string const & module, std::string const & relativedescriptor,
499  std::string const & value);
500 
501  //! Get the current value of all of a Module's (and its sub-modules & sub-components) parameters
502  /*! Returns a snapshot of all current parameter values. Using a ParamChangedListener is usually the preferred way
503  to keep up with changing parameters. This function is here to assist with saving the entire state of a
504  Blackboard federation, e.g., when a GUI wants to export a whole network as a python script. */
505  std::vector<nrt::ParameterSummary> getAllModuleParams(std::string const & module);
506 
507  //! Register a listener that should be called each time a BlackboardFederationSummary is available
508  /*! If available, the latest summary will be passed to the listener immediately upon registration. */
510 
511  //! Un-Register a listener that should be called each time a BlackboardFederationSummary is available
513 
514  //! Request that the specified Blackboard (could be us) sends a Blackboard Federation update
515  void requestBlackboardUpdate(std::string const & bbuid);
516 
517  //! Register a listener that should be called each time some GUI data is updated
518  /*! If available, the latest data will be passed to the listener immediately upon registration. */
520 
521  //! Un-Register a listener that should be called each time some GUI data is available
523 
524  //! Get GUI data for a given key of form m:ModuleUID, n:namespace, etc
525  nrt::blackboard::GUIdata getGUIdata(std::string const & key);
526 
527  //! Create/update GUI data for a given key of form m:ModuleUID or n:namespace
528  void setGUIdata(std::string const & key, nrt::blackboard::GUIdata const & gd);
529 
530  //! Get Blackboard usage data from a (local or remote) Blackboard
531  nrt::BlackboardUsageData getBlackboardUsageData(std::string const & bbuid);
532 
533  //! Register a listener that should be called each time a RunStateAction is executed
535 
536  //! Un-Register a listener that should be called each time a RunStateAction is executed
538 
539  private:
540  //! Needed by nrt::Singleton
541  friend class Singleton<Blackboard>;
542 
543  //! Be friends with people who can post/service Message and Request, and only with those
544  friend class ModuleBase;
545 
546  //! Allow our BlackboardManager base to access our internals
547  friend class BlackboardManager;
548 
549  //! Allow BlackboardMaster to dispatch remote commands
550  friend class BlackboardMaster;
551 
552  //! Allow Module to register itself with us
553  friend class Module;
554 
555  //! Allow ConnectorBase to register itself with us
556  friend class ConnectorBase;
557 
558  //! Allow Ice network client to talk to us
560 
561  //! Allow ports to notify us when their topic/filter changes
562  friend class MessagePosterCoreBase;
563  friend class MessageCheckerCoreBase;
564  friend class MessageSubscriberCoreBase;
565 
566  // ######################################################################
567  // UID Management
568  // ######################################################################
569 
570  //! Our own UID
571  BlackboardUID itsUID;
572 
573  //! Our nickname
574  std::string itsNickname;
575 
576  //! The UID (as a string) of the Blackboard federation master (could be ourselves)
577  std::string itsMasterUID;
578 
579  // ######################################################################
580  // Message Type Management
581  // ######################################################################
582 
583  std::map<std::string /* msgtype */, size_t> itsMessageTypeRegistry;
584 
585  void registerMessageType(std::string const & msgtype);
586 
587  void unRegisterMessageType(std::string const & msgtype);
588 
589  // ######################################################################
590  // Concurrency management
591  // ######################################################################
592 
593  // Shared mutex for the message repository
594  mutable boost::shared_mutex itsMsgMtx;
595 
596  // Shared mutex for all the module data
597  mutable boost::shared_mutex itsModMtx;
598 
599  // Shared mutex for all the pending remote post data
600  mutable boost::shared_mutex itsPrpMtx;
601 
602  // ######################################################################
603  // Message management
604  // ######################################################################
605 
606  //! Mapping from string to msgkey
607  nrt::IdTable<std::string /* module|posting */, nrt::blackboard::msgkey>
608  itsMessageKeys;
609 
610  //! Our set of stored messages that have been posted by local (only) posters (the actual "blackboard")
611  std::map<nrt::blackboard::msgkey, nrt::blackboard::MessageData>
612  itsMessages;
613 
614  //! Store a message onto our blackboard, by msgkey
615  template <class Msg>
616  void storeMessage(std::shared_ptr<Msg const> msg, nrt::blackboard::msgkey mkey);
617 
618  //! Delete a message from our blackboard, if any, by msgkey
619  template <class Msg>
620  void expungeMessage(nrt::blackboard::msgkey mkey);
621 
622  // Get a message from our local Blackboard storage, if any is there, by msgkey, for a given checker (local or
623  // remote), and (depending on policy) mark the message as seen by that checker. If no message is found, or if it
624  // has been returned previously to this checker (depending on policy), a null shared_ptr is returned.
625  std::shared_ptr<nrt::MessageBase const>
626  getMessage(nrt::blackboard::msgkey mkey, nrt::MessageCheckerPolicy mcp, std::string const & modulecportname);
627 
628  // ######################################################################
629  // Module management
630  // ######################################################################
631  void registerModule(nrt::Module * const module);
632  void unRegisterModule(nrt::Module * const module);
633 
634  //! The set of all local Modules that are currently alive and known
635  /*! Note that this is a shadow of our sub-component tree (only including modules) since we inherit from
636  nrt::Component. However, the list here is flat (no need to do recursive search in the sub-component tree to
637  locate a module), and by raw pointer for faster access (no need to dynamic_cast and dereference a shared_ptr;
638  we control the shared_ptrs in itsSubComponents and as long as we are bug-free here, anything in itsModules
639  should exist in itsSubComponents or their subs) */
640  std::set<nrt::Module *> itsModules;
641 
642  // ######################################################################
643  // Local MessagePoster management
644  // ######################################################################
645 
646  // Be friends with all MessagePosters
647  template <class ... Postings> friend class nrt::MessagePoster;
648  template <class Posting> friend class nrt::MessagePosterCore;
649 
650  template <class Posting>
651  void registerMessagePoster(nrt::MessagePosterCore<Posting> * pos);
652 
653  template <class Posting>
654  void unRegisterMessagePoster(nrt::MessagePosterCore<Posting> * pos);
655 
656  // Internal list of all our local posters
657  std::map<std::string /* module */,
658  std::map<std::string /* portname */,
660  itsPosters;
661 
662  // ######################################################################
663  // Remote check() servicing management
664  // ######################################################################
665 
666  // Serve a remote check for message type msgtype and for remote checker rmodulecportname with policy mcp,
667  // returning a vector of messages serialized into a string
668  void serveRemoteCheck(std::string const & msgtype, std::string const & rmodulecportname,
669  nrt::MessageCheckerPolicy mcp, std::string & bytes);
670 
671  // Mapping from serveRemoteCheck() with a string msgtype to doServeRemoteCheck() with Msg template arg, for the
672  // message types that we post
673  std::map<std::string /* msgtype */,
674  nrt::blackboard::serveRemoteCheckFunc>
675  itsServeRemoteCheckFuncMap;
676 
677  // Actual serving of remote check with the message type as template argument
678  template <class Msg>
679  void doServeRemoteCheck(std::string const & rmodulecportname, nrt::MessageCheckerPolicy mcp, std::string & bytes);
680 
681  // For each remote checker that is a match against one or more of our local posters, list of the message keys to
682  // the messages posted by our local posters
683  std::map<std::string /* rmodule|cportname */,
684  std::vector<nrt::blackboard::msgkey> >
685  itsRemoteCheckHitList;
686 
687 
688  // ######################################################################
689  // Local MessageChecker management
690  // ######################################################################
691 
692  template <class ... Checkings> friend class nrt::MessageChecker;
693  template <class Checking> friend class nrt::MessageCheckerCore;
694 
695  template <class Checking>
696  void registerMessageChecker(nrt::MessageCheckerCore<Checking> * chk);
697 
698  template <class Checking>
699  void unRegisterMessageChecker(nrt::MessageCheckerCore<Checking> * chk);
700 
701  // Internal list of all our local checkers
702  std::map<std::string /* module */,
703  std::map<std::string /* portname */,
705  itsCheckers;
706 
707  // ######################################################################
708  // Local MessageSubscriber management
709  // ######################################################################
710 
711  template <class ... Subscriptions> friend class nrt::MessageSubscriber;
712  template <class Subscription> friend class nrt::MessageSubscriberCore;
713 
714  template <class Subscription>
715  void registerMessageSubscriber(nrt::MessageSubscriberCore<Subscription> * sub);
716 
717  template <class Subscription>
718  void unRegisterMessageSubscriber(nrt::MessageSubscriberCore<Subscription> * sub);
719 
720  // Internal list of all our local subscribers
721  std::map<std::string /* module */,
722  std::map<std::string /* sportname */,
724  itsSubscribers;
725 
726  // ######################################################################
727  // Remote post and remote callback servicing management
728  // ######################################################################
729 
730  // Serve a remote post of a serialized message (in bytes) of type msgtype, posted by rbbuidrmodulepportname, with
731  // a unique transaction key and expecting numcallbacks following callbacks
732  void serveRemotePost(std::string const & bytes, std::string const & msgtype, std::string const & rmodulepportname,
733  std::string const & transactionkey, size_t const numcallbacks);
734 
735  // Mapping from serveRemotePost() with a string msgtype to doServeRemotePost() with Msg template arg, for the
736  // message types that we subscribe to
737  std::map<std::string /* msgtype */,
738  nrt::blackboard::serveRemotePostFunc>
739  itsServeRemotePostFuncMap;
740 
741  // Actual serving of remote post with the message type as template argument
742  template <class Msg>
743  void doServeRemotePost(std::string const & bytes, std::string const & rmodulepportname,
744  std::string const & transactionkey, size_t const numcallbacks);
745 
746 
747  // Our set of temporarily stored messages part of a remote post transaction
748  std::map<std::string /* transactionkey */,
750  itsPendingRemotePostMessages;
751 
752  // Execute a callback function of a local module lmodulesportname in response to remote post identified by
753  // transactionkey
754  void serveRemoteCallback(std::string const & lmodulesportname, std::string const & transactionkey,
755  std::string & retstr);
756 
757  // List of local subscribers that have been identified as matching some remote poster; for each one,
758  // CallbackForRemoteData contains the callback properly wrapped for execution while serving a remote post, along
759  // with a ref to the mutex of the subscriber so we can lock it during callback execution
760  std::map<std::string /* lmodule|sportname */,
762  itsRemoteCallbackHitList;
763 
764  // ######################################################################
765  // Remote Blackboard interfacing (non network-specific code only)
766  // ######################################################################
767 
768  nrt::Watchdog itsWatchdog;
769 
770  BlackboardNetHandler * itsNetHandler;
771 
772  // Let the distributed blackboard federation know about our interests (posts, checks, subscriptions, etc)
773  void sendUpdateToBlackboardFederation();
774 
775  // Let the distributed blackboard federation know about our interests (posts, checks, subscriptions, etc)
776  /*! This function waits until the update has gone through. Use sparingly as it can slow down your system
777  operations considerably. */
778  void sendSynchronousUpdateToBlackboardFederation();
779 
780  //! Let the distributed blackboard federation know about our interests (posts, checks, subscriptions, etc)
781  void doSendUpdateToBlackboardFederation();
782 
783  // Aggregate a bunch of BlackboardSummary objects (one per blackboard) into a BlackboardFederationSummary,
784  // here we send the BlackboardSummary updates one at a time:
785  void aggregateBlackboardFederationSummary
786  (std::string const & bbuid, nrt::blackboard::BlackboardSummary const & bs,
787  std::shared_ptr<nrt::blackboard::BlackboardFederationSummary> bfs) const;
788 
789  // Update our knowledge about all blackboards in the federation from data given by blackboard master
790  void processSummaryUpdateFromBlackboardFederation
791  (std::shared_ptr<nrt::blackboard::BlackboardFederationSummary const> bfs);
792 
793  // ######################################################################
794  // Connection management
795  // ######################################################################
796 
797  // Connect a poster to a checker/subscriber, if at least one of the two is local, so that we can update the
798  // corresponding internal table(s)
799  void
800  connectPosterToChecker(std::string const & pbbuid, std::string const & pmodule, std::string const & pportname,
801  std::string const & cbbuid, std::string const & cmodule, std::string const & cportname);
802 
803  void
804  connectPosterToSubscriber(std::string const & pbbuid, std::string const & pmodule, std::string const & pportname,
805  std::string const & pmsgtype, std::string const & sbbuid, std::string const & smodule,
806  std::string const & sportname, std::string const & smsgtype);
807 
808  void findAllConnections(nrt::blackboard::NamespaceSummary * nss);
809 
810  void findConnections(nrt::blackboard::ConnectionSearchQueue & open,
811  nrt::blackboard::ConnectionSearchQueue & closed,
812  std::string const & msgtype, std::string const & rettype,
813  std::string const & pbbuid, std::string const & pmodule, std::string const & pportname);
814 
815  void enqueueChildren(std::shared_ptr<nrt::blackboard::ConnectionSearchNode> node,
816  std::string const & msgtype, std::string const & rettype,
817  nrt::blackboard::ConnectionSearchQueue & open,
818  nrt::blackboard::ConnectionSearchQueue & closed);
819 
820  bool topicMatch(std::string const & topic, std::string const & topicfilt) const;
821 
822  bool msgTypeMatch(std::string const & msgtype1, std::string const & msgtype2) const;
823  bool retTypeMatch(std::string const & rettype1, std::string const & rettype2) const;
824 
825  void pushNode(nrt::blackboard::ConnectionSearchNode::LinkType linktype, std::string const & bbuid,
826  std::string const & conn, std::string const & topic, nrt::blackboard::NamespaceSummary * ns,
827  nrt::blackboard::ConnectionSearchQueue & open, nrt::blackboard::ConnectionSearchQueue & closed);
828 
829  // ######################################################################
830  // Parameter change listening management
831  // ######################################################################
832 
833  // Those local listeners (in our process and on our bb) monitor these (remote or local) modules
834  std::map<std::string /* module */,
835  std::set<nrt::ParamChangedListener *> >
836  itsLocalParamChangedListeners;
837 
838  // Those remote bbuids want to know about our local param changes on those local modules
839  std::map<std::string /* module */,
840  std::set<std::string /* rbbuid */> >
841  itsRemoteParamChangedListeners;
842 
843  // must be locked by caller!
844  std::vector<nrt::ParameterSummary>
845  registerRemoteParamChangedListener(std::string const & monitoredmodule, std::string const & listenerbbuid);
846 
847  // must be locked by caller!
848  void unRegisterRemoteParamChangedListener(std::string const & monitoredmodule, std::string const & listenerbbuid);
849 
850  void processRemoteParamChanged(std::string const & module, nrt::ParameterState const state,
851  nrt::ParameterSummary const & ps);
852 
853  // ######################################################################
854  // Remote commands
855  // ######################################################################
856 
857  void dispatchRemoteCommand(std::string const & bbuid, nrt::blackboard::RemoteCommand & cmd);
858 
859  void broadcastRemoteCommand(nrt::blackboard::RemoteCommand & cmd);
860 
861  void processRemoteCommand(nrt::blackboard::RemoteCommand & cmd);
862 
863  // ######################################################################
864  // BlackboardFederationSummary and associated listeners management
865  // ######################################################################
866  mutable boost::shared_mutex itsBFSmtx;
867 
868  std::shared_ptr<nrt::blackboard::BlackboardFederationSummary const> itsLatestBFS;
869 
870  std::set<nrt::BlackboardFederationSummaryListener *> itsBFSlisteners;
871 
872  void printSummary(std::ostream & os, size_t const indent,
873  std::shared_ptr<nrt::blackboard::NamespaceSummary const> nss) const;
874  void printSummary(std::ostream & os,
875  std::shared_ptr<nrt::blackboard::BlackboardFederationSummary const> bfs) const;
876 
877  // Find the bbuid that a Module lives on, according to our latest BFS
878  std::string bbuidForModule(std::string const & module) const;
879 
880  // ######################################################################
881  // RunStateAction listeners
882  // ######################################################################
883  // NOTE: protected by itsRunStateMtx that is in BlackboardManager.
884  // Use this mutex before you sheck running() and such.
885  std::set<nrt::RunStateActionListener *> itsRunStateActionListeners;
886 
887  // ######################################################################
888  // Profiling and callback activity bookkeeping
889  // ######################################################################
890  std::atomic<int> itsActiveCallbacks;
891 
892  // ######################################################################
893  // Module/macromodule (namespace) GUI coordinates managament
894  // ######################################################################
895 
896  //! Shared mutex for the GUI data listeners
897  mutable boost::shared_mutex itsGuiMtx;
898 
899  //! List of local GUIdata listeners
900  std::set<nrt::BlackboardGUIdataListener *> itsLocalGUIdataListeners;
901 
902  // ######################################################################
903  // Namespaces management:
904  // ######################################################################
905 
906  void doLocalDeleteNamespace(std::string const & namespc);
907  void doLocalRenameNamespace(std::string const & oldnamespc, std::string const & newnamespc);
908 
909  // ######################################################################
910  // Blackboard usage data
911  // ######################################################################
912  public:
913  void incrementActiveCallbackCount();
914  void decrementActiveCallbackCount();
915  private:
916  // ######################################################################
917  // Optional ModuleLibrary
918  // ######################################################################
919  nrt::ModuleLibrary * itsModuleLibrary;
920 
921  // ######################################################################
922  // Optional BlackboardMaster functionality
923  // ######################################################################
924  nrt::BlackboardMaster * itsMaster;
925  };
926 
927  /*! @} */ // **********************************************************************
928 
929 }; // namespace nrt
930 
931 #endif // INCLUDE_NRT_CORE_BLACKBOARD_BLACKBOARD_H