SUMO - Simulation of Urban MObility
CHBuilder.h
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2017 German Aerospace Center (DLR) and others.
4 /****************************************************************************/
5 //
6 // This program and the accompanying materials
7 // are made available under the terms of the Eclipse Public License v2.0
8 // which accompanies this distribution, and is available at
9 // http://www.eclipse.org/legal/epl-v20.html
10 //
11 /****************************************************************************/
19 // Contraction Hierarchy Builder for the shortest path search
20 /****************************************************************************/
21 #ifndef CHBuilder_h
22 #define CHBuilder_h
23 
24 
25 // ===========================================================================
26 // included modules
27 // ===========================================================================
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 
34 #include <string>
35 #include <functional>
36 #include <vector>
37 #include <set>
38 #include <limits>
39 #include <algorithm>
40 #include <iterator>
41 #include <utils/common/SysUtils.h>
43 #include <utils/common/StdDefs.h>
45 #include "SPTree.h"
46 
47 //#define CHRouter_DEBUG_CONTRACTION
48 //#define CHRouter_DEBUG_CONTRACTION_WITNESSES
49 //#define CHRouter_DEBUG_CONTRACTION_QUEUE
50 //#define CHRouter_DEBUG_CONTRACTION_DEGREE
51 //#define CHRouter_DEBUG_WEIGHTS
52 
53 // ===========================================================================
54 // class definitions
55 // ===========================================================================
70 template<class E, class V>
71 class CHBuilder {
72 
73 public:
75  // forward connections are used only in forward search
76  // backward connections are used only in backwards search
77  class Connection {
78  public:
79  Connection(int t, double c, SVCPermissions p): target(t), cost(c), permissions(p) {}
80  int target;
81  double cost;
83  };
84 
85  typedef std::pair<const E*, const E*> ConstEdgePair;
86  typedef std::map<ConstEdgePair, const E*> ShortcutVia;
87  struct Hierarchy {
88  ShortcutVia shortcuts;
89  std::vector<std::vector<Connection> > forwardUplinks;
90  std::vector<std::vector<Connection> > backwardUplinks;
91  };
92 
98  CHBuilder(const std::vector<E*>& edges, bool unbuildIsWarning,
99  const SUMOVehicleClass svc,
100  bool validatePermissions):
101  myEdges(edges),
102  myErrorMsgHandler(unbuildIsWarning ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
103  mySPTree(new SPTree<CHInfo, CHConnection>(4, validatePermissions)),
104  mySVC(svc),
105  myUpdateCount(0) {
106  for (typename std::vector<E*>::const_iterator i = edges.begin(); i != edges.end(); ++i) {
107  myCHInfos.push_back(CHInfo(*i));
108  }
109  }
110 
112  virtual ~CHBuilder() {
113  delete mySPTree;
114  }
115 
116 
117  const Hierarchy* buildContractionHierarchy(SUMOTime time, const V* const vehicle, const SUMOAbstractRouter<E, V>* effortProvider) {
118  Hierarchy* result = new Hierarchy();
119  const int numEdges = (int)myCHInfos.size();
120  const std::string vClass = (mySPTree->validatePermissions() ?
121  "all vehicle classes " : "vClass='" + SumoVehicleClassStrings.getString(mySVC) + "' ");
122  PROGRESS_BEGIN_MESSAGE("Building Contraction Hierarchy for " + vClass
123  + "and time=" + time2string(time) + " (" + toString(numEdges) + " edges)\n");
124  const long startMillis = SysUtils::getCurrentMillis();
125  // init queue
126  std::vector<CHInfo*> queue; // max heap: edge to be contracted is front
127  // reset previous connections etc
128  for (int i = 0; i < numEdges; i++) {
129  myCHInfos[i].resetContractionState();
130  result->forwardUplinks.push_back(std::vector<Connection>());
131  result->backwardUplinks.push_back(std::vector<Connection>());
132  }
133  // copy connections from the original net
134  const double time_seconds = STEPS2TIME(time); // timelines store seconds!
135  for (int i = 0; i < numEdges; i++) {
136  synchronize(myCHInfos[i], time_seconds, vehicle, effortProvider);
137  }
138  // synchronization is finished. now we can compute priorities for the first time
139  for (int i = 0; i < numEdges; i++) {
140  myCHInfos[i].updatePriority(mySPTree);
141  queue.push_back(&(myCHInfos[i]));
142  }
143  make_heap(queue.begin(), queue.end(), myCmp);
144  int contractionRank = 0;
145  // contraction loop
146  while (!queue.empty()) {
147  while (tryUpdateFront(queue)) {}
148  CHInfo* max = queue.front();
149  max->rank = contractionRank;
150 #ifdef CHRouter_DEBUG_CONTRACTION
151  std::cout << "contracting '" << max->edge->getID() << "' with prio: " << max->priority << " (rank " << contractionRank << ")\n";
152 #endif
153  const E* const edge = max->edge;
154  // add outgoing connections to the forward search
155  const int edgeID = edge->getNumericalID();
156  for (typename CHConnections::const_iterator it = max->followers.begin(); it != max->followers.end(); it++) {
157  const CHConnection& con = *it;
158  result->forwardUplinks[edgeID].push_back(Connection(con.target->edge->getNumericalID(), con.cost, con.permissions));
159  disconnect(con.target->approaching, max);
160  con.target->updatePriority(0);
161  }
162  // add incoming connections to the backward search
163  for (typename CHConnections::const_iterator it = max->approaching.begin(); it != max->approaching.end(); it++) {
164  const CHConnection& con = *it;
165  result->backwardUplinks[edgeID].push_back(Connection(con.target->edge->getNumericalID(), con.cost, con.permissions));
166  disconnect(con.target->followers, max);
167  con.target->updatePriority(0);
168  }
169  // add shortcuts to the net
170  for (typename std::vector<Shortcut>::const_iterator it = max->shortcuts.begin(); it != max->shortcuts.end(); it++) {
171  const ConstEdgePair& edgePair = it->edgePair;
172  result->shortcuts[edgePair] = edge;
173  CHInfo* from = getCHInfo(edgePair.first);
174  CHInfo* to = getCHInfo(edgePair.second);
175  from->followers.push_back(CHConnection(to, it->cost, it->permissions, it->underlying));
176  to->approaching.push_back(CHConnection(from, it->cost, it->permissions, it->underlying));
177  }
178  // if you need to debug the chrouter with MSVC uncomment the following line, hierarchy building will get slower and the hierarchy may change though
179  //make_heap(queue.begin(), queue.end(), myCmp);
180  // remove from queue
181  pop_heap(queue.begin(), queue.end(), myCmp);
182  queue.pop_back();
183  /*
184  if (contractionRank % 10000 == 0) {
185  // update all and rebuild queue
186  for (typename std::vector<CHInfo*>::iterator it = queue.begin(); it != queue.end(); ++it) {
187  (*it)->updatePriority(mySPTree);
188  }
189  make_heap(queue.begin(), queue.end(), myCmp);
190  }
191  */
192  contractionRank++;
193  }
194  // reporting
195  const long duration = SysUtils::getCurrentMillis() - startMillis;
196  WRITE_MESSAGE("Created " + toString(result->shortcuts.size()) + " shortcuts.");
197  WRITE_MESSAGE("Recomputed priority " + toString(myUpdateCount) + " times.");
198  MsgHandler::getMessageInstance()->endProcessMsg("done (" + toString(duration) + "ms).");
200  myUpdateCount = 0;
201  return result;
202  }
203 
204 private:
205  struct Shortcut {
206  Shortcut(ConstEdgePair e, double c, int u, SVCPermissions p):
207  edgePair(e), cost(c), underlying(u), permissions(p) {}
208  ConstEdgePair edgePair;
209  double cost;
212  };
213 
214 
215  class CHInfo;
216 
218  class CHConnection {
219  public:
220  CHConnection(CHInfo* t, double c, SVCPermissions p, int u):
221  target(t), cost(c), permissions(p), underlying(u) {}
223  double cost;
227  };
228 
229  typedef std::vector<CHConnection> CHConnections;
230  typedef std::pair<const CHConnection*, const CHConnection*> CHConnectionPair;
231  typedef std::vector<CHConnectionPair> CHConnectionPairs;
232 
233  /* @brief container class to use when building the contraction hierarchy.
234  * instances are reused every time the hierarchy is rebuilt (new time slice)
235  * but they must be synchronized first */
236  class CHInfo {
237  public:
239  CHInfo(const E* e) :
240  edge(e),
241  contractedNeighbors(0),
242  rank(-1),
243  level(0),
244  underlyingTotal(0),
245  visited(false),
246  traveltime(std::numeric_limits<double>::max()) {
247  }
248 
251  if (spTree != 0) {
252  updateShortcuts(spTree);
253  updateLevel();
254  } else {
255  contractedNeighbors += 1; // called when a connected edge was contracted
256  }
257  const double oldPriority = priority;
258  // priority term as used by abraham []
259  const int edge_difference = (int)followers.size() + (int)approaching.size() - 2 * (int)shortcuts.size();
260  priority = (double)(2 * edge_difference - contractedNeighbors - underlyingTotal - 5 * level);
261  return priority != oldPriority;
262  }
263 
266  const bool validatePermissions = spTree->validatePermissions();
267 #ifdef CHRouter_DEBUG_CONTRACTION_DEGREE
268  const int degree = (int)approaching.size() + (int)followers.size();
269  std::cout << "computing shortcuts for '" + edge->getID() + "' with degree " + toString(degree) + "\n";
270 #endif
271  shortcuts.clear();
272  underlyingTotal = 0;
273  for (typename CHConnections::iterator it_a = approaching.begin(); it_a != approaching.end(); it_a++) {
274  CHConnection& aInfo = *it_a;
275  // build shortest path tree in a fixed neighborhood
276  spTree->rebuildFrom(aInfo.target, this);
277  for (typename CHConnections::iterator it_f = followers.begin(); it_f != followers.end(); it_f++) {
278  CHConnection& fInfo = *it_f;
279  const double viaCost = aInfo.cost + fInfo.cost;
280  const SVCPermissions viaPermissions = (aInfo.permissions & fInfo.permissions);
281  if (fInfo.target->traveltime > viaCost) {
282  // found no faster path -> we need a shortcut via edge
283 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
284  debugNoWitness(aInfo, fInfo);
285 #endif
286  const int underlying = aInfo.underlying + fInfo.underlying;
287  underlyingTotal += underlying;
288  shortcuts.push_back(Shortcut(ConstEdgePair(aInfo.target->edge, fInfo.target->edge),
289  viaCost, underlying, viaPermissions));
290 
291  } else if (validatePermissions) {
292  if ((fInfo.target->permissions & viaPermissions) != viaPermissions) {
293  // witness has weaker restrictions. try to find another witness
294  spTree->registerForValidation(&aInfo, &fInfo);
295  } else {
296 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
297  debugNoWitness(aInfo, fInfo);
298 #endif
299  }
300  } else {
301 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
302  debugNoWitness(aInfo, fInfo);
303 #endif
304  }
305  }
306  }
307  // insert shortcuts needed due to unmet permissions
308  if (validatePermissions) {
309  const CHConnectionPairs& pairs = spTree->getNeededShortcuts(this);
310  for (typename CHConnectionPairs::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
311  const CHConnection* aInfo = it->first;
312  const CHConnection* fInfo = it->second;
313  const double viaCost = aInfo->cost + fInfo->cost;
314  const SVCPermissions viaPermissions = (aInfo->permissions & fInfo->permissions);
315  const int underlying = aInfo->underlying + fInfo->underlying;
316  underlyingTotal += underlying;
317  shortcuts.push_back(Shortcut(ConstEdgePair(aInfo->target->edge, fInfo->target->edge),
318  viaCost, underlying, viaPermissions));
319  }
320  }
321  }
322 
323 
324  // update level as defined by Abraham
325  void updateLevel() {
326  int maxLower = std::numeric_limits<int>::min();
327  int otherRank;
328  for (typename CHConnections::iterator it = approaching.begin(); it != approaching.end(); it++) {
329  otherRank = it->target->rank;
330  if (otherRank < rank) {
331  maxLower = MAX2(rank, maxLower);
332  }
333  }
334  for (typename CHConnections::iterator it = followers.begin(); it != followers.end(); it++) {
335  otherRank = it->target->rank;
336  if (otherRank < rank) {
337  maxLower = MAX2(rank, maxLower);
338  }
339  }
340  if (maxLower == std::numeric_limits<int>::min()) {
341  level = 0;
342  } else {
343  level = maxLower + 1;
344  }
345  }
346 
347  // resets state before rebuilding the hierarchy
349  contractedNeighbors = 0;
350  rank = -1;
351  level = 0;
352  underlyingTotal = 0;
353  shortcuts.clear();
354  followers.clear();
355  approaching.clear();
356  }
357 
358 
360  const E* edge;
362  double priority;
364  std::vector<Shortcut> shortcuts;
367  int rank;
368  int level;
370 
372  CHConnections followers;
373  CHConnections approaching;
374 
375 
377  bool visited;
379  double traveltime;
381  int depth;
383  // @note: we may miss some witness paths by making traveltime the only
384  // criteria durinng search
386 
387  inline void reset() {
388  traveltime = std::numeric_limits<double>::max();
389  visited = false;
390  }
391 
392 
394  inline void debugNoWitness(const CHConnection& aInfo, const CHConnection& fInfo) {
395  std::cout << "adding shortcut between " << aInfo.target->edge->getID() << ", " << fInfo.target->edge->getID() << " via " << edge->getID() << "\n";
396  }
397 
398  inline void debugWitness(const CHConnection& aInfo, const CHConnection& fInfo) {
399  const double viaCost = aInfo.cost + fInfo.cost;
400  std::cout << "found witness with lenght " << fInfo.target->traveltime << " against via " << edge->getID() << " (length " << viaCost << ") for " << aInfo.target->edge->getID() << ", " << fInfo.target->edge->getID() << "\n";
401  }
402 
403  };
404 
405 
411  public:
413  bool operator()(const CHInfo* a, const CHInfo* b) const {
414  if (a->priority == b->priority) {
415  return a->edge->getNumericalID() > b->edge->getNumericalID();
416  } else {
417  return a->priority < b->priority;
418  };
419  }
420  };
421 
422 
423  inline CHInfo* getCHInfo(const E* const edge) {
424  return &(myCHInfos[edge->getNumericalID()]);
425  }
426 
427 
429  void synchronize(CHInfo& info, double time, const V* const vehicle, const SUMOAbstractRouter<E, V>* effortProvider) {
430  // forward and backward connections are used only in forward search,
431  // thus approaching costs are those of the approaching edge and not of the edge itself
432  const bool prune = !mySPTree->validatePermissions();
433  const E* const edge = info.edge;
434  if (prune && ((edge->getPermissions() & mySVC) != mySVC)) {
435  return;
436  }
437  const double cost = effortProvider->getEffort(edge, vehicle, time);
438 
439  const std::vector<E*>& successors = edge->getSuccessors(mySVC);
440  for (typename std::vector<E*>::const_iterator it = successors.begin(); it != successors.end(); ++it) {
441  const E* fEdge = *it;
442  if (prune && ((fEdge->getPermissions() & mySVC) != mySVC)) {
443  continue;
444  }
445  CHInfo* follower = getCHInfo(fEdge);
446  SVCPermissions permissions = (edge->getPermissions() & follower->edge->getPermissions());
447  info.followers.push_back(CHConnection(follower, cost, permissions, 1));
448  follower->approaching.push_back(CHConnection(&info, cost, permissions, 1));
449  }
450 #ifdef CHRouter_DEBUG_WEIGHTS
451  std::cout << time << ": " << edge->getID() << " cost: " << cost << "\n";
452 #endif
453  // @todo: check whether we even need to save approaching in ROEdge;
454  }
455 
456 
458  void disconnect(CHConnections& connections, CHInfo* other) {
459  for (typename CHConnections::iterator it = connections.begin(); it != connections.end(); it++) {
460  if (it->target == other) {
461  connections.erase(it);
462  return;
463  }
464  }
465  assert(false);
466  }
467 
471  bool tryUpdateFront(std::vector<CHInfo*>& queue) {
472  myUpdateCount++;
473  CHInfo* max = queue.front();
474 #ifdef CHRouter_DEBUG_CONTRACTION_QUEUE
475  std::cout << "updating '" << max->edge->getID() << "'\n";
476  debugPrintQueue(queue);
477 #endif
478  if (max->updatePriority(mySPTree)) {
479  pop_heap(queue.begin(), queue.end(), myCmp);
480  push_heap(queue.begin(), queue.end(), myCmp);
481  return true;
482  } else {
483  return false;
484  }
485  }
486 
487  // helper method for debugging
488  void debugPrintQueue(std::vector<CHInfo*>& queue) {
489  for (typename std::vector<CHInfo*>::iterator it = queue.begin(); it != queue.end(); it++) {
490  CHInfo* chInfo = *it;
491  std::cout << "(" << chInfo->edge->getID() << "," << chInfo->priority << ") ";
492  }
493  std::cout << "\n";
494  }
495 
496 private:
498  const std::vector<E*>& myEdges;
499 
502 
504  std::vector<CHInfo> myCHInfos;
505 
508 
511 
514 
517 
518 private:
520  CHBuilder& operator=(const CHBuilder& s);
521 };
522 
523 
524 #endif
525 
526 /****************************************************************************/
527 
bool operator()(const CHInfo *a, const CHInfo *b) const
Comparing method.
Definition: CHBuilder.h:413
CHInfoComparator myCmp
Comparator for contraction priority.
Definition: CHBuilder.h:507
bool tryUpdateFront(std::vector< CHInfo *> &queue)
tries to update the priority of the first edge
Definition: CHBuilder.h:471
double getEffort(const E *const e, const V *const v, double t) const
int depth
number of edges from start
Definition: CHBuilder.h:381
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types...
int contractedNeighbors
priority subterms
Definition: CHBuilder.h:366
Connection(int t, double c, SVCPermissions p)
Definition: CHBuilder.h:79
CHInfo * getCHInfo(const E *const edge)
Definition: CHBuilder.h:423
void resetContractionState()
Definition: CHBuilder.h:348
std::map< ConstEdgePair, const E * > ShortcutVia
Definition: CHBuilder.h:86
SVCPermissions permissions
Definition: CHBuilder.h:82
void debugWitness(const CHConnection &aInfo, const CHConnection &fInfo)
Definition: CHBuilder.h:398
Shortcut(ConstEdgePair e, double c, int u, SVCPermissions p)
Definition: CHBuilder.h:206
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
const std::vector< E * > & myEdges
all edges with numerical ids
Definition: CHBuilder.h:498
MsgHandler *const myErrorMsgHandler
the handler for routing errors
Definition: CHBuilder.h:501
std::string time2string(SUMOTime t)
Definition: SUMOTime.cpp:59
void debugPrintQueue(std::vector< CHInfo *> &queue)
Definition: CHBuilder.h:488
T MAX2(T a, T b)
Definition: StdDefs.h:73
SVCPermissions permissions
Definition: CHBuilder.h:224
int underlying
the number of connections underlying this connection
Definition: CHBuilder.h:226
bool updatePriority(SPTree< CHInfo, CHConnection > *spTree)
recompute the contraction priority and report whether it changed
Definition: CHBuilder.h:250
void synchronize(CHInfo &info, double time, const V *const vehicle, const SUMOAbstractRouter< E, V > *effortProvider)
copy connections from the original net (modified destructively during contraction) ...
Definition: CHBuilder.h:429
CHConnections approaching
Definition: CHBuilder.h:373
const SUMOVehicleClass mySVC
the permissions for which the hierarchy was constructed
Definition: CHBuilder.h:513
CHConnections followers
connections (only valid after synchronization)
Definition: CHBuilder.h:372
int myUpdateCount
counters for performance logging
Definition: CHBuilder.h:516
void debugNoWitness(const CHConnection &aInfo, const CHConnection &fInfo)
debugging methods
Definition: CHBuilder.h:394
SPTree< CHInfo, CHConnection > * mySPTree
the shortest path tree to use when searching for shortcuts
Definition: CHBuilder.h:510
double traveltime
Effort to reach the edge.
Definition: CHBuilder.h:379
void registerForValidation(const C *aInfo, const C *fInfo)
save source/target pair for later validation
Definition: SPTree.h:141
std::vector< std::vector< Connection > > backwardUplinks
Definition: CHBuilder.h:90
ConstEdgePair edgePair
Definition: CHBuilder.h:208
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:55
SVCPermissions permissions
Definition: CHBuilder.h:211
const CHConnectionPairs & getNeededShortcuts(const E *excluded)
Definition: SPTree.h:149
#define STEPS2TIME(x)
Definition: SUMOTime.h:64
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:201
static MsgHandler * getMessageInstance()
Returns the instance to add normal messages to.
Definition: MsgHandler.cpp:57
std::vector< CHConnectionPair > CHConnectionPairs
Definition: CHBuilder.h:231
std::pair< const E *, const E * > ConstEdgePair
Definition: CHBuilder.h:85
StringBijection< SUMOVehicleClass > SumoVehicleClassStrings(sumoVehicleClassStringInitializer, SVC_CUSTOM2, false)
virtual ~CHBuilder()
Destructor.
Definition: CHBuilder.h:112
std::vector< std::vector< Connection > > forwardUplinks
Definition: CHBuilder.h:89
void disconnect(CHConnections &connections, CHInfo *other)
remove all connections to/from the given edge (assume it exists only once)
Definition: CHBuilder.h:458
Definition: SPTree.h:45
CHConnection(CHInfo *t, double c, SVCPermissions p, int u)
Definition: CHBuilder.h:220
double priority
The contraction priority.
Definition: CHBuilder.h:362
CHBuilder(const std::vector< E *> &edges, bool unbuildIsWarning, const SUMOVehicleClass svc, bool validatePermissions)
Constructor.
Definition: CHBuilder.h:98
bool visited
members used in SPTree
Definition: CHBuilder.h:377
const E * edge
The current edge - not const since it may receive shortcut edges.
Definition: CHBuilder.h:360
void rebuildFrom(E *start, const E *excluded)
build a shortest path tree from start to a depth of myMaxdepth. The given edge is excluded from this ...
Definition: SPTree.h:94
CHBuilder & operator=(const CHBuilder &s)
Invalidated assignment operator.
const Hierarchy * buildContractionHierarchy(SUMOTime time, const V *const vehicle, const SUMOAbstractRouter< E, V > *effortProvider)
Definition: CHBuilder.h:117
std::pair< const CHConnection *, const CHConnection * > CHConnectionPair
Definition: CHBuilder.h:230
SVCPermissions permissions
the permissions when reaching this edge on the fastest path
Definition: CHBuilder.h:385
Forward/backward connection with associated FORWARD cost.
Definition: CHBuilder.h:218
CHInfo(const E *e)
Constructor.
Definition: CHBuilder.h:239
std::vector< CHInfo > myCHInfos
static vector for lookup
Definition: CHBuilder.h:504
long long int SUMOTime
Definition: TraCIDefs.h:51
std::vector< CHConnection > CHConnections
Definition: CHBuilder.h:229
bool validatePermissions()
whether permissions should be validated;
Definition: SPTree.h:136
static long getCurrentMillis()
Returns the current time in milliseconds.
Definition: SysUtils.cpp:45
#define PROGRESS_DONE_MESSAGE()
Definition: MsgHandler.h:202
Forward/backward connection with associated forward/backward cost.
Definition: CHBuilder.h:77
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:200
std::vector< Shortcut > shortcuts
The needed shortcuts.
Definition: CHBuilder.h:364
void updateLevel()
Definition: CHBuilder.h:325
ShortcutVia shortcuts
Definition: CHBuilder.h:88
void updateShortcuts(SPTree< CHInfo, CHConnection > *spTree)
compute needed shortcuts when contracting this edge
Definition: CHBuilder.h:265
void endProcessMsg(std::string msg)
Ends a process information.
Definition: MsgHandler.cpp:126