vrpn  07.33
Virtual Reality Peripheral Network
vrpn_Imager_Stream_Buffer.C
Go to the documentation of this file.
1 #include <stdio.h> // for fprintf, NULL, stderr, etc
2 
3 #include "vrpn_BaseClass.h" // for ::vrpn_TEXT_ERROR, etc
5 
7  const char *name, const char *imager_server_name, vrpn_Connection *c)
10  name, c, 0, 0) // Default number of rows and columns for the device.
11  , d_logging_thread(NULL)
12  , d_imager_server_name(NULL)
13 {
14  // Copy the name of the server we are to connect to when we are logging.
15  d_imager_server_name = new char[strlen(imager_server_name) + 1];
16  if (d_imager_server_name == NULL) {
17  fprintf(stderr, "vrpn_Imager_Stream_Buffer::vrpn_Imager_Stream_Buffer: "
18  "Out of memory\n");
19  d_connection = NULL;
20  return;
21  }
22  strcpy(d_imager_server_name, imager_server_name);
23 
24  // Create the logging thread but do not run it yet.
25  vrpn_ThreadData td;
26  td.pvUD = this;
28  if (d_logging_thread == NULL) {
29  fprintf(stderr, "vrpn_Imager_Stream_Buffer::vrpn_Imager_Stream_Buffer: "
30  "can't create logging thread\n");
31  d_connection = NULL;
32  return;
33  }
34 
35  // Register a handler for the got first connection message.
38  if (got_first_connection_m_id == -1) {
39  fprintf(stderr, "vrpn_Imager_Stream_Buffer::vrpn_Imager_Stream_Buffer: "
40  "can't register got first connection type\n");
41  d_connection = NULL;
42  return;
43  }
46  vrpn_ANY_SENDER)) {
47  fprintf(stderr, "vrpn_Imager_Stream_Buffer::vrpn_Imager_Stream_Buffer: "
48  "can't register got first connection handler\n");
49  d_connection = NULL;
50  }
51 
52  // The base server class implements throttling for us, so we could just go
53  // ahead
54  // and try to send the messages all the time using the normal frame
55  // begin/end and
56  // region routines. If we do this, though, we're going to have to unpack
57  // and repack
58  // all of the messages. If we re-implement the throttling code, then we can
59  // just
60  // watch the packets as they go by and see what types they are, discarding
61  // as
62  // appropriate (but we still have to queue and watch them).
63  // If we implement the throttling
64  // code down in the thread that listens to the server, we can avoid putting
65  // them into the queue at all. In that case, there can be a frame or more
66  // in
67  // the queue that would drain even after the throttle message was received.
68  // We can subtract the number of frames in the buffer from the request if
69  // the
70  // request is large enough, thus removing the problem, but it won't work for
71  // the common case of requesting 0 or 1 frames. This will work in the
72  // steady
73  // state, where a sender requests one more each time it gets one, but there
74  // will be an initial bolus of images.
75  // Nonetheless, this seems like the cleanest solution. So, we will
76  // install
77  // a handler for the throttling message that will pass it on down to the
78  // thread
79  // that is baby-sitting the server object.
82  vrpn_ANY_SENDER)) {
83  fprintf(stderr, "vrpn_Imager_Stream_Buffer::vrpn_Imager_Stream_Buffer: "
84  "can't register throttle handler\n");
85  d_connection = NULL;
86  }
87 }
88 
90 {
93  delete[] d_imager_server_name;
94  d_imager_server_name = NULL;
95  }
96 }
97 
99 {
100  // Required from all servers
101  server_mainloop();
102 
103  // See if we have a new image description from a logging thread. If so,
104  // fill in our values and send a description to any attached clients.
105  const char *channelBuffer = NULL;
107  d_nChannels, &channelBuffer)) {
108  int i;
109  const char *bufptr = channelBuffer;
110  for (i = 0; i < d_nChannels; i++) {
111  d_channels[i].unbuffer(&bufptr);
112  }
113  delete[] const_cast<char *>(channelBuffer);
115  }
116 
117  // See if we have any messages waiting in the queue from the logging thread.
118  // If we do, get an initial count and then send that many messages to the
119  // client. Don't go looking again this iteration or we may never return --
120  // the server is quite possibly packing frames faster than we can send them.
121  // Note that the messages in the queue have already been transcoded for our
122  // and sender ID.
124  if (count) {
125  unsigned i;
126  for (i = 0; i < count; i++) {
127  // Read the next message from the queue.
130  fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): Could "
131  "not retrieve message from queue\n");
132  break;
133  }
134 
135  // Decrement the in-buffer frame count whenever we see a begin_frame
136  // message. This will un-block the way for later frames to be
137  // buffered.
138  if (p.type == d_begin_frame_m_id) {
140  }
141 
142  // Pack and send the message to the client, then delete the buffer
143  // associated with the message. Send them all reliably. Send them
144  // all using our sender ID.
146  d_sender_id, p.buffer,
147  vrpn_CONNECTION_RELIABLE) != 0) {
148  fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): Could "
149  "not pack message\n");
150  break;
151  }
152  delete[] const_cast<char *>(p.buffer);
153  }
154  }
155 }
156 
157 /* static */
158 // This method just passes the call on to the virtual function.
160  void *userdata, vrpn_HANDLERPARAM p)
161 {
163  static_cast<vrpn_Imager_Stream_Buffer *>(userdata);
165  return 0;
166 }
167 
168 // Handle got first connection request by (having the second thread) create
169 // a connection to the server and waiting until we get a description message
170 // from the imager server we're listening to. Timeout after a while if the
171 // connection cannot be made or the server does not respond.
172 
174 {
175  // There should be no thread in existence when this call is made.
176  // If there is, kill it and complain.
177  if (d_logging_thread->running()) {
178  struct timeval now;
179  vrpn_gettimeofday(&now, NULL);
181  "handle_got_first_connection: Thread running when it should not be",
182  now, vrpn_TEXT_ERROR);
184  return;
185  }
186 
187  // Reset the shared state before starting the thread running.
189 
190  // Create a thread whose userdata points at the object that
191  // created it. Then call the start function on that thread and wait
192  // for its vrpn_Imager_Remote to receive the info from the remote server
193  // it has connected to. We time out after a few seconds if we don't
194  // get the response, leaving us with a presumably broken connection
195  // to the server.
196  if (!d_logging_thread->go()) {
197  struct timeval now;
198  vrpn_gettimeofday(&now, NULL);
200  "handle_got_first_connection: Failed to start logging thread", now,
202  delete d_logging_thread;
203  d_logging_thread = NULL;
204  return;
205  }
206  struct timeval start, now;
207  vrpn_gettimeofday(&start, NULL);
208  do {
209  const char *channelBuffer = NULL;
211  d_nRows, d_nCols, d_nDepth, d_nChannels, &channelBuffer)) {
212  int i;
213  const char *bufptr = channelBuffer;
214  for (i = 0; i < d_nChannels; i++) {
215  d_channels[i].unbuffer(&bufptr);
216  }
217  delete[] const_cast<char *>(channelBuffer);
218  return;
219  }
220 
221  vrpn_SleepMsecs(1);
222  vrpn_gettimeofday(&now, NULL);
223  } while (vrpn_TimevalDiff(now, start).tv_sec < 3);
224 
225  // Timed out, so we won't be hearing from the server!
226  vrpn_gettimeofday(&now, NULL);
227  send_text_message("handle_got_first_connection: Didn't hear from server.",
228  now, vrpn_TEXT_WARNING);
229 }
230 
231 // Handle dropped last connection on our primary connection by shutting down the
232 // connection to the imager server (killing the logging thread).
234 {
235  if (!stop_logging_thread()) {
236  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_dropped_last_"
237  "connection(): Had to kill logging thread\n");
238  }
239 }
240 
241 // Handles a throttle request by passing it on down to the non-blocking
242 // thread to deal with.
244  void *userdata, vrpn_HANDLERPARAM p)
245 {
246  const char *bufptr = p.buffer;
248  static_cast<vrpn_Imager_Stream_Buffer *>(userdata);
249 
250  // Get the requested number of frames from the buffer
251  vrpn_int32 frames_to_send;
252  if (vrpn_unbuffer(&bufptr, &frames_to_send)) {
253  return -1;
254  }
255 
256  me->d_shared_state.set_throttle_request(frames_to_send);
257  return 0;
258 }
259 
260 // The function that is called to become the logging thread. It is passed
261 // a pointer to "this" so that it can acces the object that created it.
262 // The static function basically just unpacks the "this" pointer and
263 // then calls the non-static function.
265  vrpn_ThreadData &threadData)
266 {
268  static_cast<vrpn_Imager_Stream_Buffer *>(threadData.pvUD);
269  me->logging_thread_func();
270 }
271 
272 // Note that it must use semaphores to get at the data that will be shared
273 // between the main thread and itself.
274 // This function does all the work of the logging thread, including all
275 // interactions with the vrpn_Imager_Server connection(s) and the client
276 // object; it forwards information both ways to the main thread that is
277 // communicating with the end-user client connection.
278 // DO NOT CALL VRPN message sends on the client object's connection from
279 // this function or those it calls, because we're not the thread that is
280 // connected to the client object's connection and such calls are not
281 // thread-safe.
282 // Instead, pass the data needed to make the calls to the initial thread.
284 {
285  // Initialize everything to a clean state.
286  d_log_connection = NULL;
287  d_imager_remote = NULL;
288  d_server_dropped_due_to_throttle = 0; // None dropped yet!
289  d_server_frames_to_send = -1; // Send as many as you get
290 
291  // Open a connection to the server object, not asking it to log anything.
292  // (Logging will be started later if we receive a message from our client.)
293  // Create a vrpn_Imager_Remote object and set its callbacks to fill things
294  // into the shared data structures.
295  d_log_connection = open_new_log_connection("", "", "", "");
296  if (d_log_connection == NULL) {
297  fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): "
298  "Cannot open connection\n");
299  return;
300  }
302  fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): "
303  "Cannot set up handlers\n");
304  return;
305  }
306 
307  // Keep doing mainloop() on the client object(s) and checking
308  // for commands that we're supposed to issue until we're
309  // told that we're supposed to die. Sleep a little between iterations
310  // to avoid eating CPU time.
311  while (!d_shared_state.time_to_exit()) {
312  // Check to see if the client has sent a new throttling message.
313  // If so, then adjust our state accordingly. Note that we are
314  // duplicating
315  // the effort of the vrpn_Imager_Server base class here; it will still
316  // be
317  // keeping its own shadow copy of these values, but will not be doing
318  // anything
319  // with them because we'll never be calling its send routines. This
320  // duplicates
321  // the code in the handle_throttle_message() in the vrpn_Imager_Server
322  // base
323  // class.
324  vrpn_int32 frames_to_send;
325  if (d_shared_state.get_throttle_request(&frames_to_send)) {
326  // If the requested number of frames is negative, then we set
327  // for unbounded sending. The next time a begin_frame message
328  // arrives, it will start the process going again.
329  if (frames_to_send < 0) {
331 
332  // If we were sending continuously, store the number of frames
333  // to send. Decrement by the number of frames already in the
334  // outgoing buffer, but don't let the number go below zero.
335  }
336  else if (d_server_frames_to_send == -1) {
337  int frames_in_queue = d_shared_state.get_frames_in_queue();
338  if (frames_to_send >= frames_in_queue) {
339  frames_to_send -= frames_in_queue;
340  }
341  d_server_frames_to_send = frames_to_send;
342 
343  // If we already had a throttle limit set, then increment it
344  // by the count.
345  }
346  else {
347  d_server_frames_to_send += frames_to_send;
348  }
349  }
350 
351  // Check to see if we've been asked to create new log files. If we
352  // have,
353  // then attempt to do so. If that works, pass back the names of the
354  // files
355  // created to the initial thread. If it did not work, return empty
356  // log-file
357  // names.
358  char *lil, *lol, *ril, *rol;
359  if (d_shared_state.get_logfile_request(&lil, &lol, &ril, &rol)) {
360  if (make_new_logging_connection(lil, lol, ril, rol)) {
361  d_shared_state.set_logfile_result(lil, lol, ril, rol);
362  }
363  else {
364  d_shared_state.set_logfile_result("", "", "", "");
365  }
366  // Delete the allocated space only if there were return values.
367  delete[] lil;
368  delete[] lol;
369  delete[] ril;
370  delete[] rol;
371  }
372 
373  // Handle all of the messages coming from the server.
374  if (d_imager_remote) {
376  }
377  if (d_log_connection) {
380  }
381 
382  vrpn_SleepMsecs(1);
383  }
384 
385  // Now that we've been told to die, clean up everything and return.
386  if (d_imager_remote) {
387  delete d_imager_remote;
388  d_imager_remote = NULL;
389  }
390  if (d_log_connection) {
392  d_log_connection = NULL;
393  }
394 }
395 
396 /* static */
397 int VRPN_CALLBACK
400 {
402  static_cast<vrpn_Imager_Stream_Buffer *>(pvISB);
403  return me->handle_server_messages(p);
404 }
405 
407  const vrpn_HANDLERPARAM &p)
408 {
409  // Handle begin_frame message very specially, because it has all sorts
410  // of interactions with throttling and missed-frame reporting.
411  if (p.type == d_server_begin_frame_m_id) {
412  // This duplicates code from the send_begin_frame() method in
413  // the vrpn_Imager_Server base class that handles throttling.
414  // It further adds code to handle throttling when the queue to
415  // the initial thread is too full.
416 
417  // If we are throttling frames and the frame count has gone to zero,
418  // then increment the number of frames missed and do not add this
419  // message to the queue.
420  if (d_server_frames_to_send == 0) {
422  return 0;
423  }
424 
425  // If there are too many frames in the queue already,
426  // add one to the number lost due to throttling (which
427  // will prevent region and end-of-frame messages until the next
428  // begin_frame message) and break without forwarding the message.
429  if (d_shared_state.get_frames_in_queue() >= 2) {
431  return 0;
432  }
433 
434  // If we missed some frames due to throttling, but are now
435  // sending frames again, report how many we lost due to
436  // throttling. This is incremented both for client-requested
437  // throttling and to queue-overflow throttling.
439  // We create a new message header and body, using the server's
440  // type IDs, and then transcode and send the message through
441  // the initial connection.
442  vrpn_HANDLERPARAM tp = p;
443  vrpn_float64
444  fbuf[vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64)];
445  char *msgbuf = (char *)fbuf;
446  int buflen = sizeof(fbuf);
448 
449  if (vrpn_buffer(&msgbuf, &buflen,
451  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
452  "messages: Can't pack count\n");
453  return -1;
454  }
455  tp.buffer = static_cast<char *>(static_cast<void *>(fbuf));
456  tp.payload_len = sizeof(fbuf) - buflen;
457 
458  if (!transcode_and_send(tp)) {
459  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
460  "messages: Can't send discarded frames "
461  "count\n");
462  return -1;
463  }
464 
466  }
467 
468  // If we are throttling, then decrement the number of frames
469  // left to send.
470  if (d_server_frames_to_send > 0) {
472  }
473 
474  // No throttling going on, so add the message to the outgoing queue and
475  // also increment the count of how many outstanding frames are in the
476  // queue.
477  if (!transcode_and_send(p)) {
478  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
479  " Can't transcode and send\n");
480  return -1;
481  }
483 
484  // Handle the end_frame and all of the region messages in a similar
485  // manner,
486  // dropping them if throttling is going on and passing them on if not.
487  // This duplicates code from the send_end_frame() and the region
488  // send methods in the vrpn_Imager_Server base class that handles
489  // throttling.
490  }
491  else if ((p.type == d_server_end_frame_m_id) ||
492  (p.type == d_server_regionu8_m_id) ||
494  (p.type == d_server_regionu16_m_id) ||
495  (p.type == d_server_regionf32_m_id)) {
496 
497  // If we are discarding frames, do not queue this message.
499  return 0;
500  }
501 
502  // No throttling going on, so add this message to the outgoing queue.
503  if (!transcode_and_send(p)) {
504  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
505  " Can't transcode and send\n");
506  return -1;
507  }
508 
509  // Send these messages on without modification
510  // (Currently, these are description messages and discarded-frame
511  // messages. It also includes the generic pong response and any
512  // text messages.)
513  }
514  else if ((p.type == d_server_description_m_id) ||
516  (p.type == d_server_text_m_id) || (p.type == d_server_pong_m_id)) {
517  if (!transcode_and_send(p)) {
518  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
519  " Can't transcode and send\n");
520  return -1;
521  }
522 
523  // Ignore these messages without passing them on.
524  }
525  else if ((p.type == d_server_ping_m_id)) {
526  return 0;
527 
528  // We need to throw a warning here on unexpected types so that we get
529  // some
530  // warning if additional messages are added. This code is fragile
531  // because it
532  // relies on us knowing the types of base-level and imager messages and
533  // catching
534  // them all. This way, at least we'll know if we miss one.
535  }
536  else {
537  // We create a new message header and body, using the server's
538  // type IDs, and then transcode and send the message through
539  // the initial connection. This is a text message saying that we
540  // got a message of unknown type.
541  vrpn_HANDLERPARAM tp = p;
542  char buffer[2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN];
543  char msg[vrpn_MAX_TEXT_LEN];
545  tp.buffer = buffer;
546  tp.payload_len = sizeof(buffer);
547  sprintf(msg, "Unknown message type from server: %d",
548  static_cast<int>(p.type));
550  if (!transcode_and_send(tp)) {
551  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
552  " Can't transcode text message\n");
553  return -1;
554  }
555  }
556 
557  return 0;
558 }
559 
560 // Transcode the sender and type fields from the logging server connection to
561 // the initial client connection and pack the resulting message into the queue
562 // from the logging thread to the initial thread. The data buffer is copied;
563 // this space is allocated by the logging thread and must be freed by the
564 // initial thread.
565 // Returns true on success and false on failure. The sender is set to the
566 // d_sender_id of our server object.
568 {
569  // Copy the contents of the buffer to a newly-allocated one that will be
570  // passed to the initial thread.
571  char *newbuf = new char[p.payload_len];
572  if (newbuf == NULL) {
573  fprintf(
574  stderr,
575  "vrpn_Imager_Stream_Buffer::transcode_and_send(): Out of memory\n");
576  return false;
577  }
578  memcpy(newbuf, p.buffer, p.payload_len);
579 
580  // Copy the contents of the handlerparam to a newly-allocated one that will
581  // be passed to the initial thread. Change the sender to match ours, set
582  // the
583  // buffer pointer to the new buffer, and transcode the type.
584  vrpn_HANDLERPARAM newp = p;
585  newp.buffer = newbuf;
586  newp.sender = d_sender_id;
587  newp.type = transcode_type(p.type);
588  if (newp.type == -1) {
589  fprintf(stderr, "vrpn_Imager_Stream_Buffer::transcode_and_send(): "
590  "Unknown type (%d)\n",
591  static_cast<int>(p.type));
592  delete[] newbuf;
593  return false;
594  }
595 
596  // Add the message to the queue of messages going to the initial thread.
598  fprintf(stderr, "vrpn_Imager_Stream_Buffer::transcode_and_send(): "
599  "Can't queue message\n");
600  return false;
601  }
602 
603  return true;
604 }
605 
606 // Transcode the type from the logging thread's connection type to
607 // the initial thread's connection type. Return -1 if we don't
608 // recognize the type.
609 vrpn_int32 vrpn_Imager_Stream_Buffer::transcode_type(vrpn_int32 type)
610 {
612  return d_description_m_id;
613  }
614  else if (type == d_server_begin_frame_m_id) {
615  return d_begin_frame_m_id;
616  }
617  else if (type == d_server_end_frame_m_id) {
618  return d_end_frame_m_id;
619  }
620  else if (type == d_server_discarded_frames_m_id) {
622  }
623  else if (type == d_server_regionu8_m_id) {
624  return d_regionu8_m_id;
625  }
626  else if (type == d_server_regionu12in16_m_id) {
627  return d_regionu12in16_m_id;
628  }
629  else if (type == d_server_regionu16_m_id) {
630  return d_regionu16_m_id;
631  }
632  else if (type == d_server_regionf32_m_id) {
633  return d_regionf32_m_id;
634  }
635  else if (type == d_server_text_m_id) {
636  return d_text_message_id;
637  }
638  else if (type == d_server_ping_m_id) {
639  return d_ping_message_id;
640  }
641  else if (type == d_server_pong_m_id) {
642  return d_pong_message_id;
643  }
644  else {
645  return -1;
646  }
647 }
648 
650  const char *local_in_logfile_name, const char *local_out_logfile_name,
651  const char *remote_in_logfile_name, const char *remote_out_logfile_name)
652 {
653  vrpn_Connection *ret = NULL;
654 
655  // Find the relevant part of the name (skip past last '@'
656  // if there is one); also find the port number.
657  const char *cname = d_imager_server_name;
658  const char *where_at; // Part of name past last '@'
659  if ((where_at = strrchr(cname, '@')) != NULL) {
660  cname = where_at + 1; // Chop off the front of the name
661  }
662 
663  // Pass "true" to force_connection so that it will open a new
664  // connection even if we already have one with that name.
666  where_at, local_in_logfile_name, local_out_logfile_name,
667  remote_in_logfile_name, remote_out_logfile_name, NULL, true);
668  if (!ret || !ret->doing_okay()) {
669  struct timeval now;
670  vrpn_gettimeofday(&now, NULL);
671  fprintf(stderr, "vrpn_Imager_Stream_Buffer::open_new_log_connection: "
672  "Could not create connection (files already exist?)");
673  if (ret) {
674  delete ret;
675  return NULL;
676  }
677  }
678 
679  return ret;
680 }
681 
683  vrpn_Connection *c)
684 {
685  // Create a vrpn_Imager_Remote on this connection and set its callbacks so
686  // that they will do what needs doing; the callbacks point to the
687  // Imager_Stream_Buffer object, not to the imager_remote object; access it
688  // through the member variable pointer.
690  if (d_imager_remote == NULL) {
691  fprintf(stderr, "vrpn_Imager_Stream_Buffer::setup_handlers_for_logging_"
692  "connection(): Cannot create vrpn_Imager_Remote\n");
693  return false;
694  }
697 
698  // Figure out the remote type IDs from the server for the messages we want
699  // to forward. This is really dangerous, because we need to make sure to
700  // explicitly list all the ones we might need. If we forget an important
701  // one (or it gets added later to either the base class or the imager class)
702  // then it won't get forwarded.
704  c->register_message_type("vrpn_Imager Description");
706  c->register_message_type("vrpn_Imager Begin_Frame");
707  d_server_end_frame_m_id = c->register_message_type("vrpn_Imager End_Frame");
709  c->register_message_type("vrpn_Imager Discarded_Frames");
710  d_server_regionu8_m_id = c->register_message_type("vrpn_Imager Regionu8");
711  d_server_regionu16_m_id = c->register_message_type("vrpn_Imager Regionu16");
713  c->register_message_type("vrpn_Imager Regionu12in16");
714  d_server_regionf32_m_id = c->register_message_type("vrpn_Imager Regionf32");
715  d_server_text_m_id = c->register_message_type("vrpn_Base text_message");
716  d_server_ping_m_id = c->register_message_type("vrpn_Base ping_message");
717  d_server_pong_m_id = c->register_message_type("vrpn_Base pong_message");
718 
719  // Set up handlers for the other messages from the server so that they can
720  // be passed on up to the initial thread and on to the client as
721  // appropriate.
722  // Be sure to strip the "@" part off the device name before registering the
723  // sender
724  // so that it is the same as the one used by the d_imager_remote.
725  c->register_handler(
728 
729  return true;
730 }
731 
733  vrpn_Connection *c)
734 {
735  if (!d_imager_remote) {
736  fprintf(stderr, "vrpn_Imager_Stream_Buffer::teardown_handlers_for_"
737  "logging_connection(): No imager remote\n");
738  return false;
739  }
741  this, handle_image_description) != 0) {
742  fprintf(stderr, "vrpn_Imager_Stream_Buffer::teardown_handlers_for_"
743  "logging_connection(): Cannot unregister handler\n");
744  return false;
745  }
746 
747  // Tear down handlers for the other messages from the server.
751 
752  delete d_imager_remote;
753  d_imager_remote = NULL;
754  return true;
755 }
756 
758  const char *local_in_logfile_name, const char *local_out_logfile_name,
759  const char *remote_in_logfile_name, const char *remote_out_logfile_name)
760 {
761  // Open a new connection to do logging on before deleting the old one so
762  // that we keep at least one connection open to the server at all time.
763  // This will prevent it from doing its "dropped last connection" things
764  // which will include resetting the imager server.
765  vrpn_Connection *new_log_connection = open_new_log_connection(
766  local_in_logfile_name, local_out_logfile_name, remote_in_logfile_name,
767  remote_out_logfile_name);
768  if (new_log_connection == NULL) {
769  fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
770  "connection(): Cannot open connection\n");
771  return false;
772  }
773 
774  // Unhook the callbacks from the existing logging connection so that
775  // we don't end up with two callbacks for each message.
777  fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
778  "connection(): Cannot teardown connection\n");
779  return false;
780  }
781 
782  // Hook the callbacks up to the new connection so that we will get reports
783  // from the server.
784  if (!setup_handlers_for_logging_connection(new_log_connection)) {
785  fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
786  "connection(): Cannot setup connection\n");
787  return false;
788  }
789 
790  // Mainloop the new connection object until it becomes connected or we
791  // time out. If we time out, then put things back on the old connection
792  // and tell the thread it is time to self-destruct. The way we check
793  // for connected cannot be just that the connection's connected() method
794  // returns true (because our end can be marked connected before the other
795  // end decides it has complete the connection. Rather, we check to see
796  // that we've got a new description report from the server -- indicating
797  // that it has seen the new report. This also lets us know that the old
798  // log file will have accumulated all images up to the new report, so we
799  // can shut it off without losing any images in the switch to the new
800  // log file (there may be duplicates, but not losses).
801  struct timeval start, now;
802  vrpn_gettimeofday(&start, NULL);
803  now = start;
806  (vrpn_TimevalDiff(now, start).tv_sec < 3)) {
807  new_log_connection->mainloop(); // Enable connection set-up to occur
808  new_log_connection->save_log_so_far();
809  d_log_connection->mainloop(); // Eat up (and log) any incoming messages
811  vrpn_gettimeofday(&now, NULL);
812  vrpn_SleepMsecs(1);
813  };
815  fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
816  "connection(): Could not connect new logging "
817  "connection\n");
818  teardown_handlers_for_logging_connection(new_log_connection);
820  new_log_connection->removeReference();
822  return false;
823  }
824 
825  // Delete the old connection object by reducing its reference count.
827 
828  // Set up to use the new connection
829  d_log_connection = new_log_connection;
830  return true;
831 }
832 
834  const char *local_in_logfile_name, const char *local_out_logfile_name,
835  const char *remote_in_logfile_name, const char *remote_out_logfile_name)
836 {
837  // Request that the logging thread start new logs.
839  local_in_logfile_name, local_out_logfile_name, remote_in_logfile_name,
840  remote_out_logfile_name);
841 
842  // Wait until we hear back from the logging thread or time out;
843  // return empty if timeout and the strings we got back if not.
844  // Remember to deallocated the memory if we got a response.
845  struct timeval start, now;
846  vrpn_gettimeofday(&start, NULL);
847  do {
848  char *lil, *lol, *ril, *rol;
849  if (d_shared_state.get_logfile_result(&lil, &lol, &ril, &rol)) {
850  send_report_logging(lil, lol, ril, rol);
851  delete[] lil;
852  delete[] lol;
853  delete[] ril;
854  delete[] rol;
855  return;
856  }
857  vrpn_SleepMsecs(1);
858  vrpn_gettimeofday(&now, NULL);
859  } while (vrpn_TimevalDiff(now, start).tv_sec < 2);
860 
861  // Timeout, report failure of logging by saying that there are empty log
862  // file names.
863  send_report_logging("", "", "", "");
864 }
865 
867 {
868  char *local_in;
869  char *local_out;
870  char *remote_in;
871  char *remote_out;
872  d_shared_state.get_logfile_names(&local_in, &local_out, &remote_in,
873  &remote_out);
874  send_report_logging(local_in, local_out, remote_in, remote_out);
875  if (local_in) delete[] local_in;
876  if (local_out) delete[] local_out;
877  if (remote_in) delete[] remote_in;
878  if (remote_out) delete[] remote_out;
879 }
880 
881 /* Static */
882 // We've gotten a new imager description, so fill it into the shared data
883 // structure
884 // so that the parent object can hear about it.
886  void *pvISB, const struct timeval msg_time)
887 {
889  static_cast<vrpn_Imager_Stream_Buffer *>(pvISB);
890 
891  // Pack up description messages for all of the channels into a buffer that
892  // is at
893  // least large enough to hold them all.
894  // msgbuf must be float64-aligned!
895  vrpn_float64 *fbuf =
896  new vrpn_float64[vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64)];
897  char *buffer = static_cast<char *>(static_cast<void *>(fbuf));
898  if (buffer == NULL) {
899  fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_image_description():"
900  " Out of memory\n");
901  me->d_shared_state.time_to_exit(true);
902  return;
903  }
904  int i;
905  char *bufptr = buffer;
906  vrpn_int32 buflen = sizeof(vrpn_float64) * vrpn_CONNECTION_TCP_BUFLEN /
907  sizeof(vrpn_float64);
908  for (i = 0; i < me->d_imager_remote->nChannels(); i++) {
909  me->d_imager_remote->channel(i)->buffer(&bufptr, &buflen);
910  }
911 
915  buffer);
916 
917  // We've gotten a description report on the new connection, so we're ready
918  // to drop the old connection.
920 }
921 
922 // Stop the logging thread function, cleanly if possible. Returns true if
923 // the function stopped cleanly, false if it had to be killed.
925 {
926  // Set the flag telling the logging thread to stop.
928 
929  // Wait for up to three seconds for the logging thread to die a clean death.
930  // If it does, return true.
931  struct timeval start, now;
932  vrpn_gettimeofday(&start, NULL);
933  do {
934  if (!d_logging_thread->running()) {
935  return true;
936  }
937  vrpn_SleepMsecs(1);
938  vrpn_gettimeofday(&now, NULL);
939  } while (vrpn_TimevalDiff(now, start).tv_sec < 3);
940 
942  return false;
943 }
virtual int mainloop(const struct timeval *timeout=NULL)=0
Call each time through program main loop to handle receiving any incoming messages and sending any pa...
virtual int unregister_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
const int vrpn_ANY_TYPE
vrpn_ANY_TYPE can be used to register callbacks for any USER type of message from a given sender...
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
bool set_imager_description(vrpn_int32 nRows, vrpn_int32 nCols, vrpn_int32 nDepth, vrpn_int32 nChannels, const char *channelBuffer)
vrpn_int32 d_nRows
Definition: vrpn_Imager.h:125
virtual void handle_got_first_connection(void)
const char * vrpn_got_first_connection
These are the strings that define the system-generated message types that tell when connections are r...
struct timeval msg_time
void vrpn_SleepMsecs(double dMsecs)
Definition: vrpn_Shared.C:157
bool send_report_logging(const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name)
VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
Utility routine for taking a struct timeval from a buffer that was sent as a message.
Definition: vrpn_Shared.C:312
static void static_logging_thread_func(vrpn_ThreadData &threadData)
static int encode_text_message_to_buffer(char *buf, vrpn_TEXT_SEVERITY severity, vrpn_uint32 level, const char *msg)
Encodes the body of the text message into a buffer, preparing for sending.
vrpn_int32 nCols(void) const
Definition: vrpn_Imager.h:123
vrpn_int32 d_regionf32_m_id
Definition: vrpn_Imager.h:152
vrpn_int32 d_begin_frame_m_id
Definition: vrpn_Imager.h:137
bool get_logfile_result(char **lil, char **lol, char **ril, char **rol)
const vrpn_uint32 vrpn_CONNECTION_RELIABLE
Classes of service for messages, specify multiple by ORing them together Priority of satisfying these...
vrpn_int32 nChannels(void) const
Definition: vrpn_Imager.h:125
vrpn_int32 payload_len
virtual vrpn_int32 register_sender(const char *name)
Get a token to use for the string name of the sender or type. Remember to check for -1 meaning failur...
void set_logfile_result(const char *lil, const char *lol, const char *ril, const char *rol)
vrpn_Imager_Channel d_channels[vrpn_IMAGER_MAX_CHANNELS]
Definition: vrpn_Imager.h:132
vrpn_int32 nRows(void) const
Definition: vrpn_Imager.h:122
vrpn_int32 d_text_message_id
ID for text messages.
vrpn_int32 d_pong_message_id
Server telling that it is there.
vrpn_int32 d_throttle_frames_m_id
Definition: vrpn_Imager.h:143
vrpn_int32 d_end_frame_m_id
Definition: vrpn_Imager.h:139
vrpn_int32 nDepth(void) const
Definition: vrpn_Imager.h:124
const char * buffer
static void VRPN_CALLBACK handle_image_description(void *pvISB, const struct timeval msg_time)
bool get_logfile_request(char **lil, char **lol, char **ril, char **rol)
bool buffer(char **insertPt, vrpn_int32 *buflen) const
Definition: vrpn_Imager.h:80
timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
Definition: vrpn_Shared.C:92
vrpn_int32 d_description_m_id
Definition: vrpn_Imager.h:135
virtual void mainloop(void)
XXX It could be nice to let the user specify separate callbacks for.
Definition: vrpn_Imager.C:1194
This is the class users deal with: it tells the format and the region data when it arrives...
Definition: vrpn_Imager.h:623
vrpn_int32 d_ping_message_id
Ask the server if they are there.
virtual vrpn_bool doing_okay(void) const
Returns vrpn_true if the connection is okay, vrpn_false if not.
vrpn_int32 d_regionu8_m_id
Definition: vrpn_Imager.h:145
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Generic connection class not specific to the transport mechanism.
virtual int register_description_handler(void *userdata, vrpn_IMAGERDESCRIPTIONHANDLER handler)
Register a handler for when the object&#39;s description changes (if desired).
Definition: vrpn_Imager.h:643
vrpn_int32 d_nDepth
Definition: vrpn_Imager.h:130
bool get_imager_description(vrpn_int32 &nRows, vrpn_int32 &nCols, vrpn_int32 &nDepth, vrpn_int32 &nChannels, const char **channelBuffer)
vrpn_Imager_Stream_Shared_State d_shared_state
#define VRPN_CALLBACK
virtual int register_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Set up (or remove) a handler for a message of a given type. Optionally, specify which sender to handl...
virtual void handle_dropped_last_connection(void)
vrpn_int32 d_nChannels
Definition: vrpn_Imager.h:131
vrpn_int32 d_nCols
Definition: vrpn_Imager.h:129
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
static int VRPN_CALLBACK static_handle_throttle_message(void *userdata, vrpn_HANDLERPARAM p)
vrpn_Connection * open_new_log_connection(const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name)
void set_logfile_request(const char *lil, const char *lol, const char *ril, const char *rol)
bool retrieve_logger_to_client_message(vrpn_HANDLERPARAM *p)
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
const vrpn_Imager_Channel * channel(unsigned chanNum) const
Accessors for the member variables: can be queried in the handler for object changes.
Definition: vrpn_Imager.C:1202
bool running()
Definition: vrpn_Shared.C:1308
int handle_server_messages(const vrpn_HANDLERPARAM &p)
vrpn_Connection * d_connection
Connection that this object talks to.
This structure is what is passed to a vrpn_Connection message callback.
bool make_new_logging_connection(const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name)
virtual int pack_message(vrpn_uint32 len, struct timeval time, vrpn_int32 type, vrpn_int32 sender, const char *buffer, vrpn_uint32 class_of_service)
Pack a message that will be sent the next time mainloop() is called. Turn off the RELIABLE flag if yo...
bool transcode_and_send(const vrpn_HANDLERPARAM &p)
vrpn_int32 d_regionu12in16_m_id
Definition: vrpn_Imager.h:147
bool send_description(void)
Sends a description of the imager so the remote can process the region messages.
Definition: vrpn_Imager.C:1045
int send_text_message(const char *msg, struct timeval timestamp, vrpn_TEXT_SEVERITY type=vrpn_TEXT_NORMAL, vrpn_uint32 level=0)
Sends a NULL-terminated text message from the device d_sender_id.
const unsigned vrpn_MAX_TEXT_LEN
static int VRPN_CALLBACK static_handle_got_first_connection(void *userdata, vrpn_HANDLERPARAM p)
char * vrpn_copy_service_name(const char *fullname)
bool insert_logger_to_client_message(const vrpn_HANDLERPARAM &p)
bool setup_handlers_for_logging_connection(vrpn_Connection *c)
static int VRPN_CALLBACK static_handle_server_messages(void *pvISB, vrpn_HANDLERPARAM p)
bool teardown_handlers_for_logging_connection(vrpn_Connection *c)
virtual void handle_request_logging(const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name)
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
vrpn_Imager_Stream_Buffer(const char *name, const char *imager_server_name, vrpn_Connection *c)
vrpn_Connection * vrpn_get_connection_by_name(const char *cname, const char *local_in_logfile_name, const char *local_out_logfile_name, const char *remote_in_logfile_name, const char *remote_out_logfile_name, const char *NIC_IPaddress, bool force_connection)
Create a client connection of arbitrary type (VRPN UDP/TCP, TCP, File, Loopback, MPI).
vrpn_int32 d_regionu16_m_id
Definition: vrpn_Imager.h:150
VRPN_API int vrpn_buffer(char **insertPt, vrpn_int32 *buflen, const timeval t)
Utility routine for placing a timeval struct into a buffer that is to be sent as a message...
Definition: vrpn_Shared.C:241
virtual int save_log_so_far()
Save any messages on any endpoints which have been logged so far.
void get_logfile_names(char **local_in, char **local_out, char **remote_in, char **remote_out)
const int vrpn_CONNECTION_TCP_BUFLEN
vrpn_int32 d_sender_id
Sender ID registered with the connection.
vrpn_int32 transcode_type(vrpn_int32 type)
void set_throttle_request(vrpn_int32 throttle_count)
virtual vrpn_int32 register_message_type(const char *name)
bool unbuffer(const char **buffer)
Definition: vrpn_Imager.h:96
virtual int unregister_description_handler(void *userdata, vrpn_IMAGERDESCRIPTIONHANDLER handler)
Definition: vrpn_Imager.h:649
bool get_throttle_request(vrpn_int32 *throttle_count)
const int vrpn_ANY_SENDER
vrpn_ANY_SENDER can be used to register callbacks on a given message type from any sender...
vrpn_int32 d_discarded_frames_m_id
Definition: vrpn_Imager.h:141