event-driven
vSurfaceHandlerTh.h
1 /*
2  * Copyright (C) 2017 Event-driven Perception for Robotics
3  * Author: arren.glover@iit.it
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef __VSURFACEHANDLER__
20 #define __VSURFACEHANDLER__
21 
22 #include <yarp/os/all.h>
23 #include "event-driven/vBottle.h"
24 #include "event-driven/vCodec.h"
25 #include "event-driven/vWindow_basic.h"
26 #include "event-driven/vWindow_adv.h"
27 #include "event-driven/vFilters.h"
28 #include "event-driven/vPort.h"
29 #include <deque>
30 #include <string>
31 #include <map>
32 
33 namespace ev {
34 
36 class queueAllocator : public yarp::os::BufferedPort<ev::vBottle>
37 {
38 private:
39 
40  std::deque<ev::vQueue *> qq;
41  std::deque<yarp::os::Stamp> sq;
42  yarp::os::Mutex m;
43  yarp::os::Semaphore dataready;
44 
45  unsigned int qlimit;
46  unsigned int delay_nv;
47  long unsigned int delay_t;
48  double event_rate;
49 
50 public:
51 
54  {
55  qlimit = 0;
56  delay_nv = 0;
57  delay_t = 0;
58  event_rate = 0;
59 
60  dataready.wait();
61 
62  useCallback();
63  setStrict();
64  }
65 
68  {
69  m.lock();
70  for(std::deque<ev::vQueue *>::iterator i = qq.begin(); i != qq.end(); i++)
71  delete *i;
72  qq.clear();
73  m.unlock();
74  }
75 
78  void onRead(ev::vBottle &inputbottle)
79  {
80  //make a new vQueue
81  m.lock();
82 
83  if(qlimit && qq.size() >= qlimit) {
84  m.unlock();
85  return;
86  }
87  qq.push_back(new vQueue);
88  yarp::os::Stamp yarpstamp;
89  getEnvelope(yarpstamp);
90  sq.push_back(yarpstamp);
91 
92  m.unlock();
93 
94 
95  //and decode the data
96  inputbottle.addtoendof<ev::AddressEvent>(*(qq.back()));
97 
98  //update the meta data
99  m.lock();
100  delay_nv += qq.back()->size();
101  int dt = qq.back()->back()->stamp - qq.back()->front()->stamp;
102  if(dt < 0) dt += vtsHelper::max_stamp;
103  delay_t += dt;
104  if(dt)
105  event_rate = qq.back()->size() / (double)dt;
106  m.unlock();
107 
108  //if getNextQ is blocking - let it get the new data
109  dataready.post();
110  }
111 
113  ev::vQueue* read(yarp::os::Stamp &yarpstamp)
114  {
115  static vQueue * working_queue = nullptr;
116  if(working_queue) {
117  m.lock();
118 
119  delay_nv -= qq.front()->size();
120  int dt = qq.front()->back()->stamp - qq.front()->front()->stamp;
121  if(dt < 0) dt += vtsHelper::max_stamp;
122  delay_t -= dt;
123 
124  delete qq.front();
125  qq.pop_front();
126  sq.pop_front();
127  m.unlock();
128  }
129  dataready.wait();
130  if(qq.size()) {
131  yarpstamp = sq.front();
132  working_queue = qq.front();
133  return working_queue;
134  } else {
135  return 0;
136  }
137 
138  }
139 
142  void scrapQ()
143  {
144  m.lock();
145 
146  delay_nv -= qq.front()->size();
147  int dt = qq.front()->back()->stamp - qq.front()->front()->stamp;
148  if(dt < 0) dt += vtsHelper::max_stamp;
149  delay_t -= dt;
150 
151  delete qq.front();
152  qq.pop_front();
153  sq.pop_front();
154  m.unlock();
155  }
156 
159  void setQLimit(unsigned int number_of_qs)
160  {
161  qlimit = number_of_qs;
162  }
163 
167  {
168  dataready.post();
169  }
170 
173  {
174  return qq.size();
175  }
176 
178  unsigned int queryDelayN()
179  {
180  return delay_nv;
181  }
182 
184  double queryDelayT()
185  {
186  return delay_t * vtsHelper::tsscaler;
187  }
188 
190  double queryRate()
191  {
192  return event_rate * vtsHelper::vtsscaler;
193  }
194 
195  std::string delayStatString()
196  {
197  std::ostringstream oss;
198  oss << queryunprocessed() << " " << queryDelayN() <<
199  " " << queryDelayT() << " " << queryRate();
200  return oss.str();
201  }
202 
203 };
204 
206 class surfaceThread : public yarp::os::Thread
207 {
208 private:
209 
210  ev::temporalSurface surfaceLeft;
211  ev::temporalSurface surfaceRight;
212 
213  queueAllocator allocatorCallback;
214 
215  yarp::os::Mutex m;
216  yarp::os::Stamp yarpstamp;
217  unsigned int ctime;
218 
219  int vcount;
220 
221 
222 public:
223 
224  surfaceThread()
225  {
226  vcount = 0;
227  ctime = 0;
228  }
229 
230  void configure(int height, int width)
231  {
232  surfaceLeft = ev::temporalSurface(width, height);
233  surfaceRight = ev::temporalSurface(width, height);
234  }
235 
236  bool open(std::string portname)
237  {
238  if(!allocatorCallback.open(portname))
239  return false;
240  start();
241  return true;
242  }
243 
244  void onStop()
245  {
246  allocatorCallback.close();
247  allocatorCallback.releaseDataLock();
248  }
249 
250  void run()
251  {
252  while(true) {
253 
254  ev::vQueue *q = 0;
255  while(!q && !isStopping()) {
256  q = allocatorCallback.read(yarpstamp);
257  }
258  if(isStopping()) break;
259 
260  for(ev::vQueue::iterator qi = q->begin(); qi != q->end(); qi++) {
261 
262  m.lock();
263 
264  vcount++;
265 
266  ctime = (*qi)->stamp;
267 
268  if((*qi)->getChannel() == 0)
269  surfaceLeft.fastAddEvent(*qi);
270  else if((*qi)->getChannel() == 1)
271  surfaceRight.fastAddEvent(*qi);
272  else
273  std::cout << "Unknown channel" << std::endl;
274 
275  m.unlock();
276 
277  }
278 
279  //allocatorCallback.scrapQ();
280 
281  }
282 
283  }
284 
285  yarp::os::Stamp queryROI(ev::vQueue &fillq, int c, unsigned int t, int x, int y, int r)
286  {
287 
288  //if(!vcount) return false;
289 
290  m.lock();
291  if(c == 0)
292  fillq = surfaceLeft.getSurf_Tlim(t, x, y, r);
293  else
294  fillq = surfaceRight.getSurf_Tlim(t, x, y, r);
295  vcount = 0;
296  m.unlock();
297  return yarpstamp;
298  }
299 
300  yarp::os::Stamp queryWindow(ev::vQueue &fillq, int c, unsigned int t)
301  {
302 
303  m.lock();
304  if(c == 0)
305  fillq = surfaceLeft.getSurf_Tlim(t);
306  else
307  fillq = surfaceRight.getSurf_Tlim(t);
308  vcount = 0;
309  m.unlock();
310 
311  return yarpstamp;
312  }
313 
314  unsigned int queryVTime()
315  {
316  return ctime;
317  }
318 
319 };
320 
322 class hSurfThread : public yarp::os::Thread
323 {
324 private:
325 
326  int maxcpudelay; //maximum delay between v time and cpu time (in v time)
327 
328  queueAllocator allocatorCallback;
329  historicalSurface surfaceleft;
330  historicalSurface surfaceright;
331  yarp::os::Mutex m;
332 
333  //current stamp to propagate
334  yarp::os::Stamp ystamp;
335  unsigned int vstamp;
336 
337  //synchronising value (add to it when stamps come in, subtract from it
338  // when querying events).
339  double cputimeL;
340  int cpudelayL;
341  double cputimeR;
342  int cpudelayR;
343 
344 public:
345 
346  hSurfThread()
347  {
348  vstamp = 0;
349  cpudelayL = cpudelayR = 0;
350  cputimeL = cputimeR = yarp::os::Time::now();
351  maxcpudelay = 0.05 * vtsHelper::vtsscaler;
352  }
353 
354  void configure(int height, int width, double maxcpudelay)
355  {
356  this->maxcpudelay = maxcpudelay * vtsHelper::vtsscaler;
357  surfaceleft.initialise(height, width);
358  surfaceright.initialise(height, width);
359  }
360 
361  bool open(std::string portname)
362  {
363  if(!allocatorCallback.open(portname))
364  return false;
365 
366  start();
367  return true;
368  }
369 
370  void onStop()
371  {
372  allocatorCallback.close();
373  allocatorCallback.releaseDataLock();
374  }
375 
376 
377  void run()
378  {
379  static int maxqs = 4;
380  bool allowproc = true;
381 
382  while(true) {
383 
384  ev::vQueue *q = 0;
385  while(!q && !isStopping()) {
386  q = allocatorCallback.read(ystamp);
387  }
388  if(isStopping()) break;
389 
390 
391  int nqs = allocatorCallback.queryunprocessed();
392 
393  if(allowproc)
394  m.lock();
395 
396  if(nqs >= maxqs)
397  allowproc = false;
398  else if(nqs < maxqs)
399  allowproc = true;
400 
401  int dt = q->back()->stamp - vstamp;
402  if(dt < 0) dt += vtsHelper::max_stamp;
403  cpudelayL += dt;
404  cpudelayR += dt;
405  vstamp = q->back()->stamp;
406 
407  for(ev::vQueue::iterator qi = q->begin(); qi != q->end(); qi++) {
408 
409  if((*qi)->getChannel() == 0)
410  surfaceleft.addEvent(*qi);
411  else if((*qi)->getChannel() == 1)
412  surfaceright.addEvent(*qi);
413 
414  }
415 
416  if(allowproc)
417  m.unlock();
418 
419  //allocatorCallback.scrapQ();
420 
421  }
422 
423  }
424 
425  vQueue queryROI(int channel, int numEvts, int r)
426  {
427 
428  vQueue q;
429 
430  m.lock();
431  double cpunow = yarp::os::Time::now();
432 
433  if(channel == 0) {
434 
435  cpudelayL -= (cpunow - cputimeL) * vtsHelper::vtsscaler * 1.1;
436  cputimeL = cpunow;
437 
438  if(cpudelayL < 0) cpudelayL = 0;
439  if(cpudelayL > maxcpudelay) {
440  yWarning() << "CPU delay hit maximum";
441  cpudelayL = maxcpudelay;
442  }
443 
444  surfaceleft.getSurfaceN(q, cpudelayL, numEvts, r);
445  }
446  else {
447 
448  cpudelayR -= (cpunow - cputimeR) * vtsHelper::vtsscaler * 1.1;
449  cputimeR = cpunow;
450 
451  if(cpudelayR < 0) cpudelayR = 0;
452  if(cpudelayR > maxcpudelay) {
453  yWarning() << "CPU delay hit maximum";
454  cpudelayR = maxcpudelay;
455  }
456 
457  surfaceright.getSurfaceN(q, cpudelayR, numEvts, r);
458  }
459 
460  m.unlock();
461 
462  return q;
463  }
464 
465  vQueue queryROI(int channel, unsigned int querySize, int x, int y, int r)
466  {
467 
468 
469  vQueue q;
470 
471  m.lock();
472 
473  double cpunow = yarp::os::Time::now();
474 
475  if(channel == 0) {
476 
477  cpudelayL -= (cpunow - cputimeL) * vtsHelper::vtsscaler * 1.01;
478  cputimeL = cpunow;
479 
480  if(cpudelayL < 0) cpudelayL = 0;
481  if(cpudelayL > maxcpudelay) {
482  yWarning() << "CPU delay hit maximum";
483  cpudelayL = maxcpudelay;
484  }
485 
486  q = surfaceleft.getSurface(cpudelayL, querySize, r, x, y);
487  } else {
488 
489  cpudelayR -= (cpunow - cputimeR) * vtsHelper::vtsscaler * 1.01;
490  cputimeR = cpunow;
491 
492  if(cpudelayR < 0) cpudelayR = 0;
493  if(cpudelayR > maxcpudelay) {
494  yWarning() << "CPU delay hit maximum";
495  cpudelayR = maxcpudelay;
496  }
497 
498  q = surfaceright.getSurface(cpudelayR, querySize, r, x, y);
499  }
500 
501  m.unlock();
502 
503  return q;
504  }
505 
506  vQueue queryWindow(int channel, unsigned int querySize)
507  {
508  vQueue q;
509 
510  m.lock();
511 
512  double cpunow = yarp::os::Time::now();
513 
514  if(channel == 0) {
515 
516  cpudelayL -= (cpunow - cputimeL) * vtsHelper::vtsscaler * 1.01;
517  cputimeL = cpunow;
518 
519  if(cpudelayL < 0) cpudelayL = 0;
520  if(cpudelayL > maxcpudelay) {
521  yWarning() << "CPU delay hit maximum";
522  cpudelayL = maxcpudelay;
523  }
524 
525  q = surfaceleft.getSurface(cpudelayL, querySize);
526  }
527  else {
528 
529  cpudelayR -= (cpunow - cputimeR) * vtsHelper::vtsscaler * 1.01;
530  cputimeR = cpunow;
531 
532  if(cpudelayR < 0) cpudelayR = 0;
533  if(cpudelayR > maxcpudelay) {
534  yWarning() << "CPU delay hit maximum";
535  cpudelayR = maxcpudelay;
536  }
537 
538  q = surfaceright.getSurface(cpudelayR, querySize);
539  }
540 
541  m.unlock();
542 
543  return q;
544  }
545 
546  double queryDelay(int channel = 0)
547  {
548  if(channel) {
549  return cpudelayR * vtsHelper::tsscaler;
550  } else {
551  return cpudelayL * vtsHelper::tsscaler;
552  }
553  }
554 
555  yarp::os::Stamp queryYstamp()
556  {
557  return ystamp;
558  }
559 
560  int queryVstamp(int channel = 0)
561  {
562  int modvstamp;
563  m.lock();
564  if(channel) {
565  modvstamp = vstamp - cpudelayR;
566  } else {
567  modvstamp = vstamp - cpudelayL;
568  }
569  m.unlock();
570 
571  if(modvstamp < 0) modvstamp += vtsHelper::max_stamp;
572  return modvstamp;
573 
574  }
575 
576  int queryQDelay()
577  {
578  return allocatorCallback.queryunprocessed();
579  }
580 
581 };
582 
585 class tWinThread : public yarp::os::Thread
586 {
587 private:
588 
589  ev::vReadPort<vQueue> allocatorCallback;
590  //ev::queueAllocator allocatorCallback;
591  vTempWindow windowleft;
592  vTempWindow windowright;
593 
594  yarp::os::Mutex safety;
595 
596  int strictUpdatePeriod;
597  int currentPeriod;
598  yarp::os::Mutex waitforquery;
599  yarp::os::Stamp yarpstamp;
600  unsigned int ctime;
601  bool updated;
602 
603 public:
604 
605  tWinThread()
606  {
607  ctime = 0;
608  strictUpdatePeriod = 0;
609  currentPeriod = 0;
610  updated = false;
611  }
612 
613  bool open(std::string portname, int period = 0)
614  {
615  strictUpdatePeriod = period;
616  if(strictUpdatePeriod) yInfo() << "Forced update every" << period * vtsHelper::tsscaler <<"s, or"<< period << "event timestamps";
617  if(!allocatorCallback.open(portname))
618  return false;
619 
620  return start();
621  }
622 
623  void onStop()
624  {
625  allocatorCallback.close();
626  //allocatorCallback.releaseDataLock();
627  waitforquery.unlock();
628  }
629 
630  void run()
631  {
632  if(strictUpdatePeriod) {
633  safety.lock();
634  waitforquery.lock();
635  }
636 
637  while(!isStopping()) {
638 
639 
640  const ev::vQueue *q = allocatorCallback.read(yarpstamp);
641  if(!q) break;
642 
643  if(!strictUpdatePeriod) safety.lock();
644 
645  if(!ctime) ctime = q->front()->stamp;
646 
647  for(ev::vQueue::const_iterator qi = q->begin(); qi != q->end(); qi++) {
648  if((*qi)->getChannel() == 0)
649  windowleft.addEvent(*qi);
650  else if((*qi)->getChannel() == 1)
651  windowright.addEvent(*qi);
652  }
653 
654  if(strictUpdatePeriod) {
655  int dt = q->back()->stamp - ctime;
656  if(dt < 0) dt += vtsHelper::max_stamp;
657  currentPeriod += dt;
658  if(currentPeriod > strictUpdatePeriod) {
659  safety.unlock();
660  waitforquery.lock();
661  safety.lock();
662  currentPeriod = 0;
663  }
664 
665  }
666 
667  ctime = q->back()->stamp;
668 
669  updated = true;
670 
671  if(!strictUpdatePeriod) safety.unlock();
672 
673  }
674  if(strictUpdatePeriod)
675  safety.unlock();
676  }
677 
678  vQueue queryWindow(int channel)
679  {
680  vQueue q;
681 
682  safety.lock();
683  //std::cout << "vFramer unprcd: " << allocatorCallback.queryunprocessed() << std::endl;
684  if(channel == 0)
685  q = windowleft.getWindow();
686  else
687  q = windowright.getWindow();
688  updated = false;
689  waitforquery.unlock();
690  safety.unlock();
691  return q;
692  }
693 
694  void queryStamps(yarp::os::Stamp &yStamp, int &vStamp)
695  {
696  yStamp = yarpstamp;
697  vStamp = ctime;
698  }
699 
700  bool queryUpdated()
701  {
702  return updated;
703  }
704 
705  unsigned int queryUnprocd()
706  {
707  return allocatorCallback.queryunprocessed();
708  }
709 
710  std::string readDelayStats()
711  {
712  return allocatorCallback.delayStatString();
713  }
714 
715 };
716 
720 {
721 private:
722 
723  std::map<std::string, ev::tWinThread> iPorts;
724  //std::deque<tWinThread> iPorts;
725  yarp::os::Stamp yStamp;
726  int vStamp;
727  int strictUpdatePeriod;
728  bool using_yarp_stamps;
729  //std::map<std::string, int> labelMap;
730 
731 public:
732 
733  syncvstreams(void)
734  {
735  strictUpdatePeriod = 0;
736  vStamp = 0;
737  using_yarp_stamps = false;
738  }
739 
740  bool open(std::string moduleName, std::string eventType)
741  {
742  //check already have an input of that type
743  if(iPorts.count(eventType))
744  return true;
745 
746  //otherwise open a new port
747  if(!iPorts[eventType].open(moduleName + "/" + eventType + ":i", strictUpdatePeriod))
748  return false;
749 
750  return true;
751  }
752 
753  vQueue queryWindow(std::string vType, int channel)
754  {
755 
756  updateStamps();
757  return iPorts[vType].queryWindow(channel);
758  }
759 
760  void updateStamps()
761  {
762  //query each input port and ask for the timestamp
763  yarp::os::Stamp ys; int vs;
764  std::map<std::string, ev::tWinThread>::iterator i;
765  for(i = iPorts.begin(); i != iPorts.end(); i++) {
766  i->second.queryStamps(ys, vs);
767 
768  //we assume envelopes aren't being used
769  if(!using_yarp_stamps) vStamp = vs;
770  if(!ys.isValid()) continue;
771 
772  //set the stamps based on the yarp_stamp
773  using_yarp_stamps = true;
774  double pt = yStamp.getTime();
775  double ct = ys.getTime();
776  //if we have a more recent packet, or we went back in time 5 seconds
777  if(ct > pt || ct < pt - 5.0) {
778  yStamp = ys;
779  vStamp = vs;
780  }
781  }
782  }
783 
784  void close()
785  {
786  std::map<std::string, ev::tWinThread>::iterator i;
787  for(i = iPorts.begin(); i != iPorts.end(); i++)
788  i->second.stop();
789  }
790 
791  yarp::os::Stamp getystamp()
792  {
793  return yStamp;
794  }
795 
796  int getvstamp()
797  {
798  return vStamp;
799  }
800 
801  void setStrictUpdatePeriod(int period)
802  {
803  strictUpdatePeriod = period;
804  }
805 
806  bool hasUpdated()
807  {
808  if(strictUpdatePeriod) return true;
809  std::map<std::string, ev::tWinThread>::iterator i;
810  for(i = iPorts.begin(); i != iPorts.end(); i++)
811  if(i->second.queryUpdated()) return true;
812  return false;
813  }
814 
815  unsigned int queryMaxUnproced()
816  {
817  unsigned int unprocd = 0;
818  std::map<std::string, ev::tWinThread>::iterator i;
819  for(i = iPorts.begin(); i != iPorts.end(); i++)
820  unprocd = std::max(i->second.queryUnprocd(), unprocd);
821  return unprocd;
822  }
823 
824  std::string delayStats()
825  {
826  std::ostringstream oss;
827  std::map<std::string, ev::tWinThread>::iterator i;
828  for(i = iPorts.begin(); i != iPorts.end(); i++)
829  oss << i->first << ": " << i->second.readDelayStats() << " ";
830 
831  return oss.str();
832  }
833 
834 };
835 
836 }
837 
838 #endif
Definition: vBottle.h:29
yarp::os::Bottle wrapper for sending events through the yarp system with ensuring compatibility with ...
Definition: vBottle.h:33
static double tsscaler
a multiplier to convert an event timestamp to seconds
Definition: vtsHelper.h:41
void scrapQ()
remove the most recently read vQueue from the list and deallocate the memory
Definition: vSurfaceHandlerTh.h:142
automatically accept multiple event types from different ports (e.g. as in the vFramer) ...
Definition: vSurfaceHandlerTh.h:719
an event with a pixel location, camera number and polarity
Definition: vCodec.h:103
queueAllocator()
constructor
Definition: vSurfaceHandlerTh.h:53
asynchronously read events and push them in a vSurface
Definition: vSurfaceHandlerTh.h:206
void onRead(ev::vBottle &inputbottle)
the callback decodes the incoming vBottle and adds it to the list of received vBottles. The yarp, and event timestamps are updated.
Definition: vSurfaceHandlerTh.h:78
void addtoendof(vQueue &q)
add a specific event-type from the vBottle to the end of a vQueue
Definition: vBottle.h:124
unsigned int queryunprocessed()
ask for the number of vQueues currently allocated.
Definition: vPort.h:558
~queueAllocator()
desctructor
Definition: vSurfaceHandlerTh.h:67
asynchronously read events and push them in a historicalSurface
Definition: vSurfaceHandlerTh.h:322
static unsigned int max_stamp
the maximum value of the timestamp before a wrap occurs
Definition: vtsHelper.h:39
a surface that can be queried at any time in the past.
Definition: vWindow_adv.h:224
void setQLimit(unsigned int number_of_qs)
set the maximum number of qs that can be stored in the buffer. A value of 0 keeps all qs...
Definition: vSurfaceHandlerTh.h:159
ev::vQueue * read(yarp::os::Stamp &yarpstamp)
ask for a pointer to the next vQueue. Blocks if no data is ready.
Definition: vSurfaceHandlerTh.h:113
double queryDelayT()
ask for the total time spanned by all vQueues.
Definition: vSurfaceHandlerTh.h:184
static double vtsscaler
a multiplier to convert seconds to an event timestamp
Definition: vtsHelper.h:43
unsigned int queryDelayN()
ask for the number of events in all vQueues.
Definition: vSurfaceHandlerTh.h:178
double queryRate()
ask for the high precision event rate
Definition: vSurfaceHandlerTh.h:190
void releaseDataLock()
unBlocks the blocking call in getNextQ. Useful to ensure a graceful shutdown. No guarantee the return...
Definition: vSurfaceHandlerTh.h:166
a spatio-temporal surface storing events for a limited time
Definition: vWindow_adv.h:123
an asynchronous reading port that accepts vBottles and decodes them
Definition: vSurfaceHandlerTh.h:36
store events for a fixed amount of time in a vQueue
Definition: vWindow_basic.h:88
const T * read(yarp::os::Stamp &yarpstamp, bool wait=true)
ask for a pointer to the next vQueue. if wait is true Blocks if no data is ready. ...
Definition: vPort.h:498
int queryunprocessed()
ask for the number of vQueues currently allocated.
Definition: vSurfaceHandlerTh.h:172
automatically accept events from a port and push them into a vTempWindow
Definition: vSurfaceHandlerTh.h:585